hamlit 2.9.3

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.
Files changed (107) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +16 -0
  3. data/.travis.yml +45 -0
  4. data/CHANGELOG.md +676 -0
  5. data/Gemfile +28 -0
  6. data/LICENSE.txt +44 -0
  7. data/README.md +150 -0
  8. data/REFERENCE.md +266 -0
  9. data/Rakefile +117 -0
  10. data/benchmark/boolean_attribute.haml +6 -0
  11. data/benchmark/class_attribute.haml +5 -0
  12. data/benchmark/common_attribute.haml +3 -0
  13. data/benchmark/data_attribute.haml +4 -0
  14. data/benchmark/dynamic_attributes/boolean_attribute.haml +4 -0
  15. data/benchmark/dynamic_attributes/class_attribute.haml +4 -0
  16. data/benchmark/dynamic_attributes/common_attribute.haml +2 -0
  17. data/benchmark/dynamic_attributes/data_attribute.haml +2 -0
  18. data/benchmark/dynamic_attributes/id_attribute.haml +2 -0
  19. data/benchmark/dynamic_boolean_attribute.haml +4 -0
  20. data/benchmark/etc/attribute_builder.haml +5 -0
  21. data/benchmark/etc/real_sample.haml +888 -0
  22. data/benchmark/etc/real_sample.rb +11 -0
  23. data/benchmark/etc/static_analyzer.haml +1 -0
  24. data/benchmark/etc/string_interpolation.haml +2 -0
  25. data/benchmark/etc/tags.haml +3 -0
  26. data/benchmark/etc/tags_loop.haml +2 -0
  27. data/benchmark/ext/build_data.rb +17 -0
  28. data/benchmark/ext/build_id.rb +13 -0
  29. data/benchmark/id_attribute.haml +3 -0
  30. data/benchmark/plain.haml +4 -0
  31. data/benchmark/script.haml +4 -0
  32. data/benchmark/slim/LICENSE +21 -0
  33. data/benchmark/slim/context.rb +11 -0
  34. data/benchmark/slim/run-benchmarks.rb +94 -0
  35. data/benchmark/slim/view.erb +23 -0
  36. data/benchmark/slim/view.haml +18 -0
  37. data/benchmark/slim/view.slim +17 -0
  38. data/benchmark/utils/benchmark_ips_extension.rb +43 -0
  39. data/bin/bench +77 -0
  40. data/bin/console +11 -0
  41. data/bin/ruby +3 -0
  42. data/bin/setup +7 -0
  43. data/bin/stackprof +27 -0
  44. data/bin/test +24 -0
  45. data/exe/hamlit +6 -0
  46. data/ext/hamlit/extconf.rb +10 -0
  47. data/ext/hamlit/hamlit.c +553 -0
  48. data/ext/hamlit/hescape.c +108 -0
  49. data/ext/hamlit/hescape.h +20 -0
  50. data/hamlit.gemspec +45 -0
  51. data/lib/hamlit.rb +11 -0
  52. data/lib/hamlit/attribute_builder.rb +173 -0
  53. data/lib/hamlit/attribute_compiler.rb +123 -0
  54. data/lib/hamlit/attribute_parser.rb +110 -0
  55. data/lib/hamlit/cli.rb +130 -0
  56. data/lib/hamlit/compiler.rb +97 -0
  57. data/lib/hamlit/compiler/children_compiler.rb +112 -0
  58. data/lib/hamlit/compiler/comment_compiler.rb +36 -0
  59. data/lib/hamlit/compiler/doctype_compiler.rb +46 -0
  60. data/lib/hamlit/compiler/script_compiler.rb +102 -0
  61. data/lib/hamlit/compiler/silent_script_compiler.rb +24 -0
  62. data/lib/hamlit/compiler/tag_compiler.rb +74 -0
  63. data/lib/hamlit/engine.rb +37 -0
  64. data/lib/hamlit/error.rb +15 -0
  65. data/lib/hamlit/escapable.rb +13 -0
  66. data/lib/hamlit/filters.rb +75 -0
  67. data/lib/hamlit/filters/base.rb +12 -0
  68. data/lib/hamlit/filters/cdata.rb +20 -0
  69. data/lib/hamlit/filters/coffee.rb +17 -0
  70. data/lib/hamlit/filters/css.rb +33 -0
  71. data/lib/hamlit/filters/erb.rb +10 -0
  72. data/lib/hamlit/filters/escaped.rb +22 -0
  73. data/lib/hamlit/filters/javascript.rb +33 -0
  74. data/lib/hamlit/filters/less.rb +20 -0
  75. data/lib/hamlit/filters/markdown.rb +10 -0
  76. data/lib/hamlit/filters/plain.rb +29 -0
  77. data/lib/hamlit/filters/preserve.rb +22 -0
  78. data/lib/hamlit/filters/ruby.rb +10 -0
  79. data/lib/hamlit/filters/sass.rb +15 -0
  80. data/lib/hamlit/filters/scss.rb +15 -0
  81. data/lib/hamlit/filters/text_base.rb +25 -0
  82. data/lib/hamlit/filters/tilt_base.rb +49 -0
  83. data/lib/hamlit/force_escapable.rb +29 -0
  84. data/lib/hamlit/helpers.rb +15 -0
  85. data/lib/hamlit/html.rb +14 -0
  86. data/lib/hamlit/identity.rb +13 -0
  87. data/lib/hamlit/object_ref.rb +30 -0
  88. data/lib/hamlit/parser.rb +49 -0
  89. data/lib/hamlit/parser/MIT-LICENSE +20 -0
  90. data/lib/hamlit/parser/README.md +30 -0
  91. data/lib/hamlit/parser/haml_buffer.rb +348 -0
  92. data/lib/hamlit/parser/haml_compiler.rb +553 -0
  93. data/lib/hamlit/parser/haml_error.rb +61 -0
  94. data/lib/hamlit/parser/haml_helpers.rb +727 -0
  95. data/lib/hamlit/parser/haml_options.rb +286 -0
  96. data/lib/hamlit/parser/haml_parser.rb +800 -0
  97. data/lib/hamlit/parser/haml_util.rb +288 -0
  98. data/lib/hamlit/parser/haml_xss_mods.rb +109 -0
  99. data/lib/hamlit/rails_helpers.rb +51 -0
  100. data/lib/hamlit/rails_template.rb +59 -0
  101. data/lib/hamlit/railtie.rb +10 -0
  102. data/lib/hamlit/ruby_expression.rb +32 -0
  103. data/lib/hamlit/string_splitter.rb +88 -0
  104. data/lib/hamlit/template.rb +28 -0
  105. data/lib/hamlit/utils.rb +18 -0
  106. data/lib/hamlit/version.rb +4 -0
  107. metadata +361 -0
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+ require 'hamlit/ruby_expression'
3
+
4
+ module Hamlit
5
+ class AttributeParser
6
+ class ParseSkip < StandardError
7
+ end
8
+
9
+ def self.parse(text)
10
+ self.new.parse(text)
11
+ end
12
+
13
+ def parse(text)
14
+ exp = wrap_bracket(text)
15
+ return if RubyExpression.syntax_error?(exp)
16
+
17
+ hash = {}
18
+ tokens = Ripper.lex(exp)[1..-2] || []
19
+ each_attr(tokens) do |attr_tokens|
20
+ key = parse_key!(attr_tokens)
21
+ hash[key] = attr_tokens.map { |t| t[2] }.join.strip
22
+ end
23
+ hash
24
+ rescue ParseSkip
25
+ nil
26
+ end
27
+
28
+ private
29
+
30
+ def wrap_bracket(text)
31
+ text = text.strip
32
+ return text if text[0] == '{'
33
+ "{#{text}}"
34
+ end
35
+
36
+ def parse_key!(tokens)
37
+ _, type, str = tokens.shift
38
+ case type
39
+ when :on_sp
40
+ parse_key!(tokens)
41
+ when :on_label
42
+ str.tr(':', '')
43
+ when :on_symbeg
44
+ _, _, key = tokens.shift
45
+ assert_type!(tokens.shift, :on_tstring_end) if str != ':'
46
+ skip_until_hash_rocket!(tokens)
47
+ key
48
+ when :on_tstring_beg
49
+ _, _, key = tokens.shift
50
+ next_token = tokens.shift
51
+ unless next_token[1] == :on_label_end
52
+ assert_type!(next_token, :on_tstring_end)
53
+ skip_until_hash_rocket!(tokens)
54
+ end
55
+ key
56
+ else
57
+ raise ParseSkip
58
+ end
59
+ end
60
+
61
+ def assert_type!(token, type)
62
+ raise ParseSkip if token[1] != type
63
+ end
64
+
65
+ def skip_until_hash_rocket!(tokens)
66
+ until tokens.empty?
67
+ _, type, str = tokens.shift
68
+ break if type == :on_op && str == '=>'
69
+ end
70
+ end
71
+
72
+ def each_attr(tokens)
73
+ attr_tokens = []
74
+ open_tokens = Hash.new { |h, k| h[k] = 0 }
75
+
76
+ tokens.each do |token|
77
+ _, type, _ = token
78
+ case type
79
+ when :on_comma
80
+ if open_tokens.values.all?(&:zero?)
81
+ yield(attr_tokens)
82
+ attr_tokens = []
83
+ next
84
+ end
85
+ when :on_lbracket
86
+ open_tokens[:array] += 1
87
+ when :on_rbracket
88
+ open_tokens[:array] -= 1
89
+ when :on_lbrace
90
+ open_tokens[:block] += 1
91
+ when :on_rbrace
92
+ open_tokens[:block] -= 1
93
+ when :on_lparen
94
+ open_tokens[:paren] += 1
95
+ when :on_rparen
96
+ open_tokens[:paren] -= 1
97
+ when :on_embexpr_beg
98
+ open_tokens[:embexpr] += 1
99
+ when :on_embexpr_end
100
+ open_tokens[:embexpr] -= 1
101
+ when :on_sp
102
+ next if attr_tokens.empty?
103
+ end
104
+
105
+ attr_tokens << token
106
+ end
107
+ yield(attr_tokens) unless attr_tokens.empty?
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,130 @@
1
+ # frozen_string_literal: true
2
+ require 'hamlit'
3
+ require 'thor'
4
+
5
+ module Hamlit
6
+ class CLI < Thor
7
+ class_option :escape_html, type: :boolean, default: true
8
+ class_option :escape_attrs, type: :boolean, default: true
9
+
10
+ desc 'render HAML', 'Render haml template'
11
+ option :load_path, type: :string, aliases: %w[-I]
12
+ option :require, type: :string, aliases: %w[-r]
13
+ def render(file)
14
+ process_load_options
15
+ code = generate_code(file)
16
+ puts eval(code)
17
+ end
18
+
19
+ desc 'compile HAML', 'Show compile result'
20
+ option :actionview, type: :boolean, default: false, aliases: %w[-a]
21
+ option :color, type: :boolean, default: false, aliases: %w[-c]
22
+ def compile(file)
23
+ code = generate_code(file)
24
+ puts_code(code, color: options[:color])
25
+ end
26
+
27
+ desc 'temple HAML', 'Show temple intermediate expression'
28
+ option :color, type: :boolean, default: false, aliases: %w[-c]
29
+ def temple(file)
30
+ pp_object(generate_temple(file), color: options[:color])
31
+ end
32
+
33
+ desc 'parse HAML', 'Show parse result'
34
+ option :color, type: :boolean, default: false, aliases: %w[-c]
35
+ def parse(file)
36
+ pp_object(generate_ast(file), color: options[:color])
37
+ end
38
+
39
+ desc 'version', 'Show the used hamlit version'
40
+ def version
41
+ puts Hamlit::VERSION
42
+ end
43
+
44
+ private
45
+
46
+ def process_load_options
47
+ if options[:load_path]
48
+ options[:load_path].split(':').each do |dir|
49
+ $LOAD_PATH.unshift(dir) unless $LOAD_PATH.include?(dir)
50
+ end
51
+ end
52
+
53
+ if options[:require]
54
+ require options[:require]
55
+ end
56
+ end
57
+
58
+ def read_file(file)
59
+ if file == '-'
60
+ STDIN.read
61
+ else
62
+ File.read(file)
63
+ end
64
+ end
65
+
66
+ def generate_code(file)
67
+ template = read_file(file)
68
+ if options[:actionview]
69
+ require 'action_view'
70
+ require 'action_view/base'
71
+ require 'hamlit/rails_template'
72
+ handler = Hamlit::RailsTemplate.new
73
+ template = ActionView::Template.new(template, 'inline template', handler, { locals: [] })
74
+ code = handler.call(template)
75
+ <<-end_src
76
+ def _inline_template___2144273726781623612_70327218547300(local_assigns, output_buffer)
77
+ _old_virtual_path, @virtual_path = @virtual_path, nil;_old_output_buffer = @output_buffer;;#{code}
78
+ ensure
79
+ @virtual_path, @output_buffer = _old_virtual_path, _old_output_buffer
80
+ end
81
+ end_src
82
+ else
83
+ Hamlit::Engine.new(engine_options).call(template)
84
+ end
85
+ end
86
+
87
+ def generate_ast(file)
88
+ template = read_file(file)
89
+ Hamlit::Parser.new(engine_options).call(template)
90
+ end
91
+
92
+ def generate_temple(file)
93
+ ast = generate_ast(file)
94
+ Hamlit::Compiler.new(engine_options).call(ast)
95
+ end
96
+
97
+ def engine_options
98
+ Hamlit::Engine.options.to_h.merge(
99
+ escape_attrs: options[:escape_attrs],
100
+ escape_html: options[:escape_html],
101
+ )
102
+ end
103
+
104
+ # Flexible default_task, compatible with haml's CLI
105
+ def method_missing(*args)
106
+ return super(*args) if args.length > 1
107
+ render(args.first.to_s)
108
+ end
109
+
110
+ def puts_code(code, color: false)
111
+ if color
112
+ require 'pry'
113
+ puts Pry.Code(code).highlighted
114
+ else
115
+ puts code
116
+ end
117
+ end
118
+
119
+ # Enable colored pretty printing only for development environment.
120
+ def pp_object(arg, color: false)
121
+ if color
122
+ require 'pry'
123
+ Pry::ColorPrinter.pp(arg)
124
+ else
125
+ require 'pp'
126
+ pp(arg)
127
+ end
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+ require 'hamlit/compiler/children_compiler'
3
+ require 'hamlit/compiler/comment_compiler'
4
+ require 'hamlit/compiler/doctype_compiler'
5
+ require 'hamlit/compiler/script_compiler'
6
+ require 'hamlit/compiler/silent_script_compiler'
7
+ require 'hamlit/compiler/tag_compiler'
8
+ require 'hamlit/filters'
9
+ require 'hamlit/identity'
10
+
11
+ module Hamlit
12
+ class Compiler
13
+ def initialize(options = {})
14
+ identity = Identity.new
15
+ @children_compiler = ChildrenCompiler.new
16
+ @comment_compiler = CommentCompiler.new
17
+ @doctype_compiler = DoctypeCompiler.new(options)
18
+ @filter_compiler = Filters.new(options)
19
+ @script_compiler = ScriptCompiler.new(identity)
20
+ @silent_script_compiler = SilentScriptCompiler.new
21
+ @tag_compiler = TagCompiler.new(identity, options)
22
+ end
23
+
24
+ def call(ast)
25
+ return runtime_error(ast) if ast.is_a?(HamlError)
26
+ compile(ast)
27
+ rescue Error => e
28
+ runtime_error(e)
29
+ end
30
+
31
+ private
32
+
33
+ def compile(node)
34
+ case node.type
35
+ when :root
36
+ compile_children(node)
37
+ when :comment
38
+ compile_comment(node)
39
+ when :doctype
40
+ compile_doctype(node)
41
+ when :filter
42
+ compile_filter(node)
43
+ when :plain
44
+ compile_plain(node)
45
+ when :script
46
+ compile_script(node)
47
+ when :silent_script
48
+ compile_silent_script(node)
49
+ when :tag
50
+ compile_tag(node)
51
+ when :haml_comment
52
+ [:multi]
53
+ else
54
+ raise InternalError.new("Unexpected node type: #{node.type}")
55
+ end
56
+ end
57
+
58
+ def compile_children(node)
59
+ @children_compiler.compile(node) { |n| compile(n) }
60
+ end
61
+
62
+ def compile_comment(node)
63
+ @comment_compiler.compile(node) { |n| compile_children(n) }
64
+ end
65
+
66
+ def compile_doctype(node)
67
+ @doctype_compiler.compile(node)
68
+ end
69
+
70
+ def compile_filter(node)
71
+ @filter_compiler.compile(node)
72
+ end
73
+
74
+ def compile_plain(node)
75
+ [:static, node.value[:text]]
76
+ end
77
+
78
+ def compile_script(node)
79
+ @script_compiler.compile(node) { |n| compile_children(n) }
80
+ end
81
+
82
+ def compile_silent_script(node)
83
+ @silent_script_compiler.compile(node) { |n| compile_children(n) }
84
+ end
85
+
86
+ def compile_tag(node)
87
+ @tag_compiler.compile(node) { |n| compile_children(n) }
88
+ end
89
+
90
+ def runtime_error(error)
91
+ [:multi].tap do |temple|
92
+ error.line.times { temple << [:newline] } if error.line
93
+ temple << [:code, %Q[raise #{error.class}.new(%q[#{error.message}], #{error.line.inspect})]]
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+ module Hamlit
3
+ class Compiler
4
+ class ChildrenCompiler
5
+ def initialize
6
+ @lineno = 1
7
+ end
8
+
9
+ def compile(node, &block)
10
+ temple = [:multi]
11
+ return temple if node.children.empty?
12
+
13
+ temple << :whitespace if prepend_whitespace?(node)
14
+ node.children.each do |n|
15
+ rstrip_whitespace!(temple) if nuke_prev_whitespace?(n)
16
+ insert_newlines!(temple, n)
17
+ temple << yield(n)
18
+ temple << :whitespace if insert_whitespace?(n)
19
+ end
20
+ rstrip_whitespace!(temple) if nuke_inner_whitespace?(node)
21
+ confirm_whitespace(temple)
22
+ end
23
+
24
+ private
25
+
26
+ def insert_newlines!(temple, node)
27
+ (node.line - @lineno).times do
28
+ temple << [:newline]
29
+ end
30
+ @lineno = node.line
31
+
32
+ case node.type
33
+ when :script, :silent_script
34
+ @lineno += 1
35
+ when :filter
36
+ @lineno += (node.value[:text] || '').split("\n").size
37
+ when :tag
38
+ node.value[:attributes_hashes].each do |attribute_hash|
39
+ @lineno += attribute_hash.count("\n")
40
+ end
41
+ @lineno += 1 if node.children.empty? && node.value[:parse]
42
+ end
43
+ end
44
+
45
+ def confirm_whitespace(temple)
46
+ temple.map do |exp|
47
+ case exp
48
+ when :whitespace
49
+ [:static, "\n"]
50
+ else
51
+ exp
52
+ end
53
+ end
54
+ end
55
+
56
+ def prepend_whitespace?(node)
57
+ return false unless %i[comment tag].include?(node.type)
58
+ !nuke_inner_whitespace?(node)
59
+ end
60
+
61
+ def nuke_inner_whitespace?(node)
62
+ case
63
+ when node.type == :tag
64
+ node.value[:nuke_inner_whitespace]
65
+ when node.parent.nil?
66
+ false
67
+ else
68
+ nuke_inner_whitespace?(node.parent)
69
+ end
70
+ end
71
+
72
+ def nuke_prev_whitespace?(node)
73
+ case node.type
74
+ when :tag
75
+ node.value[:nuke_outer_whitespace]
76
+ when :silent_script
77
+ !node.children.empty? && nuke_prev_whitespace?(node.children.first)
78
+ else
79
+ false
80
+ end
81
+ end
82
+
83
+ def nuke_outer_whitespace?(node)
84
+ return false if node.type != :tag
85
+ node.value[:nuke_outer_whitespace]
86
+ end
87
+
88
+ def rstrip_whitespace!(temple)
89
+ if temple[-1] == :whitespace
90
+ temple.delete_at(-1)
91
+ end
92
+ end
93
+
94
+ def insert_whitespace?(node)
95
+ return false if nuke_outer_whitespace?(node)
96
+
97
+ case node.type
98
+ when :doctype
99
+ node.value[:type] != 'xml'
100
+ when :comment, :plain, :tag
101
+ true
102
+ when :script
103
+ node.children.empty? && !nuke_inner_whitespace?(node)
104
+ when :filter
105
+ !%w[ruby].include?(node.value[:name])
106
+ else
107
+ false
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end