blueprint-html2slim 1.0.0 → 1.1.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: 460bb27c7423062c1fe30a58b2f432f370361e1134a933ca550644e5f8d05b99
4
- data.tar.gz: d0cace18262a72410fe46ac69ae055523aec83f51d2da1c6fb8cd7cda137544c
3
+ metadata.gz: bcd9f4a5d346b4edee89ff58f86e84501a642530ce563be68daca7ccaa4747e4
4
+ data.tar.gz: b8d50860a181f7b4077be2aba82c520a5c3c0e498fed7b3c069e6e40077447ee
5
5
  SHA512:
6
- metadata.gz: '08bd9ceb42074244e5323c01120dff8bf91c1daaedee6574b9e09a994b286996ac3bad0e7f4a0e104066890e87943348c4dcfe11ea954fd4ee492149366059c5'
7
- data.tar.gz: 59b01b4f8f0d86ca4cab804e246149d37dbc57ccb2345687b181b79dd0e878ee7f3e4c72a4c41921b8e2f73b343f005d817247b9cd15920dd0401e732dc4fad9
6
+ metadata.gz: f53a93b08cb863037eadc43536bbdf5a551a58586d495254cfbfff181d2e0db1512f8a2533453a4c60b914b78ef5e7adfa3ce9fe3780c8ff3bd76c07ad0f388d
7
+ data.tar.gz: d0206ced006d3113f391387223c77b96e958d1e6f6b5bafb44882badbd93eba8f75d786ba5a03d60787b873bb8bcb36ef05bf932f2b6c8c7a804d21e1f670382
data/CHANGELOG.md CHANGED
@@ -2,6 +2,23 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [1.1.0] - 2025-01-16
6
+
7
+ ### Added
8
+ - Unicode/UTF-8 character support (CJK, emoji, European accents, Cyrillic, Arabic, Hebrew)
9
+ - Multiline ERB code block handling using `ruby:` blocks for better Slim syntax
10
+ - Support for inline arrays with chained methods in ERB blocks
11
+
12
+ ### Fixed
13
+ - Text starting with "/" now uses pipe notation to avoid being interpreted as Slim comments
14
+ - Multiline Ruby code (hashes, arrays, method calls) now generates valid Slim syntax
15
+ - UTF-8 encoding issues in CLI tool when reading and writing files
16
+
17
+ ### Changed
18
+ - Improved handling of complex ERB structures with proper indentation
19
+ - Better detection and formatting of multiline ERB blocks
20
+ - Updated RuboCop configuration to disable unnecessary cops
21
+
5
22
  ## [1.0.0] - 2024-01-01
6
23
 
7
24
  ### Added
data/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  A Ruby gem providing a command-line tool to convert HTML and ERB files to Slim format.
4
4
 
5
+ **Online Converter**: Try the web-based version at [https://railsblueprint.com/html2slim](https://railsblueprint.com/html2slim)
6
+
5
7
  ## Requirements
6
8
 
7
9
  - Ruby 2.7 - 3.4 (compatible with latest Ruby versions)
data/bin/html2slim CHANGED
@@ -1,5 +1,4 @@
1
1
  #!/usr/bin/env ruby
2
-
3
2
  require 'thor'
4
3
  require 'pathname'
5
4
  require_relative '../lib/blueprint/html2slim'
@@ -42,7 +41,7 @@ class Html2SlimCLI < Thor
42
41
  puts ' html2slim -d old_views/*.erb # Convert and delete sources'
43
42
  exit 0
44
43
  elsif given_args.first == 'version' || given_args.include?('-v') || given_args.include?('--version')
45
- puts 'html2slim 1.0.0'
44
+ puts 'html2slim 1.1.0'
46
45
  exit 0
47
46
  else
48
47
  args = given_args.dup
@@ -71,15 +70,13 @@ class Html2SlimCLI < Thor
71
70
  puts 'Error: -o/--output can only be used with a single input file'
72
71
  exit 1
73
72
  end
74
-
73
+
75
74
  if options[:output] && options[:target_dir]
76
75
  puts 'Error: Cannot use both -o/--output and -t/--target-dir together'
77
76
  exit 1
78
77
  end
79
-
80
- if options[:backup] && options[:delete]
81
- puts 'Warning: -b/--backup takes precedence over -d/--delete'
82
- end
78
+
79
+ puts 'Warning: -b/--backup takes precedence over -d/--delete' if options[:backup] && options[:delete]
83
80
 
84
81
  converter = Blueprint::Html2Slim::Converter.new(indent_size: options[:indent])
85
82
  processed_count = 0
@@ -136,7 +133,7 @@ class Html2SlimCLI < Thor
136
133
  else
137
134
  input_path.basename
138
135
  end
139
-
136
+
140
137
  # Apply smart naming convention to the filename
141
138
  output_name = case relative_path.to_s
142
139
  when /\.html\.erb$/
@@ -148,7 +145,7 @@ class Html2SlimCLI < Thor
148
145
  else
149
146
  "#{relative_path}.slim"
150
147
  end
151
-
148
+
152
149
  Pathname.new(options[:target_dir]).join(output_name)
153
150
  else
154
151
  # Smart naming convention in place
@@ -182,7 +179,7 @@ class Html2SlimCLI < Thor
182
179
  end
183
180
 
184
181
  begin
185
- html_content = File.read(input_path)
182
+ html_content = File.read(input_path, encoding: 'UTF-8')
186
183
  slim_content = converter.convert(html_content)
187
184
 
188
185
  # Create backup if requested
@@ -195,16 +192,16 @@ class Html2SlimCLI < Thor
195
192
  output_path.dirname.mkpath
196
193
  # Ensure content ends with newline when writing to file
197
194
  slim_content += "\n" unless slim_content.end_with?("\n")
198
- File.write(output_path, slim_content)
195
+ File.write(output_path, slim_content, encoding: 'UTF-8')
199
196
 
200
197
  puts "Converted: #{input_path} -> #{output_path}"
201
-
198
+
202
199
  # Delete source file if requested (and not backing up)
203
200
  if options[:delete] && !options[:backup]
204
201
  File.delete(input_path)
205
202
  puts "Deleted: #{input_path}"
206
203
  end
207
-
204
+
208
205
  true
209
206
  rescue StandardError => e
210
207
  puts "Error converting #{input_path}: #{e.message}"
@@ -17,25 +17,25 @@ module Blueprint
17
17
 
18
18
  def convert(html_content)
19
19
  lines = []
20
-
20
+
21
21
  # Handle DOCTYPE declaration
22
22
  if html_content =~ /<!DOCTYPE\s+(.+?)>/i
23
23
  doctype_content = ::Regexp.last_match(1)
24
- if doctype_content =~ /strict/i
25
- lines << "doctype strict"
26
- elsif doctype_content =~ /transitional/i
27
- lines << "doctype transitional"
28
- elsif doctype_content =~ /frameset/i
29
- lines << "doctype frameset"
30
- elsif doctype_content =~ /html$/i
31
- lines << "doctype html"
32
- else
33
- lines << "doctype"
34
- end
24
+ lines << if doctype_content =~ /strict/i
25
+ 'doctype strict'
26
+ elsif doctype_content =~ /transitional/i
27
+ 'doctype transitional'
28
+ elsif doctype_content =~ /frameset/i
29
+ 'doctype frameset'
30
+ elsif doctype_content =~ /html$/i
31
+ 'doctype html'
32
+ else
33
+ 'doctype'
34
+ end
35
35
  # Remove DOCTYPE from content for further processing
36
36
  html_content = html_content.sub(/<!DOCTYPE\s+.+?>/i, '')
37
37
  end
38
-
38
+
39
39
  html_content = preprocess_erb(html_content)
40
40
  # Use HTML.parse for full documents, DocumentFragment for fragments
41
41
  if html_content =~ /<html/i
@@ -68,7 +68,7 @@ module Blueprint
68
68
  def preprocess_erb(content)
69
69
  # Convert ERB blocks to span elements to preserve hierarchy
70
70
  # This approach is inspired by the original html2slim gem
71
-
71
+
72
72
  # Keep ERB tags in attributes unchanged by temporarily replacing them
73
73
  erb_in_attrs = []
74
74
  content = content.gsub(/(<[^>]*)(<%=?.+?%>)([^>]*>)/) do
@@ -79,65 +79,79 @@ module Blueprint
79
79
  erb_in_attrs << erb
80
80
  "#{before}#{placeholder}#{after}"
81
81
  end
82
-
82
+
83
83
  # Handle multi-line ERB blocks first (with m flag for multiline)
84
84
  content = content.gsub(/<%\s*\n(.*?)\n\s*-?%>/m) do
85
- code_lines = ::Regexp.last_match(1).strip.split("\n")
86
- # Convert to multiple single-line ERB comments
87
- code_lines.map { |line| "<!--ERB_CODE:#{line.strip}-->" }.join("\n")
85
+ code_block = ::Regexp.last_match(1).strip
86
+ # Mark as multiline block for special handling
87
+ "<!--ERB_MULTILINE_CODE:#{code_block.gsub("-->", "__ARROW__")}-->"
88
+ end
89
+
90
+ # Handle multiline ERB output blocks (e.g., <%= form_with(...) spanning multiple lines %>)
91
+ content = content.gsub(/<%=\s*\n(.*?)\n\s*-?%>/m) do
92
+ code_block = ::Regexp.last_match(1).strip
93
+ # Check if it ends with do block
94
+ if code_block =~ /\bdo\s*(\|[^|]*\|)?\s*$/
95
+ # Convert to single line for block processing
96
+ single_line = code_block.gsub(/\s+/, ' ')
97
+ "<%= #{single_line} %>" # Keep for block processing
98
+ else
99
+ # Mark as multiline output for special handling
100
+ "<!--ERB_MULTILINE_OUTPUT:#{code_block.gsub("-->", "__ARROW__")}-->"
101
+ end
88
102
  end
89
-
103
+
90
104
  # Convert simple ERB output tags that don't create blocks
91
105
  # This prevents them from being caught by the block regex
92
106
  content = content.gsub(/<%=\s*([^%]+?)\s*%>/) do
93
107
  code = ::Regexp.last_match(1).strip
94
108
  # Skip if it's a do block
95
109
  if code =~ /\bdo\s*(\|[^|]*\|)?\s*$/
96
- "<%= #{code} %>" # Keep original, will be processed later
110
+ "<%= #{code} %>" # Keep original, will be processed later
97
111
  else
98
112
  %(<!--ERB_OUTPUT:#{code}-->)
99
113
  end
100
114
  end
101
-
115
+
102
116
  # Convert ERB blocks that create structure (do...end, if...end, etc.)
103
117
  # to span elements so their content becomes proper children
104
- content = content.gsub(/<%(-|=)?\s*((\s*(case|if|for|unless|until|while) .+?)|.+?do\s*(\|[^|]*\|)?\s*)-?%>/) do
118
+ content = content.gsub(/<%(-|=)?\s*((\s*(case|if|for|unless|until|while) .+?)|.+?do\s*(\|[^|]*\|)?\s*)-?%>/m) do
105
119
  type = ::Regexp.last_match(1)
106
120
  code = ::Regexp.last_match(2).strip
107
121
  # Preserve whether it was = or - in the code attribute
108
122
  prefix = type == '=' ? '=' : ''
109
- %(<span erb-code="#{prefix}#{code.gsub('"', '&quot;')}">)
123
+ %(<span erb-code="#{prefix}#{code.gsub('"', "&quot;")}">)
110
124
  end
111
-
125
+
112
126
  # Handle else
113
127
  content = content.gsub(/<%-?\s*else\s*-?%>/, %(</span><span erb-code="else">))
114
-
128
+
115
129
  # Handle elsif
116
130
  content = content.gsub(/<%-?\s*(elsif .+?)\s*-?%>/) do
117
131
  code = ::Regexp.last_match(1).strip
118
- %(</span><span erb-code="#{code.gsub('"', '&quot;')}">)
132
+ %(</span><span erb-code="#{code.gsub('"', "&quot;")}">)
119
133
  end
120
-
134
+
121
135
  # Handle when
122
136
  content = content.gsub(/<%-?\s*(when .+?)\s*-?%>/) do
123
137
  code = ::Regexp.last_match(1).strip
124
- %(</span><span erb-code="#{code.gsub('"', '&quot;')}">)
138
+ %(</span><span erb-code="#{code.gsub('"', "&quot;")}">)
125
139
  end
126
-
140
+
127
141
  # Handle end statements - close the span
128
142
  content = content.gsub(/<%\s*(end|}|end\s+-)\s*%>/, %(</span>))
129
-
143
+
130
144
  # Convert any remaining ERB code tags to comments
131
145
  content = content.gsub(/<%-?\s*(.+?)\s*%>/) do
132
146
  code = ::Regexp.last_match(1).strip
133
147
  %(<!--ERB_CODE:#{code}-->)
134
148
  end
135
-
149
+
136
150
  # Restore ERB tags in attributes
137
151
  erb_in_attrs.each_with_index do |erb, i|
138
152
  content = content.gsub("ERB_IN_ATTR_#{i}", erb)
139
153
  end
140
-
154
+
141
155
  content
142
156
  end
143
157
 
@@ -161,24 +175,24 @@ module Blueprint
161
175
  # Check if this is an ERB span element
162
176
  if node.name == 'span' && node['erb-code']
163
177
  erb_code = node['erb-code'].gsub('&quot;', '"')
164
-
178
+
165
179
  # Determine if it's output (=) or code (-)
166
- if erb_code =~ /^(if|unless|case|for|while|elsif|else|when)\b/
167
- lines << "#{indent}- #{erb_code}"
168
- elsif erb_code.start_with?('=')
169
- # It was originally <%= ... %>, use = prefix
170
- lines << "#{indent}= #{erb_code[1..-1].strip}"
171
- else
172
- # It was originally <% ... %>, use - prefix
173
- lines << "#{indent}- #{erb_code}"
174
- end
175
-
180
+ lines << if erb_code =~ /^(if|unless|case|for|while|elsif|else|when)\b/
181
+ "#{indent}- #{erb_code}"
182
+ elsif erb_code.start_with?('=')
183
+ # It was originally <%= ... %>, use = prefix
184
+ "#{indent}= #{erb_code[1..-1].strip}"
185
+ else
186
+ # It was originally <% ... %>, use - prefix
187
+ "#{indent}- #{erb_code}"
188
+ end
189
+
176
190
  # Process children with increased depth
177
191
  node.children.each do |child|
178
192
  child_lines = process_node(child, depth + 1)
179
193
  lines.concat(child_lines) unless child_lines.empty?
180
194
  end
181
-
195
+
182
196
  return lines
183
197
  end
184
198
 
@@ -206,6 +220,10 @@ module Blueprint
206
220
  text = process_inline_text(text.strip)
207
221
  if text.empty?
208
222
  lines << "#{indent}#{tag_line}"
223
+ elsif text.start_with?('/')
224
+ # Text starting with / needs pipe notation to avoid being treated as comment
225
+ lines << "#{indent}#{tag_line}"
226
+ lines << "#{" " * ((depth + 1) * @indent_size)}| #{text}"
209
227
  else
210
228
  lines << "#{indent}#{tag_line} #{text}"
211
229
  end
@@ -227,7 +245,7 @@ module Blueprint
227
245
  # Strip and split classes, filtering out empty strings
228
246
  classes = node['class']&.strip&.split(/\s+/)&.reject(&:empty?) || []
229
247
  attributes = collect_attributes(node)
230
-
248
+
231
249
  # Treat empty id as no id
232
250
  id = nil if id && id.strip.empty?
233
251
 
@@ -299,14 +317,36 @@ module Blueprint
299
317
  # Extract indentation level if present
300
318
  extra_indent = 0
301
319
  if comment_text =~ /:INDENT:(\d+)$/
302
- extra_indent = $1.to_i
320
+ extra_indent = ::Regexp.last_match(1).to_i
303
321
  comment_text = comment_text.sub(/:INDENT:\d+$/, '')
304
322
  end
305
-
323
+
306
324
  total_depth = depth + extra_indent
307
325
  indent = ' ' * (total_depth * @indent_size)
308
326
 
309
- if comment_text.start_with?('ERB_OUTPUT_BLOCK:')
327
+ if comment_text.start_with?('ERB_MULTILINE_CODE:')
328
+ erb_content = comment_text.sub('ERB_MULTILINE_CODE:', '').gsub('__ARROW__', '-->')
329
+ # Use ruby: block for multiline code
330
+ lines = ["#{indent}ruby:"]
331
+ erb_content.lines.each do |line|
332
+ lines << "#{indent} #{line.rstrip}"
333
+ end
334
+ lines
335
+ elsif comment_text.start_with?('ERB_MULTILINE_OUTPUT:')
336
+ erb_content = comment_text.sub('ERB_MULTILINE_OUTPUT:', '').gsub('__ARROW__', '-->')
337
+ # For multiline output, use line continuation
338
+ lines = erb_content.lines.map(&:rstrip)
339
+ if lines.length == 1
340
+ ["#{indent}= #{lines[0]}"]
341
+ else
342
+ result = ["#{indent}= #{lines[0]} \\"]
343
+ lines[1..-2].each do |line|
344
+ result << "#{indent} #{line} \\"
345
+ end
346
+ result << "#{indent} #{lines[-1]}" if lines.length > 1
347
+ result
348
+ end
349
+ elsif comment_text.start_with?('ERB_OUTPUT_BLOCK:')
310
350
  erb_content = comment_text.sub('ERB_OUTPUT_BLOCK:', '')
311
351
  ["#{indent}= #{erb_content}"]
312
352
  elsif comment_text.start_with?('ERB_OUTPUT:')
@@ -1,5 +1,5 @@
1
1
  module Blueprint
2
2
  module Html2Slim
3
- VERSION = '1.0.0'.freeze
3
+ VERSION = '1.1.0'.freeze
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: blueprint-html2slim
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vladimir Elchinov