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,36 @@
1
+ module Hamlit
2
+ class Compiler
3
+ class CommentCompiler
4
+ def compile(node, &block)
5
+ if node.value[:conditional]
6
+ compile_conditional_comment(node, &block)
7
+ else
8
+ compile_html_comment(node, &block)
9
+ end
10
+ end
11
+
12
+ private
13
+
14
+ def compile_html_comment(node, &block)
15
+ if node.children.empty?
16
+ [:html, :comment, [:static, " #{node.value[:text]} "]]
17
+ else
18
+ [:html, :comment, yield(node)]
19
+ end
20
+ end
21
+
22
+ def compile_conditional_comment(node, &block)
23
+ condition = node.value[:conditional]
24
+ if node.value[:conditional] =~ /\A\[(\[*[^\[\]]+\]*)\]/
25
+ condition = $1
26
+ end
27
+
28
+ if node.children.empty?
29
+ [:html, :condcomment, condition, [:static, " #{node.value[:text]} "]]
30
+ else
31
+ [:html, :condcomment, condition, yield(node)]
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+ module Hamlit
3
+ class Compiler
4
+ class DoctypeCompiler
5
+ def initialize(options = {})
6
+ @format = options[:format]
7
+ end
8
+
9
+ def compile(node)
10
+ case node.value[:type]
11
+ when 'xml'
12
+ xml_doctype
13
+ when ''
14
+ html_doctype(node)
15
+ else
16
+ [:html, :doctype, node.value[:type]]
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ def html_doctype(node)
23
+ version = node.value[:version] || :transitional
24
+ case @format
25
+ when :xhtml
26
+ [:html, :doctype, version]
27
+ when :html4
28
+ [:html, :doctype, :transitional]
29
+ when :html5
30
+ [:html, :doctype, :html]
31
+ else
32
+ [:html, :doctype, @format]
33
+ end
34
+ end
35
+
36
+ def xml_doctype
37
+ case @format
38
+ when :xhtml
39
+ [:static, "<?xml version='1.0' encoding='utf-8' ?>\n"]
40
+ else
41
+ [:multi]
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+ require 'temple/static_analyzer'
3
+ require 'hamlit/ruby_expression'
4
+ require 'hamlit/string_splitter'
5
+
6
+ module Hamlit
7
+ class Compiler
8
+ class ScriptCompiler
9
+ def initialize(identity)
10
+ @identity = identity
11
+ end
12
+
13
+ def compile(node, &block)
14
+ no_children = node.children.empty?
15
+ case
16
+ when no_children && node.value[:escape_interpolation]
17
+ compile_interpolated_plain(node)
18
+ when no_children && RubyExpression.string_literal?(node.value[:text])
19
+ delegate_optimization(node)
20
+ when no_children && Temple::StaticAnalyzer.static?(node.value[:text])
21
+ static_compile(node)
22
+ else
23
+ dynamic_compile(node, &block)
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ # String-interpolated plain text must be compiled with this method
30
+ # because we have to escape only interpolated values.
31
+ def compile_interpolated_plain(node)
32
+ temple = [:multi]
33
+ StringSplitter.compile(node.value[:text]).each do |type, value|
34
+ case type
35
+ when :static
36
+ temple << [:static, value]
37
+ when :dynamic
38
+ temple << [:escape, node.value[:escape_interpolation], [:dynamic, value]]
39
+ end
40
+ end
41
+ temple << [:newline]
42
+ end
43
+
44
+ # :dynamic is optimized in other filter: StringSplitter
45
+ def delegate_optimization(node)
46
+ [:multi,
47
+ [:escape, node.value[:escape_html], [:dynamic, node.value[:text]]],
48
+ [:newline],
49
+ ]
50
+ end
51
+
52
+ def static_compile(node)
53
+ str = eval(node.value[:text]).to_s
54
+ if node.value[:escape_html]
55
+ str = Hamlit::Utils.escape_html(str)
56
+ elsif node.value[:preserve]
57
+ str = ::Hamlit::HamlHelpers.find_and_preserve(str, %w(textarea pre code))
58
+ end
59
+ [:multi, [:static, str], [:newline]]
60
+ end
61
+
62
+ def dynamic_compile(node, &block)
63
+ var = @identity.generate
64
+ temple = compile_script_assign(var, node, &block)
65
+ temple << compile_script_result(var, node)
66
+ end
67
+
68
+ def compile_script_assign(var, node, &block)
69
+ if node.children.empty?
70
+ [:multi,
71
+ [:code, "#{var} = (#{node.value[:text]}"],
72
+ [:newline],
73
+ [:code, ')'],
74
+ ]
75
+ else
76
+ [:multi,
77
+ [:block, "#{var} = #{node.value[:text]}",
78
+ [:multi, [:newline], yield(node)],
79
+ ],
80
+ ]
81
+ end
82
+ end
83
+
84
+ def compile_script_result(result, node)
85
+ if !node.value[:escape_html] && node.value[:preserve]
86
+ result = find_and_preserve(result)
87
+ else
88
+ result = "(#{result}).to_s"
89
+ end
90
+ [:escape, node.value[:escape_html], [:dynamic, result]]
91
+ end
92
+
93
+ def find_and_preserve(code)
94
+ %Q[::Hamlit::HamlHelpers.find_and_preserve(#{code}, %w(textarea pre code))]
95
+ end
96
+
97
+ def escape_html(temple)
98
+ [:escape, true, temple]
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+ module Hamlit
3
+ class Compiler
4
+ class SilentScriptCompiler
5
+ def compile(node, &block)
6
+ if node.children.empty?
7
+ [:multi, [:code, node.value[:text]], [:newline]]
8
+ else
9
+ compile_with_children(node, &block)
10
+ end
11
+ end
12
+
13
+ private
14
+
15
+ def compile_with_children(node, &block)
16
+ [:multi,
17
+ [:block, node.value[:text],
18
+ [:multi, [:newline], yield(node)],
19
+ ],
20
+ ]
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+ require 'hamlit/parser/haml_util'
3
+ require 'hamlit/attribute_compiler'
4
+ require 'hamlit/string_splitter'
5
+
6
+ module Hamlit
7
+ class Compiler
8
+ class TagCompiler
9
+ def initialize(identity, options)
10
+ @autoclose = options[:autoclose]
11
+ @identity = identity
12
+ @attribute_compiler = AttributeCompiler.new(identity, options)
13
+ end
14
+
15
+ def compile(node, &block)
16
+ attrs = @attribute_compiler.compile(node)
17
+ contents = compile_contents(node, &block)
18
+ [:html, :tag, node.value[:name], attrs, contents]
19
+ end
20
+
21
+ private
22
+
23
+ def compile_contents(node, &block)
24
+ case
25
+ when !node.children.empty?
26
+ yield(node)
27
+ when node.value[:value].nil? && self_closing?(node)
28
+ nil
29
+ when node.value[:parse]
30
+ return compile_interpolated_plain(node) if node.value[:escape_interpolation]
31
+ return delegate_optimization(node) if RubyExpression.string_literal?(node.value[:value])
32
+ return delegate_optimization(node) if Temple::StaticAnalyzer.static?(node.value[:value])
33
+
34
+ var = @identity.generate
35
+ [:multi,
36
+ [:code, "#{var} = (#{node.value[:value]}"],
37
+ [:newline],
38
+ [:code, ')'],
39
+ [:escape, node.value[:escape_html], [:dynamic, var]]
40
+ ]
41
+ else
42
+ [:static, node.value[:value]]
43
+ end
44
+ end
45
+
46
+ # :dynamic is optimized in other filters: StringSplitter or StaticAnalyzer
47
+ def delegate_optimization(node)
48
+ [:multi,
49
+ [:escape, node.value[:escape_html], [:dynamic, node.value[:value]]],
50
+ [:newline],
51
+ ]
52
+ end
53
+
54
+ # We should handle interpolation here to escape only interpolated values.
55
+ def compile_interpolated_plain(node)
56
+ temple = [:multi]
57
+ StringSplitter.compile(node.value[:value]).each do |type, value|
58
+ case type
59
+ when :static
60
+ temple << [:static, value]
61
+ when :dynamic
62
+ temple << [:escape, node.value[:escape_interpolation], [:dynamic, value]]
63
+ end
64
+ end
65
+ temple << [:newline]
66
+ end
67
+
68
+ def self_closing?(node)
69
+ return true if @autoclose && @autoclose.include?(node.value[:name])
70
+ node.value[:self_closing]
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+ require 'temple'
3
+ require 'hamlit/parser'
4
+ require 'hamlit/compiler'
5
+ require 'hamlit/escapable'
6
+ require 'hamlit/force_escapable'
7
+ require 'hamlit/html'
8
+ require 'hamlit/string_splitter'
9
+
10
+ module Hamlit
11
+ class Engine < Temple::Engine
12
+ define_options(
13
+ :buffer_class,
14
+ generator: Temple::Generators::ArrayBuffer,
15
+ format: :html,
16
+ attr_quote: "'",
17
+ escape_html: true,
18
+ escape_attrs: true,
19
+ autoclose: %w(area base basefont br col command embed frame
20
+ hr img input isindex keygen link menuitem meta
21
+ param source track wbr),
22
+ filename: "",
23
+ )
24
+
25
+ use Parser
26
+ use Compiler
27
+ use HTML
28
+ use StringSplitter
29
+ filter :StaticAnalyzer
30
+ use Escapable
31
+ use ForceEscapable
32
+ filter :ControlFlow
33
+ filter :MultiFlattener
34
+ filter :StaticMerger
35
+ use :Generator, -> { options[:generator] }
36
+ end
37
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+ module Hamlit
3
+ class Error < StandardError
4
+ attr_reader :line
5
+
6
+ def initialize(message = nil, line = nil)
7
+ super(message)
8
+ @line = line
9
+ end
10
+ end
11
+
12
+ class SyntaxError < Error; end
13
+ class InternalError < Error; end
14
+ class FilterNotFound < Error; end
15
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+ require 'hamlit/utils'
3
+
4
+ module Hamlit
5
+ class Escapable < Temple::Filters::Escapable
6
+ def initialize(opts = {})
7
+ super
8
+ @escape_code = options[:escape_code] ||
9
+ "::Hamlit::Utils.escape_html#{options[:use_html_safe] ? '_safe' : ''}((%s))"
10
+ @escaper = eval("proc {|v| #{@escape_code % 'v'} }")
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+ require 'hamlit/filters/base'
3
+ require 'hamlit/filters/text_base'
4
+ require 'hamlit/filters/tilt_base'
5
+ require 'hamlit/filters/coffee'
6
+ require 'hamlit/filters/css'
7
+ require 'hamlit/filters/erb'
8
+ require 'hamlit/filters/escaped'
9
+ require 'hamlit/filters/javascript'
10
+ require 'hamlit/filters/less'
11
+ require 'hamlit/filters/markdown'
12
+ require 'hamlit/filters/plain'
13
+ require 'hamlit/filters/preserve'
14
+ require 'hamlit/filters/ruby'
15
+ require 'hamlit/filters/sass'
16
+ require 'hamlit/filters/scss'
17
+ require 'hamlit/filters/cdata'
18
+
19
+ module Hamlit
20
+ class Filters
21
+ @registered = {}
22
+
23
+ class << self
24
+ attr_reader :registered
25
+
26
+ def remove_filter(name)
27
+ registered.delete(name.to_s.downcase.to_sym)
28
+ if constants.map(&:to_s).include?(name.to_s)
29
+ remove_const name.to_sym
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def register(name, compiler)
36
+ registered[name] = compiler
37
+ end
38
+ end
39
+
40
+ register :coffee, Coffee
41
+ register :coffeescript, CoffeeScript
42
+ register :css, Css
43
+ register :erb, Erb
44
+ register :escaped, Escaped
45
+ register :javascript, Javascript
46
+ register :less, Less
47
+ register :markdown, Markdown
48
+ register :plain, Plain
49
+ register :preserve, Preserve
50
+ register :ruby, Ruby
51
+ register :sass, Sass
52
+ register :scss, Scss
53
+ register :cdata, Cdata
54
+
55
+ def initialize(options = {})
56
+ @options = options
57
+ @compilers = {}
58
+ end
59
+
60
+ def compile(node)
61
+ node.value[:text] ||= ''
62
+ find_compiler(node).compile(node)
63
+ end
64
+
65
+ private
66
+
67
+ def find_compiler(node)
68
+ name = node.value[:name].to_sym
69
+ compiler = Filters.registered[name]
70
+ raise FilterNotFound.new("FilterCompiler for '#{name}' was not found", node.line.to_i - 1) unless compiler
71
+
72
+ @compilers[name] ||= compiler.new(@options)
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+ require 'hamlit/parser/haml_util'
3
+
4
+ module Hamlit
5
+ class Filters
6
+ class Base
7
+ def initialize(options = {})
8
+ @format = options[:format]
9
+ end
10
+ end
11
+ end
12
+ end