canon 0.3.77 → 0.3.78

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: 07b15f2de75b50f25cccb2d078d9dddc6c3d2fe79c5cef1a177ac288e8d667fb
4
- data.tar.gz: d80d886320e5f3a00af951d2685429c48f4e0cce8b5c518c1523e55c9ca06a4b
3
+ metadata.gz: 031d0a1ecb48c26cb773e79d791d7ae090fa1231eb3132fbdf3d9138b0afcab6
4
+ data.tar.gz: 63dd55aa45a123cc1c39e6072033363def2bd0f6b6fc8de9ab88edd8a7f16ae4
5
5
  SHA512:
6
- metadata.gz: 5fd91e21dce05280d14c4e9fcecf87df9b3693d261ed7837d7a5f9a156706950737a9d1038aa70ee6854b36fed88252c81d4d1e8ccefe5b9db9caf99c59bf633
7
- data.tar.gz: dfe2e5ca7caacc830e27ac0d4f135081bb09d5c34139f8658d21662571e90d47bbfa246435719b784cf176ad6437c39fc5b596eca3246410216f6584f01e7c4b
6
+ metadata.gz: a77a02d18d89482275d8e288951b7bda5f4bb144fea76e08e21ce484df42ee4928d666cb45b7cba0f63d025b3bbda59f4e2550ed81e6190f7154cbe4c2c79931
7
+ data.tar.gz: 4bb93c91e98b9f04ff0513a17559a4eed805c5f7d8a3385dfd3f11e5af929c31d327eab1228c616b5712ace68cdcd8fdabcd49dbb25e5af14b3fb2160c40cc0f
@@ -28,6 +28,21 @@ module Canon
28
28
  def nokogiri_format(xml_string)
29
29
  Canon::NokogiriLoader.require!("Nokogiri-engine XML pretty-printing")
30
30
  doc = Nokogiri::XML(xml_string, &:noblanks)
31
+
32
+ # The loud/tolerant contract, Nokogiri-lane edition: the
33
+ # non-strict parse recovers malformed input and records the
34
+ # errors — non-HTML malformed input stays loud, HTML-shaped
35
+ # input falls back to the HTML parser (mirrors
36
+ # XmlParsing.parse_with_html_fallback).
37
+ if doc.errors.any?
38
+ unless Canon::XmlParsing.html_shaped?(xml_string)
39
+ raise Moxml::ParseError,
40
+ doc.errors.map(&:message).join("; ")
41
+ end
42
+
43
+ doc = Nokogiri::HTML4(xml_string, &:noblanks)
44
+ end
45
+
31
46
  if @indent_type == "tab"
32
47
  doc.to_xml(indent: 1, indent_text: "\t", encoding: "UTF-8")
33
48
  else
@@ -37,8 +52,12 @@ module Canon
37
52
 
38
53
  def moxml_format(xml_string)
39
54
  # noblanks mutates the tree (strips whitespace-only text), so
40
- # the document cannot be readonly.
41
- doc = Canon::XmlParsing.moxml_context.parse(xml_string, noblanks: true)
55
+ # the document cannot be readonly. HTML producers' output
56
+ # (Asciidoctor cover pages) routes through the tolerant HTML
57
+ # mode instead of failing the comparison (leptris#1564).
58
+ doc = Canon::XmlParsing.parse_with_html_fallback(
59
+ xml_string, noblanks: true
60
+ )
42
61
  if @indent_type == "tab"
43
62
  doc.to_xml(declaration: true, encoding: "UTF-8",
44
63
  indent: 1, indent_text: "\t", expand_empty: false)
data/lib/canon/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Canon
4
- VERSION = "0.3.77"
4
+ VERSION = "0.3.78"
5
5
  end
@@ -6,8 +6,11 @@ module Canon
6
6
  # Two independent concerns, deliberately separated (MECE):
7
7
  # - XML engine — :nokogiri (raw) or :moxml (adapter-mediated). Drives DOM
8
8
  # parsing and serialization of XML (see Canon::XmlParsing).
9
- # - HTML support — always Nokogiri on CRuby; moxml has no HTML adapter and
10
- # leptris no HTML parser (see Canon::Html::NokogiriSupport).
9
+ # - HTML support — the dedicated HTML pipeline stays on Nokogiri
10
+ # (see Canon::Html::NokogiriSupport); the XML lane additionally
11
+ # accepts HTML-shaped content through moxml's Context#parse_html
12
+ # (engine HTML modes: leptris >= 1.9.80, nokogiri) — see
13
+ # XmlParsing.parse_with_html_fallback (leptris#1564).
11
14
  #
12
15
  # SSOT: moxml owns adapter preference (Moxml::Config prefers leptris
13
16
  # when installed; see XmlParsing.moxml_adapter_name). Both engines
@@ -44,6 +44,36 @@ module Canon
44
44
  end
45
45
  end
46
46
 
47
+ # leptris#1564: HTML producers' output (void elements, unclosed
48
+ # tags, inline JS with bare <) is not well-formed XML, but the
49
+ # comparison pretty-printer still needs a tree. HTML-SHAPED
50
+ # content — a <!DOCTYPE html> declaration or an <html> root —
51
+ # parses through the engine's tolerant HTML mode (moxml
52
+ # Context#parse_html; adapters with an engine HTML mode, leptris
53
+ # >= 1.9.80 and nokogiri alike). Anything else re-raises:
54
+ # malformed XML stays loud.
55
+ def parse_with_html_fallback(xml_string, options = {})
56
+ doc = moxml_context.parse(xml_string, options)
57
+
58
+ # Engines differ in how strict:false treats malformed input —
59
+ # some raise, some recover and record. The loud/tolerant
60
+ # contract keys off the RECORD, not the raise: non-HTML
61
+ # malformed input stays loud; HTML-shaped input falls back to
62
+ # the HTML parser. Engine-independent by construction.
63
+ recorded = doc.respond_to?(:parse_errors) ? doc.parse_errors : []
64
+ if recorded.any?
65
+ raise Moxml::ParseError, recorded.join("; ") unless html_shaped?(xml_string)
66
+
67
+ return moxml_context.parse_html(xml_string)
68
+ end
69
+
70
+ doc
71
+ rescue Moxml::ParseError
72
+ raise unless html_shaped?(xml_string)
73
+
74
+ moxml_context.parse_html(xml_string)
75
+ end
76
+
47
77
  def parse_fragment(xml_string)
48
78
  if XmlBackend.nokogiri?
49
79
  Nokogiri::XML.fragment(xml_string).children.to_a
@@ -53,6 +83,27 @@ module Canon
53
83
  end
54
84
  end
55
85
 
86
+ # Conservative HTML sniff on the document head: the fallback
87
+ # must never swallow a genuinely malformed XML comparison —
88
+ # only content that declares itself HTML. Index-based scan,
89
+ # no regex: the previous regex form was polynomial on
90
+ # uncontrolled data (CodeQL).
91
+ def html_shaped?(xml_string)
92
+ head = xml_string.to_s.slice(0, 2048).downcase
93
+ return true if head.include?("<!doctype html")
94
+
95
+ root = head.lstrip
96
+ return false unless root.start_with?("<?xml")
97
+
98
+ close = root.index("?>")
99
+ return false unless close
100
+
101
+ root = root[(close + 2)..].lstrip
102
+ delimiter = root[5]
103
+ root.start_with?("<html") &&
104
+ (delimiter.nil? || " \t\n\r\f>/'\"".include?(delimiter))
105
+ end
106
+
56
107
  # --- Serialization ---
57
108
 
58
109
  def serialize(node)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: canon
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.77
4
+ version: 0.3.78
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-10-01 00:00:00.000000000 Z
11
+ date: 2026-10-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: diff-lcs
@@ -44,28 +44,28 @@ dependencies:
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: 1.9.282
47
+ version: 1.9.314
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: 1.9.282
54
+ version: 1.9.314
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: moxml
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: 0.5.98
61
+ version: 0.5.116
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: 0.5.98
68
+ version: 0.5.116
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: paint
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -142,14 +142,14 @@ dependencies:
142
142
  requirements:
143
143
  - - "~>"
144
144
  - !ruby/object:Gem::Version
145
- version: 0.6.28
145
+ version: 0.6.29
146
146
  type: :runtime
147
147
  prerelease: false
148
148
  version_requirements: !ruby/object:Gem::Requirement
149
149
  requirements:
150
150
  - - "~>"
151
151
  - !ruby/object:Gem::Version
152
- version: 0.6.28
152
+ version: 0.6.29
153
153
  description: |-
154
154
  Canon provides canonicalization and pretty-printing for various serialization
155
155
  formats (XML, HTML, JSON, YAML), producing standardized forms suitable for