better_html 1.0.16 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +4 -4
  2. data/MIT-LICENSE +1 -0
  3. data/Rakefile +19 -14
  4. data/ext/better_html_ext/better_html.h +1 -0
  5. data/ext/better_html_ext/extconf.rb +16 -0
  6. data/ext/better_html_ext/html_tokenizer.c +12 -0
  7. data/ext/better_html_ext/html_tokenizer.h +7 -0
  8. data/ext/better_html_ext/parser.c +793 -0
  9. data/ext/better_html_ext/parser.h +93 -0
  10. data/ext/better_html_ext/tokenizer.c +717 -0
  11. data/ext/better_html_ext/tokenizer.h +80 -0
  12. data/lib/better_html/ast/iterator.rb +14 -9
  13. data/lib/better_html/ast/node.rb +4 -2
  14. data/lib/better_html/better_erb/erubi_implementation.rb +43 -39
  15. data/lib/better_html/better_erb/runtime_checks.rb +140 -133
  16. data/lib/better_html/better_erb/validated_output_buffer.rb +30 -22
  17. data/lib/better_html/better_erb.rb +58 -54
  18. data/lib/better_html/config.rb +7 -4
  19. data/lib/better_html/errors.rb +4 -2
  20. data/lib/better_html/helpers.rb +7 -3
  21. data/lib/better_html/html_attributes.rb +6 -2
  22. data/lib/better_html/parser.rb +21 -14
  23. data/lib/better_html/railtie.rb +8 -4
  24. data/lib/better_html/test_helper/ruby_node.rb +15 -10
  25. data/lib/better_html/test_helper/safe_erb/allowed_script_type.rb +8 -4
  26. data/lib/better_html/test_helper/safe_erb/base.rb +12 -9
  27. data/lib/better_html/test_helper/safe_erb/no_javascript_tag_helper.rb +7 -3
  28. data/lib/better_html/test_helper/safe_erb/no_statements.rb +7 -3
  29. data/lib/better_html/test_helper/safe_erb/script_interpolation.rb +9 -4
  30. data/lib/better_html/test_helper/safe_erb/tag_interpolation.rb +23 -20
  31. data/lib/better_html/test_helper/safe_erb_tester.rb +33 -31
  32. data/lib/better_html/test_helper/safe_lodash_tester.rb +36 -35
  33. data/lib/better_html/test_helper/safety_error.rb +2 -0
  34. data/lib/better_html/tokenizer/base_erb.rb +14 -10
  35. data/lib/better_html/tokenizer/html_erb.rb +3 -2
  36. data/lib/better_html/tokenizer/html_lodash.rb +22 -14
  37. data/lib/better_html/tokenizer/javascript_erb.rb +3 -1
  38. data/lib/better_html/tokenizer/location.rb +17 -6
  39. data/lib/better_html/tokenizer/token.rb +2 -0
  40. data/lib/better_html/tokenizer/token_array.rb +8 -8
  41. data/lib/better_html/tree/attribute.rb +10 -6
  42. data/lib/better_html/tree/attributes_list.rb +9 -5
  43. data/lib/better_html/tree/tag.rb +10 -6
  44. data/lib/better_html/version.rb +3 -1
  45. data/lib/better_html.rb +19 -17
  46. data/lib/tasks/better_html_tasks.rake +1 -0
  47. metadata +39 -147
  48. data/lib/better_html/better_erb/erubis_implementation.rb +0 -44
  49. data/test/better_html/better_erb/implementation_test.rb +0 -406
  50. data/test/better_html/errors_test.rb +0 -13
  51. data/test/better_html/helpers_test.rb +0 -49
  52. data/test/better_html/parser_test.rb +0 -314
  53. data/test/better_html/test_helper/ruby_node_test.rb +0 -288
  54. data/test/better_html/test_helper/safe_erb/allowed_script_type_test.rb +0 -46
  55. data/test/better_html/test_helper/safe_erb/no_javascript_tag_helper_test.rb +0 -37
  56. data/test/better_html/test_helper/safe_erb/no_statements_test.rb +0 -129
  57. data/test/better_html/test_helper/safe_erb/script_interpolation_test.rb +0 -149
  58. data/test/better_html/test_helper/safe_erb/tag_interpolation_test.rb +0 -303
  59. data/test/better_html/test_helper/safe_lodash_tester_test.rb +0 -90
  60. data/test/better_html/tokenizer/html_erb_test.rb +0 -180
  61. data/test/better_html/tokenizer/html_lodash_test.rb +0 -98
  62. data/test/better_html/tokenizer/location_test.rb +0 -75
  63. data/test/better_html/tokenizer/token_array_test.rb +0 -146
  64. data/test/better_html/tokenizer/token_test.rb +0 -15
  65. data/test/dummy/README.rdoc +0 -28
  66. data/test/dummy/Rakefile +0 -6
  67. data/test/dummy/app/assets/javascripts/application.js +0 -13
  68. data/test/dummy/app/assets/stylesheets/application.css +0 -15
  69. data/test/dummy/app/controllers/application_controller.rb +0 -5
  70. data/test/dummy/app/helpers/application_helper.rb +0 -2
  71. data/test/dummy/app/views/layouts/application.html.erb +0 -14
  72. data/test/dummy/bin/bundle +0 -3
  73. data/test/dummy/bin/rails +0 -4
  74. data/test/dummy/bin/rake +0 -4
  75. data/test/dummy/bin/setup +0 -29
  76. data/test/dummy/config/application.rb +0 -26
  77. data/test/dummy/config/boot.rb +0 -5
  78. data/test/dummy/config/database.yml +0 -25
  79. data/test/dummy/config/environment.rb +0 -5
  80. data/test/dummy/config/environments/development.rb +0 -41
  81. data/test/dummy/config/environments/production.rb +0 -79
  82. data/test/dummy/config/environments/test.rb +0 -42
  83. data/test/dummy/config/initializers/assets.rb +0 -11
  84. data/test/dummy/config/initializers/backtrace_silencers.rb +0 -7
  85. data/test/dummy/config/initializers/cookies_serializer.rb +0 -3
  86. data/test/dummy/config/initializers/filter_parameter_logging.rb +0 -4
  87. data/test/dummy/config/initializers/inflections.rb +0 -16
  88. data/test/dummy/config/initializers/mime_types.rb +0 -4
  89. data/test/dummy/config/initializers/session_store.rb +0 -3
  90. data/test/dummy/config/initializers/wrap_parameters.rb +0 -14
  91. data/test/dummy/config/locales/en.yml +0 -23
  92. data/test/dummy/config/routes.rb +0 -56
  93. data/test/dummy/config/secrets.yml +0 -22
  94. data/test/dummy/config.ru +0 -4
  95. data/test/dummy/public/404.html +0 -67
  96. data/test/dummy/public/422.html +0 -67
  97. data/test/dummy/public/500.html +0 -66
  98. data/test/dummy/public/favicon.ico +0 -0
  99. data/test/test_helper.rb +0 -29
@@ -1,5 +1,7 @@
1
- require_relative 'base'
2
- require 'better_html/test_helper/ruby_node'
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base"
4
+ require "better_html/test_helper/ruby_node"
3
5
 
4
6
  module BetterHtml
5
7
  module TestHelper
@@ -7,7 +9,7 @@ module BetterHtml
7
9
  class ScriptInterpolation < Base
8
10
  def validate
9
11
  script_tags.each do |tag, content_node|
10
- if content_node.present? && (tag.attributes['type']&.value || "text/javascript") == "text/javascript"
12
+ if content_node.present? && (tag.attributes["type"]&.value || "text/javascript") == "text/javascript"
11
13
  validate_script(content_node)
12
14
  end
13
15
  end
@@ -24,8 +26,10 @@ module BetterHtml
24
26
  def validate_script(node)
25
27
  erb_nodes(node).each do |erb_node, indicator_node, code_node|
26
28
  next unless indicator_node.present?
29
+
27
30
  indicator = indicator_node.loc.source
28
- next if indicator == '#' || indicator == '%'
31
+ next if indicator == "#" || indicator == "%"
32
+
29
33
  source = code_node.loc.source
30
34
 
31
35
  ruby_node = begin
@@ -34,6 +38,7 @@ module BetterHtml
34
38
  nil
35
39
  end
36
40
  next unless ruby_node
41
+
37
42
  validate_script_interpolation(erb_node, ruby_node)
38
43
  end
39
44
  end
@@ -1,15 +1,14 @@
1
- require_relative 'base'
2
- require 'better_html/test_helper/ruby_node'
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base"
4
+ require "better_html/test_helper/ruby_node"
3
5
 
4
6
  module BetterHtml
5
7
  module TestHelper
6
8
  module SafeErb
7
9
  class TagInterpolation < Base
8
-
9
- NO_HTML_TAGS = %w(
10
- title textarea script
11
- style xmp iframe noembed noframes listing plaintext
12
- )
10
+ NO_HTML_TAGS = ["title", "textarea", "script", "style", "xmp", "iframe", "noembed", "noframes", "listing",
11
+ "plaintext",]
13
12
 
14
13
  def validate
15
14
  @parser.nodes_with_type(:tag).each do |tag_node|
@@ -43,7 +42,7 @@ module BetterHtml
43
42
  indicator = indicator_node.loc.source
44
43
  source = code_node.loc.source
45
44
 
46
- if indicator == '='
45
+ if indicator == "="
47
46
  ruby_node = begin
48
47
  RubyNode.parse(source)
49
48
  rescue ::Parser::SyntaxError
@@ -55,7 +54,7 @@ module BetterHtml
55
54
  handle_missing_safe_wrapper(code_node, ruby_node, attribute.name)
56
55
  end
57
56
  end
58
- elsif indicator == '=='
57
+ elsif indicator == "=="
59
58
  add_error(
60
59
  "erb interpolation with '<%==' inside html attribute is never safe",
61
60
  location: erb_node.loc
@@ -65,9 +64,10 @@ module BetterHtml
65
64
  end
66
65
 
67
66
  def validate_text_node(text_node)
68
- erb_nodes(text_node).each do |erb_node, indicator_node, code_node|
67
+ erb_nodes(text_node).each do |_erb_node, indicator_node, code_node|
69
68
  indicator = indicator_node&.loc&.source
70
- next if indicator == '#' || indicator == '%'
69
+ next if indicator == "#" || indicator == "%"
70
+
71
71
  source = code_node.loc.source
72
72
 
73
73
  ruby_node = begin
@@ -76,6 +76,7 @@ module BetterHtml
76
76
  nil
77
77
  end
78
78
  next unless ruby_node
79
+
79
80
  no_unsafe_calls(code_node, ruby_node)
80
81
  validate_ruby_helper(code_node, ruby_node)
81
82
  end
@@ -93,12 +94,13 @@ module BetterHtml
93
94
 
94
95
  def validate_ruby_helper_hash_entry(parent_node, ruby_node, key_prefix, key_node, value_node)
95
96
  return unless [:sym, :str].include?(key_node.type)
96
- key = [key_prefix, key_node.children.first.to_s].compact.join('-').dasherize
97
+
98
+ key = [key_prefix, key_node.children.first.to_s].compact.join("-").dasherize
97
99
  case value_node.type
98
100
  when :dstr
99
101
  validate_ruby_helper_hash_value(parent_node, ruby_node, key, value_node)
100
102
  when :hash
101
- if key == 'data'
103
+ if key == "data"
102
104
  value_node.child_nodes.select(&:pair?).each do |pair_node|
103
105
  validate_ruby_helper_hash_entry(parent_node, ruby_node, key, *pair_node.children)
104
106
  end
@@ -114,6 +116,7 @@ module BetterHtml
114
116
 
115
117
  def handle_missing_safe_wrapper(parent_node, ruby_node, attr_name)
116
118
  return unless @config.javascript_attribute_name?(attr_name)
119
+
117
120
  method_calls = ruby_node.return_values.select(&:method_call?)
118
121
  unsafe_calls = method_calls.select { |node| !@config.javascript_safe_method?(node.method_name) }
119
122
  if method_calls.empty?
@@ -140,13 +143,13 @@ module BetterHtml
140
143
  ruby_node.return_values.each do |call_node|
141
144
  next if call_node.static_return_value?
142
145
 
143
- if @config.javascript_attribute_name?(attr_name) &&
144
- !@config.javascript_safe_method?(call_node.method_name)
145
- add_error(
146
- "erb interpolation in javascript attribute must be wrapped in safe helper such as '(...).to_json'",
147
- location: nested_location(parent_node, ruby_node)
148
- )
149
- end
146
+ next unless @config.javascript_attribute_name?(attr_name) &&
147
+ !@config.javascript_safe_method?(call_node.method_name)
148
+
149
+ add_error(
150
+ "erb interpolation in javascript attribute must be wrapped in safe helper such as '(...).to_json'",
151
+ location: nested_location(parent_node, ruby_node)
152
+ )
150
153
  end
151
154
  end
152
155
 
@@ -1,43 +1,45 @@
1
- require 'better_html/parser'
2
- require 'better_html/test_helper/safety_error'
3
- require 'better_html/test_helper/safe_erb/base'
4
- require 'better_html/test_helper/safe_erb/no_statements'
5
- require 'better_html/test_helper/safe_erb/allowed_script_type'
6
- require 'better_html/test_helper/safe_erb/no_javascript_tag_helper'
7
- require 'better_html/test_helper/safe_erb/tag_interpolation'
8
- require 'better_html/test_helper/safe_erb/script_interpolation'
9
- require 'better_html/tree/tag'
1
+ # frozen_string_literal: true
2
+
3
+ require "better_html/parser"
4
+ require "better_html/test_helper/safety_error"
5
+ require "better_html/test_helper/safe_erb/base"
6
+ require "better_html/test_helper/safe_erb/no_statements"
7
+ require "better_html/test_helper/safe_erb/allowed_script_type"
8
+ require "better_html/test_helper/safe_erb/no_javascript_tag_helper"
9
+ require "better_html/test_helper/safe_erb/tag_interpolation"
10
+ require "better_html/test_helper/safe_erb/script_interpolation"
11
+ require "better_html/tree/tag"
10
12
 
11
13
  module BetterHtml
12
14
  module TestHelper
13
15
  module SafeErbTester
14
- SAFETY_TIPS = <<-EOF
15
- -----------
16
+ SAFETY_TIPS = <<~EOF
17
+ -----------
16
18
 
17
- The javascript snippets listed above do not appear to be escaped properly
18
- in a javascript context. Here are some tips:
19
+ The javascript snippets listed above do not appear to be escaped properly
20
+ in a javascript context. Here are some tips:
19
21
 
20
- Never use html_safe inside a html tag, since it is _never_ safe:
21
- <a href="<%= value.html_safe %>">
22
- ^^^^^^^^^^
22
+ Never use html_safe inside a html tag, since it is _never_ safe:
23
+ <a href="<%= value.html_safe %>">
24
+ ^^^^^^^^^^
23
25
 
24
- Always use .to_json for html attributes which contain javascript, like 'onclick',
25
- or twine attributes like 'data-define', 'data-context', 'data-eval', 'data-bind', etc:
26
- <div onclick="<%= value.to_json %>">
27
- ^^^^^^^^
26
+ Always use .to_json for html attributes which contain javascript, like 'onclick',
27
+ or twine attributes like 'data-define', 'data-context', 'data-eval', 'data-bind', etc:
28
+ <div onclick="<%= value.to_json %>">
29
+ ^^^^^^^^
28
30
 
29
- Always use raw and to_json together within <script> tags:
30
- <script type="text/javascript">
31
- var yourValue = <%= raw value.to_json %>;
32
- </script> ^^^ ^^^^^^^^
31
+ Always use raw and to_json together within <script> tags:
32
+ <script type="text/javascript">
33
+ var yourValue = <%= raw value.to_json %>;
34
+ </script> ^^^ ^^^^^^^^
33
35
 
34
- -----------
35
- EOF
36
+ -----------
37
+ EOF
36
38
 
37
39
  def assert_erb_safety(data, **options)
38
40
  options = options.present? ? options.dup : {}
39
41
  options[:template_language] ||= :html
40
- buffer = ::Parser::Source::Buffer.new(options[:filename] || '(buffer)')
42
+ buffer = ::Parser::Source::Buffer.new(options[:filename] || "(buffer)")
41
43
  buffer.source = data
42
44
  parser = BetterHtml::Parser.new(buffer, **options)
43
45
 
@@ -59,14 +61,14 @@ EOF
59
61
 
60
62
  messages = errors.map do |error|
61
63
  <<~EOL
62
- In #{buffer.name}:#{error.location.line}
63
- #{error.message}
64
- #{error.location.line_source_with_underline}\n
64
+ In #{buffer.name}:#{error.location.line}
65
+ #{error.message}
66
+ #{error.location.line_source_with_underline}\n
65
67
  EOL
66
68
  end
67
69
  messages << SAFETY_TIPS
68
70
 
69
- assert_predicate errors, :empty?, messages.join
71
+ assert_predicate(errors, :empty?, messages.join)
70
72
  end
71
73
  end
72
74
  end
@@ -1,35 +1,37 @@
1
- require 'better_html/test_helper/safety_error'
2
- require 'better_html/ast/iterator'
3
- require 'better_html/tree/tag'
4
- require 'better_html/parser'
1
+ # frozen_string_literal: true
2
+
3
+ require "better_html/test_helper/safety_error"
4
+ require "better_html/ast/iterator"
5
+ require "better_html/tree/tag"
6
+ require "better_html/parser"
5
7
 
6
8
  module BetterHtml
7
9
  module TestHelper
8
10
  module SafeLodashTester
9
- SAFETY_TIPS = <<-EOF
10
- -----------
11
+ SAFETY_TIPS = <<~EOF
12
+ -----------
11
13
 
12
- The javascript snippets listed above do not appear to be escaped properly
13
- in their context. Here are some tips:
14
+ The javascript snippets listed above do not appear to be escaped properly
15
+ in their context. Here are some tips:
14
16
 
15
- Always use lodash's escape syntax inside a html tag:
16
- <a href="[%= value %]">
17
- ^^^^
17
+ Always use lodash's escape syntax inside a html tag:
18
+ <a href="[%= value %]">
19
+ ^^^^
18
20
 
19
- Always use JSON.stringify() for html attributes which contain javascript, like 'onclick',
20
- or twine attributes like 'data-define', 'data-context', 'data-eval', 'data-bind', etc:
21
- <div onclick="[%= JSON.stringify(value) %]">
22
- ^^^^^^^^^^^^^^
21
+ Always use JSON.stringify() for html attributes which contain javascript, like 'onclick',
22
+ or twine attributes like 'data-define', 'data-context', 'data-eval', 'data-bind', etc:
23
+ <div onclick="[%= JSON.stringify(value) %]">
24
+ ^^^^^^^^^^^^^^
23
25
 
24
- Never use <script> tags inside lodash template.
25
- <script type="text/javascript">
26
- ^^^^^^^
26
+ Never use <script> tags inside lodash template.
27
+ <script type="text/javascript">
28
+ ^^^^^^^
27
29
 
28
- -----------
29
- EOF
30
+ -----------
31
+ EOF
30
32
 
31
33
  def assert_lodash_safety(data, **options)
32
- buffer = ::Parser::Source::Buffer.new(options[:filename] || '(buffer)')
34
+ buffer = ::Parser::Source::Buffer.new(options[:filename] || "(buffer)")
33
35
  buffer.source = data
34
36
  tester = Tester.new(buffer, **options)
35
37
 
@@ -44,11 +46,9 @@ EOF
44
46
 
45
47
  message << SAFETY_TIPS
46
48
 
47
- assert_predicate tester.errors, :empty?, message
49
+ assert_predicate(tester.errors, :empty?, message)
48
50
  end
49
51
 
50
- private
51
-
52
52
  class Tester
53
53
  attr_reader :errors
54
54
 
@@ -70,12 +70,12 @@ EOF
70
70
  validate_tag_attributes(tag)
71
71
  validate_no_statements(tag_node)
72
72
 
73
- if tag.name == 'script' && !tag.closing?
74
- add_error(
75
- "No script tags allowed nested in lodash templates",
76
- location: tag_node.loc
77
- )
78
- end
73
+ next unless tag.name == "script" && !tag.closing?
74
+
75
+ add_error(
76
+ "No script tags allowed nested in lodash templates",
77
+ location: tag_node.loc
78
+ )
79
79
  end
80
80
 
81
81
  @parser.nodes_with_type(:cdata, :comment).each do |node|
@@ -86,6 +86,7 @@ EOF
86
86
  def lodash_nodes(node)
87
87
  Enumerator.new do |yielder|
88
88
  next if node.nil?
89
+
89
90
  node.descendants(:lodash).each do |lodash_node|
90
91
  indicator_node, code_node = *lodash_node
91
92
  yielder.yield(lodash_node, indicator_node, code_node)
@@ -95,12 +96,12 @@ EOF
95
96
 
96
97
  def validate_tag_attributes(tag)
97
98
  tag.attributes.each do |attribute|
98
- lodash_nodes(attribute.value_node).each do |lodash_node, indicator_node, code_node|
99
+ lodash_nodes(attribute.value_node).each do |lodash_node, indicator_node, _code_node|
99
100
  next if indicator_node.nil?
100
101
 
101
- if indicator_node.loc.source == '='
102
+ if indicator_node.loc.source == "="
102
103
  validate_tag_expression(attribute, lodash_node)
103
- elsif indicator_node.loc.source == '!'
104
+ elsif indicator_node.loc.source == "!"
104
105
  add_error(
105
106
  "lodash interpolation with '[%!' inside html attribute is never safe",
106
107
  location: lodash_node.loc
@@ -116,14 +117,14 @@ EOF
116
117
  if @config.javascript_attribute_name?(attribute.name) && !@config.lodash_safe_javascript_expression?(source)
117
118
  add_error(
118
119
  "lodash interpolation in javascript attribute "\
119
- "`#{attribute.name}` must call `JSON.stringify(#{source})`",
120
+ "`#{attribute.name}` must call `JSON.stringify(#{source})`",
120
121
  location: lodash_node.loc
121
122
  )
122
123
  end
123
124
  end
124
125
 
125
126
  def validate_no_statements(node)
126
- lodash_nodes(node).each do |lodash_node, indicator_node, code_node|
127
+ lodash_nodes(node).each do |lodash_node, indicator_node, _code_node|
127
128
  add_no_statement_error(lodash_node.loc) if indicator_node.nil?
128
129
  end
129
130
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module BetterHtml
2
4
  module TestHelper
3
5
  class SafetyError < InterpolatorError
@@ -1,7 +1,9 @@
1
- require 'erubi'
2
- require_relative 'token'
3
- require_relative 'location'
4
- require 'parser/source/buffer'
1
+ # frozen_string_literal: true
2
+
3
+ require "erubi"
4
+ require_relative "token"
5
+ require_relative "location"
6
+ require "parser/source/buffer"
5
7
 
6
8
  module BetterHtml
7
9
  module Tokenizer
@@ -14,7 +16,9 @@ module BetterHtml
14
16
  attr_reader :current_position
15
17
 
16
18
  def initialize(buffer)
17
- raise ArgumentError, 'first argument must be Parser::Source::Buffer' unless buffer.is_a?(::Parser::Source::Buffer)
19
+ raise ArgumentError,
20
+ "first argument must be Parser::Source::Buffer" unless buffer.is_a?(::Parser::Source::Buffer)
21
+
18
22
  @buffer = buffer
19
23
  @tokens = []
20
24
  @current_position = 0
@@ -28,13 +32,13 @@ module BetterHtml
28
32
  end
29
33
 
30
34
  def add_code(code)
31
- if code[0] == '%'
32
- add_erb_tokens(nil, '%', code[1..-1], nil)
35
+ if code[0] == "%"
36
+ add_erb_tokens(nil, "%", code[1..-1], nil)
33
37
  append("<%#{code}%>")
34
38
  else
35
39
  _, ltrim_or_comment, code, rtrim = *STMT_TRIM_MATCHER.match(code)
36
- ltrim = ltrim_or_comment if ltrim_or_comment == '-'
37
- indicator = ltrim_or_comment if ltrim_or_comment == '#'
40
+ ltrim = ltrim_or_comment if ltrim_or_comment == "-"
41
+ indicator = ltrim_or_comment if ltrim_or_comment == "#"
38
42
  add_erb_tokens(ltrim, indicator, code, rtrim)
39
43
  append("<%#{ltrim}#{indicator}#{code}#{rtrim}%>")
40
44
  end
@@ -66,7 +70,7 @@ module BetterHtml
66
70
  pos += code.length
67
71
 
68
72
  if rtrim
69
- token = add_token(:trim, pos, pos + rtrim.length)
73
+ add_token(:trim, pos, pos + rtrim.length)
70
74
  pos += rtrim.length
71
75
  end
72
76
 
@@ -1,5 +1,6 @@
1
- require 'html_tokenizer'
2
- require_relative 'base_erb'
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base_erb"
3
4
 
4
5
  module BetterHtml
5
6
  module Tokenizer
@@ -1,7 +1,8 @@
1
- require 'active_support'
2
- require 'html_tokenizer'
3
- require_relative 'token'
4
- require_relative 'location'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support"
4
+ require_relative "token"
5
+ require_relative "location"
5
6
 
6
7
  module BetterHtml
7
8
  module Tokenizer
@@ -10,9 +11,9 @@ module BetterHtml
10
11
  attr_reader :parser
11
12
 
12
13
  cattr_accessor :lodash_escape, :lodash_evaluate, :lodash_interpolate
13
- self.lodash_escape = %r{(?:\[\%)=(.+?)(?:\%\])}m
14
- self.lodash_evaluate = %r{(?:\[\%)(.+?)(?:\%\])}m
15
- self.lodash_interpolate = %r{(?:\[\%)!(.+?)(?:\%\])}m
14
+ self.lodash_escape = /(?:\[\%)=(.+?)(?:\%\])/m
15
+ self.lodash_evaluate = /(?:\[\%)(.+?)(?:\%\])/m
16
+ self.lodash_interpolate = /(?:\[\%)!(.+?)(?:\%\])/m
16
17
 
17
18
  def initialize(buffer)
18
19
  @buffer = buffer
@@ -27,25 +28,32 @@ module BetterHtml
27
28
  def scan!
28
29
  while @scanner.rest?
29
30
  scanned = @scanner.scan_until(scan_pattern)
31
+
30
32
  if scanned.present?
31
33
  captures = scan_pattern.match(scanned).captures
32
- if pre_match = captures[0]
33
- add_text(pre_match) if pre_match.present?
34
+
35
+ if (pre_match = captures[0])
36
+ add_text(pre_match) if pre_match.present? # rubocop:disable Metrics/BlockNesting
34
37
  end
38
+
35
39
  match = captures[1]
36
- if code = lodash_escape.match(match)
40
+
41
+ if (code = lodash_escape.match(match))
37
42
  add_lodash_tokens("=", code.captures[0])
38
- elsif code = lodash_interpolate.match(match)
43
+ elsif (code = lodash_interpolate.match(match))
39
44
  add_lodash_tokens("!", code.captures[0])
40
- elsif code = lodash_evaluate.match(match)
45
+ elsif (code = lodash_evaluate.match(match))
41
46
  add_lodash_tokens(nil, code.captures[0])
42
47
  else
43
- raise RuntimeError, 'unexpected match'
48
+ raise "unexpected match"
44
49
  end
50
+
45
51
  @parser.append_placeholder(match)
46
52
  else
47
53
  text = @buffer.source[(@scanner.pos)..(@buffer.source.size)]
54
+
48
55
  add_text(text) unless text.blank?
56
+
49
57
  break
50
58
  end
51
59
  end
@@ -56,7 +64,7 @@ module BetterHtml
56
64
  patterns = [
57
65
  lodash_escape,
58
66
  lodash_interpolate,
59
- lodash_evaluate
67
+ lodash_evaluate,
60
68
  ].map(&:source).join("|")
61
69
  Regexp.new("(?<pre_patch>.*?)(?<match>" + patterns + ")", Regexp::MULTILINE)
62
70
  end
@@ -1,4 +1,6 @@
1
- require_relative 'base_erb'
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base_erb"
2
4
 
3
5
  module BetterHtml
4
6
  module Tokenizer
@@ -1,13 +1,24 @@
1
- require 'parser/source/buffer'
2
- require 'parser/source/range'
1
+ # frozen_string_literal: true
2
+
3
+ require "parser/source/buffer"
4
+ require "parser/source/range"
3
5
 
4
6
  module BetterHtml
5
7
  module Tokenizer
6
8
  class Location < ::Parser::Source::Range
7
9
  def initialize(buffer, begin_pos, end_pos)
8
- raise ArgumentError, 'first argument must be Parser::Source::Buffer' unless buffer.is_a?(::Parser::Source::Buffer)
9
- raise ArgumentError, "begin_pos location #{begin_pos} is out of range for document of size #{buffer.source.size}" if begin_pos > buffer.source.size
10
- raise ArgumentError, "end_pos location #{end_pos} is out of range for document of size #{buffer.source.size}" if (end_pos - 1) > buffer.source.size
10
+ raise ArgumentError,
11
+ "first argument must be Parser::Source::Buffer" unless buffer.is_a?(::Parser::Source::Buffer)
12
+
13
+ if begin_pos > buffer.source.size
14
+ raise ArgumentError,
15
+ "begin_pos location #{begin_pos} is out of range for document of size #{buffer.source.size}"
16
+ end
17
+
18
+ if (end_pos - 1) > buffer.source.size
19
+ raise ArgumentError,
20
+ "end_pos location #{end_pos} is out of range for document of size #{buffer.source.size}"
21
+ end
11
22
 
12
23
  super(buffer, begin_pos, end_pos)
13
24
  end
@@ -29,7 +40,7 @@ module BetterHtml
29
40
  spaces = source_line.scan(/\A\s*/).first
30
41
  column_without_spaces = [column - spaces.length, 0].max
31
42
  underscore_length = [[end_pos - begin_pos, source_line.length - column_without_spaces].min, 1].max
32
- "#{source_line.gsub(/\A\s*/, '')}\n#{' ' * column_without_spaces}#{'^' * underscore_length}"
43
+ "#{source_line.gsub(/\A\s*/, "")}\n#{" " * column_without_spaces}#{"^" * underscore_length}"
33
44
  end
34
45
 
35
46
  def with(begin_pos: @begin_pos, end_pos: @end_pos)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module BetterHtml
2
4
  module Tokenizer
3
5
  class Token
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module BetterHtml
2
4
  module Tokenizer
3
5
  class TokenArray
@@ -8,26 +10,24 @@ module BetterHtml
8
10
  end
9
11
 
10
12
  def shift
11
- raise RuntimeError, 'no tokens left to shift' if empty?
13
+ raise "no tokens left to shift" if empty?
14
+
12
15
  item = @list[@current]
13
16
  @current += 1
14
17
  item
15
18
  end
16
19
 
17
20
  def pop
18
- raise RuntimeError, 'no tokens left to pop' if empty?
21
+ raise "no tokens left to pop" if empty?
22
+
19
23
  item = @list[@last - 1]
20
24
  @last -= 1
21
25
  item
22
26
  end
23
27
 
24
28
  def trim(type)
25
- while current&.type == type
26
- shift
27
- end
28
- while last&.type == type
29
- pop
30
- end
29
+ shift while current&.type == type
30
+ pop while last&.type == type
31
31
  end
32
32
 
33
33
  def empty?
@@ -1,19 +1,23 @@
1
- require 'ast'
1
+ # frozen_string_literal: true
2
+
3
+ require "ast"
2
4
 
3
5
  module BetterHtml
4
6
  module Tree
5
7
  class Attribute
6
8
  attr_reader :node, :name_node, :equal_node, :value_node
7
9
 
10
+ class << self
11
+ def from_node(node)
12
+ new(node)
13
+ end
14
+ end
15
+
8
16
  def initialize(node)
9
17
  @node = node
10
18
  @name_node, @equal_node, @value_node = *node if @node.type == :attribute
11
19
  end
12
20
 
13
- def self.from_node(node)
14
- new(node)
15
- end
16
-
17
21
  def erb?
18
22
  @node.type == :erb
19
23
  end
@@ -27,7 +31,7 @@ module BetterHtml
27
31
  end
28
32
 
29
33
  def value
30
- parts = value_node.to_a.reject{ |node| node.is_a?(::AST::Node) && node.type == :quote }
34
+ parts = value_node.to_a.reject { |node| node.is_a?(::AST::Node) && node.type == :quote }
31
35
  parts.map { |s| s.is_a?(::AST::Node) ? s.loc.source : CGI.unescapeHTML(s) }.join
32
36
  end
33
37
  end
@@ -1,14 +1,18 @@
1
- require 'better_html/tree/attribute'
1
+ # frozen_string_literal: true
2
+
3
+ require "better_html/tree/attribute"
2
4
 
3
5
  module BetterHtml
4
6
  module Tree
5
7
  class AttributesList
6
- def initialize(list)
7
- @list = list || []
8
+ class << self
9
+ def from_nodes(nodes)
10
+ new(nodes&.map { |node| Tree::Attribute.from_node(node) })
11
+ end
8
12
  end
9
13
 
10
- def self.from_nodes(nodes)
11
- new(nodes&.map { |node| Tree::Attribute.from_node(node) })
14
+ def initialize(list)
15
+ @list = list || []
12
16
  end
13
17
 
14
18
  def [](name)