herb 0.8.10-arm-linux-gnu → 0.9.0-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 (209) 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 +317 -34
  6. data/ext/herb/error_helpers.c +367 -140
  7. data/ext/herb/error_helpers.h +1 -0
  8. data/ext/herb/extconf.rb +67 -28
  9. data/ext/herb/extension.c +317 -51
  10. data/ext/herb/extension.h +1 -0
  11. data/ext/herb/extension_helpers.c +23 -14
  12. data/ext/herb/extension_helpers.h +2 -2
  13. data/ext/herb/nodes.c +537 -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 +1132 -157
  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 +82 -35
  36. data/lib/herb/errors.rb +563 -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 +57 -0
  41. data/lib/herb/position.rb +1 -0
  42. data/lib/herb/prism_inspect.rb +116 -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 +37 -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 +641 -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 +17 -3
  59. data/sig/herb/errors.rbs +258 -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 +42 -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 +25 -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/serialized_ast_errors.rbs +54 -6
  72. data/sig/serialized_ast_nodes.rbs +60 -6
  73. data/src/analyze/action_view/attribute_extraction_helpers.c +290 -0
  74. data/src/analyze/action_view/content_tag.c +70 -0
  75. data/src/analyze/action_view/link_to.c +143 -0
  76. data/src/analyze/action_view/registry.c +60 -0
  77. data/src/analyze/action_view/tag.c +64 -0
  78. data/src/analyze/action_view/tag_helper_node_builders.c +305 -0
  79. data/src/analyze/action_view/tag_helpers.c +748 -0
  80. data/src/analyze/action_view/turbo_frame_tag.c +88 -0
  81. data/src/analyze/analyze.c +882 -0
  82. data/src/{analyzed_ruby.c → analyze/analyzed_ruby.c} +13 -11
  83. data/src/analyze/builders.c +343 -0
  84. data/src/analyze/conditional_elements.c +594 -0
  85. data/src/analyze/conditional_open_tags.c +640 -0
  86. data/src/analyze/control_type.c +250 -0
  87. data/src/{analyze_helpers.c → analyze/helpers.c} +48 -23
  88. data/src/analyze/invalid_structures.c +193 -0
  89. data/src/{analyze_missing_end.c → analyze/missing_end.c} +33 -22
  90. data/src/analyze/parse_errors.c +84 -0
  91. data/src/analyze/prism_annotate.c +397 -0
  92. data/src/{analyze_transform.c → analyze/transform.c} +17 -3
  93. data/src/ast_node.c +17 -7
  94. data/src/ast_nodes.c +662 -387
  95. data/src/ast_pretty_print.c +190 -6
  96. data/src/errors.c +1076 -520
  97. data/src/extract.c +145 -49
  98. data/src/herb.c +52 -34
  99. data/src/html_util.c +241 -12
  100. data/src/include/analyze/action_view/attribute_extraction_helpers.h +36 -0
  101. data/src/include/analyze/action_view/tag_helper_handler.h +41 -0
  102. data/src/include/analyze/action_view/tag_helper_node_builders.h +70 -0
  103. data/src/include/analyze/action_view/tag_helpers.h +38 -0
  104. data/src/include/{analyze.h → analyze/analyze.h} +14 -4
  105. data/src/include/{analyzed_ruby.h → analyze/analyzed_ruby.h} +3 -3
  106. data/src/include/analyze/builders.h +27 -0
  107. data/src/include/analyze/conditional_elements.h +9 -0
  108. data/src/include/analyze/conditional_open_tags.h +9 -0
  109. data/src/include/analyze/control_type.h +14 -0
  110. data/src/include/{analyze_helpers.h → analyze/helpers.h} +4 -2
  111. data/src/include/analyze/invalid_structures.h +11 -0
  112. data/src/include/analyze/prism_annotate.h +16 -0
  113. data/src/include/ast_node.h +11 -5
  114. data/src/include/ast_nodes.h +117 -38
  115. data/src/include/ast_pretty_print.h +5 -0
  116. data/src/include/element_source.h +3 -8
  117. data/src/include/errors.h +148 -55
  118. data/src/include/extract.h +21 -5
  119. data/src/include/herb.h +18 -6
  120. data/src/include/herb_prism_node.h +13 -0
  121. data/src/include/html_util.h +7 -2
  122. data/src/include/io.h +3 -1
  123. data/src/include/lex_helpers.h +29 -0
  124. data/src/include/lexer.h +1 -1
  125. data/src/include/lexer_peek_helpers.h +87 -13
  126. data/src/include/lexer_struct.h +2 -0
  127. data/src/include/location.h +2 -1
  128. data/src/include/parser.h +27 -2
  129. data/src/include/parser_helpers.h +19 -3
  130. data/src/include/pretty_print.h +10 -5
  131. data/src/include/prism_context.h +45 -0
  132. data/src/include/prism_helpers.h +10 -7
  133. data/src/include/prism_serialized.h +12 -0
  134. data/src/include/token.h +16 -4
  135. data/src/include/token_struct.h +10 -3
  136. data/src/include/utf8.h +2 -1
  137. data/src/include/util/hb_allocator.h +78 -0
  138. data/src/include/util/hb_arena.h +6 -1
  139. data/src/include/util/hb_arena_debug.h +12 -1
  140. data/src/include/util/hb_array.h +7 -3
  141. data/src/include/util/hb_buffer.h +6 -4
  142. data/src/include/util/hb_foreach.h +79 -0
  143. data/src/include/util/hb_narray.h +8 -4
  144. data/src/include/util/hb_string.h +56 -9
  145. data/src/include/util.h +6 -3
  146. data/src/include/version.h +1 -1
  147. data/src/io.c +3 -2
  148. data/src/lexer.c +42 -30
  149. data/src/lexer_peek_helpers.c +12 -74
  150. data/src/location.c +2 -2
  151. data/src/main.c +53 -28
  152. data/src/parser.c +783 -247
  153. data/src/parser_helpers.c +110 -23
  154. data/src/parser_match_tags.c +109 -48
  155. data/src/pretty_print.c +29 -24
  156. data/src/prism_helpers.c +30 -27
  157. data/src/ruby_parser.c +2 -0
  158. data/src/token.c +151 -66
  159. data/src/token_matchers.c +0 -1
  160. data/src/utf8.c +7 -6
  161. data/src/util/hb_allocator.c +341 -0
  162. data/src/util/hb_arena.c +81 -56
  163. data/src/util/hb_arena_debug.c +32 -17
  164. data/src/util/hb_array.c +30 -15
  165. data/src/util/hb_buffer.c +17 -21
  166. data/src/util/hb_narray.c +22 -7
  167. data/src/util/hb_string.c +49 -35
  168. data/src/util.c +21 -11
  169. data/src/visitor.c +47 -0
  170. data/templates/ext/herb/error_helpers.c.erb +24 -11
  171. data/templates/ext/herb/error_helpers.h.erb +1 -0
  172. data/templates/ext/herb/nodes.c.erb +50 -16
  173. data/templates/ext/herb/nodes.h.erb +1 -0
  174. data/templates/java/error_helpers.c.erb +1 -1
  175. data/templates/java/nodes.c.erb +30 -8
  176. data/templates/java/org/herb/ast/Errors.java.erb +24 -1
  177. data/templates/java/org/herb/ast/Nodes.java.erb +80 -21
  178. data/templates/javascript/packages/core/src/errors.ts.erb +16 -3
  179. data/templates/javascript/packages/core/src/node-type-guards.ts.erb +3 -1
  180. data/templates/javascript/packages/core/src/nodes.ts.erb +109 -32
  181. data/templates/javascript/packages/node/extension/error_helpers.cpp.erb +13 -4
  182. data/templates/javascript/packages/node/extension/nodes.cpp.erb +43 -4
  183. data/templates/lib/herb/ast/nodes.rb.erb +88 -31
  184. data/templates/lib/herb/errors.rb.erb +15 -3
  185. data/templates/lib/herb/visitor.rb.erb +2 -2
  186. data/templates/rust/src/ast/nodes.rs.erb +97 -44
  187. data/templates/rust/src/errors.rs.erb +2 -1
  188. data/templates/rust/src/nodes.rs.erb +167 -15
  189. data/templates/rust/src/union_types.rs.erb +60 -0
  190. data/templates/rust/src/visitor.rs.erb +81 -0
  191. data/templates/src/{analyze_missing_end.c.erb → analyze/missing_end.c.erb} +9 -6
  192. data/templates/src/{analyze_transform.c.erb → analyze/transform.c.erb} +2 -2
  193. data/templates/src/ast_nodes.c.erb +34 -26
  194. data/templates/src/ast_pretty_print.c.erb +24 -5
  195. data/templates/src/errors.c.erb +60 -54
  196. data/templates/src/include/ast_nodes.h.erb +6 -2
  197. data/templates/src/include/ast_pretty_print.h.erb +5 -0
  198. data/templates/src/include/errors.h.erb +15 -11
  199. data/templates/src/include/util/hb_foreach.h.erb +20 -0
  200. data/templates/src/parser_match_tags.c.erb +10 -4
  201. data/templates/src/visitor.c.erb +2 -2
  202. data/templates/template.rb +204 -29
  203. data/templates/wasm/error_helpers.cpp.erb +9 -5
  204. data/templates/wasm/nodes.cpp.erb +41 -4
  205. metadata +57 -16
  206. data/src/analyze.c +0 -1608
  207. data/src/element_source.c +0 -12
  208. data/src/include/util/hb_system.h +0 -9
  209. 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,18 @@ 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
+ require "prism"
152
+
153
+ prism_result = Prism.parse(@src)
154
+ syntax_errors = prism_result.errors.reject { |e| e.type == :invalid_yield }
155
+
156
+ if syntax_errors.any?
157
+ details = syntax_errors.map { |e| " - #{e.message} (line #{e.location.start_line})" }.join("\n")
158
+ raise InvalidRubyError.new("Compiled template produced invalid Ruby:\n#{details}", compiled_source: @src)
159
+ end
160
+ end
161
+
135
162
  @src.freeze
136
163
  freeze
137
164
  end
@@ -172,6 +199,14 @@ module Herb
172
199
  end
173
200
  end
174
201
 
202
+ def self.comment?(code)
203
+ code.include?("#")
204
+ end
205
+
206
+ def self.heredoc?(code)
207
+ code.match?(/<<[~-]?\s*['"`]?\w/)
208
+ end
209
+
175
210
  protected
176
211
 
177
212
  def add_text(text)
@@ -193,8 +228,8 @@ module Herb
193
228
  @src << " " << code
194
229
 
195
230
  # 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"
231
+ if self.class.comment?(code) || self.class.heredoc?(code)
232
+ @src << "\n" unless code[-1] == "\n"
198
233
  else
199
234
  @src << ";" unless code[-1] == "\n"
200
235
  end
@@ -213,13 +248,13 @@ module Herb
213
248
 
214
249
  def add_expression_result(code)
215
250
  with_buffer {
216
- @src << " << (" << code << comment_aware_newline(code) << ").to_s"
251
+ @src << " << (" << code << trailing_newline(code) << ").to_s"
217
252
  }
218
253
  end
219
254
 
220
255
  def add_expression_result_escaped(code)
221
256
  with_buffer {
222
- @src << " << " << @escapefunc << "((" << code << comment_aware_newline(code) << "))"
257
+ @src << " << " << @escapefunc << "((" << code << trailing_newline(code) << "))"
223
258
  }
224
259
  end
225
260
 
@@ -233,18 +268,37 @@ module Herb
233
268
 
234
269
  def add_expression_block_result(code)
235
270
  with_buffer {
236
- @src << " << " << code << comment_aware_newline(code)
271
+ @src << " << (" << code << trailing_newline(code)
237
272
  }
238
273
  end
239
274
 
240
275
  def add_expression_block_result_escaped(code)
241
276
  with_buffer {
242
- @src << " << " << @escapefunc << "(" << code << comment_aware_newline(code) << ")"
277
+ @src << " << " << @escapefunc << "((" << code << trailing_newline(code)
243
278
  }
244
279
  end
245
280
 
246
- def comment_aware_newline(code)
247
- code.include?("#") ? "\n" : ""
281
+ def add_expression_block_end(code, escaped: false)
282
+ terminate_expression
283
+
284
+ trailing_newline = code.end_with?("\n")
285
+ code_stripped = code.chomp
286
+
287
+ @src.chomp! if @src.end_with?("\n") && code_stripped.start_with?(" ")
288
+
289
+ @src << " " << code_stripped
290
+ @src << "\n" if self.class.comment?(code_stripped)
291
+ @src << (escaped ? "))" : ")")
292
+ @src << (trailing_newline ? "\n" : ";")
293
+
294
+ @buffer_on_stack = false
295
+ end
296
+
297
+ def trailing_newline(code)
298
+ return "\n" if self.class.comment?(code)
299
+ return "\n" if self.class.heredoc?(code)
300
+
301
+ ""
248
302
  end
249
303
 
250
304
  def add_postamble(postamble)
@@ -272,19 +326,16 @@ module Herb
272
326
 
273
327
  def run_validation(ast)
274
328
  validators = [
275
- Validators::SecurityValidator.new,
276
- Validators::NestingValidator.new,
277
- Validators::AccessibilityValidator.new
329
+ Validators::SecurityValidator.new(enabled: @enabled_validators[:security]),
330
+ Validators::NestingValidator.new(enabled: @enabled_validators[:nesting]),
331
+ Validators::AccessibilityValidator.new(enabled: @enabled_validators[:accessibility])
278
332
  ]
279
333
 
280
- errors = [] #: Array[untyped]
281
-
282
- validators.each do |validator|
334
+ validators.select(&:enabled?).each do |validator|
283
335
  ast.accept(validator)
284
- errors.concat(validator.errors)
285
336
  end
286
337
 
287
- errors
338
+ validators
288
339
  end
289
340
 
290
341
  def handle_parser_errors(parser_errors, input, _ast)
@@ -304,24 +355,19 @@ module Herb
304
355
  end
305
356
  end
306
357
 
307
- def handle_validation_errors(errors, input)
358
+ def handle_validation_errors(validators, input)
359
+ errors = validators.select(&:enabled?).flat_map(&:errors)
308
360
  return unless errors.any?
309
361
 
310
- security_error = errors.find { |error|
311
- error.is_a?(Hash) && error[:source] == "SecurityValidator"
312
- }
362
+ security_error = errors.find { |error| error[:source] == "SecurityValidator" }
313
363
 
314
364
  if security_error
315
- line = security_error[:location]&.start&.line
316
- column = security_error[:location]&.start&.column
317
- suggestion = security_error[:suggestion]
318
-
319
365
  raise SecurityError.new(
320
366
  security_error[:message],
321
- line: line,
322
- column: column,
367
+ line: security_error[:location]&.start&.line,
368
+ column: security_error[:location]&.start&.column,
323
369
  filename: @filename,
324
- suggestion: suggestion
370
+ suggestion: security_error[:suggestion]
325
371
  )
326
372
  end
327
373
 
@@ -330,7 +376,8 @@ module Herb
330
376
  raise CompilationError, "\n#{message}"
331
377
  end
332
378
 
333
- def add_validation_overlay(errors, input = nil)
379
+ def add_validation_overlay(validators, input = nil)
380
+ errors = validators.select(&:enabled?).flat_map(&:errors)
334
381
  return unless errors.any?
335
382
 
336
383
  templates = errors.map { |error|