coradoc-html 1.1.21 → 1.1.23

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: 78a6409c37e529e753bea5a39adc627dc5c2c059970feac19f0026fd3faf640b
4
- data.tar.gz: e3edeb7c93d81f268f52919a36960061c9ea3c56c8aa31f0095d3c18708a6585
3
+ metadata.gz: 13fc1658ebea3a1a812dbe92977e3e42dcf47e3586caaf7faf9f195f3c06070e
4
+ data.tar.gz: 7e39f8f780d457d4b002517737663ef93ba282f3ee56fc3cc9a465bceed141d7
5
5
  SHA512:
6
- metadata.gz: a522853052a688ed9342752416eecb09118250e3c175191a8e3a88baa0ae1e4976f17446a5daddf82e849a9b34c6313258a6926e1f8efc2aa93725dcd086d275
7
- data.tar.gz: addbcb3a1a19879f4dc4f83b68ae2f34cd5687488fa017d31a1367d847f2c693c7dcc639bdeb3445d4437ccfa9d958cf21d67428c8ec8992d4d8ab9aed854d31
6
+ metadata.gz: fbb70e244e35b6e3961e5238db99f4303e0ad798d2cdfb5b562d5ae2ee2919e91f3791b255d78a36867480b09ad7067b82e1e8509640deb93af9dde53ed93005
7
+ data.tar.gz: 4a50767b69167bda8f232249cf99b371139d55a7860d1e475a35a0bc24cd6e36fd95cf34408e6804b09b73a3545b55ef147113567e2aac8894b969dc77aa7e34
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'nokogiri'
4
-
5
3
  module Coradoc
6
4
  module Html
7
5
  module AssetResolver
@@ -55,11 +53,7 @@ module Coradoc
55
53
 
56
54
  def css_link_tag(options = {})
57
55
  href = stylesheet_path(options)
58
- doc = Nokogiri::HTML::Document.new
59
- node = Nokogiri::XML::Node.new('link', doc)
60
- node['rel'] = 'stylesheet'
61
- node['href'] = href
62
- node.to_html
56
+ Builder.new { |doc| doc.link(rel: 'stylesheet', href: href) }.to_html
63
57
  end
64
58
 
65
59
  def css_style_tag(options = {})
@@ -122,11 +116,7 @@ module Coradoc
122
116
 
123
117
  def js_link_tag(options = {})
124
118
  src = javascript_path(options)
125
- doc = Nokogiri::HTML::Document.new
126
- node = Nokogiri::XML::Node.new('script', doc)
127
- node['src'] = src
128
- node['defer'] = ''
129
- node.to_html
119
+ Builder.new { |doc| doc.script(src: src, defer: '') }.to_html
130
120
  end
131
121
 
132
122
  def js_script_tag(options = {})
@@ -164,28 +154,19 @@ module Coradoc
164
154
 
165
155
  def highlightjs_tags(options = {})
166
156
  theme = options[:highlightjs_theme] || Config::DEFAULT_OPTIONS[:highlightjs_theme]
167
- doc = Nokogiri::HTML::Document.new
168
-
169
- link_node = Nokogiri::XML::Node.new('link', doc)
170
- link_node['rel'] = 'stylesheet'
171
- link_node['href'] = "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/#{theme}.min.css"
172
-
173
- script_node = Nokogiri::XML::Node.new('script', doc)
174
- script_node['src'] = 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js'
175
-
176
- init_node = Nokogiri::XML::Node.new('script', doc)
177
- init_node.content = 'hljs.highlightAll();'
178
157
 
179
- [link_node.to_html, script_node.to_html, init_node.to_html].join("\n")
158
+ Builder.new do |doc|
159
+ doc.link(rel: 'stylesheet',
160
+ href: "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/#{theme}.min.css")
161
+ doc.script(src: 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js')
162
+ doc.script('hljs.highlightAll();')
163
+ end.to_html
180
164
  end
181
165
 
182
166
  private
183
167
 
184
168
  def build_text_element(tag_name, content)
185
- doc = Nokogiri::HTML::Document.new
186
- node = Nokogiri::XML::Node.new(tag_name, doc)
187
- node.content = content
188
- node.to_html
169
+ Builder.new { |doc| doc.send(tag_name, content) }.to_html
189
170
  end
190
171
  end
191
172
  end
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coradoc
4
+ module Html
5
+ # Builds HTML trees over the Leptris DOM using a
6
+ # Nokogiri::HTML::Builder-style DSL. Single source of truth for
7
+ # programmatic HTML construction in this gem (layout fallbacks,
8
+ # asset tag emitters) — no raw HTML string concatenation anywhere.
9
+ #
10
+ # Builder.new do |doc|
11
+ # doc.html(lang: 'en') do
12
+ # doc.head { doc.meta(charset: 'UTF-8') }
13
+ # doc.body { doc << body_html }
14
+ # end
15
+ # end.to_html
16
+ #
17
+ # Serialization uses leptris-ruby's HTML mode (#309): script/style
18
+ # text is emitted unescaped and void elements are unclosed.
19
+ class Builder
20
+ # Methods that are builder API, not tag names.
21
+ RESERVED = %i[text to_html to_s document parent].freeze
22
+
23
+ attr_reader :document
24
+
25
+ def initialize(document = Leptris::XML::Document.create, &block)
26
+ @document = document
27
+ @roots = []
28
+ @parents = []
29
+ instance_eval(&block) if block
30
+ end
31
+
32
+ # Tag DSL. A positional String argument becomes the element's text
33
+ # content; the block receives the builder with the new element as
34
+ # insertion parent; keyword arguments become attributes.
35
+ def method_missing(name, *args, **attrs, &block)
36
+ element = @document.create_element(name.to_s)
37
+ attrs.each { |key, value| element[key.to_s] = value.to_s }
38
+
39
+ args.each do |arg|
40
+ next unless arg.is_a?(String)
41
+
42
+ element.add_child(@document.create_text_node(arg))
43
+ end
44
+
45
+ if block
46
+ @parents.push(element)
47
+ begin
48
+ yield self
49
+ ensure
50
+ @parents.pop
51
+ end
52
+ end
53
+
54
+ if current
55
+ current.add_child(element)
56
+ else
57
+ @roots << element
58
+ end
59
+ element
60
+ end
61
+
62
+ def respond_to_missing?(name, include_private = false)
63
+ !RESERVED.include?(name) || super
64
+ end
65
+
66
+ # Raw markup insertion into the current element (parsed, never
67
+ # concatenated as text).
68
+ def <<(markup)
69
+ current&.add_child(markup)
70
+ self
71
+ end
72
+
73
+ # Text node insertion into the current element.
74
+ def text(content)
75
+ current&.add_child(@document.create_text_node(content.to_s))
76
+ self
77
+ end
78
+
79
+ # Serializes with HTML semantics: script/style text unescaped,
80
+ # void elements unclosed. Multiple top-level elements are joined
81
+ # with newlines.
82
+ def to_html
83
+ @roots.map(&:to_html).join("\n")
84
+ end
85
+ alias to_s to_html
86
+
87
+ private
88
+
89
+ def current
90
+ @parents.last
91
+ end
92
+ end
93
+ end
94
+ end
@@ -23,12 +23,14 @@ module Coradoc
23
23
  end
24
24
 
25
25
  def node_has_ancestor?(node, name)
26
- case name
27
- when String
28
- node.ancestors(name).any?
29
- when Array
30
- name.any? { |n| node.ancestors(n).any? }
26
+ names = Array(name)
27
+ parent = node.parent
28
+ while parent
29
+ return true if parent.element? && names.include?(parent.name)
30
+
31
+ parent = parent.parent
31
32
  end
33
+ false
32
34
  end
33
35
 
34
36
  def textnode_before_end_with?(node, str)
@@ -39,7 +41,7 @@ module Coradoc
39
41
  str = /(?:#{str})\z/
40
42
 
41
43
  node2 = node.at_xpath('preceding-sibling::node()[1]')
42
- node2.is_a?(Nokogiri::XML::Text) && node2.text.match?(str)
44
+ node2.is_a?(Leptris::XML::Text) && node2.content.match?(str)
43
45
  end
44
46
 
45
47
  def textnode_after_start_with?(node, str)
@@ -50,7 +52,7 @@ module Coradoc
50
52
  str = /\A(?:#{str})/
51
53
 
52
54
  node2 = node.at_xpath('following-sibling::node()[1]')
53
- node2.is_a?(Nokogiri::XML::Text) && node2.text.match?(str)
55
+ node2.is_a?(Leptris::XML::Text) && node2.content.match?(str)
54
56
  end
55
57
 
56
58
  def extract_leading_trailing_whitespace(node)
@@ -58,14 +60,14 @@ module Coradoc
58
60
  leading_whitespace = ::Regexp.last_match(1)
59
61
  unless leading_whitespace.nil?
60
62
  first_text = node.at_xpath('./text()[1]')
61
- first_text&.replace(first_text.text.lstrip)
63
+ first_text&.content = first_text.content.lstrip
62
64
  leading_whitespace = ' '
63
65
  end
64
66
  node.text =~ /(\s+)$/
65
67
  trailing_whitespace = ::Regexp.last_match(1)
66
68
  unless trailing_whitespace.nil?
67
69
  last_text = node.at_xpath('./text()[last()]')
68
- last_text&.replace(last_text.text.rstrip)
70
+ last_text&.content = last_text.content.rstrip
69
71
  trailing_whitespace = ' '
70
72
  end
71
73
  [leading_whitespace, trailing_whitespace]
@@ -7,7 +7,7 @@ module Coradoc
7
7
  INSTANCE = new
8
8
 
9
9
  def to_coradoc(node, _state = {})
10
- node.to_s
10
+ node.is_a?(Leptris::XML::Node) ? node.to_xml(no_decl: true) : node.to_s
11
11
  end
12
12
  end
13
13
  end
@@ -71,7 +71,7 @@ module Coradoc
71
71
  end
72
72
 
73
73
  def self.process_coradoc(node, state)
74
- node = node.to_a if node.is_a? Nokogiri::XML::NodeSet
74
+ node = node.to_a if node.is_a? Leptris::XML::NodeSet
75
75
  return node.map { |i| process_coradoc(i, state) } if node.is_a? Array
76
76
 
77
77
  plugins = state[:plugin_instances] || {}
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'nokogiri'
4
-
5
3
  module Coradoc
6
4
  module Html
7
5
  # Single source of truth for FrontmatterBlock -> HTML `<meta>` and
@@ -9,7 +7,7 @@ module Coradoc
9
7
  # HTML gem; CoreModel has no knowledge of HTML.)
10
8
  #
11
9
  # The module produces a small data structure that layout templates
12
- # and Nokogiri::HTML::Builder fallbacks both consume, so we never
10
+ # and Builder fallbacks both consume, so we never
13
11
  # duplicate the mapping rule across output paths (DRY).
14
12
  #
15
13
  # Mapping table (single source of truth — extend here only):
@@ -50,7 +48,7 @@ module Coradoc
50
48
  }
51
49
  end
52
50
 
53
- # Emit meta + link tags into a Nokogiri head builder context.
51
+ # Emit meta + link tags into a Builder head context.
54
52
  def emit_into_builder(builder_doc, block)
55
53
  data = extract(block)
56
54
  Array(data[:metas]).each do |meta|
@@ -10,10 +10,12 @@ module Coradoc
10
10
  root = track_time 'Loading input HTML document' do
11
11
  case input
12
12
  when String
13
- Nokogiri::HTML(input).root
14
- when Nokogiri::XML::Document
13
+ next nil if input.strip.empty?
14
+
15
+ html_parse(input).root
16
+ when Leptris::XML::Document
15
17
  input.root
16
- when Nokogiri::XML::Node
18
+ when Leptris::XML::Node
17
19
  input
18
20
  end
19
21
  end
@@ -57,6 +59,23 @@ module Coradoc
57
59
  options[:plugin_instances] || Html.input_config.plugins.map(&:new)
58
60
  end
59
61
 
62
+ # Single dispatch point for the HTML parsing lane: :html4 keeps
63
+ # Nokogiri::HTML byte-parity; :html5 selects the WHATWG-conformant
64
+ # engine (implied tbody/tr, foster parenting, full implied head).
65
+ # A synthesized empty <head/> is stripped before conversion — an
66
+ # authored empty head (html4 lane, explicit markup) still produces
67
+ # a DocumentElement with the placeholder title.
68
+ def self.html_parse(input)
69
+ if Html.input_config.html_version == :html5
70
+ doc = Leptris::HTML5.parse(input)
71
+ head = doc.at_css('head')
72
+ head.parent&.remove_child(head) if head && head.children.empty?
73
+ doc
74
+ else
75
+ Leptris::HTML.parse(input)
76
+ end
77
+ end
78
+
60
79
  @track_time_indentation = 0
61
80
  def self.track_time(task)
62
81
  if Html.input_config.track_time
@@ -8,6 +8,7 @@ module Coradoc
8
8
  def initialize
9
9
  @unknown_tags = :pass_through
10
10
  @input_format = :html
11
+ @html_version = :html4
11
12
  @mathml2asciimath = false
12
13
  @external_images = false
13
14
 
@@ -57,6 +58,7 @@ module Coradoc
57
58
  declare_option :image_counter
58
59
  declare_option :image_counter_pattern
59
60
  declare_option :input_format
61
+ declare_option :html_version
60
62
  declare_option :split_sections
61
63
  declare_option :doc_width
62
64
  declare_option :plugins
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'nokogiri'
4
3
  require 'liquid'
5
4
 
6
5
  module Coradoc
@@ -90,7 +89,7 @@ module Coradoc
90
89
 
91
90
  def build_static_fallback(document, body_html, opts)
92
91
  frontmatter = extract_frontmatter(document)
93
- Nokogiri::HTML::Builder.new do |doc|
92
+ Builder.new do |doc|
94
93
  doc.html(lang: resolve_lang(opts)) do
95
94
  doc.head do
96
95
  doc.meta(charset: 'UTF-8')
@@ -152,7 +151,7 @@ module Coradoc
152
151
 
153
152
  def build_spa_fallback(document, opts, assets, safe_json)
154
153
  frontmatter = extract_frontmatter(document)
155
- Nokogiri::HTML::Builder.new do |doc|
154
+ Builder.new do |doc|
156
155
  doc.html(lang: resolve_lang(opts)) do
157
156
  doc.head do
158
157
  doc.meta(charset: 'UTF-8')
@@ -41,12 +41,15 @@ module Coradoc
41
41
  end
42
42
 
43
43
  def html_tree_remove_by_css(css)
44
- html_tree.css(css).each(&:remove)
44
+ html_tree.css(css).each do |e|
45
+ e.parent&.remove_child(e)
46
+ end
45
47
  end
46
48
 
47
49
  def html_tree_replace_with_children_by_css(css)
48
50
  html_tree.css(css).each do |e|
49
- e.replace(e.children)
51
+ e.children.each { |child| e.add_previous_sibling(child) }
52
+ e.parent&.remove_child(e)
50
53
  end
51
54
  end
52
55
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'liquid'
4
- require 'nokogiri'
4
+ require 'leptris'
5
5
 
6
6
  module Coradoc
7
7
  module Html
@@ -118,12 +118,9 @@ module Coradoc
118
118
  resolved = TitleText.resolve(drop.model)
119
119
  text = resolved ? Escape.escape_html(resolved) : ''
120
120
 
121
- fragment = Nokogiri::HTML.fragment
122
- div = Nokogiri::XML::Node.new('div', fragment.document)
123
- div['class'] = "element element-#{type}"
124
- div.content = text
125
- fragment.add_child(div)
126
- fragment.to_html
121
+ Builder.new do |doc|
122
+ doc.div(class: "element element-#{type}") { doc.text text }
123
+ end.to_html
127
124
  end
128
125
 
129
126
  def normalize_dirs(dirs)
@@ -1,28 +1,28 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'nokogiri'
3
+ require 'leptris'
4
4
  require 'coradoc'
5
5
 
6
6
  module Coradoc
7
7
  module Html
8
8
  module Transform
9
- # Transforms Nokogiri HTML nodes to CoreModel
9
+ # Transforms Leptris HTML nodes to CoreModel
10
10
  #
11
- # Nokogiri serves as the HTML model layer. This transformer converts
12
- # Nokogiri::XML::Document or Nokogiri::XML::Node objects into CoreModel
11
+ # Leptris serves as the HTML model layer. This transformer converts
12
+ # Leptris::XML::Document or Leptris::XML::Node objects into CoreModel
13
13
  # by delegating to the existing input converter pipeline.
14
14
  class ToCoreModel
15
15
  class << self
16
- # Transform an HTML model (Nokogiri node) to CoreModel
16
+ # Transform an HTML model (Leptris node) to CoreModel
17
17
  #
18
- # @param model [Nokogiri::XML::Document, Nokogiri::XML::Node, Coradoc::CoreModel::Base]
18
+ # @param model [Leptris::XML::Document, Leptris::XML::Node, Coradoc::CoreModel::Base]
19
19
  # HTML input model to transform
20
20
  # @return [Coradoc::CoreModel::Base] CoreModel equivalent
21
21
  def transform(model)
22
22
  case model
23
23
  when Coradoc::CoreModel::Base
24
24
  model
25
- when Nokogiri::XML::Document, Nokogiri::XML::Node
25
+ when Leptris::XML::Document, Leptris::XML::Node
26
26
  ::Coradoc::Html::HtmlConverter.to_core_model(model)
27
27
  when Array
28
28
  model.map { |item| transform(item) }
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Coradoc
4
4
  module Html
5
- VERSION = '1.1.21'
5
+ VERSION = '1.1.23'
6
6
  end
7
7
  end
data/lib/coradoc/html.rb CHANGED
@@ -73,6 +73,7 @@ module Coradoc
73
73
  require_relative 'html/template_helpers'
74
74
  autoload :Renderer, 'coradoc/html/renderer'
75
75
  autoload :LayoutRenderer, 'coradoc/html/layout_renderer'
76
+ autoload :Builder, 'coradoc/html/builder'
76
77
  autoload :RenderOptions, 'coradoc/html/render_options'
77
78
  autoload :TocSerializer, 'coradoc/html/toc_serializer'
78
79
 
@@ -208,14 +209,14 @@ module Coradoc
208
209
 
209
210
  # Check if this format can transform the given model to CoreModel
210
211
  #
211
- # HTML uses Nokogiri as its model layer. Accepts Nokogiri nodes
212
+ # HTML uses Leptris::XML as its model layer. Accepts Leptris nodes
212
213
  # and CoreModel objects (pass-through).
213
214
  #
214
215
  # @param model [Object] The model to check
215
- # @return [Boolean] true if the model is a Nokogiri node or CoreModel
216
+ # @return [Boolean] true if the model is a Leptris node or CoreModel
216
217
  def self.handles_model?(model)
217
- model.is_a?(Nokogiri::XML::Node) ||
218
- model.is_a?(Nokogiri::XML::Document) ||
218
+ model.is_a?(Leptris::XML::Node) ||
219
+ model.is_a?(Leptris::XML::Document) ||
219
220
  model.is_a?(Coradoc::CoreModel::Base)
220
221
  end
221
222
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: coradoc-html
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.21
4
+ version: 1.1.23
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
@@ -52,19 +52,19 @@ dependencies:
52
52
  - !ruby/object:Gem::Version
53
53
  version: '1.0'
54
54
  - !ruby/object:Gem::Dependency
55
- name: nokogiri
55
+ name: leptris
56
56
  requirement: !ruby/object:Gem::Requirement
57
57
  requirements:
58
- - - "~>"
58
+ - - ">="
59
59
  - !ruby/object:Gem::Version
60
- version: '1.0'
60
+ version: '0'
61
61
  type: :runtime
62
62
  prerelease: false
63
63
  version_requirements: !ruby/object:Gem::Requirement
64
64
  requirements:
65
- - - "~>"
65
+ - - ">="
66
66
  - !ruby/object:Gem::Version
67
- version: '1.0'
67
+ version: '0'
68
68
  - !ruby/object:Gem::Dependency
69
69
  name: rspec
70
70
  requirement: !ruby/object:Gem::Requirement
@@ -118,6 +118,7 @@ files:
118
118
  - LICENSE.txt
119
119
  - lib/coradoc/html.rb
120
120
  - lib/coradoc/html/asset_resolver.rb
121
+ - lib/coradoc/html/builder.rb
121
122
  - lib/coradoc/html/cleaner.rb
122
123
  - lib/coradoc/html/config.rb
123
124
  - lib/coradoc/html/converter_base.rb