Files
openfoodnetwork/lib/haml_up.rb
2024-02-22 15:01:14 +11:00

119 lines
2.9 KiB
Ruby

# frozen_string_literal: true
# Upgrade HAML attribute syntax to prepare for HAML 6.
#
# HAML 6 stopped supporting nested hash attributes other than `data` and `aria`.
# We used to be able to write:
#
# %div{ ng: { class: "upper", bind: "model" } }
#
# This needs to be written in a flat structure now:
#
# %div{ "ng-class" => "upper", "ng-bind" => "model" }
#
require "fileutils"
require "haml"
class HamlUp
def upgrade_file(filename)
template = File.read(filename)
rewrite_template(template)
File.write(filename, template)
end
def rewrite_template(template)
haml_attributes(template).compact.each do |attributes|
rewrite_attributes(template, attributes)
end
end
def rewrite_attributes(template, original)
attributes = parse_attributes(original)
if attributes.nil? # parser failed
puts "Warning: failed to parse:\n" # rubocop:disable Rails/Output
puts original # rubocop:disable Rails/Output
return
end
parse_deprecated_hashes(attributes)
to_transform = attributes.select { |_k, v| v.is_a? Hash }
return if to_transform.empty?
to_transform.each do |key, hash|
add_full_keys(attributes, key, hash)
attributes.delete(key)
end
replace_attributes(template, original, attributes)
end
def haml_attributes(template)
options = Haml::Options.new
parsed_tree = Haml::Parser.new(options).call(template)
elements = flatten_tree(parsed_tree)
elements.map { |e| e.value[:dynamic_attributes]&.old }
end
def flatten_tree(parent)
parent.children.flat_map do |child|
[child, *flatten_tree(child)]
end
end
def parse_attributes(string)
Haml::AttributeParser.parse(string)
end
def parse_deprecated_hashes(hash)
hash.each do |key, value|
next if ["aria", "data"].include?(key)
parsed = parse_attributes(value)
next unless parsed.is_a? Hash
parse_deprecated_hashes(parsed)
hash[key] = parsed
end
end
def add_full_keys(attributes, key, hash)
hash.each do |subkey, value|
full_key = "#{key}-#{subkey}"
if value.is_a? Hash
add_full_keys(attributes, full_key, value)
else
attributes[full_key] = value
end
end
end
def replace_attributes(template, original, attributes)
parsed_lines = original.split("\n")
lines_as_regex = parsed_lines.map(&Regexp.method(:escape))
pattern = lines_as_regex.join("\n\s*")
template.gsub!(/#{pattern}/, stringify(attributes))
end
def stringify(hash)
entries = hash.map do |key, value|
value = stringify(value) if value.is_a? Hash
# We prefer the Ruby 1.9 hash syntax with symbols followed by a colon
# like this:
#
# %button{ disabled: true, "ng-class": "primary-button" }
#
# Symbols start with `:` which we slice off. It gets appended below.
key = key.to_sym.inspect.slice(1..-1)
"#{key}: #{value}"
end
"{ #{entries.join(', ')} }"
end
end