html2slim2 0.3.0 → 0.4.0

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: c6ff64c9a5fd086b72c5db5c43df4416f4408052a1bf5e35f920bf1a26df46c5
4
- data.tar.gz: 92df93ab18679e51386f70c9045fd168b5507094a070e5a5e219dfbb60795dbe
3
+ metadata.gz: 43220a31938b70d0b9a90e146c897e5ae26e4bab1b12b982f9e54203bd9a3212
4
+ data.tar.gz: 20594bd2657e9413dd54f5f358c8bfc4c3130784a027ad6f835cd743c68230d8
5
5
  SHA512:
6
- metadata.gz: 2f56c1abc267eb88573528a0546bf34265da07578923854fc1f82ecbe92a4af262ae6a88059d8d1680d1fda5a3fa19d96245a15a9e5082958679e88d04c373c7
7
- data.tar.gz: bd67aa82f0336fa656c93ac391db4c1b6529ed1b5e288c5295c8601c9ef3a501a10f51b4f5a93b7f9608a03a17566fe100b0468712b7b38b1a212bb2df23738b
6
+ metadata.gz: b4243fa2f631f4c50334b25b0777a96de02dabc30363705935b8a810b98482507e12c2ed946399a5911b18b0e8df1495b7fec37cd5a3d049e428ac370ddabfa2
7
+ data.tar.gz: ba9bb2d88c379b2992b44c2a5289806c17480a5db50fff32942c3dbdf8f4f5b07445f38588bd37c458380c2581661a2e4a9dbf817e02425f0513448e451a964c
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # HTML2Slim2
2
2
 
3
- ![Version](https://img.shields.io/gem/v/html2slim2.svg)
3
+ [![Gem Version](https://badge.fury.io/rb/html2slim2.svg)](https://badge.fury.io/rb/html2slim2)
4
4
 
5
5
  <!--
6
6
  [![Build Status](https://travis-ci.org/slim-template/html2slim.png?branch=master)](https://travis-ci.org/slim-template/html2slim)
data/bin/erb2slim CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  $:.unshift File.dirname(__FILE__) + '/../lib'
4
- require 'html2slim/command'
4
+ require 'html2slim2/command'
5
5
 
6
- cmd = HTML2Slim::ERBCommand.new(ARGV)
6
+ cmd = HTML2Slim2::ERBCommand.new(ARGV)
7
7
  cmd.run
data/bin/html2slim CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  $:.unshift File.dirname(__FILE__) + '/../lib'
4
- require 'html2slim/command'
4
+ require 'html2slim2/command'
5
5
 
6
- cmd = HTML2Slim::HTMLCommand.new(ARGV)
6
+ cmd = HTML2Slim2::HTMLCommand.new(ARGV)
7
7
  cmd.run
@@ -1,9 +1,8 @@
1
1
  require 'optparse'
2
- require 'html2slim'
2
+ require 'html2slim2'
3
3
 
4
- module HTML2Slim
4
+ module HTML2Slim2
5
5
  class Command
6
-
7
6
  def initialize(args)
8
7
  @args = args
9
8
  @options = {}
@@ -85,7 +84,7 @@ module HTML2Slim
85
84
  else
86
85
  slim_file = destination || slim_file
87
86
  end
88
-
87
+
89
88
  fail(ArgumentError, "Source and destination files can't be the same.") if @options[:input] != '-' && file == slim_file
90
89
 
91
90
  in_file = if @options[:input] == "-"
@@ -95,7 +94,7 @@ module HTML2Slim
95
94
  end
96
95
 
97
96
  @options[:output] = slim_file && slim_file != '-' ? File.open(slim_file, 'w') : $stdout
98
- @options[:output].puts HTML2Slim.convert!(in_file, format)
97
+ @options[:output].puts HTML2Slim2.convert!(in_file, format)
99
98
  @options[:output].close
100
99
 
101
100
  File.delete(file) if @options[:delete]
@@ -1,8 +1,15 @@
1
- require_relative 'nokogiri_monkeypatches'
1
+ module HTML2Slim2
2
+ MULTILINE_ATTR_MARKER = 'html2slim2-multiline'
3
+ end
4
+
5
+ require_relative 'nokogiri_monkey_patches'
2
6
 
3
- module HTML2Slim
7
+ module HTML2Slim2
4
8
  class Converter
9
+ OPENING_TAG_RE = /<([A-Za-z][A-Za-z0-9:-]*)((?:[^>"']|"[^"]*"|'[^']*')*)(\/?)>/m
10
+
5
11
  def initialize(html)
12
+ html = mark_multiline_opening_tags(html)
6
13
  nokogiri = html[..1] == '<!' ? Nokogiri.parse(html) : Nokogiri::HTML.fragment(html)
7
14
  @slim = nokogiri.to_slim
8
15
  end
@@ -10,6 +17,19 @@ module HTML2Slim
10
17
  def to_s
11
18
  @slim
12
19
  end
20
+
21
+ private
22
+
23
+ def mark_multiline_opening_tags(html)
24
+ html.gsub(OPENING_TAG_RE) do |match|
25
+ attrs = Regexp.last_match[2]
26
+ next match unless attrs.include?("\n")
27
+
28
+ name = Regexp.last_match[1]
29
+ slash = Regexp.last_match[3]
30
+ %(<#{name}#{attrs} #{MULTILINE_ATTR_MARKER}="1"#{slash}>)
31
+ end
32
+ end
13
33
  end
14
34
 
15
35
  class HTMLConverter < Converter
@@ -0,0 +1,189 @@
1
+ require 'nokogiri'
2
+
3
+ class Nokogiri::XML::Text
4
+ def to_slim(lvl = 0)
5
+ str = escape(content)
6
+ return nil if str.strip.empty?
7
+
8
+ # A lone text line with no indentation is content, not HTML formatting space.
9
+ # "\na\n" → "| a" (not "| a ")
10
+ normalized = if str.match?(/\A\n+\S/) && str.match?(/\S\n+\z/)
11
+ str.strip.gsub(/\s+/, ' ')
12
+ else
13
+ str.gsub(/\s+/, ' ')
14
+ end
15
+
16
+ (' ' * lvl) + %(| #{normalized})
17
+ end
18
+
19
+ private
20
+
21
+ def escape(str)
22
+ str.gsub!('&', '&amp;')
23
+ str.gsub!('©', '&copy;')
24
+ str.gsub!("\u00A0", '&nbsp;')
25
+ str.gsub!('»', '&raquo;')
26
+ str.gsub!('<', '&lt;')
27
+ str.gsub!('>', '&gt;')
28
+ str
29
+ end
30
+ end
31
+
32
+ class Nokogiri::XML::DTD
33
+ def to_slim(_lvl = 0)
34
+ if to_xml.include?('xml')
35
+ to_xml.include?('iso-8859-1') ? 'doctype xml ISO-88591' : 'doctype xml'
36
+ elsif to_xml.include?('XHTML') || to_xml.include?('HTML 4.01')
37
+ available_versions = Regexp.union ['Basic', '1.1', 'strict', 'Frameset', 'Mobile', 'Transitional']
38
+ version = to_xml.match(available_versions).to_s.downcase
39
+ "doctype #{version}"
40
+ else
41
+ 'doctype html'
42
+ end
43
+ end
44
+ end
45
+
46
+ class Nokogiri::XML::Element
47
+ BLANK_RE = /\A[[:space:]]*\z/.freeze
48
+
49
+ def slim(lvl = 0)
50
+ indent = ' ' * lvl
51
+
52
+ return indent + slim_ruby_code(indent) if ruby?
53
+
54
+ r = indent
55
+ r += name unless skip_tag_name?
56
+ r += slim_id
57
+ r += slim_class
58
+ r += slim_attributes(indent)
59
+ r
60
+ end
61
+
62
+ def to_slim(lvl = 0)
63
+ if children.any?
64
+ %(#{slim(lvl)}\n#{children.filter_map { |c| c.to_slim(lvl + 1) }.join("\n")})
65
+ else
66
+ r = slim(lvl)
67
+ # Empty multiline tags still end with a newline so sibling spacing
68
+ # matches tags that only contained ignorable whitespace.
69
+ r.include?("\n") ? "#{r}\n" : r
70
+ end
71
+ end
72
+
73
+ private
74
+
75
+ def slim_ruby_code(indent)
76
+ (code.strip[0] == '=' ? '' : '- ') + code.strip.gsub('\\n', "\n#{indent}- ")
77
+ end
78
+
79
+ def code
80
+ attributes['code'].to_s
81
+ end
82
+
83
+ def skip_tag_name?
84
+ div? && (has_id? || has_class?)
85
+ end
86
+
87
+ def slim_id
88
+ has_id? ? "##{self['id']}" : ''
89
+ end
90
+
91
+ def slim_class
92
+ has_class? ? ".#{self['class'].to_s.strip.split(/\s+/).join('.')}" : ''
93
+ end
94
+
95
+ def slim_attributes(indent = '')
96
+ multiline = has_attribute?(HTML2Slim2::MULTILINE_ATTR_MARKER)
97
+ remove_attribute(HTML2Slim2::MULTILINE_ATTR_MARKER)
98
+ remove_attribute('class')
99
+ remove_attribute('id')
100
+ return '' unless has_attributes?
101
+
102
+ if multiline && multiline_attribute_output?
103
+ slim_multiline_attributes(indent)
104
+ else
105
+ "[#{attributes_as_html.to_s.strip}]"
106
+ end
107
+ end
108
+
109
+ def multiline_attribute_output?
110
+ attrs = attributes.to_hash
111
+ attrs.size > 1 || attrs.values.any? { |value| value.to_s.include?("\n") }
112
+ end
113
+
114
+ def slim_multiline_attributes(indent)
115
+ parts = attributes.map do |attr_name, attr_val|
116
+ attr_val ? "#{attr_name}=#{html_quote attr_val.to_s}" : attr_name.to_s
117
+ end
118
+ return "[#{parts.first}]" if parts.size <= 1
119
+
120
+ inner_indent = "#{indent} "
121
+ "[\n#{parts.map { |part| "#{inner_indent}#{part}" }.join("\n")}\n#{indent}]"
122
+ end
123
+
124
+ def has_attributes? # rubocop:disable Naming/PredicatePrefix
125
+ attributes.to_hash.any?
126
+ end
127
+
128
+ def has_id? # rubocop:disable Naming/PredicatePrefix
129
+ has_attribute?('id') && !(BLANK_RE === self['id'])
130
+ end
131
+
132
+ def has_class? # rubocop:disable Naming/PredicatePrefix
133
+ has_attribute?('class') && !(BLANK_RE === self['class'])
134
+ end
135
+
136
+ def ruby?
137
+ name == 'ruby'
138
+ end
139
+
140
+ def div?
141
+ name == 'div'
142
+ end
143
+
144
+ def html_quote(str)
145
+ "\"#{str.gsub('"', '\\"')}\""
146
+ end
147
+
148
+ def attributes_as_html
149
+ attributes.map do |attr_name, attr_val|
150
+ " #{attr_name}" + (attr_val ? "=#{html_quote attr_val.to_s}" : '')
151
+ end.join
152
+ end
153
+ end
154
+
155
+ class Nokogiri::XML::DocumentFragment
156
+ def to_slim
157
+ if children.any?
158
+ children.filter_map(&:to_slim).join("\n")
159
+ else
160
+ ''
161
+ end
162
+ end
163
+ end
164
+
165
+ class Nokogiri::XML::Document
166
+ def to_slim
167
+ if children.any?
168
+ children.filter_map(&:to_slim).join("\n")
169
+ else
170
+ ''
171
+ end
172
+ end
173
+ end
174
+
175
+ class Nokogiri::XML::Comment
176
+ def to_slim(lvl = 0)
177
+ r = ' ' * lvl
178
+
179
+ # Render as a Slim comment, multiline if necessary
180
+ str = content.strip
181
+ return nil if str.empty?
182
+
183
+ if str.include?("\n")
184
+ "#{r}/\n#{r} " + str.gsub("\n", "\n#{r} ")
185
+ else
186
+ "#{r}/ #{str}"
187
+ end
188
+ end
189
+ end
@@ -0,0 +1,3 @@
1
+ module HTML2Slim2
2
+ VERSION = '0.4.0'
3
+ end
data/lib/html2slim2.rb ADDED
@@ -0,0 +1,12 @@
1
+ require_relative 'html2slim2/version'
2
+ require_relative 'html2slim2/converter'
3
+
4
+ module HTML2Slim2
5
+ def self.convert!(input, format = :html)
6
+ if format.to_s == "html"
7
+ HTMLConverter.new(input)
8
+ else
9
+ ERBConverter.new(input)
10
+ end
11
+ end
12
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: html2slim2
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - sanadan
8
8
  - Maiz Lulkin
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-05-08 00:00:00.000000000 Z
11
+ date: 2026-09-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nokogiri
@@ -24,62 +24,6 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
- - !ruby/object:Gem::Dependency
28
- name: minitest
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: '0'
34
- type: :development
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - ">="
39
- - !ruby/object:Gem::Version
40
- version: '0'
41
- - !ruby/object:Gem::Dependency
42
- name: rake
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - ">="
46
- - !ruby/object:Gem::Version
47
- version: '0'
48
- type: :development
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - ">="
53
- - !ruby/object:Gem::Version
54
- version: '0'
55
- - !ruby/object:Gem::Dependency
56
- name: rubocop-rails-omakase
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - ">="
60
- - !ruby/object:Gem::Version
61
- version: '0'
62
- type: :development
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - ">="
67
- - !ruby/object:Gem::Version
68
- version: '0'
69
- - !ruby/object:Gem::Dependency
70
- name: slim
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - ">="
74
- - !ruby/object:Gem::Version
75
- version: '0'
76
- type: :development
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - ">="
81
- - !ruby/object:Gem::Version
82
- version: '0'
83
27
  description: Convert HTML to Slim templates. Because HTML sux and Slim rules. That's
84
28
  why.
85
29
  email:
@@ -95,14 +39,17 @@ files:
95
39
  - README.md
96
40
  - bin/erb2slim
97
41
  - bin/html2slim
98
- - lib/html2slim.rb
99
- - lib/html2slim/command.rb
100
- - lib/html2slim/converter.rb
101
- - lib/html2slim/nokogiri_monkeypatches.rb
102
- - lib/html2slim/version.rb
103
- homepage: https://github.com/sanadan/html2slim
104
- licenses: []
105
- metadata: {}
42
+ - lib/html2slim2.rb
43
+ - lib/html2slim2/command.rb
44
+ - lib/html2slim2/converter.rb
45
+ - lib/html2slim2/nokogiri_monkey_patches.rb
46
+ - lib/html2slim2/version.rb
47
+ homepage: https://github.com/sanadan/html2slim2
48
+ licenses:
49
+ - MIT
50
+ metadata:
51
+ homepage_uri: https://github.com/sanadan/html2slim2
52
+ rubygems_mfa_required: 'true'
106
53
  rdoc_options:
107
54
  - "--charset=UTF-8"
108
55
  require_paths:
@@ -111,14 +58,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
111
58
  requirements:
112
59
  - - ">="
113
60
  - !ruby/object:Gem::Version
114
- version: '0'
61
+ version: '3.3'
115
62
  required_rubygems_version: !ruby/object:Gem::Requirement
116
63
  requirements:
117
64
  - - ">="
118
65
  - !ruby/object:Gem::Version
119
66
  version: '0'
120
67
  requirements: []
121
- rubygems_version: 3.6.7
68
+ rubygems_version: 4.0.16
122
69
  specification_version: 4
123
70
  summary: HTML to Slim converter.
124
71
  test_files: []
@@ -1,138 +0,0 @@
1
- require 'nokogiri'
2
-
3
- class Nokogiri::XML::Text
4
- def to_slim(lvl = 0)
5
- str = escape(content)
6
- return nil if str.strip.empty?
7
-
8
- (' ' * lvl) + %(| #{str.gsub(/\s+/, ' ')})
9
- end
10
-
11
- private
12
-
13
- def escape(str)
14
- str.gsub!('&', '&amp;')
15
- str.gsub!('©', '&copy;')
16
- str.gsub!("\u00A0", '&nbsp;')
17
- str.gsub!('»', '&raquo;')
18
- str.gsub!('<', '&lt;')
19
- str.gsub!('>', '&gt;')
20
- str
21
- end
22
- end
23
-
24
- class Nokogiri::XML::DTD
25
- def to_slim(_lvl = 0)
26
- if to_xml.include?('xml')
27
- to_xml.include?('iso-8859-1') ? 'doctype xml ISO-88591' : 'doctype xml'
28
- elsif to_xml.include?('XHTML') || to_xml.include?('HTML 4.01')
29
- available_versions = Regexp.union ['Basic', '1.1', 'strict', 'Frameset', 'Mobile', 'Transitional']
30
- version = to_xml.match(available_versions).to_s.downcase
31
- "doctype #{version}"
32
- else
33
- 'doctype html'
34
- end
35
- end
36
- end
37
-
38
- class Nokogiri::XML::Element
39
- BLANK_RE = /\A[[:space:]]*\z/.freeze
40
-
41
- def slim(lvl = 0)
42
- r = ' ' * lvl
43
-
44
- return r + slim_ruby_code(r) if ruby?
45
-
46
- r += name unless skip_tag_name?
47
- r += slim_id
48
- r += slim_class
49
- r += slim_attributes
50
- r
51
- end
52
-
53
- def to_slim(lvl = 0)
54
- if children.count.positive?
55
- %(#{slim(lvl)}\n#{children.filter_map { |c| c.to_slim(lvl + 1) }.join("\n")})
56
- else
57
- slim(lvl)
58
- end
59
- end
60
-
61
- private
62
-
63
- def slim_ruby_code(r)
64
- (code.strip[0] == '=' ? '' : '- ') + code.strip.gsub('\\n', "\n#{r}- ")
65
- end
66
-
67
- def code
68
- attributes['code'].to_s
69
- end
70
-
71
- def skip_tag_name?
72
- div? && (has_id? || has_class?)
73
- end
74
-
75
- def slim_id
76
- has_id? ? "##{self['id']}" : ''
77
- end
78
-
79
- def slim_class
80
- has_class? ? ".#{self['class'].to_s.strip.split(/\s+/).join('.')}" : ''
81
- end
82
-
83
- def slim_attributes
84
- remove_attribute('class')
85
- remove_attribute('id')
86
- has_attributes? ? "[#{attributes_as_html.to_s.strip}]" : ''
87
- end
88
-
89
- def has_attributes? # rubocop:disable Naming/PredicateName
90
- attributes.to_hash.any?
91
- end
92
-
93
- def has_id? # rubocop:disable Naming/PredicateName
94
- has_attribute?('id') && !(BLANK_RE === self['id'])
95
- end
96
-
97
- def has_class? # rubocop:disable Naming/PredicateName
98
- has_attribute?('class') && !(BLANK_RE === self['class'])
99
- end
100
-
101
- def ruby?
102
- name == 'ruby'
103
- end
104
-
105
- def div?
106
- name == 'div'
107
- end
108
-
109
- def html_quote(str)
110
- "\"#{str.gsub('"', '\\"')}\""
111
- end
112
-
113
- def attributes_as_html
114
- attributes.map do |aname, aval|
115
- " #{aname}" + (aval ? "=#{html_quote aval.to_s}" : '')
116
- end.join
117
- end
118
- end
119
-
120
- class Nokogiri::XML::DocumentFragment
121
- def to_slim
122
- if children.count.positive?
123
- children.filter_map(&:to_slim).join("\n")
124
- else
125
- ''
126
- end
127
- end
128
- end
129
-
130
- class Nokogiri::XML::Document
131
- def to_slim
132
- if children.count.positive?
133
- children.filter_map(&:to_slim).join("\n")
134
- else
135
- ''
136
- end
137
- end
138
- end
@@ -1,3 +0,0 @@
1
- module HTML2Slim
2
- VERSION = '0.3.0'
3
- end
data/lib/html2slim.rb DELETED
@@ -1,12 +0,0 @@
1
- require_relative 'html2slim/version'
2
- require_relative 'html2slim/converter'
3
-
4
- module HTML2Slim
5
- def self.convert!(input, format=:html)
6
- if format.to_s == "html"
7
- HTMLConverter.new(input)
8
- else
9
- ERBConverter.new(input)
10
- end
11
- end
12
- end