haml2html 0.1.3 → 0.1.4

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: 54fab4cd9a6853ba2f3bb3568c17a18f1667b7507a2de432966bea62e6a218e1
4
- data.tar.gz: 11e567f5d534e34e2f1a88029f94e20847d13953cdd995e169a65246aaba8ed9
3
+ metadata.gz: 14f09bc19dfb59cd98fd339e28b7a9a82031ff68a6f9bcb1f09bea25ad5eef24
4
+ data.tar.gz: 2dd302143f309d861781c904673a986c9525df51538538212194c148eb629929
5
5
  SHA512:
6
- metadata.gz: e5506405f5757513488fd1771d86adbee964c08d89d5f42f54e1236e6a2e32f1d871ca29c9ee7db83687629325d241416b222e279253f1a464b603ef4c0425af
7
- data.tar.gz: bed4c344c88cf6cbd41d89f59398c15ea724a63afb2b91c852f1a1d3402d676dda2be37d6a85414173437fad54064dd4642591e1547d2d38403ba1e38abdb976
6
+ metadata.gz: f64fb147af2fe01e270f30f5ada9c75776c3434cbd8cf3c724b5f6893edee8ec53462288b3349c0350bd3a90a0e66b5d1f16950849e1671da524abedebe557be
7
+ data.tar.gz: '04956657bb059627298a8e38502386229a2bdc8ca05f2af5687cf37783b4548ddf52562169c0fab171f1542795d32a1a9159d310cee821234d131b8be7230233'
data/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.4
4
+
5
+ - Remove the `tag.attributes` fallback and emit literal dynamic attributes directly.
6
+
3
7
  ## 0.1.3
4
8
 
5
9
  - Fix generated ERB for Haml attributes with mixed static and dynamic classes.
data/README.md CHANGED
@@ -79,7 +79,7 @@ end
79
79
  - Ruby output and control flow: `=`, `!=`, `- if`, `- each do`, and similar blocks.
80
80
  - Public comments and silent comments.
81
81
  - `:plain`, `:escaped`, `:javascript`, `:css`, `:erb`, and `:ruby` filters.
82
- - Dynamic Haml attributes through Rails `tag.attributes`.
82
+ - Literal dynamic Haml attribute hashes, including simple `class`, `data`, `aria`, and boolean attributes.
83
83
 
84
84
  ## Limitations
85
85
 
@@ -9,6 +9,12 @@ require_relative "diagnostic"
9
9
  module Haml2html
10
10
  class Converter
11
11
  VOID_TAGS = %w[area base br col embed hr img input link meta param source track wbr].freeze
12
+ BOOLEAN_ATTRIBUTES = %w[
13
+ allowfullscreen async autofocus autoplay checked compact controls declare default defaultchecked defaultmuted
14
+ defaultselected defer disabled enabled formnovalidate hidden indeterminate inert ismap itemscope loop multiple
15
+ muted nohref nomodule noresize noshade novalidate nowrap open pauseonexit playsinline readonly required
16
+ reversed scoped seamless selected sortable truespeed typemustmatch visible
17
+ ].freeze
12
18
  SUPPORTED_FILTERS = %w[plain escaped javascript css erb ruby].freeze
13
19
 
14
20
  attr_reader :diagnostics
@@ -106,12 +112,14 @@ module Haml2html
106
112
  dynamic = dynamic_attributes_expression(value[:dynamic_attributes])
107
113
  return static_attributes(attrs) unless dynamic
108
114
 
109
- if (simple_attrs = simple_dynamic_attributes(dynamic))
110
- dynamic_class = simple_attrs.delete("class")
111
- return "#{static_attributes(attrs, dynamic_class: dynamic_class)}#{simple_attrs.map { |name, attr_value| dynamic_attribute(name, attr_value) }.join}"
115
+ simple_attrs = simple_dynamic_attributes(dynamic)
116
+ unless simple_attrs
117
+ unsupported(node, "dynamic attributes", "only literal dynamic attribute hashes can be converted safely")
118
+ return static_attributes(attrs)
112
119
  end
113
120
 
114
- "#{static_attributes(attrs)}#{dynamic_attributes(dynamic)}"
121
+ dynamic_class = simple_attrs.delete("class")
122
+ "#{static_attributes(attrs, dynamic_class: dynamic_class&.then { |attr| dynamic_attribute_value(attr) })}#{simple_attrs.values.map { |attr| dynamic_attribute(attr) }.join}"
115
123
  end
116
124
 
117
125
  def emit_script(node, indent)
@@ -195,12 +203,15 @@ module Haml2html
195
203
  end.join
196
204
  end
197
205
 
198
- def dynamic_attributes(dynamic)
199
- %(<%== (_haml2html_attrs = tag.attributes(**#{dynamic})).empty? ? "" : " \#{_haml2html_attrs}" %>)
200
- end
206
+ def dynamic_attribute(attr)
207
+ name = attr.fetch(:name)
208
+ if BOOLEAN_ATTRIBUTES.include?(name)
209
+ return attr.fetch(:value) ? " #{name}" : "" unless attr.fetch(:dynamic)
210
+
211
+ return %(<%= (#{attr.fetch(:value)}) ? " #{name}" : "" %>)
212
+ end
201
213
 
202
- def dynamic_attribute(name, value)
203
- %( #{name}="#{value}")
214
+ %( #{name}="#{dynamic_attribute_value(attr)}")
204
215
  end
205
216
 
206
217
  def simple_dynamic_attributes(dynamic)
@@ -212,33 +223,147 @@ module Haml2html
212
223
  return nil unless associations.is_a?(Array)
213
224
 
214
225
  associations.each_with_object({}) do |association, attrs|
215
- return nil unless association&.first == :assoc_new
226
+ name = dynamic_attribute_name(association)
227
+ return nil unless name
228
+
229
+ if name == "data"
230
+ data_attrs = simple_prefixed_attributes("data", association[2], dynamic)
231
+ return nil unless data_attrs
232
+
233
+ attrs.merge!(data_attrs)
234
+ elsif name == "aria"
235
+ aria_attrs = simple_prefixed_attributes("aria", association[2], dynamic)
236
+ return nil unless aria_attrs
237
+
238
+ attrs.merge!(aria_attrs)
239
+ else
240
+ value = simple_dynamic_attribute_value(name, association[2], dynamic)
241
+ return nil unless value
242
+
243
+ attrs[name] = value.merge(name: name)
244
+ end
245
+ end
246
+ end
216
247
 
217
- label = association[1]
218
- return nil unless label&.first == :@label
248
+ def dynamic_attribute_name(association)
249
+ return nil unless association&.first == :assoc_new
219
250
 
220
- value = simple_dynamic_attribute_value(association[2])
251
+ label = association[1]
252
+ return nil unless label&.first == :@label
253
+
254
+ label[1].delete_suffix(":")
255
+ end
256
+
257
+ def simple_prefixed_attributes(prefix, node, source)
258
+ return nil unless node&.first == :hash
259
+
260
+ associations = node.dig(1, 1)
261
+ return nil unless associations.is_a?(Array)
262
+
263
+ associations.each_with_object({}) do |association, attrs|
264
+ name = dynamic_attribute_name(association)
265
+ return nil unless name
266
+
267
+ value = simple_dynamic_attribute_value(name, association[2], source)
221
268
  return nil unless value
269
+ next if !value.fetch(:dynamic) && [false, nil].include?(value.fetch(:value))
222
270
 
223
- attrs[label[1].delete_suffix(":")] = value
271
+ attr_name = "#{prefix}-#{name.tr("_", "-")}"
272
+ attrs[attr_name] = value.merge(name: attr_name)
224
273
  end
225
274
  end
226
275
 
227
- def simple_dynamic_attribute_value(node)
276
+ def simple_dynamic_attribute_value(name, node, source)
228
277
  case node&.first
229
278
  when :string_literal
230
- escape_attr(string_literal_content(node))
279
+ string = string_literal_content(node)
280
+ return nil unless string
281
+
282
+ { dynamic: false, value: string }
283
+ when :symbol_literal
284
+ { dynamic: false, value: symbol_literal_content(node) }
285
+ when :var_ref
286
+ if (keyword = node.dig(1, 1)) && %w[true false nil].include?(keyword)
287
+ return { dynamic: false, value: literal_keyword_value(keyword) }
288
+ end
289
+
290
+ expression = ruby_expression(node, source)
291
+ expression && { dynamic: true, value: expression }
231
292
  when :vcall
232
- ident = node.dig(1, 1)
233
- return nil unless ident
293
+ expression = ruby_expression(node, source)
294
+ expression && { dynamic: true, value: expression }
295
+ when :array
296
+ return nil unless name == "class"
297
+
298
+ expression = class_names_expression(node, source)
299
+ expression && { dynamic: true, value: expression }
300
+ else
301
+ expression = ruby_expression(node, source)
302
+ expression && { dynamic: true, value: expression }
303
+ end
304
+ end
305
+
306
+ def dynamic_attribute_value(attr)
307
+ value = attr.fetch(:value)
308
+ attr.fetch(:dynamic) ? "<%= #{value} %>" : escape_attr(value.to_s)
309
+ end
310
+
311
+ def class_names_expression(node, source)
312
+ values = node[1]
313
+ return "class_names" if values.nil? || values.empty?
314
+
315
+ args = values.map { |value| ruby_expression(value, source) }
316
+ return nil if args.any?(&:nil?)
317
+
318
+ "class_names(#{args.join(", ")})"
319
+ end
234
320
 
235
- "<%= #{ident} %>"
321
+ def ruby_expression(node, source)
322
+ case node&.first
323
+ when :string_literal
324
+ string = string_literal_content(node)
325
+ string&.inspect
326
+ when :symbol_literal
327
+ ":#{symbol_literal_content(node)}"
328
+ when :var_ref, :vcall
329
+ node.dig(1, 1)
330
+ when :unary
331
+ expression = ruby_expression(node[2], source)
332
+ expression && "#{node[1]}#{expression}"
333
+ when :call
334
+ receiver = ruby_expression(node[1], source)
335
+ method = node.dig(3, 1)
336
+ receiver && method && "#{receiver}.#{method}"
337
+ when :method_add_arg
338
+ ruby_expression(node[1], source) if node[2]&.empty?
339
+ when :fcall
340
+ node.dig(1, 1)
236
341
  end
237
342
  end
238
343
 
239
344
  def string_literal_content(node)
240
- content = node.dig(1, 1, 1)
241
- content.to_s
345
+ content = node[1]
346
+ return nil unless content&.first == :string_content
347
+
348
+ parts = content[1..] || []
349
+ return nil unless parts.all? { |part| part.is_a?(Array) && part.first == :@tstring_content }
350
+
351
+ parts.map { |part| part[1] }.join
352
+ end
353
+
354
+ def symbol_literal_content(node)
355
+ node.dig(1, 1, 1).to_s
356
+ end
357
+
358
+ def literal_keyword_value(keyword)
359
+ case keyword
360
+ when "true"
361
+ true
362
+ when "false"
363
+ false
364
+ when "nil"
365
+ nil
366
+ end
242
367
  end
243
368
 
244
369
  def dynamic_attributes_expression(dynamic)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Haml2html
4
- VERSION = "0.1.3"
4
+ VERSION = "0.1.4"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: haml2html
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kyle
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-05-08 00:00:00.000000000 Z
11
+ date: 2026-05-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: haml