herb 0.8.10-arm-linux-gnu → 0.9.1-arm-linux-gnu

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (212) hide show
  1. checksums.yaml +4 -4
  2. data/Makefile +11 -3
  3. data/README.md +64 -34
  4. data/Rakefile +48 -40
  5. data/config.yml +473 -34
  6. data/ext/herb/error_helpers.c +535 -140
  7. data/ext/herb/error_helpers.h +1 -0
  8. data/ext/herb/extconf.rb +67 -28
  9. data/ext/herb/extension.c +321 -51
  10. data/ext/herb/extension.h +1 -0
  11. data/ext/herb/extension_helpers.c +24 -14
  12. data/ext/herb/extension_helpers.h +2 -2
  13. data/ext/herb/nodes.c +647 -270
  14. data/ext/herb/nodes.h +1 -0
  15. data/herb.gemspec +3 -2
  16. data/lib/herb/3.0/herb.so +0 -0
  17. data/lib/herb/3.1/herb.so +0 -0
  18. data/lib/herb/3.2/herb.so +0 -0
  19. data/lib/herb/3.3/herb.so +0 -0
  20. data/lib/herb/3.4/herb.so +0 -0
  21. data/lib/herb/4.0/herb.so +0 -0
  22. data/lib/herb/ast/helpers.rb +3 -3
  23. data/lib/herb/ast/node.rb +15 -2
  24. data/lib/herb/ast/nodes.rb +1530 -179
  25. data/lib/herb/bootstrap.rb +87 -0
  26. data/lib/herb/cli.rb +341 -31
  27. data/lib/herb/configuration.rb +248 -0
  28. data/lib/herb/defaults.yml +32 -0
  29. data/lib/herb/engine/compiler.rb +78 -11
  30. data/lib/herb/engine/debug_visitor.rb +13 -3
  31. data/lib/herb/engine/error_formatter.rb +13 -9
  32. data/lib/herb/engine/parser_error_overlay.rb +10 -6
  33. data/lib/herb/engine/validator.rb +8 -3
  34. data/lib/herb/engine/validators/nesting_validator.rb +2 -2
  35. data/lib/herb/engine.rb +119 -43
  36. data/lib/herb/errors.rb +808 -88
  37. data/lib/herb/lex_result.rb +1 -0
  38. data/lib/herb/location.rb +7 -3
  39. data/lib/herb/parse_result.rb +12 -2
  40. data/lib/herb/parser_options.rb +62 -0
  41. data/lib/herb/position.rb +1 -0
  42. data/lib/herb/prism_inspect.rb +120 -0
  43. data/lib/herb/project.rb +923 -331
  44. data/lib/herb/range.rb +1 -0
  45. data/lib/herb/token.rb +7 -1
  46. data/lib/herb/version.rb +1 -1
  47. data/lib/herb/visitor.rb +47 -2
  48. data/lib/herb/warnings.rb +6 -1
  49. data/lib/herb.rb +35 -3
  50. data/sig/herb/ast/helpers.rbs +2 -2
  51. data/sig/herb/ast/node.rbs +12 -2
  52. data/sig/herb/ast/nodes.rbs +773 -128
  53. data/sig/herb/bootstrap.rbs +31 -0
  54. data/sig/herb/configuration.rbs +89 -0
  55. data/sig/herb/engine/compiler.rbs +9 -1
  56. data/sig/herb/engine/debug_visitor.rbs +2 -0
  57. data/sig/herb/engine/validator.rbs +5 -1
  58. data/sig/herb/engine.rbs +21 -3
  59. data/sig/herb/errors.rbs +372 -63
  60. data/sig/herb/location.rbs +4 -0
  61. data/sig/herb/parse_result.rbs +4 -2
  62. data/sig/herb/parser_options.rbs +46 -0
  63. data/sig/herb/position.rbs +1 -0
  64. data/sig/herb/prism_inspect.rbs +28 -0
  65. data/sig/herb/range.rbs +1 -0
  66. data/sig/herb/token.rbs +6 -0
  67. data/sig/herb/visitor.rbs +31 -4
  68. data/sig/herb/warnings.rbs +6 -1
  69. data/sig/herb.rbs +14 -0
  70. data/sig/herb_c_extension.rbs +5 -2
  71. data/sig/rubyvm.rbs +5 -0
  72. data/sig/serialized_ast_errors.rbs +82 -6
  73. data/sig/serialized_ast_nodes.rbs +91 -6
  74. data/src/analyze/action_view/attribute_extraction_helpers.c +303 -0
  75. data/src/analyze/action_view/content_tag.c +78 -0
  76. data/src/analyze/action_view/link_to.c +167 -0
  77. data/src/analyze/action_view/registry.c +83 -0
  78. data/src/analyze/action_view/tag.c +70 -0
  79. data/src/analyze/action_view/tag_helper_node_builders.c +305 -0
  80. data/src/analyze/action_view/tag_helpers.c +815 -0
  81. data/src/analyze/action_view/turbo_frame_tag.c +88 -0
  82. data/src/analyze/analyze.c +885 -0
  83. data/src/{analyzed_ruby.c → analyze/analyzed_ruby.c} +13 -11
  84. data/src/analyze/builders.c +343 -0
  85. data/src/analyze/conditional_elements.c +594 -0
  86. data/src/analyze/conditional_open_tags.c +640 -0
  87. data/src/analyze/control_type.c +250 -0
  88. data/src/{analyze_helpers.c → analyze/helpers.c} +48 -23
  89. data/src/analyze/invalid_structures.c +193 -0
  90. data/src/{analyze_missing_end.c → analyze/missing_end.c} +33 -22
  91. data/src/analyze/parse_errors.c +84 -0
  92. data/src/analyze/prism_annotate.c +399 -0
  93. data/src/analyze/render_nodes.c +761 -0
  94. data/src/{analyze_transform.c → analyze/transform.c} +24 -3
  95. data/src/ast_node.c +17 -7
  96. data/src/ast_nodes.c +759 -387
  97. data/src/ast_pretty_print.c +264 -6
  98. data/src/errors.c +1454 -519
  99. data/src/extract.c +145 -49
  100. data/src/herb.c +52 -34
  101. data/src/html_util.c +241 -12
  102. data/src/include/analyze/action_view/attribute_extraction_helpers.h +36 -0
  103. data/src/include/analyze/action_view/tag_helper_handler.h +43 -0
  104. data/src/include/analyze/action_view/tag_helper_node_builders.h +70 -0
  105. data/src/include/analyze/action_view/tag_helpers.h +38 -0
  106. data/src/include/{analyze.h → analyze/analyze.h} +14 -4
  107. data/src/include/{analyzed_ruby.h → analyze/analyzed_ruby.h} +3 -3
  108. data/src/include/analyze/builders.h +27 -0
  109. data/src/include/analyze/conditional_elements.h +9 -0
  110. data/src/include/analyze/conditional_open_tags.h +9 -0
  111. data/src/include/analyze/control_type.h +14 -0
  112. data/src/include/{analyze_helpers.h → analyze/helpers.h} +4 -2
  113. data/src/include/analyze/invalid_structures.h +11 -0
  114. data/src/include/analyze/prism_annotate.h +16 -0
  115. data/src/include/analyze/render_nodes.h +11 -0
  116. data/src/include/ast_node.h +11 -5
  117. data/src/include/ast_nodes.h +154 -38
  118. data/src/include/ast_pretty_print.h +5 -0
  119. data/src/include/element_source.h +3 -8
  120. data/src/include/errors.h +206 -55
  121. data/src/include/extract.h +21 -5
  122. data/src/include/herb.h +18 -6
  123. data/src/include/herb_prism_node.h +13 -0
  124. data/src/include/html_util.h +7 -2
  125. data/src/include/io.h +3 -1
  126. data/src/include/lex_helpers.h +29 -0
  127. data/src/include/lexer.h +1 -1
  128. data/src/include/lexer_peek_helpers.h +87 -13
  129. data/src/include/lexer_struct.h +2 -0
  130. data/src/include/location.h +2 -1
  131. data/src/include/parser.h +28 -2
  132. data/src/include/parser_helpers.h +19 -3
  133. data/src/include/pretty_print.h +10 -5
  134. data/src/include/prism_context.h +45 -0
  135. data/src/include/prism_helpers.h +10 -7
  136. data/src/include/prism_serialized.h +12 -0
  137. data/src/include/token.h +16 -4
  138. data/src/include/token_struct.h +10 -3
  139. data/src/include/utf8.h +2 -1
  140. data/src/include/util/hb_allocator.h +78 -0
  141. data/src/include/util/hb_arena.h +6 -1
  142. data/src/include/util/hb_arena_debug.h +12 -1
  143. data/src/include/util/hb_array.h +7 -3
  144. data/src/include/util/hb_buffer.h +6 -4
  145. data/src/include/util/hb_foreach.h +79 -0
  146. data/src/include/util/hb_narray.h +8 -4
  147. data/src/include/util/hb_string.h +56 -9
  148. data/src/include/util.h +6 -3
  149. data/src/include/version.h +1 -1
  150. data/src/io.c +3 -2
  151. data/src/lexer.c +42 -30
  152. data/src/lexer_peek_helpers.c +12 -74
  153. data/src/location.c +2 -2
  154. data/src/main.c +53 -28
  155. data/src/parser.c +784 -247
  156. data/src/parser_helpers.c +110 -23
  157. data/src/parser_match_tags.c +129 -48
  158. data/src/pretty_print.c +29 -24
  159. data/src/prism_helpers.c +30 -27
  160. data/src/ruby_parser.c +2 -0
  161. data/src/token.c +151 -66
  162. data/src/token_matchers.c +0 -1
  163. data/src/utf8.c +7 -6
  164. data/src/util/hb_allocator.c +341 -0
  165. data/src/util/hb_arena.c +81 -56
  166. data/src/util/hb_arena_debug.c +32 -17
  167. data/src/util/hb_array.c +30 -15
  168. data/src/util/hb_buffer.c +17 -21
  169. data/src/util/hb_narray.c +22 -7
  170. data/src/util/hb_string.c +49 -35
  171. data/src/util.c +21 -11
  172. data/src/visitor.c +67 -0
  173. data/templates/ext/herb/error_helpers.c.erb +24 -11
  174. data/templates/ext/herb/error_helpers.h.erb +1 -0
  175. data/templates/ext/herb/nodes.c.erb +50 -16
  176. data/templates/ext/herb/nodes.h.erb +1 -0
  177. data/templates/java/error_helpers.c.erb +1 -1
  178. data/templates/java/nodes.c.erb +30 -8
  179. data/templates/java/org/herb/ast/Errors.java.erb +24 -1
  180. data/templates/java/org/herb/ast/Nodes.java.erb +80 -21
  181. data/templates/javascript/packages/core/src/errors.ts.erb +16 -3
  182. data/templates/javascript/packages/core/src/node-type-guards.ts.erb +3 -1
  183. data/templates/javascript/packages/core/src/nodes.ts.erb +109 -32
  184. data/templates/javascript/packages/node/extension/error_helpers.cpp.erb +13 -4
  185. data/templates/javascript/packages/node/extension/nodes.cpp.erb +43 -4
  186. data/templates/lib/herb/ast/nodes.rb.erb +95 -32
  187. data/templates/lib/herb/errors.rb.erb +15 -3
  188. data/templates/lib/herb/visitor.rb.erb +2 -2
  189. data/templates/rust/src/ast/nodes.rs.erb +97 -44
  190. data/templates/rust/src/errors.rs.erb +2 -1
  191. data/templates/rust/src/nodes.rs.erb +168 -16
  192. data/templates/rust/src/union_types.rs.erb +60 -0
  193. data/templates/rust/src/visitor.rs.erb +81 -0
  194. data/templates/src/{analyze_missing_end.c.erb → analyze/missing_end.c.erb} +9 -6
  195. data/templates/src/{analyze_transform.c.erb → analyze/transform.c.erb} +2 -2
  196. data/templates/src/ast_nodes.c.erb +34 -26
  197. data/templates/src/ast_pretty_print.c.erb +24 -5
  198. data/templates/src/errors.c.erb +60 -54
  199. data/templates/src/include/ast_nodes.h.erb +6 -2
  200. data/templates/src/include/ast_pretty_print.h.erb +5 -0
  201. data/templates/src/include/errors.h.erb +15 -11
  202. data/templates/src/include/util/hb_foreach.h.erb +20 -0
  203. data/templates/src/parser_match_tags.c.erb +10 -4
  204. data/templates/src/visitor.c.erb +2 -2
  205. data/templates/template.rb +204 -29
  206. data/templates/wasm/error_helpers.cpp.erb +9 -5
  207. data/templates/wasm/nodes.cpp.erb +41 -4
  208. metadata +60 -16
  209. data/src/analyze.c +0 -1608
  210. data/src/element_source.c +0 -12
  211. data/src/include/util/hb_system.h +0 -9
  212. data/src/util/hb_system.c +0 -30
data/lib/herb/engine.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+ # typed: false
2
3
 
3
4
  require "json"
4
5
  require "time"
@@ -17,7 +18,7 @@ require_relative "engine/validators/accessibility_validator"
17
18
  module Herb
18
19
  class Engine
19
20
  attr_reader :src, :filename, :project_path, :relative_file_path, :bufvar, :debug, :content_for_head,
20
- :validation_error_template, :visitors
21
+ :validation_error_template, :visitors, :enabled_validators
21
22
 
22
23
  ESCAPE_TABLE = {
23
24
  "&" => "&",
@@ -30,6 +31,16 @@ module Herb
30
31
  class CompilationError < StandardError
31
32
  end
32
33
 
34
+ class InvalidRubyError < CompilationError
35
+ attr_reader :compiled_source
36
+
37
+ def initialize(message, compiled_source: nil)
38
+ @compiled_source = compiled_source
39
+
40
+ super(message)
41
+ end
42
+ end
43
+
33
44
  def initialize(input, properties = {})
34
45
  @filename = properties[:filename] ? ::Pathname.new(properties[:filename]) : nil
35
46
  @project_path = ::Pathname.new(properties[:project_path] || Dir.pwd)
@@ -51,6 +62,8 @@ module Herb
51
62
  @content_for_head = properties[:content_for_head]
52
63
  @validation_error_template = nil
53
64
  @validation_mode = properties.fetch(:validation_mode, :raise)
65
+ @enabled_validators = Herb.configuration.enabled_validators(properties[:validators] || {})
66
+ @strict = properties.fetch(:strict, true)
54
67
  @visitors = properties.fetch(:visitors, default_visitors)
55
68
 
56
69
  if @debug && @visitors.empty?
@@ -75,6 +88,8 @@ module Herb
75
88
  preamble = properties[:preamble] || "#{@bufvar} = #{bufval};"
76
89
  postamble = properties[:postamble] || "#{@bufvar}.to_s\n"
77
90
 
91
+ preamble = "#{preamble}; " unless preamble.empty? || preamble.end_with?(";", " ", "\n")
92
+
78
93
  @src << "# frozen_string_literal: true\n" if @freeze
79
94
 
80
95
  if properties[:ensure]
@@ -89,7 +104,7 @@ module Herb
89
104
  @src << "__herb = ::Herb::Engine; " if @escape && @escapefunc == "__herb.h"
90
105
  @src << preamble
91
106
 
92
- parse_result = ::Herb.parse(input, track_whitespace: true)
107
+ parse_result = ::Herb.parse(input, track_whitespace: true, strict: @strict)
93
108
  ast = parse_result.value
94
109
  parser_errors = parse_result.errors
95
110
 
@@ -104,12 +119,12 @@ module Herb
104
119
  # Skip both errors and compilation, but still need minimal Ruby code
105
120
  end
106
121
  else
107
- validation_errors = run_validation(ast) unless @validation_mode == :none
108
- all_errors = parser_errors + (validation_errors || [])
122
+ validators = run_validation(ast) unless @validation_mode == :none
109
123
 
110
- handle_validation_errors(all_errors, input) if @validation_mode == :raise && all_errors.any?
111
-
112
- add_validation_overlay(validation_errors, input) if @validation_mode == :overlay && validation_errors&.any?
124
+ if validators
125
+ handle_validation_errors(validators, input) if @validation_mode == :raise
126
+ add_validation_overlay(validators, input) if @validation_mode == :overlay
127
+ end
113
128
 
114
129
  @visitors.each do |visitor|
115
130
  ast.accept(visitor)
@@ -132,6 +147,10 @@ module Herb
132
147
 
133
148
  @src << "; ensure\n #{@bufvar} = __original_outvar\nend\n" if properties[:ensure]
134
149
 
150
+ if properties.fetch(:validate_ruby, false)
151
+ ensure_valid_ruby!(@src)
152
+ end
153
+
135
154
  @src.freeze
136
155
  freeze
137
156
  end
@@ -172,6 +191,14 @@ module Herb
172
191
  end
173
192
  end
174
193
 
194
+ def self.comment?(code)
195
+ code.include?("#")
196
+ end
197
+
198
+ def self.heredoc?(code)
199
+ code.match?(/<<[~-]?\s*['"`]?\w/)
200
+ end
201
+
175
202
  protected
176
203
 
177
204
  def add_text(text)
@@ -193,8 +220,8 @@ module Herb
193
220
  @src << " " << code
194
221
 
195
222
  # TODO: rework and check for Prism::InlineComment as soon as we expose the Prism Nodes in the Herb AST
196
- if code.include?("#")
197
- @src << "\n"
223
+ if self.class.comment?(code) || self.class.heredoc?(code)
224
+ @src << "\n" unless code[-1] == "\n"
198
225
  else
199
226
  @src << ";" unless code[-1] == "\n"
200
227
  end
@@ -203,48 +230,82 @@ module Herb
203
230
  @buffer_on_stack = false
204
231
  end
205
232
 
233
+ def expression_block?
234
+ @_in_expression_block || false
235
+ end
236
+
206
237
  def add_expression(indicator, code)
207
- if (indicator == "=") ^ @escape
208
- add_expression_result(code)
238
+ unescaped = (indicator == "=") ^ @escape
239
+
240
+ if expression_block?
241
+ unescaped ? add_expression_block_result(code) : add_expression_block_result_escaped(code)
209
242
  else
210
- add_expression_result_escaped(code)
243
+ unescaped ? add_expression_result(code) : add_expression_result_escaped(code)
211
244
  end
212
245
  end
213
246
 
214
247
  def add_expression_result(code)
215
248
  with_buffer {
216
- @src << " << (" << code << comment_aware_newline(code) << ").to_s"
249
+ @src << " << (" << code << trailing_newline(code) << ").to_s"
217
250
  }
218
251
  end
219
252
 
220
253
  def add_expression_result_escaped(code)
221
254
  with_buffer {
222
- @src << " << " << @escapefunc << "((" << code << comment_aware_newline(code) << "))"
255
+ @src << " << " << @escapefunc << "((" << code << trailing_newline(code) << "))"
223
256
  }
224
257
  end
225
258
 
226
259
  def add_expression_block(indicator, code)
227
- if (indicator == "=") ^ @escape
228
- add_expression_block_result(code)
229
- else
230
- add_expression_block_result_escaped(code)
231
- end
260
+ @_in_expression_block = true
261
+ @_expression_block_open_paren = false
262
+
263
+ add_expression(indicator, code)
264
+ ensure
265
+ @_in_expression_block = false
232
266
  end
233
267
 
234
268
  def add_expression_block_result(code)
269
+ @_expression_block_open_paren = true
270
+
235
271
  with_buffer {
236
- @src << " << " << code << comment_aware_newline(code)
272
+ @src << " << (" << code << trailing_newline(code)
237
273
  }
238
274
  end
239
275
 
240
276
  def add_expression_block_result_escaped(code)
277
+ @_expression_block_open_paren = true
278
+
241
279
  with_buffer {
242
- @src << " << " << @escapefunc << "(" << code << comment_aware_newline(code) << ")"
280
+ @src << " << " << @escapefunc << "((" << code << trailing_newline(code)
243
281
  }
244
282
  end
245
283
 
246
- def comment_aware_newline(code)
247
- code.include?("#") ? "\n" : ""
284
+ def add_expression_block_end(code, escaped: false)
285
+ if @_expression_block_open_paren
286
+ terminate_expression
287
+
288
+ trailing_newline = code.end_with?("\n")
289
+ code_stripped = code.chomp
290
+
291
+ @src.chomp! if @src.end_with?("\n") && code_stripped.start_with?(" ")
292
+
293
+ @src << " " << code_stripped
294
+ @src << "\n" if self.class.comment?(code_stripped)
295
+ @src << (escaped ? "))" : ")")
296
+ @src << (trailing_newline ? "\n" : ";")
297
+
298
+ @buffer_on_stack = false
299
+ else
300
+ add_code(code)
301
+ end
302
+ end
303
+
304
+ def trailing_newline(code)
305
+ return "\n" if self.class.comment?(code)
306
+ return "\n" if self.class.heredoc?(code)
307
+
308
+ ""
248
309
  end
249
310
 
250
311
  def add_postamble(postamble)
@@ -272,19 +333,16 @@ module Herb
272
333
 
273
334
  def run_validation(ast)
274
335
  validators = [
275
- Validators::SecurityValidator.new,
276
- Validators::NestingValidator.new,
277
- Validators::AccessibilityValidator.new
336
+ Validators::SecurityValidator.new(enabled: @enabled_validators[:security]),
337
+ Validators::NestingValidator.new(enabled: @enabled_validators[:nesting]),
338
+ Validators::AccessibilityValidator.new(enabled: @enabled_validators[:accessibility])
278
339
  ]
279
340
 
280
- errors = [] #: Array[untyped]
281
-
282
- validators.each do |validator|
341
+ validators.select(&:enabled?).each do |validator|
283
342
  ast.accept(validator)
284
- errors.concat(validator.errors)
285
343
  end
286
344
 
287
- errors
345
+ validators
288
346
  end
289
347
 
290
348
  def handle_parser_errors(parser_errors, input, _ast)
@@ -304,24 +362,19 @@ module Herb
304
362
  end
305
363
  end
306
364
 
307
- def handle_validation_errors(errors, input)
365
+ def handle_validation_errors(validators, input)
366
+ errors = validators.select(&:enabled?).flat_map(&:errors)
308
367
  return unless errors.any?
309
368
 
310
- security_error = errors.find { |error|
311
- error.is_a?(Hash) && error[:source] == "SecurityValidator"
312
- }
369
+ security_error = errors.find { |error| error[:source] == "SecurityValidator" }
313
370
 
314
371
  if security_error
315
- line = security_error[:location]&.start&.line
316
- column = security_error[:location]&.start&.column
317
- suggestion = security_error[:suggestion]
318
-
319
372
  raise SecurityError.new(
320
373
  security_error[:message],
321
- line: line,
322
- column: column,
374
+ line: security_error[:location]&.start&.line,
375
+ column: security_error[:location]&.start&.column,
323
376
  filename: @filename,
324
- suggestion: suggestion
377
+ suggestion: security_error[:suggestion]
325
378
  )
326
379
  end
327
380
 
@@ -330,7 +383,8 @@ module Herb
330
383
  raise CompilationError, "\n#{message}"
331
384
  end
332
385
 
333
- def add_validation_overlay(errors, input = nil)
386
+ def add_validation_overlay(validators, input = nil)
387
+ errors = validators.select(&:enabled?).flat_map(&:errors)
334
388
  return unless errors.any?
335
389
 
336
390
  templates = errors.map { |error|
@@ -393,5 +447,27 @@ module Herb
393
447
  def default_visitors
394
448
  []
395
449
  end
450
+
451
+ def ensure_valid_ruby!(source)
452
+ RubyVM::InstructionSequence.compile(source)
453
+ rescue SyntaxError => e
454
+ return if e.message.include?("Invalid yield")
455
+
456
+ begin
457
+ require "prism"
458
+ rescue LoadError
459
+ # Prism not available, fall through
460
+ end
461
+
462
+ raise InvalidRubyError.new("Compiled template produced invalid Ruby:\n - #{e.message}", compiled_source: @src) unless defined?(Prism)
463
+
464
+ prism_result = Prism.parse(@src)
465
+ syntax_errors = prism_result.errors.reject { |error| error.type == :invalid_yield }
466
+
467
+ if syntax_errors.any?
468
+ details = syntax_errors.map { |err| " - #{err.message} (line #{err.location.start_line})" }.join("\n")
469
+ raise InvalidRubyError.new("Compiled template produced invalid Ruby:\n#{details}", compiled_source: @src)
470
+ end
471
+ end
396
472
  end
397
473
  end