moxml 0.5.31 → 0.5.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 627111c496efca4687eabeeb78989d611d3d308adc5ed32443c7058da51c57c9
|
|
4
|
+
data.tar.gz: 86d1cc6c471d07084e0d2e0de9412b286b5486b202f9fe677b7417f514c1fe55
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f1108c98dc8af92913605dd08b84eb50bbfec92a5dd286e15656c3ecd57937ce56bc186c9393a740f6bd8a8316f06fb059db226f20ffc9f0b84cfb433e1a532d
|
|
7
|
+
data.tar.gz: 02d109a32659b30fc34ac5d6d3f01bc0d32d9e1ca6a38cfdc2fe3880a02c779ce5ff41fa2f0eda04adc2a9ccd6702f372d29b4d2a6fe33faeb0456cb8852d26b
|
|
@@ -74,10 +74,17 @@ so script bodies with markup characters round-trip through a strict
|
|
|
74
74
|
XML reparse. The adapter stands the entity-marker pipeline down for
|
|
75
75
|
HTML parses — the engine decodes HTML entities directly into text.
|
|
76
76
|
|
|
77
|
-
Measured on 1.9.105 (named-entity decoding fixed, engine #848):
|
|
77
|
+
Measured on 1.9.105+ (named-entity decoding fixed, engine #848):
|
|
78
78
|
parse 2.5-3.5x, serialize 4.1x, xpath 6-29x faster than Nokogiri —
|
|
79
79
|
uniformly, including entity-laden pages.
|
|
80
80
|
|
|
81
|
+
Since 1.9.118/1.9.121 the engine also handles WHATWG foreign
|
|
82
|
+
content — embedded SVG and MathML in HTML parse into the tree
|
|
83
|
+
(preserving `foreignObject` subtrees that Nokogiri drops), the
|
|
84
|
+
source HTML doctype round-trips, and `template`/`colgroup` place
|
|
85
|
+
correctly. Foreign-content camelCase names are currently
|
|
86
|
+
lowercased (engine conformance note leptris/leptris#1013).
|
|
87
|
+
|
|
81
88
|
Adapters without an engine HTML mode (Ox, Oga, REXML, LibXML) raise
|
|
82
89
|
`Moxml::AdapterError`; Nokogiri parses through libxml2's HTML mode.
|
|
83
90
|
|
|
@@ -64,6 +64,14 @@ module Moxml
|
|
|
64
64
|
texts << child
|
|
65
65
|
attachments.set(doc, :document_text, texts)
|
|
66
66
|
child
|
|
67
|
+
when ::Leptris::XML::Comment
|
|
68
|
+
# The tree model supports document comments (they parse
|
|
69
|
+
# and serialize, libleptris 1.9.3 #578) but the engine
|
|
70
|
+
# has no add entry yet (leptris/leptris#1032).
|
|
71
|
+
raise Moxml::NotImplementedError.new(
|
|
72
|
+
"Adding document-level comments requires an engine entry (leptris/leptris#1032)",
|
|
73
|
+
feature: "add_document_child", adapter: "Leptris",
|
|
74
|
+
)
|
|
67
75
|
else
|
|
68
76
|
raise Moxml::DocumentStructureError.new(
|
|
69
77
|
"Unsupported document child: #{child.class}",
|
|
@@ -33,7 +33,13 @@ module Moxml
|
|
|
33
33
|
# Entity restoration belongs to the wrapper layer
|
|
34
34
|
# (Node#to_xml runs adapter.restore_entities for every
|
|
35
35
|
# adapter); doing it here scanned the output a second time.
|
|
36
|
-
xml =
|
|
36
|
+
xml = if native_expand?(node, options)
|
|
37
|
+
opts = options.dup
|
|
38
|
+
opts[:__expand_handled_natively] = true
|
|
39
|
+
normalize_serialization(raw_serialize(node, opts), opts)
|
|
40
|
+
else
|
|
41
|
+
normalize_serialization(raw_serialize(node, options), options)
|
|
42
|
+
end
|
|
37
43
|
# The binding's FFI strings come back binary-tagged; the
|
|
38
44
|
# engine encoded the bytes per this option, so tag them.
|
|
39
45
|
xml.force_encoding(options[:encoding]) if options[:encoding]
|
|
@@ -47,7 +53,32 @@ module Moxml
|
|
|
47
53
|
|
|
48
54
|
# The binding's element face with all-default options — the
|
|
49
55
|
# frozen splat keeps the hot argless path allocation-free.
|
|
56
|
+
# Native expand-empty (engine #882, libleptris 1.9.95,
|
|
57
|
+
# bindings 1.9.144): empty elements emit <a></a> through a
|
|
58
|
+
# C-side ext entry — the Ruby full-output regex rewrite and
|
|
59
|
+
# its "/>" probe scan drop out of the element path. The
|
|
60
|
+
# document face does not expose the option yet; documents
|
|
61
|
+
# keep the Ruby pass.
|
|
62
|
+
EXPAND_EMPTY_NATIVE =
|
|
63
|
+
Gem::Version.new(::Leptris::VERSION) >= Gem::Version.new("1.9.144")
|
|
64
|
+
|
|
50
65
|
ELEMENT_DEFAULT_KWARGS = { indent: 0, no_decl: true, encoding: nil }.freeze
|
|
66
|
+
ELEMENT_EXPAND_KWARGS =
|
|
67
|
+
if EXPAND_EMPTY_NATIVE
|
|
68
|
+
{ indent: 0, no_decl: true, encoding: nil, expand_empty: true }.freeze
|
|
69
|
+
else
|
|
70
|
+
ELEMENT_DEFAULT_KWARGS
|
|
71
|
+
end.freeze
|
|
72
|
+
|
|
73
|
+
# Elements (not documents) with expand_empty and NO
|
|
74
|
+
# indent-unit string: the C ext entry handles expansion. The
|
|
75
|
+
# element unit serializer does not take the flag — those keep
|
|
76
|
+
# the Ruby pass.
|
|
77
|
+
def native_expand?(node, options)
|
|
78
|
+
EXPAND_EMPTY_NATIVE && options[:expand_empty] &&
|
|
79
|
+
!node.is_a?(::Leptris::XML::Document) &&
|
|
80
|
+
!options[:indent_text].is_a?(String)
|
|
81
|
+
end
|
|
51
82
|
|
|
52
83
|
def raw_serialize(node, options)
|
|
53
84
|
# CDATA must precede Text in this chain: CDATA < Text in the
|
|
@@ -79,10 +110,12 @@ module Moxml
|
|
|
79
110
|
# per call; the wrapper force-tags the string either way.
|
|
80
111
|
indent = options.fetch(:indent, 0)
|
|
81
112
|
encoding = options[:encoding] == "UTF-8" ? nil : options[:encoding]
|
|
113
|
+
native_expand = native_expand?(node, options)
|
|
82
114
|
xml = if indent.zero? && encoding.nil?
|
|
83
|
-
node.to_xml(**ELEMENT_DEFAULT_KWARGS)
|
|
115
|
+
node.to_xml(**(native_expand ? ELEMENT_EXPAND_KWARGS : ELEMENT_DEFAULT_KWARGS))
|
|
84
116
|
else
|
|
85
117
|
kwargs = { indent: indent, no_decl: true, encoding: encoding }
|
|
118
|
+
kwargs[:expand_empty] = true if native_expand
|
|
86
119
|
if INDENT_UNIT_SUPPORTED && options[:indent_text].is_a?(String)
|
|
87
120
|
kwargs[:indent_text] = options[:indent_text]
|
|
88
121
|
end
|
|
@@ -119,7 +152,9 @@ module Moxml
|
|
|
119
152
|
# The libxml2-layout serializer (>= 1.9.42) keeps attribute
|
|
120
153
|
# apostrophes literal; older engines escaped them.
|
|
121
154
|
needs_apos = !LIBXML2_LAYOUT_PARITY && xml.include?("'")
|
|
122
|
-
needs_expand = options[:expand_empty] &&
|
|
155
|
+
needs_expand = options[:expand_empty] &&
|
|
156
|
+
!options[:__expand_handled_natively] &&
|
|
157
|
+
xml.include?("/>")
|
|
123
158
|
# Corruption guards: the 1-char ampersand probe is ~1µs
|
|
124
159
|
# (memchr-class); the raw-< scan runs only on builds that
|
|
125
160
|
# still carry the parse race (leptris-ruby#131).
|
data/lib/moxml/version.rb
CHANGED
|
@@ -374,6 +374,37 @@ RSpec.describe Moxml::Adapter::Leptris do
|
|
|
374
374
|
expect(reparsed.errors).to be_empty
|
|
375
375
|
end
|
|
376
376
|
|
|
377
|
+
it "preserves foreign content (SVG/MathML) in HTML documents" do
|
|
378
|
+
doc = ctx.parse_html(%(<p>a</p><svg viewBox="0 0 1 1"><circle r="1"/></svg><math><mi>a</mi></math>))
|
|
379
|
+
body = doc.root.children.find(&:element?)
|
|
380
|
+
names = body.children.select(&:element?).map(&:name)
|
|
381
|
+
expect(names).to include("svg", "math")
|
|
382
|
+
expect(body.at_xpath(".//circle")["r"]).to eq("1")
|
|
383
|
+
expect(body.at_xpath(".//mi").text).to eq("a")
|
|
384
|
+
end
|
|
385
|
+
|
|
386
|
+
it "preserves foreignObject content Nokogiri drops (name lowercased)" do
|
|
387
|
+
# WHATWG keeps foreign-content camelCase (foreignObject,
|
|
388
|
+
# viewBox); the engine currently lowercases like it does HTML
|
|
389
|
+
# names, but preserves the subtree — Nokogiri drops it
|
|
390
|
+
# entirely. Pins current behavior; engine conformance note
|
|
391
|
+
# filed for the adjust-tables.
|
|
392
|
+
doc = ctx.parse_html(%(<svg><foreignObject><p>x</p></foreignObject></svg>))
|
|
393
|
+
out = doc.to_xml
|
|
394
|
+
expect(out).to include("<foreignobject>")
|
|
395
|
+
expect(out).to include("<p>x</p>")
|
|
396
|
+
end
|
|
397
|
+
|
|
398
|
+
it "round-trips an HTML doctype with external identifiers" do
|
|
399
|
+
doc = ctx.parse_html(%(<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "x.dtd"><p>a</p>))
|
|
400
|
+
expect(doc.to_xml).to include(%(<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "x.dtd">))
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
it "preserves template element placement" do
|
|
404
|
+
doc = ctx.parse_html(%(<div><template><p>t</p></template></div>))
|
|
405
|
+
expect(doc.at_xpath("//template/p")&.text).to eq("t")
|
|
406
|
+
end
|
|
407
|
+
|
|
377
408
|
it "round-trips a parsed tree through mutation" do
|
|
378
409
|
doc = ctx.parse_html(%(<ul><li>a<li>b</ul>))
|
|
379
410
|
doc.at_xpath("//ul").add_child(doc.create_element("li"))
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: moxml
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.5.
|
|
4
|
+
version: 0.5.32
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-09-
|
|
11
|
+
date: 2026-09-13 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: |
|
|
14
14
|
Moxml is a unified XML manipulation library that provides a common API
|