haml 5.2.2 → 6.0.0.beta.1
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 +4 -4
- data/.github/FUNDING.yml +1 -0
- data/.github/workflows/test.yml +13 -9
- data/.gitignore +16 -16
- data/CHANGELOG.md +13 -3
- data/Gemfile +18 -11
- data/MIT-LICENSE +1 -1
- data/README.md +13 -19
- data/Rakefile +95 -93
- data/bin/bench +66 -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/haml +6 -0
- data/ext/haml/extconf.rb +10 -0
- data/ext/haml/haml.c +537 -0
- data/ext/haml/hescape.c +108 -0
- data/ext/haml/hescape.h +20 -0
- data/haml.gemspec +39 -37
- data/lib/haml/ambles.rb +20 -0
- data/lib/haml/attribute_builder.rb +135 -179
- data/lib/haml/attribute_compiler.rb +85 -194
- data/lib/haml/attribute_parser.rb +86 -126
- data/lib/haml/cli.rb +154 -0
- data/lib/haml/compiler/children_compiler.rb +126 -0
- data/lib/haml/compiler/comment_compiler.rb +39 -0
- data/lib/haml/compiler/doctype_compiler.rb +46 -0
- data/lib/haml/compiler/script_compiler.rb +116 -0
- data/lib/haml/compiler/silent_script_compiler.rb +24 -0
- data/lib/haml/compiler/tag_compiler.rb +76 -0
- data/lib/haml/compiler.rb +63 -296
- data/lib/haml/dynamic_merger.rb +67 -0
- data/lib/haml/engine.rb +42 -227
- data/lib/haml/error.rb +3 -52
- data/lib/haml/escapable.rb +6 -70
- data/lib/haml/filters/base.rb +12 -0
- data/lib/haml/filters/cdata.rb +20 -0
- data/lib/haml/filters/coffee.rb +17 -0
- data/lib/haml/filters/css.rb +33 -0
- data/lib/haml/filters/erb.rb +10 -0
- data/lib/haml/filters/escaped.rb +22 -0
- data/lib/haml/filters/javascript.rb +33 -0
- data/lib/haml/filters/less.rb +20 -0
- data/lib/haml/filters/markdown.rb +11 -0
- data/lib/haml/filters/plain.rb +29 -0
- data/lib/haml/filters/preserve.rb +22 -0
- data/lib/haml/filters/ruby.rb +10 -0
- data/lib/haml/filters/sass.rb +15 -0
- data/lib/haml/filters/scss.rb +15 -0
- data/lib/haml/filters/text_base.rb +25 -0
- data/lib/haml/filters/tilt_base.rb +49 -0
- data/lib/haml/filters.rb +54 -378
- data/lib/haml/force_escapable.rb +29 -0
- data/lib/haml/haml_error.rb +66 -0
- data/lib/haml/helpers.rb +3 -697
- data/lib/haml/html.rb +22 -0
- data/lib/haml/identity.rb +13 -0
- data/lib/haml/object_ref.rb +30 -0
- data/lib/haml/parser.rb +179 -49
- data/lib/haml/rails_helpers.rb +51 -0
- data/lib/haml/rails_template.rb +55 -0
- data/lib/haml/railtie.rb +7 -45
- data/lib/haml/ruby_expression.rb +32 -0
- data/lib/haml/string_splitter.rb +20 -0
- data/lib/haml/template.rb +15 -34
- data/lib/haml/temple_line_counter.rb +2 -1
- data/lib/haml/util.rb +17 -15
- data/lib/haml/version.rb +1 -2
- data/lib/haml.rb +8 -20
- metadata +211 -57
- data/.gitmodules +0 -3
- data/.yardopts +0 -22
- data/TODO +0 -24
- data/benchmark.rb +0 -70
- data/bin/haml +0 -9
- data/lib/haml/.gitattributes +0 -1
- data/lib/haml/buffer.rb +0 -182
- data/lib/haml/exec.rb +0 -347
- data/lib/haml/generator.rb +0 -42
- data/lib/haml/helpers/action_view_extensions.rb +0 -60
- data/lib/haml/helpers/action_view_mods.rb +0 -132
- data/lib/haml/helpers/action_view_xss_mods.rb +0 -60
- data/lib/haml/helpers/safe_erubi_template.rb +0 -20
- data/lib/haml/helpers/safe_erubis_template.rb +0 -33
- data/lib/haml/helpers/xss_mods.rb +0 -114
- data/lib/haml/options.rb +0 -273
- data/lib/haml/plugin.rb +0 -54
- data/lib/haml/sass_rails_filter.rb +0 -47
- data/lib/haml/template/options.rb +0 -27
- data/lib/haml/temple_engine.rb +0 -124
- data/yard/default/.gitignore +0 -1
- data/yard/default/fulldoc/html/css/common.sass +0 -15
- data/yard/default/layout/html/footer.erb +0 -12
data/lib/haml/cli.rb
ADDED
@@ -0,0 +1,154 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'haml'
|
3
|
+
require 'thor'
|
4
|
+
|
5
|
+
module Haml
|
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, binding, file)
|
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: true
|
22
|
+
option :check, type: :boolean, default: false, aliases: %w[-c]
|
23
|
+
def compile(file)
|
24
|
+
code = generate_code(file)
|
25
|
+
if options[:check]
|
26
|
+
if error = validate_ruby(code, file)
|
27
|
+
abort error.message.split("\n").first
|
28
|
+
end
|
29
|
+
puts "Syntax OK"
|
30
|
+
return
|
31
|
+
end
|
32
|
+
puts_code(code, color: options[:color])
|
33
|
+
end
|
34
|
+
|
35
|
+
desc 'temple HAML', 'Show temple intermediate expression'
|
36
|
+
option :color, type: :boolean, default: true
|
37
|
+
def temple(file)
|
38
|
+
pp_object(generate_temple(file), color: options[:color])
|
39
|
+
end
|
40
|
+
|
41
|
+
desc 'parse HAML', 'Show parse result'
|
42
|
+
option :color, type: :boolean, default: true
|
43
|
+
def parse(file)
|
44
|
+
pp_object(generate_ast(file), color: options[:color])
|
45
|
+
end
|
46
|
+
|
47
|
+
desc 'version', 'Show the used haml version'
|
48
|
+
def version
|
49
|
+
puts Haml::VERSION
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def process_load_options
|
55
|
+
if options[:load_path]
|
56
|
+
options[:load_path].split(':').each do |dir|
|
57
|
+
$LOAD_PATH.unshift(dir) unless $LOAD_PATH.include?(dir)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
if options[:require]
|
62
|
+
require options[:require]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def read_file(file)
|
67
|
+
if file == '-'
|
68
|
+
STDIN.read
|
69
|
+
else
|
70
|
+
File.read(file)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def generate_code(file)
|
75
|
+
template = read_file(file)
|
76
|
+
if options[:actionview]
|
77
|
+
require 'action_view'
|
78
|
+
require 'action_view/base'
|
79
|
+
require 'haml/rails_template'
|
80
|
+
handler = Haml::RailsTemplate.new
|
81
|
+
template = ActionView::Template.new(template, 'inline template', handler, { locals: [] })
|
82
|
+
code = handler.call(template)
|
83
|
+
<<-end_src
|
84
|
+
def _inline_template___2144273726781623612_70327218547300(local_assigns, output_buffer)
|
85
|
+
_old_virtual_path, @virtual_path = @virtual_path, nil;_old_output_buffer = @output_buffer;;#{code}
|
86
|
+
ensure
|
87
|
+
@virtual_path, @output_buffer = _old_virtual_path, _old_output_buffer
|
88
|
+
end
|
89
|
+
end_src
|
90
|
+
else
|
91
|
+
Haml::Engine.new(engine_options).call(template)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def generate_ast(file)
|
96
|
+
template = read_file(file)
|
97
|
+
Haml::Parser.new(engine_options).call(template)
|
98
|
+
end
|
99
|
+
|
100
|
+
def generate_temple(file)
|
101
|
+
ast = generate_ast(file)
|
102
|
+
Haml::Compiler.new(engine_options).call(ast)
|
103
|
+
end
|
104
|
+
|
105
|
+
def engine_options
|
106
|
+
Haml::Engine.options.to_h.merge(
|
107
|
+
escape_attrs: options[:escape_attrs],
|
108
|
+
escape_html: options[:escape_html],
|
109
|
+
)
|
110
|
+
end
|
111
|
+
|
112
|
+
# Flexible default_task, compatible with haml's CLI
|
113
|
+
def method_missing(*args)
|
114
|
+
return super(*args) if args.length > 1
|
115
|
+
render(args.first.to_s)
|
116
|
+
end
|
117
|
+
|
118
|
+
def puts_code(code, color: true)
|
119
|
+
begin
|
120
|
+
require 'irb/color'
|
121
|
+
rescue LoadError
|
122
|
+
color = false
|
123
|
+
end
|
124
|
+
if color
|
125
|
+
puts IRB::Color.colorize_code(code)
|
126
|
+
else
|
127
|
+
puts code
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
# Enable colored pretty printing only for development environment.
|
132
|
+
def pp_object(arg, color: true)
|
133
|
+
begin
|
134
|
+
require 'irb/color_printer'
|
135
|
+
rescue LoadError
|
136
|
+
color = false
|
137
|
+
end
|
138
|
+
if color
|
139
|
+
IRB::ColorPrinter.pp(arg)
|
140
|
+
else
|
141
|
+
require 'pp'
|
142
|
+
pp(arg)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def validate_ruby(code, file)
|
147
|
+
begin
|
148
|
+
eval("BEGIN {return nil}; #{code}", binding, file)
|
149
|
+
rescue ::SyntaxError # Not to be confused with Haml::SyntaxError
|
150
|
+
$!
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'haml/temple_line_counter'
|
3
|
+
|
4
|
+
module Haml
|
5
|
+
class Compiler
|
6
|
+
class ChildrenCompiler
|
7
|
+
def initialize
|
8
|
+
@lineno = 1
|
9
|
+
end
|
10
|
+
|
11
|
+
def compile(node, &block)
|
12
|
+
temple = [:multi]
|
13
|
+
return temple if node.children.empty?
|
14
|
+
|
15
|
+
temple << :whitespace if prepend_whitespace?(node)
|
16
|
+
node.children.each do |n|
|
17
|
+
rstrip_whitespace!(temple) if nuke_prev_whitespace?(n)
|
18
|
+
insert_newlines!(temple, n)
|
19
|
+
temple << moving_lineno(n) { block.call(n) }
|
20
|
+
temple << :whitespace if insert_whitespace?(n)
|
21
|
+
end
|
22
|
+
rstrip_whitespace!(temple) if nuke_inner_whitespace?(node)
|
23
|
+
confirm_whitespace(temple)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def insert_newlines!(temple, node)
|
29
|
+
(node.line - @lineno).times do
|
30
|
+
temple << [:newline]
|
31
|
+
end
|
32
|
+
|
33
|
+
@lineno = node.line
|
34
|
+
end
|
35
|
+
|
36
|
+
def moving_lineno(node, &block)
|
37
|
+
# before: As they may have children, we need to increment lineno before compilation.
|
38
|
+
case node.type
|
39
|
+
when :script, :silent_script
|
40
|
+
@lineno += 1
|
41
|
+
when :tag
|
42
|
+
[node.value[:dynamic_attributes].new, node.value[:dynamic_attributes].old].compact.each do |attribute_hash|
|
43
|
+
@lineno += attribute_hash.count("\n")
|
44
|
+
end
|
45
|
+
@lineno += 1 if node.children.empty? && node.value[:parse]
|
46
|
+
end
|
47
|
+
|
48
|
+
temple = block.call # compile
|
49
|
+
|
50
|
+
# after: filter may not have children, and for some dynamic filters we can't predict the number of lines.
|
51
|
+
case node.type
|
52
|
+
when :filter
|
53
|
+
@lineno += TempleLineCounter.count_lines(temple)
|
54
|
+
end
|
55
|
+
|
56
|
+
temple
|
57
|
+
end
|
58
|
+
|
59
|
+
def confirm_whitespace(temple)
|
60
|
+
temple.map do |exp|
|
61
|
+
case exp
|
62
|
+
when :whitespace
|
63
|
+
[:static, "\n"]
|
64
|
+
else
|
65
|
+
exp
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def prepend_whitespace?(node)
|
71
|
+
return false unless %i[comment tag].include?(node.type)
|
72
|
+
!nuke_inner_whitespace?(node)
|
73
|
+
end
|
74
|
+
|
75
|
+
def nuke_inner_whitespace?(node)
|
76
|
+
case
|
77
|
+
when node.type == :tag
|
78
|
+
node.value[:nuke_inner_whitespace]
|
79
|
+
when node.parent.nil?
|
80
|
+
false
|
81
|
+
else
|
82
|
+
nuke_inner_whitespace?(node.parent)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def nuke_prev_whitespace?(node)
|
87
|
+
case node.type
|
88
|
+
when :tag
|
89
|
+
node.value[:nuke_outer_whitespace]
|
90
|
+
when :silent_script
|
91
|
+
!node.children.empty? && nuke_prev_whitespace?(node.children.first)
|
92
|
+
else
|
93
|
+
false
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def nuke_outer_whitespace?(node)
|
98
|
+
return false if node.type != :tag
|
99
|
+
node.value[:nuke_outer_whitespace]
|
100
|
+
end
|
101
|
+
|
102
|
+
def rstrip_whitespace!(temple)
|
103
|
+
if temple[-1] == :whitespace
|
104
|
+
temple.delete_at(-1)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def insert_whitespace?(node)
|
109
|
+
return false if nuke_outer_whitespace?(node)
|
110
|
+
|
111
|
+
case node.type
|
112
|
+
when :doctype
|
113
|
+
node.value[:type] != 'xml'
|
114
|
+
when :comment, :plain, :tag
|
115
|
+
true
|
116
|
+
when :script
|
117
|
+
node.children.empty? && !nuke_inner_whitespace?(node)
|
118
|
+
when :filter
|
119
|
+
!%w[ruby].include?(node.value[:name])
|
120
|
+
else
|
121
|
+
false
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Haml
|
3
|
+
class Compiler
|
4
|
+
class CommentCompiler
|
5
|
+
def compile(node, &block)
|
6
|
+
if node.value[:conditional]
|
7
|
+
compile_conditional_comment(node, &block)
|
8
|
+
else
|
9
|
+
compile_html_comment(node, &block)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def compile_html_comment(node, &block)
|
16
|
+
if node.children.empty?
|
17
|
+
[:html, :comment, [:static, " #{node.value[:text]} "]]
|
18
|
+
else
|
19
|
+
[:html, :comment, yield(node)]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def compile_conditional_comment(node, &block)
|
24
|
+
condition = node.value[:conditional]
|
25
|
+
if node.value[:conditional] =~ /\A\[(\[*[^\[\]]+\]*)\]/
|
26
|
+
condition = $1
|
27
|
+
end
|
28
|
+
|
29
|
+
content =
|
30
|
+
if node.children.empty?
|
31
|
+
[:static, " #{node.value[:text]} "]
|
32
|
+
else
|
33
|
+
yield(node)
|
34
|
+
end
|
35
|
+
[:html, :condcomment, condition, content, node.value[:revealed]]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Haml
|
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,116 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'temple/static_analyzer'
|
3
|
+
require 'haml/ruby_expression'
|
4
|
+
require 'haml/string_splitter'
|
5
|
+
|
6
|
+
module Haml
|
7
|
+
class Compiler
|
8
|
+
class ScriptCompiler
|
9
|
+
def self.find_and_preserve(input, tags)
|
10
|
+
tags = tags.map { |tag| Regexp.escape(tag) }.join('|')
|
11
|
+
re = /<(#{tags})([^>]*)>(.*?)(<\/\1>)/im
|
12
|
+
input.to_s.gsub(re) do |s|
|
13
|
+
s =~ re # Can't rely on $1, etc. existing since Rails' SafeBuffer#gsub is incompatible
|
14
|
+
"<#{$1}#{$2}>#{Haml::Helpers.preserve($3)}</#{$1}>"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(identity, options)
|
19
|
+
@identity = identity
|
20
|
+
@disable_capture = options[:disable_capture]
|
21
|
+
end
|
22
|
+
|
23
|
+
def compile(node, &block)
|
24
|
+
unless Ripper.respond_to?(:lex) # No Ripper.lex in truffleruby
|
25
|
+
return dynamic_compile(node, &block)
|
26
|
+
end
|
27
|
+
|
28
|
+
no_children = node.children.empty?
|
29
|
+
case
|
30
|
+
when no_children && node.value[:escape_interpolation]
|
31
|
+
compile_interpolated_plain(node)
|
32
|
+
when no_children && RubyExpression.string_literal?(node.value[:text])
|
33
|
+
delegate_optimization(node)
|
34
|
+
when no_children && Temple::StaticAnalyzer.static?(node.value[:text])
|
35
|
+
static_compile(node)
|
36
|
+
else
|
37
|
+
dynamic_compile(node, &block)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
# String-interpolated plain text must be compiled with this method
|
44
|
+
# because we have to escape only interpolated values.
|
45
|
+
def compile_interpolated_plain(node)
|
46
|
+
temple = [:multi]
|
47
|
+
StringSplitter.compile(node.value[:text]).each do |type, value|
|
48
|
+
case type
|
49
|
+
when :static
|
50
|
+
temple << [:static, value]
|
51
|
+
when :dynamic
|
52
|
+
temple << [:escape, node.value[:escape_interpolation], [:dynamic, value]]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
temple << [:newline]
|
56
|
+
end
|
57
|
+
|
58
|
+
# :dynamic is optimized in other filter: StringSplitter
|
59
|
+
def delegate_optimization(node)
|
60
|
+
[:multi,
|
61
|
+
[:escape, node.value[:escape_html], [:dynamic, node.value[:text]]],
|
62
|
+
[:newline],
|
63
|
+
]
|
64
|
+
end
|
65
|
+
|
66
|
+
def static_compile(node)
|
67
|
+
str = eval(node.value[:text]).to_s
|
68
|
+
if node.value[:escape_html]
|
69
|
+
str = Haml::Util.escape_html(str)
|
70
|
+
elsif node.value[:preserve]
|
71
|
+
str = ScriptCompiler.find_and_preserve(str, %w(textarea pre code))
|
72
|
+
end
|
73
|
+
[:multi, [:static, str], [:newline]]
|
74
|
+
end
|
75
|
+
|
76
|
+
def dynamic_compile(node, &block)
|
77
|
+
var = @identity.generate
|
78
|
+
temple = compile_script_assign(var, node, &block)
|
79
|
+
temple << compile_script_result(var, node)
|
80
|
+
end
|
81
|
+
|
82
|
+
def compile_script_assign(var, node, &block)
|
83
|
+
if node.children.empty?
|
84
|
+
[:multi,
|
85
|
+
[:code, "#{var} = (#{node.value[:text]}"],
|
86
|
+
[:newline],
|
87
|
+
[:code, ')'],
|
88
|
+
]
|
89
|
+
else
|
90
|
+
[:multi,
|
91
|
+
[:block, "#{var} = #{node.value[:text]}",
|
92
|
+
[:multi, [:newline], @disable_capture ? yield(node) : [:capture, Temple::Utils.unique_name, yield(node)]]
|
93
|
+
],
|
94
|
+
]
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def compile_script_result(result, node)
|
99
|
+
if !node.value[:escape_html] && node.value[:preserve]
|
100
|
+
result = find_and_preserve(result)
|
101
|
+
else
|
102
|
+
result = "(#{result}).to_s"
|
103
|
+
end
|
104
|
+
[:escape, node.value[:escape_html], [:dynamic, result]]
|
105
|
+
end
|
106
|
+
|
107
|
+
def find_and_preserve(code)
|
108
|
+
%Q[::Haml::Compiler::ScriptCompiler.find_and_preserve(#{code}, %w(textarea pre code))]
|
109
|
+
end
|
110
|
+
|
111
|
+
def escape_html(temple)
|
112
|
+
[:escape, true, temple]
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Haml
|
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,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'haml/util'
|
3
|
+
require 'haml/attribute_compiler'
|
4
|
+
require 'haml/string_splitter'
|
5
|
+
|
6
|
+
module Haml
|
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
|
+
if Ripper.respond_to?(:lex) # No Ripper.lex in truffleruby
|
32
|
+
return delegate_optimization(node) if RubyExpression.string_literal?(node.value[:value])
|
33
|
+
return delegate_optimization(node) if Temple::StaticAnalyzer.static?(node.value[:value])
|
34
|
+
end
|
35
|
+
|
36
|
+
var = @identity.generate
|
37
|
+
[:multi,
|
38
|
+
[:code, "#{var} = (#{node.value[:value]}"],
|
39
|
+
[:newline],
|
40
|
+
[:code, ')'],
|
41
|
+
[:escape, node.value[:escape_html], [:dynamic, var]]
|
42
|
+
]
|
43
|
+
else
|
44
|
+
[:static, node.value[:value]]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# :dynamic is optimized in other filters: StringSplitter or StaticAnalyzer
|
49
|
+
def delegate_optimization(node)
|
50
|
+
[:multi,
|
51
|
+
[:escape, node.value[:escape_html], [:dynamic, node.value[:value]]],
|
52
|
+
[:newline],
|
53
|
+
]
|
54
|
+
end
|
55
|
+
|
56
|
+
# We should handle interpolation here to escape only interpolated values.
|
57
|
+
def compile_interpolated_plain(node)
|
58
|
+
temple = [:multi]
|
59
|
+
StringSplitter.compile(node.value[:value]).each do |type, value|
|
60
|
+
case type
|
61
|
+
when :static
|
62
|
+
temple << [:static, value]
|
63
|
+
when :dynamic
|
64
|
+
temple << [:escape, node.value[:escape_interpolation], [:dynamic, value]]
|
65
|
+
end
|
66
|
+
end
|
67
|
+
temple << [:newline]
|
68
|
+
end
|
69
|
+
|
70
|
+
def self_closing?(node)
|
71
|
+
return true if @autoclose && @autoclose.include?(node.value[:name])
|
72
|
+
node.value[:self_closing]
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|