1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
|
#!/usr/bin/env ruby
require 'fileutils'
require 'tmpdir'
HAML_REPO = 'haml/haml'
HAML_VERSION = '5.2.1'
module GitHubFetcher
def self.fetch(repo, tag:, path:)
Dir.mktmpdir do |dir|
Dir.chdir(dir) do
url = "https://github.com/#{repo}/archive/#{tag}.tar.gz"
system("curl -L --fail --retry 3 --retry-delay 1 #{url} -o - | tar zxf -")
FileUtils.mv("#{File.basename(repo)}-#{tag.sub(/\Av/, '')}", path)
end
end
end
end
class LicenseBuilder
DELIMITER = "\n==="
def initialize(haml_license:, hamlit_license:)
@haml_license = haml_license
@hamlit_license = hamlit_license
end
def build
license = [
File.read(@haml_license),
File.read(@hamlit_license).split(DELIMITER, 2).last,
].join(DELIMITER)
File.write(@hamlit_license, license)
end
end
# Generate lib/hamlit/parser from haml
class HamlitParserBuilder
TARGET_FILES = [
'attribute_builder.rb',
'buffer.rb',
'error.rb',
'helpers.rb',
'options.rb',
'temple_engine.rb',
# TODO: make the upstream sharable first
# 'parser.rb',
'util.rb',
'helpers/xss_mods.rb',
]
# Classes which are just referenced by Options and not really used by the parser
DUMMY_CLASSES = {
'compiler.rb' => 'Compiler',
'escapable.rb' => 'Escapable',
'generator.rb' => 'Generator',
}
def initialize(haml:, hamlit_parser:)
@haml = haml
@hamlit_parser = hamlit_parser
end
def build
TARGET_FILES.each do |file|
src_path = File.join(@haml, file)
dest_path = File.join(@hamlit_parser, "haml_#{file}")
FileUtils.mkdir_p(File.dirname(dest_path))
FileUtils.cp(src_path, dest_path)
src = File.read(dest_path)
patch_source!(src, file: file)
File.write(dest_path, src)
end
DUMMY_CLASSES.each do |file, klass|
dest_path = File.join(@hamlit_parser, "haml_#{file}")
src = "class Hamlit::Haml#{klass}; end\n"
File.write(dest_path, src)
end
end
private
def patch_source!(src, file:)
# Use Hamlit::HamlFoo instead of Haml::Foo
src.gsub!(/^module Haml\n((?: #[^\n]+\n)*) (module|class) ([^ ]+)/, "module Hamlit\n\\1 \\2 Haml\\3")
src.gsub!(/\bHaml::/, 'Hamlit::Haml')
# Prefix Haml to references without Haml::
src.gsub!(/\b(AttributeBuilder|Error|InvalidAttributeNameError|Options|Parser|SyntaxError)\./, 'Haml\0')
src.gsub!(/\brescue SyntaxError /, 'rescue HamlSyntaxError ')
# Hamlit should not rely on Haml
src.gsub!(/^require 'haml\/([^']+)'/, "require 'hamlit/parser/haml_\\1'")
case file
when 'error.rb'
src.gsub!(/^ class ([^ ]+) < ([^ ]+);/, ' class Haml\1 < Haml\2;')
when 'helpers.rb'
src.gsub!(/^ def is_haml\?\n false\n end/m) { |str| str.gsub(/^ /, ' # ') } # not needed for the parser
when 'options.rb'
src.gsub!(/\.is_a\?\(Options\)/, '.is_a?(HamlOptions)')
when 'temple_engine.rb'
src.gsub!(/\buse (Generator|Escapable)$/, 'use Haml\1')
end
end
end
FileUtils.rm_rf(haml = File.expand_path('../haml', __dir__))
GitHubFetcher.fetch(HAML_REPO, tag: HAML_VERSION, path: haml)
hamlit = File.expand_path('..', __dir__)
LicenseBuilder.new(
haml_license: File.join(haml, 'MIT-LICENSE'),
hamlit_license: File.join(hamlit, 'LICENSE.txt'),
).build
hamlit_parser = File.join(hamlit, 'lib/hamlit/parser')
# TODO: FileUtils.rm_rf(hamlit_parser = File.join(hamlit, 'lib/hamlit/parser'))
HamlitParserBuilder.new(
haml: File.join(haml, 'lib/haml'),
hamlit_parser: hamlit_parser,
).build
|