Madogiwa Blog

主に技術系の学習メモに使っていきます。

Ruby: Haml v6アップデート時のカスタム属性の振る舞いの互換性を維持するメモ📝

Haml v6からHamlの内部実装がHamlitに置き換わりパフォーマンス向上等のメリットがありますが、

github.com

以下のissueでコメントされている通り、

github.com

Vue.js等を利用している場合に以下のようなfalsyの値がv5系では<cutsom-element />となっていたのが、

%cutsom-element{ ":costom-attributes": nil  }

Haml v6では<cutsom-element :costom-attributes='' />となってしまいます。

上記の場合Vue.jsを利用しているHaml v6の挙動ではpropsのデフォルト値が利用されなくなり、明示的にundefinedを渡すような実装に修正する必要があり非常に影響が大きいです😢

これに対応する仕組みが以下のHaml v6.2.2でリリースされたHaml::BOOLEAN_ATTRIBUTESに任意の属性名の文字列・正規表現を追加することでHaml v5相当の振る舞いにすることができます。

github.com

全部やるなら以下とすれば良さそうですが、

Haml::BOOLEAN_ATTRIBUTES.push(/.*/)

この変更自体がパフォーマンス向上のトレードオフで発生しているもののようなので、パフォーマンスに問題がないか等は計測して対応を入れる必要がありそうです。

It varies. No impact, as slow as Haml 5, or slower than Haml 5, depending on the benchmark.

https://github.com/haml/haml/issues/1148#issuecomment-1754421295

📝以下のようなスクリプトで既存のhamlファイルを読み込んで利用されているカスタムタグの属性のリストを抽出し、それがだけ許可するような感じでもいいのかもしれない🤔

require 'bundler/inline'

gemfile do
  source 'https://rubygems.org'
  gem 'haml'
end

require 'haml/parser'

haml = <<~HAML
  %h1 Hello!
  %custom-element{ ":custom-attributes1": false, "string-attributes1": "hello" }
  %custom-element{ ":custom-attributes2": nil, "string-attributes2": "" }
  %p
    %span World!
HAML

parser = Haml::Parser.new({})
parsedHaml = parser.call(haml)

def extract_tags_and_attributes(node)
  result = []

  node.children.each do |child|
    tag_name = child.value[:name]
    attribute_names = child.value[:attributes].keys
    dynamic_attributes = child.value[:dynamic_attributes]
    dynamic_attribute_hash = dynamic_attributes.old ? eval(dynamic_attributes.old) : {}
    result << { name: tag_name, attributes: attribute_names + dynamic_attribute_hash.keys }
    result.concat(extract_tags_and_attributes(child))
  end

  result
end

tags_and_attributes = extract_tags_and_attributes(parsedHaml)
is_custom_tag = ->(tag) { tag[:name].include?("-") }

puts tags_and_attributes.select(&is_custom_tag)
                        .flat_map { |tag| tag[:attributes] }
                        .uniq
# =>
# :custom-attributes1
# string-attributes1
# :custom-attributes2
# string-attributes2