moxml 0.5.128 → 0.5.129
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 +4 -4
- data/lib/moxml/adapter/leptris/document_parts.rb +27 -5
- data/lib/moxml/adapter/nokogiri.rb +13 -0
- data/lib/moxml/version.rb +1 -1
- data/spec/consistency/html_doctype_spec.rb +44 -0
- data/spec/moxml/to_html_spec.rb +13 -3
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9f040a5d5fc3e041e15a54cc42aeb10ca5d3cc882ecc22ca97faa7d45cbbb2d5
|
|
4
|
+
data.tar.gz: de59fd55dd4eff18451ddec5a20f246325be236313a55d0aeb4cab5af56ce4f9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 668587c18c1bf04415c99e715460b1dece9f5619206d6a7fb1ffa8260a32be81f6faa20853eeca322b3b016523a589550711647362f03ff79f64ccc507ef8e03
|
|
7
|
+
data.tar.gz: 7ff1f188edb97ee6f36c0294745232f1e1a4078bd6e94d567d56e649c01f5939617b345ebed2fe5b21bf325b6be1863f68b5af1acfb7171bd63b0a4bef58bd2a
|
|
@@ -129,15 +129,37 @@ module Moxml
|
|
|
129
129
|
end
|
|
130
130
|
|
|
131
131
|
doctype = attachments.get(doc, :doctype)
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
132
|
+
if doctype.is_a?(CustomizedLeptris::Doctype)
|
|
133
|
+
parts << doctype.to_xml << "\n"
|
|
134
|
+
else
|
|
135
|
+
# #364: the same DOCTYPE renders ONCE — the attachment
|
|
136
|
+
# is the parse-time capture; native_doctype_xml re-reads
|
|
137
|
+
# the engine's own doctype, and both firing doubled the
|
|
138
|
+
# line (moxml#364's \n\n\n).
|
|
139
|
+
native = native_doctype_xml(doc)
|
|
140
|
+
parts << native << "\n" if native
|
|
141
|
+
end
|
|
136
142
|
|
|
137
143
|
# The libxml2-model document node: prolog PIs/comments, the
|
|
138
144
|
# root, epilog PIs/comments — in document order, so epilog
|
|
139
145
|
# parts serialize after the root (issue #130).
|
|
140
|
-
|
|
146
|
+
# #364: document-level WHITESPACE-only text is layout
|
|
147
|
+
# residue (the input's newline after the doctype), not
|
|
148
|
+
# content — the newline-terminated part shape already
|
|
149
|
+
# carries the separation; rendering the text node stacked
|
|
150
|
+
# a second (or third) newline.
|
|
151
|
+
doc.children.each do |child|
|
|
152
|
+
# #364: document-level WHITESPACE-only text is layout
|
|
153
|
+
# residue (the input's newline after the doctype) — the
|
|
154
|
+
# newline-terminated part shape already carries the
|
|
155
|
+
# separation. Binding nodes walk here (native children).
|
|
156
|
+
if child.is_a?(::Leptris::XML::Text) &&
|
|
157
|
+
child.content.match?(/\A\s*\z/)
|
|
158
|
+
next
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
parts << raw_serialize(child, options) << "\n"
|
|
162
|
+
end
|
|
141
163
|
|
|
142
164
|
texts = attachments.get(doc, :document_text)
|
|
143
165
|
texts&.each { |text| parts << XmlEmitter.escape_text(text.content.to_s) }
|
|
@@ -160,7 +160,20 @@ module Moxml
|
|
|
160
160
|
# malformed input never raises.
|
|
161
161
|
def parse_html(html, _options = {}, _context = nil)
|
|
162
162
|
html_string = html.is_a?(IO) || html.is_a?(StringIO) ? html.read : html.to_s
|
|
163
|
+
# #364: preserve-input-only — libxml2's HTML parser
|
|
164
|
+
# synthesizes an HTML 4.0 doctype when the input has none
|
|
165
|
+
# (leptris doesn't); dropping the synthesized node keeps
|
|
166
|
+
# the shared contract doctype-faithful. A doctype before
|
|
167
|
+
# the root (after optional BOM/xml-decl/whitespace) means
|
|
168
|
+
# the source had one.
|
|
169
|
+
had_doctype = html_string.match?(
|
|
170
|
+
/\A(\xEF\xBB\xBF|\s|<\?xml[^>]*\?>)*<!DOCTYPE/i,
|
|
171
|
+
)
|
|
163
172
|
native_doc = ::Nokogiri::HTML(html_string, &:huge)
|
|
173
|
+
if !had_doctype &&
|
|
174
|
+
(dtd = native_doc.children.find { |n| n.is_a?(::Nokogiri::XML::DTD) })
|
|
175
|
+
dtd.unlink
|
|
176
|
+
end
|
|
164
177
|
Wrappers::Document.new(native_doc, _context || Context.new(:nokogiri))
|
|
165
178
|
end
|
|
166
179
|
|
data/lib/moxml/version.rb
CHANGED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
# #364: DOCTYPE semantics converge across HTML-capable adapters —
|
|
6
|
+
# preserve-input-only (never synthesize: libxml2's HTML parser
|
|
7
|
+
# invents an HTML 4.0 doctype, which the nokogiri adapter drops),
|
|
8
|
+
# and the post-DOCTYPE separator is one newline (the leptris
|
|
9
|
+
# document-part walk used to stack the input's whitespace residue
|
|
10
|
+
# on top of the part newlines).
|
|
11
|
+
RSpec.describe "HTML DOCTYPE convergence (#364)" do
|
|
12
|
+
let(:body) do
|
|
13
|
+
"<html><head><title>t</title></head><body><p>x</p></body></html>"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
%i[leptris nokogiri].each do |adapter_name|
|
|
17
|
+
describe "on #{adapter_name}" do
|
|
18
|
+
let(:ctx) { Moxml.new(adapter_name) }
|
|
19
|
+
|
|
20
|
+
it "never synthesizes a DOCTYPE the input lacked" do
|
|
21
|
+
out = ctx.parse_html(body).to_s
|
|
22
|
+
expect(out).not_to include("<!DOCTYPE")
|
|
23
|
+
expect(out).to include("<html")
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it "preserves the input DOCTYPE with a single separator" do
|
|
27
|
+
out = ctx.parse_html("<!DOCTYPE html>\n#{body}").to_s
|
|
28
|
+
expect(out).to start_with("<!DOCTYPE html>\n")
|
|
29
|
+
expect(out.scan("<!DOCTYPE").size).to eq(1)
|
|
30
|
+
expect(out).not_to include("\n\n\n")
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it "preserves public/system identifiers verbatim" do
|
|
34
|
+
src =
|
|
35
|
+
'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" ' \
|
|
36
|
+
'"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">' \
|
|
37
|
+
"\n#{body}"
|
|
38
|
+
out = ctx.parse_html(src).to_s
|
|
39
|
+
expect(out).to include("XHTML 1.0 Strict")
|
|
40
|
+
expect(out.scan("<!DOCTYPE").size).to eq(1)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
data/spec/moxml/to_html_spec.rb
CHANGED
|
@@ -13,20 +13,30 @@ RSpec.describe "Moxml::Document#to_html" do
|
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
describe "nokogiri" do
|
|
16
|
+
# The oracle mirrors the adapter's #364 preserve-input-only
|
|
17
|
+
# parse: the synthesized HTML 4.0 doctype is dropped the same
|
|
18
|
+
# way, so pass-through equality stays meaningful.
|
|
19
|
+
let(:oracle) do
|
|
20
|
+
d = Nokogiri::HTML(src)
|
|
21
|
+
if (dtd = d.children.find { |n| n.is_a?(Nokogiri::XML::DTD) })
|
|
22
|
+
dtd.unlink
|
|
23
|
+
end
|
|
24
|
+
d
|
|
25
|
+
end
|
|
16
26
|
let(:doc) { Moxml.new(:nokogiri).parse_html(src) }
|
|
17
27
|
|
|
18
28
|
it "serializes the engine's HTML bytes by default" do
|
|
19
|
-
expect(doc.to_html).to eq(
|
|
29
|
+
expect(doc.to_html).to eq(oracle.to_html)
|
|
20
30
|
end
|
|
21
31
|
|
|
22
32
|
it "threads indent through to the engine" do
|
|
23
33
|
expect(doc.to_html(indent: 2))
|
|
24
|
-
.to eq(
|
|
34
|
+
.to eq(oracle.to_html(indent: 2))
|
|
25
35
|
end
|
|
26
36
|
|
|
27
37
|
it "threads the indent unit through to the engine" do
|
|
28
38
|
expect(doc.to_html(indent: 1, indent_text: "\t"))
|
|
29
|
-
.to eq(
|
|
39
|
+
.to eq(oracle.to_html(indent: 1, indent_text: "\t"))
|
|
30
40
|
end
|
|
31
41
|
end
|
|
32
42
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
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.129
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
@@ -312,6 +312,7 @@ files:
|
|
|
312
312
|
- spec/consistency/README.md
|
|
313
313
|
- spec/consistency/adapter_parity_spec.rb
|
|
314
314
|
- spec/consistency/eol_normalization_spec.rb
|
|
315
|
+
- spec/consistency/html_doctype_spec.rb
|
|
315
316
|
- spec/consistency/round_trip_spec.rb
|
|
316
317
|
- spec/consistency/xslt_spec.rb
|
|
317
318
|
- spec/examples/README.md
|