hamlit 2.9.5 → 2.12.0
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/.gitignore +1 -0
- data/.travis.yml +12 -10
- data/CHANGELOG.md +39 -0
- data/Gemfile +2 -8
- data/REFERENCE.md +13 -4
- data/benchmark/dynamic_merger/benchmark.rb +25 -0
- data/benchmark/dynamic_merger/hello.haml +50 -0
- data/benchmark/dynamic_merger/hello.string +50 -0
- data/bin/bench +3 -3
- data/ext/hamlit/hamlit.c +0 -1
- data/hamlit.gemspec +3 -1
- data/lib/hamlit/attribute_builder.rb +2 -2
- data/lib/hamlit/attribute_compiler.rb +3 -3
- data/lib/hamlit/compiler/children_compiler.rb +18 -4
- data/lib/hamlit/compiler/comment_compiler.rb +7 -5
- data/lib/hamlit/compiler/tag_compiler.rb +0 -4
- data/lib/hamlit/dynamic_merger.rb +67 -0
- data/lib/hamlit/engine.rb +5 -6
- data/lib/hamlit/filters/plain.rb +0 -4
- data/lib/hamlit/html.rb +8 -0
- data/lib/hamlit/parser/haml_attribute_builder.rb +164 -0
- data/lib/hamlit/parser/haml_helpers.rb +6 -0
- data/lib/hamlit/parser/haml_parser.rb +32 -9
- data/lib/hamlit/parser/haml_xss_mods.rb +6 -3
- data/lib/hamlit/string_splitter.rb +9 -78
- data/lib/hamlit/template.rb +1 -1
- data/lib/hamlit/temple_line_counter.rb +31 -0
- data/lib/hamlit/version.rb +1 -1
- metadata +39 -6
- data/lib/hamlit/hamlit.su +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d8a587e57cbdc02c90d18591926efb8bf4fd990d60c31969501418a1eed0d2d9
|
|
4
|
+
data.tar.gz: 7f97e39c33bad82a1cc5b4056b8ac0765a2939f2a9c0a6fecfd156daefa98fa3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: dba41938cf6697aeef29f404178de0ccf46cb438402510e989af63593e5ea853627f3f0594fd0359e4b10c68b3c53a7ae64b9c3e2d2f717707e5996006b49721
|
|
7
|
+
data.tar.gz: 61ce345a747e0fffee535ecab7caee9b0320858f0243b405643115b97cef012039ed9d72766f5bbd4f1b0cde8fa2ffca7b7cea74a8b9ce2a875106c680f5274d
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
|
@@ -9,11 +9,13 @@ matrix:
|
|
|
9
9
|
include:
|
|
10
10
|
- rvm: 2.3.8
|
|
11
11
|
env: TASK=test
|
|
12
|
-
- rvm: 2.4.
|
|
12
|
+
- rvm: 2.4.9
|
|
13
13
|
env: TASK=test
|
|
14
|
-
- rvm: 2.5.
|
|
14
|
+
- rvm: 2.5.7
|
|
15
15
|
env: TASK=test
|
|
16
|
-
- rvm: 2.6.
|
|
16
|
+
- rvm: 2.6.5
|
|
17
|
+
env: TASK=test
|
|
18
|
+
- rvm: 2.7.0
|
|
17
19
|
env: TASK=test
|
|
18
20
|
- rvm: ruby-head
|
|
19
21
|
env: TASK=test
|
|
@@ -21,19 +23,19 @@ matrix:
|
|
|
21
23
|
env: TASK=test
|
|
22
24
|
- rvm: truffleruby
|
|
23
25
|
env: TASK=test
|
|
24
|
-
- rvm: 2.
|
|
26
|
+
- rvm: 2.7.0
|
|
25
27
|
env: TASK=bench TEMPLATE=benchmark/boolean_attribute.haml,benchmark/class_attribute.haml,benchmark/id_attribute.haml,benchmark/data_attribute.haml,benchmark/common_attribute.haml
|
|
26
|
-
- rvm: 2.
|
|
28
|
+
- rvm: 2.7.0
|
|
27
29
|
env: TASK=bench TEMPLATE=benchmark/dynamic_attributes/boolean_attribute.haml,benchmark/dynamic_attributes/class_attribute.haml,benchmark/dynamic_attributes/id_attribute.haml,benchmark/dynamic_attributes/data_attribute.haml,benchmark/dynamic_attributes/common_attribute.haml
|
|
28
|
-
- rvm: 2.
|
|
30
|
+
- rvm: 2.7.0
|
|
29
31
|
env: TASK=bench SLIM_BENCH=1
|
|
30
|
-
- rvm: 2.
|
|
32
|
+
- rvm: 2.7.0
|
|
31
33
|
env: TASK=bench TEMPLATE=benchmark/etc/attribute_builder.haml
|
|
32
|
-
- rvm: 2.
|
|
34
|
+
- rvm: 2.7.0
|
|
33
35
|
env: TASK=bench TEMPLATE=benchmark/etc/static_analyzer.haml
|
|
34
|
-
- rvm: 2.
|
|
36
|
+
- rvm: 2.7.0
|
|
35
37
|
env: TASK=bench TEMPLATE=benchmark/etc/string_interpolation.haml
|
|
36
|
-
- rvm: 2.
|
|
38
|
+
- rvm: 2.7.0
|
|
37
39
|
env: TASK=bench TEMPLATE=test/haml/templates/standard.haml COMPILE=1
|
|
38
40
|
allow_failures:
|
|
39
41
|
- rvm: ruby-head
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,45 @@ All notable changes to this project will be documented in this file. This
|
|
|
4
4
|
project adheres to [Semantic Versioning](http://semver.org/). This change log is based upon
|
|
5
5
|
[keep-a-changelog](https://github.com/olivierlacan/keep-a-changelog).
|
|
6
6
|
|
|
7
|
+
## [2.12.0](https://github.com/k0kubun/hamlit/compare/v2.11.1...v2.12.0) - 2020-09-30
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- Class names are no longer ordered alphabetically
|
|
12
|
+
*Thanks to @aliismayilov*
|
|
13
|
+
- This is compatible with [Haml 5.2](https://github.com/haml/haml/blob/v5.2.0/CHANGELOG.md#52)
|
|
14
|
+
|
|
15
|
+
## [2.11.1](https://github.com/k0kubun/hamlit/compare/v2.11.0...v2.11.1) - 2020-08-25
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- Fix a line number on an error after filters like preserve, plain, and ruby.
|
|
20
|
+
*Thanks to @rgisiger*
|
|
21
|
+
|
|
22
|
+
## [2.11.0](https://github.com/k0kubun/hamlit/compare/v2.10.1...v2.11.0) - 2019-12-12
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
|
|
26
|
+
- Support Haml's _revealed_ conditional comment feature on `/![if !IE]` [#153](https://github.com/k0kubun/hamlit/issues/153).
|
|
27
|
+
*Thanks to @esb*
|
|
28
|
+
|
|
29
|
+
## [2.10.1](https://github.com/k0kubun/hamlit/compare/v2.10.0...v2.10.1) - 2019-11-28
|
|
30
|
+
|
|
31
|
+
### Added
|
|
32
|
+
|
|
33
|
+
- Register `Hamlit::Template` to Tilt as :hamlit as well, in addition to :haml
|
|
34
|
+
|
|
35
|
+
## [2.10.0](https://github.com/k0kubun/hamlit/compare/v2.9.5...v2.10.0) - 2019-09-15
|
|
36
|
+
|
|
37
|
+
### Added
|
|
38
|
+
|
|
39
|
+
- Optimize template rendering by string interpolation [#146](https://github.com/k0kubun/hamlit/issues/146)
|
|
40
|
+
- Exploiting pre-allocation of string interpolation introduced in Ruby 2.5 [ruby/ruby#1626](https://github.com/ruby/ruby/pull/1626)
|
|
41
|
+
|
|
42
|
+
### Changed
|
|
43
|
+
|
|
44
|
+
- Require temple.gem >= 0.8.2
|
|
45
|
+
|
|
7
46
|
## [2.9.5](https://github.com/k0kubun/hamlit/compare/v2.9.4...v2.9.5) - 2019-09-08
|
|
8
47
|
|
|
9
48
|
### Added
|
data/Gemfile
CHANGED
|
@@ -8,23 +8,17 @@ end
|
|
|
8
8
|
# Specify your gem's dependencies in hamlit.gemspec
|
|
9
9
|
gemspec
|
|
10
10
|
|
|
11
|
-
if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.2.2')
|
|
12
|
-
gem 'rack', '< 2'
|
|
13
|
-
end
|
|
14
|
-
|
|
15
11
|
gem 'benchmark-ips', '2.3.0'
|
|
16
12
|
gem 'maxitest'
|
|
13
|
+
gem 'pry'
|
|
17
14
|
|
|
18
15
|
if /java/ === RUBY_PLATFORM # JRuby
|
|
19
16
|
gem 'pandoc-ruby'
|
|
20
17
|
else
|
|
21
|
-
gem 'pry-byebug'
|
|
22
18
|
gem 'redcarpet'
|
|
23
19
|
|
|
24
20
|
if RUBY_PLATFORM !~ /mswin|mingw/ && RUBY_ENGINE != 'truffleruby'
|
|
25
|
-
|
|
26
|
-
gem 'faml'
|
|
27
|
-
end
|
|
21
|
+
gem 'faml'
|
|
28
22
|
gem 'stackprof'
|
|
29
23
|
end
|
|
30
24
|
end
|
data/REFERENCE.md
CHANGED
|
@@ -57,6 +57,7 @@ for full features in original implementation.
|
|
|
57
57
|
- [x] :plain
|
|
58
58
|
- [x] :preserve
|
|
59
59
|
- [x] :ruby
|
|
60
|
+
- `haml_io` API is not supported. Use [hamlit-haml\_io.gem](https://github.com/hamlit/hamlit-haml_io) if you need.
|
|
60
61
|
- [x] :sass
|
|
61
62
|
- [x] :scss
|
|
62
63
|
- [ ] :textile
|
|
@@ -121,10 +122,10 @@ and merging multiple ids results in concatenation by "\_".
|
|
|
121
122
|
%div{ id: false }
|
|
122
123
|
|
|
123
124
|
# Output
|
|
124
|
-
<div id='foo_bar'></
|
|
125
|
-
<div id='foo_bar'></
|
|
126
|
-
<div id='foo_bar'></
|
|
127
|
-
<div id=''></
|
|
125
|
+
<div id='foo_bar'></div>
|
|
126
|
+
<div id='foo_bar'></div>
|
|
127
|
+
<div id='foo_bar'></div>
|
|
128
|
+
<div id=''></div>
|
|
128
129
|
```
|
|
129
130
|
|
|
130
131
|
### class attribute
|
|
@@ -224,6 +225,14 @@ Hamlit::RailsTemplate.set_options attr_quote: '"'
|
|
|
224
225
|
set :haml, { attr_quote: '"' }
|
|
225
226
|
```
|
|
226
227
|
|
|
228
|
+
## Ruby module
|
|
229
|
+
|
|
230
|
+
`Hamlit::Template` is a module registered to `Tilt`. You can use it like:
|
|
231
|
+
|
|
232
|
+
```rb
|
|
233
|
+
Hamlit::Template.new { "%strong Yay for HAML!" }.render
|
|
234
|
+
```
|
|
235
|
+
|
|
227
236
|
## Creating a custom filter
|
|
228
237
|
|
|
229
238
|
Currently it doesn't have filter registering interface compatible with Haml.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Original: https://github.com/amatsuda/string_template/blob/master/benchmark.rb
|
|
2
|
+
require 'benchmark_driver'
|
|
3
|
+
|
|
4
|
+
Benchmark.driver(repeat_count: 8) do |x|
|
|
5
|
+
x.prelude %{
|
|
6
|
+
require 'rails'
|
|
7
|
+
require 'action_view'
|
|
8
|
+
require 'string_template'
|
|
9
|
+
StringTemplate::Railtie.run_initializers
|
|
10
|
+
require 'hamlit'
|
|
11
|
+
Hamlit::Railtie.run_initializers
|
|
12
|
+
Hamlit::RailsTemplate.set_options(escape_html: false, generator: Temple::Generators::ArrayBuffer)
|
|
13
|
+
require 'action_view/base'
|
|
14
|
+
|
|
15
|
+
(view = Class.new(ActionView::Base).new(ActionView::LookupContext.new(''))).instance_variable_set(:@world, 'world!')
|
|
16
|
+
|
|
17
|
+
# compile template
|
|
18
|
+
hello = 'benchmark/dynamic_merger/hello'
|
|
19
|
+
view.render(template: hello, handlers: 'string')
|
|
20
|
+
view.render(template: hello, handlers: 'haml')
|
|
21
|
+
}
|
|
22
|
+
x.report 'string', %{ view.render(template: hello, handlers: 'string') }
|
|
23
|
+
x.report 'hamlit', %{ view.render(template: hello, handlers: 'haml') }
|
|
24
|
+
x.loop_count 100_000
|
|
25
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
hello, #{ @world }
|
|
2
|
+
hello, #{ @world }
|
|
3
|
+
hello, #{ @world }
|
|
4
|
+
hello, #{ @world }
|
|
5
|
+
hello, #{ @world }
|
|
6
|
+
hello, #{ @world }
|
|
7
|
+
hello, #{ @world }
|
|
8
|
+
hello, #{ @world }
|
|
9
|
+
hello, #{ @world }
|
|
10
|
+
hello, #{ @world }
|
|
11
|
+
hello, #{ @world }
|
|
12
|
+
hello, #{ @world }
|
|
13
|
+
hello, #{ @world }
|
|
14
|
+
hello, #{ @world }
|
|
15
|
+
hello, #{ @world }
|
|
16
|
+
hello, #{ @world }
|
|
17
|
+
hello, #{ @world }
|
|
18
|
+
hello, #{ @world }
|
|
19
|
+
hello, #{ @world }
|
|
20
|
+
hello, #{ @world }
|
|
21
|
+
hello, #{ @world }
|
|
22
|
+
hello, #{ @world }
|
|
23
|
+
hello, #{ @world }
|
|
24
|
+
hello, #{ @world }
|
|
25
|
+
hello, #{ @world }
|
|
26
|
+
hello, #{ @world }
|
|
27
|
+
hello, #{ @world }
|
|
28
|
+
hello, #{ @world }
|
|
29
|
+
hello, #{ @world }
|
|
30
|
+
hello, #{ @world }
|
|
31
|
+
hello, #{ @world }
|
|
32
|
+
hello, #{ @world }
|
|
33
|
+
hello, #{ @world }
|
|
34
|
+
hello, #{ @world }
|
|
35
|
+
hello, #{ @world }
|
|
36
|
+
hello, #{ @world }
|
|
37
|
+
hello, #{ @world }
|
|
38
|
+
hello, #{ @world }
|
|
39
|
+
hello, #{ @world }
|
|
40
|
+
hello, #{ @world }
|
|
41
|
+
hello, #{ @world }
|
|
42
|
+
hello, #{ @world }
|
|
43
|
+
hello, #{ @world }
|
|
44
|
+
hello, #{ @world }
|
|
45
|
+
hello, #{ @world }
|
|
46
|
+
hello, #{ @world }
|
|
47
|
+
hello, #{ @world }
|
|
48
|
+
hello, #{ @world }
|
|
49
|
+
hello, #{ @world }
|
|
50
|
+
hello, #{ @world }
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
hello, #{ @world }
|
|
2
|
+
hello, #{ @world }
|
|
3
|
+
hello, #{ @world }
|
|
4
|
+
hello, #{ @world }
|
|
5
|
+
hello, #{ @world }
|
|
6
|
+
hello, #{ @world }
|
|
7
|
+
hello, #{ @world }
|
|
8
|
+
hello, #{ @world }
|
|
9
|
+
hello, #{ @world }
|
|
10
|
+
hello, #{ @world }
|
|
11
|
+
hello, #{ @world }
|
|
12
|
+
hello, #{ @world }
|
|
13
|
+
hello, #{ @world }
|
|
14
|
+
hello, #{ @world }
|
|
15
|
+
hello, #{ @world }
|
|
16
|
+
hello, #{ @world }
|
|
17
|
+
hello, #{ @world }
|
|
18
|
+
hello, #{ @world }
|
|
19
|
+
hello, #{ @world }
|
|
20
|
+
hello, #{ @world }
|
|
21
|
+
hello, #{ @world }
|
|
22
|
+
hello, #{ @world }
|
|
23
|
+
hello, #{ @world }
|
|
24
|
+
hello, #{ @world }
|
|
25
|
+
hello, #{ @world }
|
|
26
|
+
hello, #{ @world }
|
|
27
|
+
hello, #{ @world }
|
|
28
|
+
hello, #{ @world }
|
|
29
|
+
hello, #{ @world }
|
|
30
|
+
hello, #{ @world }
|
|
31
|
+
hello, #{ @world }
|
|
32
|
+
hello, #{ @world }
|
|
33
|
+
hello, #{ @world }
|
|
34
|
+
hello, #{ @world }
|
|
35
|
+
hello, #{ @world }
|
|
36
|
+
hello, #{ @world }
|
|
37
|
+
hello, #{ @world }
|
|
38
|
+
hello, #{ @world }
|
|
39
|
+
hello, #{ @world }
|
|
40
|
+
hello, #{ @world }
|
|
41
|
+
hello, #{ @world }
|
|
42
|
+
hello, #{ @world }
|
|
43
|
+
hello, #{ @world }
|
|
44
|
+
hello, #{ @world }
|
|
45
|
+
hello, #{ @world }
|
|
46
|
+
hello, #{ @world }
|
|
47
|
+
hello, #{ @world }
|
|
48
|
+
hello, #{ @world }
|
|
49
|
+
hello, #{ @world }
|
|
50
|
+
hello, #{ @world }
|
data/bin/bench
CHANGED
|
@@ -25,7 +25,7 @@ class Bench < Thor
|
|
|
25
25
|
haml = File.read(file)
|
|
26
26
|
|
|
27
27
|
Benchmark.ips do |x|
|
|
28
|
-
x.report("haml v#{Haml::VERSION}") { Haml::Engine.new(haml, escape_html: true, escape_attrs: true
|
|
28
|
+
x.report("haml v#{Haml::VERSION}") { Haml::Engine.new(haml, escape_html: true, escape_attrs: true).precompiled }
|
|
29
29
|
x.report("faml v#{Faml::VERSION}") { Faml::Engine.new.call(haml) }
|
|
30
30
|
x.report("hamlit v#{Hamlit::VERSION}") { Hamlit::Engine.new.call(haml) }
|
|
31
31
|
x.compare!
|
|
@@ -43,7 +43,7 @@ class Bench < Thor
|
|
|
43
43
|
object.instance_eval(File.read(ruby_file))
|
|
44
44
|
end
|
|
45
45
|
|
|
46
|
-
Haml::Engine.new(haml, escape_html: true, escape_attrs: true
|
|
46
|
+
Haml::Engine.new(haml, escape_html: true, escape_attrs: true).def_method(object, :haml)
|
|
47
47
|
object.instance_eval "def faml; #{Faml::Engine.new.call(haml)}; end"
|
|
48
48
|
object.instance_eval "def hamlit; #{Hamlit::Engine.new.call(haml)}; end"
|
|
49
49
|
|
|
@@ -59,7 +59,7 @@ class Bench < Thor
|
|
|
59
59
|
def code(file)
|
|
60
60
|
haml = File.read(file)
|
|
61
61
|
puts "#{?= * 49}\n Haml Source: #{file}\n#{?= * 49}"
|
|
62
|
-
puts Haml::Engine.new(haml, escape_html: true, escape_attrs: true
|
|
62
|
+
puts Haml::Engine.new(haml, escape_html: true, escape_attrs: true).precompiled
|
|
63
63
|
puts "\n#{?= * 49}\n Faml Source: #{file}\n#{?= * 49}"
|
|
64
64
|
puts Faml::Engine.new.call(haml)
|
|
65
65
|
puts "\n#{?= * 49}\n Hamlit Source: #{file}\n#{?= * 49}"
|
data/ext/hamlit/hamlit.c
CHANGED
data/hamlit.gemspec
CHANGED
|
@@ -26,10 +26,11 @@ Gem::Specification.new do |spec|
|
|
|
26
26
|
spec.required_ruby_version = '>= 2.1.0'
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
spec.add_dependency 'temple', '>= 0.8.
|
|
29
|
+
spec.add_dependency 'temple', '>= 0.8.2'
|
|
30
30
|
spec.add_dependency 'thor'
|
|
31
31
|
spec.add_dependency 'tilt'
|
|
32
32
|
|
|
33
|
+
spec.add_development_dependency 'benchmark_driver'
|
|
33
34
|
spec.add_development_dependency 'bundler'
|
|
34
35
|
spec.add_development_dependency 'coffee-script'
|
|
35
36
|
spec.add_development_dependency 'erubi'
|
|
@@ -41,5 +42,6 @@ Gem::Specification.new do |spec|
|
|
|
41
42
|
spec.add_development_dependency 'rake-compiler'
|
|
42
43
|
spec.add_development_dependency 'sass'
|
|
43
44
|
spec.add_development_dependency 'slim'
|
|
45
|
+
spec.add_development_dependency 'string_template'
|
|
44
46
|
spec.add_development_dependency 'unindent'
|
|
45
47
|
end
|
|
@@ -47,7 +47,7 @@ module Hamlit::AttributeBuilder
|
|
|
47
47
|
when value.is_a?(String)
|
|
48
48
|
# noop
|
|
49
49
|
when value.is_a?(Array)
|
|
50
|
-
value = value.flatten.select { |v| v }.map(&:to_s).
|
|
50
|
+
value = value.flatten.select { |v| v }.map(&:to_s).uniq.join(' ')
|
|
51
51
|
when value
|
|
52
52
|
value = value.to_s
|
|
53
53
|
else
|
|
@@ -67,7 +67,7 @@ module Hamlit::AttributeBuilder
|
|
|
67
67
|
classes << value.to_s
|
|
68
68
|
end
|
|
69
69
|
end
|
|
70
|
-
escape_html(escape_attrs, classes.map(&:to_s).
|
|
70
|
+
escape_html(escape_attrs, classes.map(&:to_s).uniq.join(' '))
|
|
71
71
|
end
|
|
72
72
|
|
|
73
73
|
def build_data(escape_attrs, quote, *hashes)
|
|
@@ -17,7 +17,7 @@ module Hamlit
|
|
|
17
17
|
if node.value[:object_ref] != :nil || !Ripper.respond_to?(:lex) # No Ripper.lex in truffleruby
|
|
18
18
|
return runtime_compile(node)
|
|
19
19
|
end
|
|
20
|
-
node.value[:
|
|
20
|
+
[node.value[:dynamic_attributes].new, node.value[:dynamic_attributes].old].compact.each do |attribute_str|
|
|
21
21
|
hash = AttributeParser.parse(attribute_str)
|
|
22
22
|
return runtime_compile(node) unless hash
|
|
23
23
|
hashes << hash
|
|
@@ -28,11 +28,11 @@ module Hamlit
|
|
|
28
28
|
private
|
|
29
29
|
|
|
30
30
|
def runtime_compile(node)
|
|
31
|
-
attrs =
|
|
31
|
+
attrs = []
|
|
32
32
|
attrs.unshift(node.value[:attributes].inspect) if node.value[:attributes] != {}
|
|
33
33
|
|
|
34
34
|
args = [@escape_attrs.inspect, "#{@quote.inspect}.freeze", @format.inspect].push(node.value[:object_ref]) + attrs
|
|
35
|
-
[:html, :attrs, [:dynamic, "::Hamlit::AttributeBuilder.build(#{args.join(', ')})"]]
|
|
35
|
+
[:html, :attrs, [:dynamic, "::Hamlit::AttributeBuilder.build(#{args.join(', ')}, #{node.value[:dynamic_attributes].to_literal})"]]
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
def static_compile(static_hash, dynamic_hashes)
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
require 'hamlit/temple_line_counter'
|
|
3
|
+
|
|
2
4
|
module Hamlit
|
|
3
5
|
class Compiler
|
|
4
6
|
class ChildrenCompiler
|
|
@@ -14,7 +16,7 @@ module Hamlit
|
|
|
14
16
|
node.children.each do |n|
|
|
15
17
|
rstrip_whitespace!(temple) if nuke_prev_whitespace?(n)
|
|
16
18
|
insert_newlines!(temple, n)
|
|
17
|
-
temple <<
|
|
19
|
+
temple << moving_lineno(n) { block.call(n) }
|
|
18
20
|
temple << :whitespace if insert_whitespace?(n)
|
|
19
21
|
end
|
|
20
22
|
rstrip_whitespace!(temple) if nuke_inner_whitespace?(node)
|
|
@@ -27,19 +29,31 @@ module Hamlit
|
|
|
27
29
|
(node.line - @lineno).times do
|
|
28
30
|
temple << [:newline]
|
|
29
31
|
end
|
|
32
|
+
|
|
30
33
|
@lineno = node.line
|
|
34
|
+
end
|
|
31
35
|
|
|
36
|
+
def moving_lineno(node, &block)
|
|
37
|
+
# before: As they may have children, we need to increment lineno before compilation.
|
|
32
38
|
case node.type
|
|
33
39
|
when :script, :silent_script
|
|
34
40
|
@lineno += 1
|
|
35
|
-
when :filter
|
|
36
|
-
@lineno += (node.value[:text] || '').split("\n").size
|
|
37
41
|
when :tag
|
|
38
|
-
node.value[:
|
|
42
|
+
[node.value[:dynamic_attributes].new, node.value[:dynamic_attributes].old].compact.each do |attribute_hash|
|
|
39
43
|
@lineno += attribute_hash.count("\n")
|
|
40
44
|
end
|
|
41
45
|
@lineno += 1 if node.children.empty? && node.value[:parse]
|
|
42
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
|
|
43
57
|
end
|
|
44
58
|
|
|
45
59
|
def confirm_whitespace(temple)
|
|
@@ -25,11 +25,13 @@ module Hamlit
|
|
|
25
25
|
condition = $1
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
content =
|
|
29
|
+
if node.children.empty?
|
|
30
|
+
[:static, " #{node.value[:text]} "]
|
|
31
|
+
else
|
|
32
|
+
yield(node)
|
|
33
|
+
end
|
|
34
|
+
[:html, :condcomment, condition, content, node.value[:revealed]]
|
|
33
35
|
end
|
|
34
36
|
end
|
|
35
37
|
end
|
|
@@ -55,10 +55,6 @@ module Hamlit
|
|
|
55
55
|
|
|
56
56
|
# We should handle interpolation here to escape only interpolated values.
|
|
57
57
|
def compile_interpolated_plain(node)
|
|
58
|
-
unless Ripper.respond_to?(:lex) # No Ripper.lex in truffleruby
|
|
59
|
-
return [:multi, [:escape, node.value[:escape_interpolation], [:dynamic, "%Q[#{node.value[:value]}]"]], [:newline]]
|
|
60
|
-
end
|
|
61
|
-
|
|
62
58
|
temple = [:multi]
|
|
63
59
|
StringSplitter.compile(node.value[:value]).each do |type, value|
|
|
64
60
|
case type
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module Hamlit
|
|
3
|
+
# Compile [:multi, [:static, 'foo'], [:dynamic, 'bar']] to [:dynamic, '"foo#{bar}"']
|
|
4
|
+
class DynamicMerger < Temple::Filter
|
|
5
|
+
def on_multi(*exps)
|
|
6
|
+
exps = exps.dup
|
|
7
|
+
result = [:multi]
|
|
8
|
+
buffer = []
|
|
9
|
+
|
|
10
|
+
until exps.empty?
|
|
11
|
+
type, arg = exps.first
|
|
12
|
+
if type == :dynamic && arg.count("\n") == 0
|
|
13
|
+
buffer << exps.shift
|
|
14
|
+
elsif type == :static && exps.size > (count = arg.count("\n")) &&
|
|
15
|
+
exps[1, count].all? { |e| e == [:newline] }
|
|
16
|
+
(1 + count).times { buffer << exps.shift }
|
|
17
|
+
elsif type == :newline && exps.size > (count = count_newline(exps)) &&
|
|
18
|
+
exps[count].first == :static && count == exps[count].last.count("\n")
|
|
19
|
+
(count + 1).times { buffer << exps.shift }
|
|
20
|
+
else
|
|
21
|
+
result.concat(merge_dynamic(buffer))
|
|
22
|
+
buffer = []
|
|
23
|
+
result << compile(exps.shift)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
result.concat(merge_dynamic(buffer))
|
|
27
|
+
|
|
28
|
+
result.size == 2 ? result[1] : result
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def merge_dynamic(exps)
|
|
34
|
+
# Merge exps only when they have both :static and :dynamic
|
|
35
|
+
unless exps.any? { |type,| type == :static } && exps.any? { |type,| type == :dynamic }
|
|
36
|
+
return exps
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
strlit_body = String.new
|
|
40
|
+
exps.each do |type, arg|
|
|
41
|
+
case type
|
|
42
|
+
when :static
|
|
43
|
+
strlit_body << arg.dump.sub!(/\A"/, '').sub!(/"\z/, '').gsub('\n', "\n")
|
|
44
|
+
when :dynamic
|
|
45
|
+
strlit_body << "\#{#{arg}}"
|
|
46
|
+
when :newline
|
|
47
|
+
# newline is added by `gsub('\n', "\n")`
|
|
48
|
+
else
|
|
49
|
+
raise "unexpected type #{type.inspect} is given to #merge_dynamic"
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
[[:dynamic, "%Q\0#{strlit_body}\0"]]
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def count_newline(exps)
|
|
56
|
+
count = 0
|
|
57
|
+
exps.each do |exp|
|
|
58
|
+
if exp == [:newline]
|
|
59
|
+
count += 1
|
|
60
|
+
else
|
|
61
|
+
return count
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
return count
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
data/lib/hamlit/engine.rb
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
require 'temple'
|
|
3
3
|
require 'hamlit/parser'
|
|
4
4
|
require 'hamlit/compiler'
|
|
5
|
+
require 'hamlit/html'
|
|
5
6
|
require 'hamlit/escapable'
|
|
6
7
|
require 'hamlit/force_escapable'
|
|
7
|
-
require 'hamlit/
|
|
8
|
-
require 'hamlit/string_splitter'
|
|
8
|
+
require 'hamlit/dynamic_merger'
|
|
9
9
|
|
|
10
10
|
module Hamlit
|
|
11
11
|
class Engine < Temple::Engine
|
|
@@ -25,15 +25,14 @@ module Hamlit
|
|
|
25
25
|
use Parser
|
|
26
26
|
use Compiler
|
|
27
27
|
use HTML
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
filter :StaticAnalyzer
|
|
31
|
-
end
|
|
28
|
+
filter :StringSplitter
|
|
29
|
+
filter :StaticAnalyzer
|
|
32
30
|
use Escapable
|
|
33
31
|
use ForceEscapable
|
|
34
32
|
filter :ControlFlow
|
|
35
33
|
filter :MultiFlattener
|
|
36
34
|
filter :StaticMerger
|
|
35
|
+
use DynamicMerger
|
|
37
36
|
use :Generator, -> { options[:generator] }
|
|
38
37
|
end
|
|
39
38
|
end
|
data/lib/hamlit/filters/plain.rb
CHANGED
|
@@ -14,10 +14,6 @@ module Hamlit
|
|
|
14
14
|
|
|
15
15
|
def compile_plain(text)
|
|
16
16
|
string_literal = ::Hamlit::HamlUtil.unescape_interpolation(text)
|
|
17
|
-
unless Ripper.respond_to?(:lex) # truffleruby doesn't have Ripper.lex
|
|
18
|
-
return [[:escape, false, [:dynamic, string_literal]]]
|
|
19
|
-
end
|
|
20
|
-
|
|
21
17
|
StringSplitter.compile(string_literal).map do |temple|
|
|
22
18
|
type, str = temple
|
|
23
19
|
case type
|
data/lib/hamlit/html.rb
CHANGED
|
@@ -10,5 +10,13 @@ module Hamlit
|
|
|
10
10
|
end
|
|
11
11
|
super(opts)
|
|
12
12
|
end
|
|
13
|
+
|
|
14
|
+
# This dispatcher supports Haml's "revealed" conditional comment.
|
|
15
|
+
def on_html_condcomment(condition, content, revealed = false)
|
|
16
|
+
on_html_comment [:multi,
|
|
17
|
+
[:static, "[#{condition}]>#{'<!-->' if revealed}"],
|
|
18
|
+
content,
|
|
19
|
+
[:static, "#{'<!--' if revealed}<![endif]"]]
|
|
20
|
+
end
|
|
13
21
|
end
|
|
14
22
|
end
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Hamlit
|
|
4
|
+
module HamlAttributeBuilder
|
|
5
|
+
# https://html.spec.whatwg.org/multipage/syntax.html#attributes-2
|
|
6
|
+
INVALID_ATTRIBUTE_NAME_REGEX = /[ \0"'>\/=]/
|
|
7
|
+
|
|
8
|
+
class << self
|
|
9
|
+
def build_attributes(is_html, attr_wrapper, escape_attrs, hyphenate_data_attrs, attributes = {})
|
|
10
|
+
# @TODO this is an absolutely ridiculous amount of arguments. At least
|
|
11
|
+
# some of this needs to be moved into an instance method.
|
|
12
|
+
join_char = hyphenate_data_attrs ? '-' : '_'
|
|
13
|
+
|
|
14
|
+
attributes.each do |key, value|
|
|
15
|
+
if value.is_a?(Hash)
|
|
16
|
+
data_attributes = attributes.delete(key)
|
|
17
|
+
data_attributes = flatten_data_attributes(data_attributes, '', join_char)
|
|
18
|
+
data_attributes = build_data_keys(data_attributes, hyphenate_data_attrs, key)
|
|
19
|
+
verify_attribute_names!(data_attributes.keys)
|
|
20
|
+
attributes = data_attributes.merge(attributes)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
result = attributes.collect do |attr, value|
|
|
25
|
+
next if value.nil?
|
|
26
|
+
|
|
27
|
+
value = filter_and_join(value, ' ') if attr == 'class'
|
|
28
|
+
value = filter_and_join(value, '_') if attr == 'id'
|
|
29
|
+
|
|
30
|
+
if value == true
|
|
31
|
+
next " #{attr}" if is_html
|
|
32
|
+
next " #{attr}=#{attr_wrapper}#{attr}#{attr_wrapper}"
|
|
33
|
+
elsif value == false
|
|
34
|
+
next
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
value =
|
|
38
|
+
if escape_attrs == :once
|
|
39
|
+
Hamlit::HamlHelpers.escape_once_without_haml_xss(value.to_s)
|
|
40
|
+
elsif escape_attrs
|
|
41
|
+
Hamlit::HamlHelpers.html_escape_without_haml_xss(value.to_s)
|
|
42
|
+
else
|
|
43
|
+
value.to_s
|
|
44
|
+
end
|
|
45
|
+
" #{attr}=#{attr_wrapper}#{value}#{attr_wrapper}"
|
|
46
|
+
end
|
|
47
|
+
result.compact!
|
|
48
|
+
result.sort!
|
|
49
|
+
result.join
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# @return [String, nil]
|
|
53
|
+
def filter_and_join(value, separator)
|
|
54
|
+
return '' if (value.respond_to?(:empty?) && value.empty?)
|
|
55
|
+
|
|
56
|
+
if value.is_a?(Array)
|
|
57
|
+
value = value.flatten
|
|
58
|
+
value.map! {|item| item ? item.to_s : nil}
|
|
59
|
+
value.compact!
|
|
60
|
+
value = value.join(separator)
|
|
61
|
+
else
|
|
62
|
+
value = value ? value.to_s : nil
|
|
63
|
+
end
|
|
64
|
+
!value.nil? && !value.empty? && value
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Merges two attribute hashes.
|
|
68
|
+
# This is the same as `to.merge!(from)`,
|
|
69
|
+
# except that it merges id, class, and data attributes.
|
|
70
|
+
#
|
|
71
|
+
# ids are concatenated with `"_"`,
|
|
72
|
+
# and classes are concatenated with `" "`.
|
|
73
|
+
# data hashes are simply merged.
|
|
74
|
+
#
|
|
75
|
+
# Destructively modifies `to`.
|
|
76
|
+
#
|
|
77
|
+
# @param to [{String => String,Hash}] The attribute hash to merge into
|
|
78
|
+
# @param from [{String => Object}] The attribute hash to merge from
|
|
79
|
+
# @return [{String => String,Hash}] `to`, after being merged
|
|
80
|
+
def merge_attributes!(to, from)
|
|
81
|
+
from.keys.each do |key|
|
|
82
|
+
to[key] = merge_value(key, to[key], from[key])
|
|
83
|
+
end
|
|
84
|
+
to
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Merge multiple values to one attribute value. No destructive operation.
|
|
88
|
+
#
|
|
89
|
+
# @param key [String]
|
|
90
|
+
# @param values [Array<Object>]
|
|
91
|
+
# @return [String,Hash]
|
|
92
|
+
def merge_values(key, *values)
|
|
93
|
+
values.inject(nil) do |to, from|
|
|
94
|
+
merge_value(key, to, from)
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def verify_attribute_names!(attribute_names)
|
|
99
|
+
attribute_names.each do |attribute_name|
|
|
100
|
+
if attribute_name =~ INVALID_ATTRIBUTE_NAME_REGEX
|
|
101
|
+
raise InvalidAttributeNameError.new("Invalid attribute name '#{attribute_name}' was rendered")
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
private
|
|
107
|
+
|
|
108
|
+
# Merge a couple of values to one attribute value. No destructive operation.
|
|
109
|
+
#
|
|
110
|
+
# @param to [String,Hash,nil]
|
|
111
|
+
# @param from [Object]
|
|
112
|
+
# @return [String,Hash]
|
|
113
|
+
def merge_value(key, to, from)
|
|
114
|
+
if from.kind_of?(Hash) || to.kind_of?(Hash)
|
|
115
|
+
from = { nil => from } if !from.is_a?(Hash)
|
|
116
|
+
to = { nil => to } if !to.is_a?(Hash)
|
|
117
|
+
to.merge(from)
|
|
118
|
+
elsif key == 'id'
|
|
119
|
+
merged_id = filter_and_join(from, '_')
|
|
120
|
+
if to && merged_id
|
|
121
|
+
merged_id = "#{to}_#{merged_id}"
|
|
122
|
+
elsif to || merged_id
|
|
123
|
+
merged_id ||= to
|
|
124
|
+
end
|
|
125
|
+
merged_id
|
|
126
|
+
elsif key == 'class'
|
|
127
|
+
merged_class = filter_and_join(from, ' ')
|
|
128
|
+
if to && merged_class
|
|
129
|
+
merged_class = (to.split(' ') | merged_class.split(' ')).join(' ')
|
|
130
|
+
elsif to || merged_class
|
|
131
|
+
merged_class ||= to
|
|
132
|
+
end
|
|
133
|
+
merged_class
|
|
134
|
+
else
|
|
135
|
+
from
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def build_data_keys(data_hash, hyphenate, attr_name="data")
|
|
140
|
+
Hash[data_hash.map do |name, value|
|
|
141
|
+
if name == nil
|
|
142
|
+
[attr_name, value]
|
|
143
|
+
elsif hyphenate
|
|
144
|
+
["#{attr_name}-#{name.to_s.tr('_', '-')}", value]
|
|
145
|
+
else
|
|
146
|
+
["#{attr_name}-#{name}", value]
|
|
147
|
+
end
|
|
148
|
+
end]
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def flatten_data_attributes(data, key, join_char, seen = [])
|
|
152
|
+
return {key => data} unless data.is_a?(Hash)
|
|
153
|
+
|
|
154
|
+
return {key => nil} if seen.include? data.object_id
|
|
155
|
+
seen << data.object_id
|
|
156
|
+
|
|
157
|
+
data.sort {|x, y| x[0].to_s <=> y[0].to_s}.inject({}) do |hash, (k, v)|
|
|
158
|
+
joined = key == '' ? k : [key, k].join(join_char)
|
|
159
|
+
hash.merge! flatten_data_attributes(v, joined, join_char, seen)
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
@@ -617,6 +617,9 @@ MESSAGE
|
|
|
617
617
|
text.gsub(HTML_ESCAPE_REGEX, HTML_ESCAPE)
|
|
618
618
|
end
|
|
619
619
|
|
|
620
|
+
# Always escape text regardless of html_safe?
|
|
621
|
+
alias_method :html_escape_without_haml_xss, :html_escape
|
|
622
|
+
|
|
620
623
|
HTML_ESCAPE_ONCE_REGEX = /[\"><]|&(?!(?:[a-zA-Z]+|#(?:\d+|[xX][0-9a-fA-F]+));)/
|
|
621
624
|
|
|
622
625
|
# Escapes HTML entities in `text`, but without escaping an ampersand
|
|
@@ -629,6 +632,9 @@ MESSAGE
|
|
|
629
632
|
text.gsub(HTML_ESCAPE_ONCE_REGEX, HTML_ESCAPE)
|
|
630
633
|
end
|
|
631
634
|
|
|
635
|
+
# Always escape text once regardless of html_safe?
|
|
636
|
+
alias_method :escape_once_without_haml_xss, :escape_once
|
|
637
|
+
|
|
632
638
|
# Returns whether or not the current template is a Haml template.
|
|
633
639
|
#
|
|
634
640
|
# This function, unlike other {Haml::Helpers} functions,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
require 'strscan'
|
|
2
2
|
require 'hamlit/parser/haml_util'
|
|
3
3
|
require 'hamlit/parser/haml_error'
|
|
4
|
+
require 'hamlit/parser/haml_attribute_builder'
|
|
4
5
|
|
|
5
6
|
module Hamlit
|
|
6
7
|
class HamlParser
|
|
@@ -206,6 +207,31 @@ module Hamlit
|
|
|
206
207
|
end
|
|
207
208
|
end
|
|
208
209
|
|
|
210
|
+
# @param [String] new - Hash literal including dynamic values.
|
|
211
|
+
# @param [String] old - Hash literal including dynamic values or Ruby literal of multiple Hashes which MUST be interpreted as method's last arguments.
|
|
212
|
+
DynamicAttributes = Struct.new(:new, :old) do
|
|
213
|
+
undef :old=
|
|
214
|
+
def old=(value)
|
|
215
|
+
unless value =~ /\A{.*}\z/m
|
|
216
|
+
raise ArgumentError.new('Old attributes must start with "{" and end with "}"')
|
|
217
|
+
end
|
|
218
|
+
self[:old] = value
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
# This will be a literal for Haml::Buffer#attributes's last argument, `attributes_hashes`.
|
|
222
|
+
def to_literal
|
|
223
|
+
[new, stripped_old].compact.join(', ')
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
private
|
|
227
|
+
|
|
228
|
+
# For `%foo{ { foo: 1 }, bar: 2 }`, :old is "{ { foo: 1 }, bar: 2 }" and this method returns " { foo: 1 }, bar: 2 " for last argument.
|
|
229
|
+
def stripped_old
|
|
230
|
+
return nil if old.nil?
|
|
231
|
+
old.sub!(/\A{/, '').sub!(/}\z/m, '')
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
|
|
209
235
|
# Processes and deals with lowering indentation.
|
|
210
236
|
def process_indent(line)
|
|
211
237
|
return unless line.tabs <= @template_tabs && @template_tabs > 0
|
|
@@ -403,22 +429,20 @@ module Hamlit
|
|
|
403
429
|
end
|
|
404
430
|
|
|
405
431
|
attributes = ::Hamlit::HamlParser.parse_class_and_id(attributes)
|
|
406
|
-
|
|
432
|
+
dynamic_attributes = DynamicAttributes.new
|
|
407
433
|
|
|
408
434
|
if attributes_hashes[:new]
|
|
409
435
|
static_attributes, attributes_hash = attributes_hashes[:new]
|
|
410
|
-
|
|
411
|
-
|
|
436
|
+
HamlAttributeBuilder.merge_attributes!(attributes, static_attributes) if static_attributes
|
|
437
|
+
dynamic_attributes.new = attributes_hash
|
|
412
438
|
end
|
|
413
439
|
|
|
414
440
|
if attributes_hashes[:old]
|
|
415
441
|
static_attributes = parse_static_hash(attributes_hashes[:old])
|
|
416
|
-
|
|
417
|
-
|
|
442
|
+
HamlAttributeBuilder.merge_attributes!(attributes, static_attributes) if static_attributes
|
|
443
|
+
dynamic_attributes.old = attributes_hashes[:old] unless static_attributes || @options.suppress_eval
|
|
418
444
|
end
|
|
419
445
|
|
|
420
|
-
attributes_list.compact!
|
|
421
|
-
|
|
422
446
|
raise ::Hamlit::HamlSyntaxError.new(::Hamlit::HamlError.message(:illegal_nesting_self_closing), @next_line.index) if block_opened? && self_closing
|
|
423
447
|
raise ::Hamlit::HamlSyntaxError.new(::Hamlit::HamlError.message(:no_ruby_code, action), last_line - 1) if parse && value.empty?
|
|
424
448
|
raise ::Hamlit::HamlSyntaxError.new(::Hamlit::HamlError.message(:self_closing_content), last_line - 1) if self_closing && !value.empty?
|
|
@@ -433,7 +457,7 @@ module Hamlit
|
|
|
433
457
|
line = handle_ruby_multiline(line) if parse
|
|
434
458
|
|
|
435
459
|
ParseNode.new(:tag, line.index + 1, :name => tag_name, :attributes => attributes,
|
|
436
|
-
:
|
|
460
|
+
:dynamic_attributes => dynamic_attributes, :self_closing => self_closing,
|
|
437
461
|
:nuke_inner_whitespace => nuke_inner_whitespace,
|
|
438
462
|
:nuke_outer_whitespace => nuke_outer_whitespace, :object_ref => object_ref,
|
|
439
463
|
:escape_html => escape_html, :preserve_tag => preserve_tag,
|
|
@@ -641,7 +665,6 @@ module Hamlit
|
|
|
641
665
|
raise e
|
|
642
666
|
end
|
|
643
667
|
|
|
644
|
-
attributes_hash = attributes_hash[1...-1] if attributes_hash
|
|
645
668
|
return attributes_hash, rest, last_line
|
|
646
669
|
end
|
|
647
670
|
|
|
@@ -6,12 +6,15 @@ module Hamlit
|
|
|
6
6
|
# to work with Rails' XSS protection methods.
|
|
7
7
|
module XssMods
|
|
8
8
|
def self.included(base)
|
|
9
|
-
%w[
|
|
10
|
-
precede succeed capture_haml haml_concat haml_internal_concat haml_indent
|
|
11
|
-
escape_once].each do |name|
|
|
9
|
+
%w[find_and_preserve preserve list_of surround
|
|
10
|
+
precede succeed capture_haml haml_concat haml_internal_concat haml_indent].each do |name|
|
|
12
11
|
base.send(:alias_method, "#{name}_without_haml_xss", name)
|
|
13
12
|
base.send(:alias_method, name, "#{name}_with_haml_xss")
|
|
14
13
|
end
|
|
14
|
+
# Those two always have _without_haml_xss
|
|
15
|
+
%w[html_escape escape_once].each do |name|
|
|
16
|
+
base.send(:alias_method, name, "#{name}_with_haml_xss")
|
|
17
|
+
end
|
|
15
18
|
end
|
|
16
19
|
|
|
17
20
|
# Don't escape text that's already safe,
|
|
@@ -2,87 +2,18 @@ require 'ripper'
|
|
|
2
2
|
require 'hamlit/ruby_expression'
|
|
3
3
|
|
|
4
4
|
module Hamlit
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
[]
|
|
10
|
-
tokens = Ripper.lex(code.strip)
|
|
11
|
-
tokens.pop while tokens.last && %i[on_comment on_sp].include?(tokens.last[1])
|
|
12
|
-
|
|
13
|
-
if tokens.size < 2
|
|
14
|
-
raise Hamlit::InternalError.new("Expected token size >= 2 but got: #{tokens.size}")
|
|
15
|
-
end
|
|
16
|
-
compile_tokens!(exps, tokens)
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
private
|
|
21
|
-
|
|
22
|
-
def strip_quotes!(tokens)
|
|
23
|
-
_, type, beg_str = tokens.shift
|
|
24
|
-
if type != :on_tstring_beg
|
|
25
|
-
raise Hamlit::InternalError.new("Expected :on_tstring_beg but got: #{type}")
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
_, type, end_str = tokens.pop
|
|
29
|
-
if type != :on_tstring_end
|
|
30
|
-
raise Hamlit::InternalError.new("Expected :on_tstring_end but got: #{type}")
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
[beg_str, end_str]
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def compile_tokens!(exps, tokens)
|
|
37
|
-
beg_str, end_str = strip_quotes!(tokens)
|
|
38
|
-
|
|
39
|
-
until tokens.empty?
|
|
40
|
-
_, type, str = tokens.shift
|
|
41
|
-
|
|
42
|
-
case type
|
|
43
|
-
when :on_tstring_content
|
|
44
|
-
exps << [:static, eval("#{beg_str}#{str}#{end_str}")]
|
|
45
|
-
when :on_embexpr_beg
|
|
46
|
-
embedded = shift_balanced_embexpr(tokens)
|
|
47
|
-
exps << [:dynamic, embedded] unless embedded.empty?
|
|
48
|
-
end
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
def shift_balanced_embexpr(tokens)
|
|
53
|
-
String.new.tap do |embedded|
|
|
54
|
-
embexpr_open = 1
|
|
55
|
-
|
|
56
|
-
until tokens.empty?
|
|
57
|
-
_, type, str = tokens.shift
|
|
58
|
-
case type
|
|
59
|
-
when :on_embexpr_beg
|
|
60
|
-
embexpr_open += 1
|
|
61
|
-
when :on_embexpr_end
|
|
62
|
-
embexpr_open -= 1
|
|
63
|
-
break if embexpr_open == 0
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
embedded << str
|
|
67
|
-
end
|
|
68
|
-
end
|
|
5
|
+
module StringSplitter
|
|
6
|
+
# `code` param must be valid string literal
|
|
7
|
+
def self.compile(code)
|
|
8
|
+
unless Ripper.respond_to?(:lex) # truffleruby doesn't have Ripper.lex
|
|
9
|
+
return [[:dynamic, code]]
|
|
69
10
|
end
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
def on_dynamic(code)
|
|
73
|
-
return [:dynamic, code] unless RubyExpression.string_literal?(code)
|
|
74
|
-
return [:dynamic, code] if code.include?("\n")
|
|
75
11
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
temple << [:static, content]
|
|
81
|
-
when :dynamic
|
|
82
|
-
temple << on_dynamic(content)
|
|
83
|
-
end
|
|
12
|
+
begin
|
|
13
|
+
Temple::Filters::StringSplitter.compile(code)
|
|
14
|
+
rescue Temple::FilterError => e
|
|
15
|
+
raise Hamlit::InternalError.new(e.message)
|
|
84
16
|
end
|
|
85
|
-
temple
|
|
86
17
|
end
|
|
87
18
|
end
|
|
88
19
|
end
|
data/lib/hamlit/template.rb
CHANGED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module Hamlit
|
|
3
|
+
# A module to count lines of expected code. This would be faster than actual code generation
|
|
4
|
+
# and counting newlines in it.
|
|
5
|
+
module TempleLineCounter
|
|
6
|
+
class UnexpectedExpression < StandardError; end
|
|
7
|
+
|
|
8
|
+
def self.count_lines(exp)
|
|
9
|
+
type, *args = exp
|
|
10
|
+
case type
|
|
11
|
+
when :multi
|
|
12
|
+
args.map { |a| count_lines(a) }.reduce(:+) || 0
|
|
13
|
+
when :dynamic, :code
|
|
14
|
+
args.first.count("\n")
|
|
15
|
+
when :static
|
|
16
|
+
0 # It has not real newline "\n" but escaped "\\n".
|
|
17
|
+
when :case
|
|
18
|
+
arg, *cases = args
|
|
19
|
+
arg.count("\n") + cases.map do |cond, e|
|
|
20
|
+
(cond == :else ? 0 : cond.count("\n")) + count_lines(e)
|
|
21
|
+
end.reduce(:+)
|
|
22
|
+
when :escape
|
|
23
|
+
count_lines(args[1])
|
|
24
|
+
when :newline
|
|
25
|
+
1
|
|
26
|
+
else
|
|
27
|
+
raise UnexpectedExpression.new("[HAML BUG] Unexpected Temple expression '#{type}' is given!")
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
data/lib/hamlit/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: hamlit
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.12.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Takashi Kokubun
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2020-10-01 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: temple
|
|
@@ -16,14 +16,14 @@ dependencies:
|
|
|
16
16
|
requirements:
|
|
17
17
|
- - ">="
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: 0.8.
|
|
19
|
+
version: 0.8.2
|
|
20
20
|
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
24
|
- - ">="
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: 0.8.
|
|
26
|
+
version: 0.8.2
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: thor
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -52,6 +52,20 @@ dependencies:
|
|
|
52
52
|
- - ">="
|
|
53
53
|
- !ruby/object:Gem::Version
|
|
54
54
|
version: '0'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: benchmark_driver
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - ">="
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '0'
|
|
62
|
+
type: :development
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - ">="
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '0'
|
|
55
69
|
- !ruby/object:Gem::Dependency
|
|
56
70
|
name: bundler
|
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -206,6 +220,20 @@ dependencies:
|
|
|
206
220
|
- - ">="
|
|
207
221
|
- !ruby/object:Gem::Version
|
|
208
222
|
version: '0'
|
|
223
|
+
- !ruby/object:Gem::Dependency
|
|
224
|
+
name: string_template
|
|
225
|
+
requirement: !ruby/object:Gem::Requirement
|
|
226
|
+
requirements:
|
|
227
|
+
- - ">="
|
|
228
|
+
- !ruby/object:Gem::Version
|
|
229
|
+
version: '0'
|
|
230
|
+
type: :development
|
|
231
|
+
prerelease: false
|
|
232
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
233
|
+
requirements:
|
|
234
|
+
- - ">="
|
|
235
|
+
- !ruby/object:Gem::Version
|
|
236
|
+
version: '0'
|
|
209
237
|
- !ruby/object:Gem::Dependency
|
|
210
238
|
name: unindent
|
|
211
239
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -247,6 +275,9 @@ files:
|
|
|
247
275
|
- benchmark/dynamic_attributes/data_attribute.haml
|
|
248
276
|
- benchmark/dynamic_attributes/id_attribute.haml
|
|
249
277
|
- benchmark/dynamic_boolean_attribute.haml
|
|
278
|
+
- benchmark/dynamic_merger/benchmark.rb
|
|
279
|
+
- benchmark/dynamic_merger/hello.haml
|
|
280
|
+
- benchmark/dynamic_merger/hello.string
|
|
250
281
|
- benchmark/etc/attribute_builder.haml
|
|
251
282
|
- benchmark/etc/real_sample.haml
|
|
252
283
|
- benchmark/etc/real_sample.rb
|
|
@@ -290,6 +321,7 @@ files:
|
|
|
290
321
|
- lib/hamlit/compiler/script_compiler.rb
|
|
291
322
|
- lib/hamlit/compiler/silent_script_compiler.rb
|
|
292
323
|
- lib/hamlit/compiler/tag_compiler.rb
|
|
324
|
+
- lib/hamlit/dynamic_merger.rb
|
|
293
325
|
- lib/hamlit/engine.rb
|
|
294
326
|
- lib/hamlit/error.rb
|
|
295
327
|
- lib/hamlit/escapable.rb
|
|
@@ -311,7 +343,6 @@ files:
|
|
|
311
343
|
- lib/hamlit/filters/text_base.rb
|
|
312
344
|
- lib/hamlit/filters/tilt_base.rb
|
|
313
345
|
- lib/hamlit/force_escapable.rb
|
|
314
|
-
- lib/hamlit/hamlit.su
|
|
315
346
|
- lib/hamlit/helpers.rb
|
|
316
347
|
- lib/hamlit/html.rb
|
|
317
348
|
- lib/hamlit/identity.rb
|
|
@@ -319,6 +350,7 @@ files:
|
|
|
319
350
|
- lib/hamlit/parser.rb
|
|
320
351
|
- lib/hamlit/parser/MIT-LICENSE
|
|
321
352
|
- lib/hamlit/parser/README.md
|
|
353
|
+
- lib/hamlit/parser/haml_attribute_builder.rb
|
|
322
354
|
- lib/hamlit/parser/haml_buffer.rb
|
|
323
355
|
- lib/hamlit/parser/haml_compiler.rb
|
|
324
356
|
- lib/hamlit/parser/haml_error.rb
|
|
@@ -333,6 +365,7 @@ files:
|
|
|
333
365
|
- lib/hamlit/ruby_expression.rb
|
|
334
366
|
- lib/hamlit/string_splitter.rb
|
|
335
367
|
- lib/hamlit/template.rb
|
|
368
|
+
- lib/hamlit/temple_line_counter.rb
|
|
336
369
|
- lib/hamlit/utils.rb
|
|
337
370
|
- lib/hamlit/version.rb
|
|
338
371
|
homepage: https://github.com/k0kubun/hamlit
|
|
@@ -354,7 +387,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
354
387
|
- !ruby/object:Gem::Version
|
|
355
388
|
version: '0'
|
|
356
389
|
requirements: []
|
|
357
|
-
rubygems_version: 3.
|
|
390
|
+
rubygems_version: 3.1.2
|
|
358
391
|
signing_key:
|
|
359
392
|
specification_version: 4
|
|
360
393
|
summary: High Performance Haml Implementation
|
data/lib/hamlit/hamlit.su
DELETED
|
Binary file
|