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.
- checksums.yaml +7 -0
- data/.gitignore +16 -0
- data/.travis.yml +45 -0
- data/CHANGELOG.md +676 -0
- data/Gemfile +28 -0
- data/LICENSE.txt +44 -0
- data/README.md +150 -0
- data/REFERENCE.md +266 -0
- data/Rakefile +117 -0
- data/benchmark/boolean_attribute.haml +6 -0
- data/benchmark/class_attribute.haml +5 -0
- data/benchmark/common_attribute.haml +3 -0
- data/benchmark/data_attribute.haml +4 -0
- data/benchmark/dynamic_attributes/boolean_attribute.haml +4 -0
- data/benchmark/dynamic_attributes/class_attribute.haml +4 -0
- data/benchmark/dynamic_attributes/common_attribute.haml +2 -0
- data/benchmark/dynamic_attributes/data_attribute.haml +2 -0
- data/benchmark/dynamic_attributes/id_attribute.haml +2 -0
- data/benchmark/dynamic_boolean_attribute.haml +4 -0
- data/benchmark/etc/attribute_builder.haml +5 -0
- data/benchmark/etc/real_sample.haml +888 -0
- data/benchmark/etc/real_sample.rb +11 -0
- data/benchmark/etc/static_analyzer.haml +1 -0
- data/benchmark/etc/string_interpolation.haml +2 -0
- data/benchmark/etc/tags.haml +3 -0
- data/benchmark/etc/tags_loop.haml +2 -0
- data/benchmark/ext/build_data.rb +17 -0
- data/benchmark/ext/build_id.rb +13 -0
- data/benchmark/id_attribute.haml +3 -0
- data/benchmark/plain.haml +4 -0
- data/benchmark/script.haml +4 -0
- data/benchmark/slim/LICENSE +21 -0
- data/benchmark/slim/context.rb +11 -0
- data/benchmark/slim/run-benchmarks.rb +94 -0
- data/benchmark/slim/view.erb +23 -0
- data/benchmark/slim/view.haml +18 -0
- data/benchmark/slim/view.slim +17 -0
- data/benchmark/utils/benchmark_ips_extension.rb +43 -0
- data/bin/bench +77 -0
- data/bin/console +11 -0
- data/bin/ruby +3 -0
- data/bin/setup +7 -0
- data/bin/stackprof +27 -0
- data/bin/test +24 -0
- data/exe/hamlit +6 -0
- data/ext/hamlit/extconf.rb +10 -0
- data/ext/hamlit/hamlit.c +553 -0
- data/ext/hamlit/hescape.c +108 -0
- data/ext/hamlit/hescape.h +20 -0
- data/hamlit.gemspec +45 -0
- data/lib/hamlit.rb +11 -0
- data/lib/hamlit/attribute_builder.rb +173 -0
- data/lib/hamlit/attribute_compiler.rb +123 -0
- data/lib/hamlit/attribute_parser.rb +110 -0
- data/lib/hamlit/cli.rb +130 -0
- data/lib/hamlit/compiler.rb +97 -0
- data/lib/hamlit/compiler/children_compiler.rb +112 -0
- data/lib/hamlit/compiler/comment_compiler.rb +36 -0
- data/lib/hamlit/compiler/doctype_compiler.rb +46 -0
- data/lib/hamlit/compiler/script_compiler.rb +102 -0
- data/lib/hamlit/compiler/silent_script_compiler.rb +24 -0
- data/lib/hamlit/compiler/tag_compiler.rb +74 -0
- data/lib/hamlit/engine.rb +37 -0
- data/lib/hamlit/error.rb +15 -0
- data/lib/hamlit/escapable.rb +13 -0
- data/lib/hamlit/filters.rb +75 -0
- data/lib/hamlit/filters/base.rb +12 -0
- data/lib/hamlit/filters/cdata.rb +20 -0
- data/lib/hamlit/filters/coffee.rb +17 -0
- data/lib/hamlit/filters/css.rb +33 -0
- data/lib/hamlit/filters/erb.rb +10 -0
- data/lib/hamlit/filters/escaped.rb +22 -0
- data/lib/hamlit/filters/javascript.rb +33 -0
- data/lib/hamlit/filters/less.rb +20 -0
- data/lib/hamlit/filters/markdown.rb +10 -0
- data/lib/hamlit/filters/plain.rb +29 -0
- data/lib/hamlit/filters/preserve.rb +22 -0
- data/lib/hamlit/filters/ruby.rb +10 -0
- data/lib/hamlit/filters/sass.rb +15 -0
- data/lib/hamlit/filters/scss.rb +15 -0
- data/lib/hamlit/filters/text_base.rb +25 -0
- data/lib/hamlit/filters/tilt_base.rb +49 -0
- data/lib/hamlit/force_escapable.rb +29 -0
- data/lib/hamlit/helpers.rb +15 -0
- data/lib/hamlit/html.rb +14 -0
- data/lib/hamlit/identity.rb +13 -0
- data/lib/hamlit/object_ref.rb +30 -0
- data/lib/hamlit/parser.rb +49 -0
- data/lib/hamlit/parser/MIT-LICENSE +20 -0
- data/lib/hamlit/parser/README.md +30 -0
- data/lib/hamlit/parser/haml_buffer.rb +348 -0
- data/lib/hamlit/parser/haml_compiler.rb +553 -0
- data/lib/hamlit/parser/haml_error.rb +61 -0
- data/lib/hamlit/parser/haml_helpers.rb +727 -0
- data/lib/hamlit/parser/haml_options.rb +286 -0
- data/lib/hamlit/parser/haml_parser.rb +800 -0
- data/lib/hamlit/parser/haml_util.rb +288 -0
- data/lib/hamlit/parser/haml_xss_mods.rb +109 -0
- data/lib/hamlit/rails_helpers.rb +51 -0
- data/lib/hamlit/rails_template.rb +59 -0
- data/lib/hamlit/railtie.rb +10 -0
- data/lib/hamlit/ruby_expression.rb +32 -0
- data/lib/hamlit/string_splitter.rb +88 -0
- data/lib/hamlit/template.rb +28 -0
- data/lib/hamlit/utils.rb +18 -0
- data/lib/hamlit/version.rb +4 -0
- 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
|
data/lib/hamlit/error.rb
ADDED
@@ -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
|