herb 0.9.2-x86_64-linux-gnu → 0.9.3-x86_64-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.
- checksums.yaml +4 -4
- data/README.md +2 -0
- data/config.yml +125 -0
- data/ext/herb/error_helpers.c +172 -2
- data/ext/herb/extconf.rb +6 -0
- data/ext/herb/extension.c +16 -2
- data/ext/herb/extension_helpers.c +6 -5
- data/ext/herb/extension_helpers.h +4 -4
- data/ext/herb/nodes.c +89 -3
- data/lib/herb/3.0/herb.so +0 -0
- data/lib/herb/3.1/herb.so +0 -0
- data/lib/herb/3.2/herb.so +0 -0
- data/lib/herb/3.3/herb.so +0 -0
- data/lib/herb/3.4/herb.so +0 -0
- data/lib/herb/4.0/herb.so +0 -0
- data/lib/herb/ast/erb_content_node.rb +32 -0
- data/lib/herb/ast/nodes.rb +244 -3
- data/lib/herb/cli.rb +12 -2
- data/lib/herb/engine/compiler.rb +122 -59
- data/lib/herb/engine/validators/security_validator.rb +40 -0
- data/lib/herb/errors.rb +268 -0
- data/lib/herb/parser_options.rb +7 -2
- data/lib/herb/version.rb +1 -1
- data/lib/herb/visitor.rb +82 -0
- data/lib/herb.rb +1 -0
- data/sig/herb/ast/erb_content_node.rbs +13 -0
- data/sig/herb/ast/nodes.rbs +98 -2
- data/sig/herb/engine/compiler.rbs +15 -2
- data/sig/herb/engine/validators/security_validator.rbs +4 -0
- data/sig/herb/errors.rbs +122 -0
- data/sig/herb/parser_options.rbs +6 -2
- data/sig/herb/visitor.rbs +12 -0
- data/sig/serialized_ast_errors.rbs +29 -0
- data/sig/serialized_ast_nodes.rbs +19 -0
- data/src/analyze/action_view/attribute_extraction_helpers.c +420 -91
- data/src/analyze/action_view/image_tag.c +87 -0
- data/src/analyze/action_view/javascript_include_tag.c +22 -12
- data/src/analyze/action_view/registry.c +6 -3
- data/src/analyze/action_view/tag.c +19 -2
- data/src/analyze/action_view/tag_helper_node_builders.c +105 -36
- data/src/analyze/action_view/tag_helpers.c +792 -44
- data/src/analyze/analyze.c +165 -10
- data/src/analyze/{helpers.c → analyze_helpers.c} +1 -1
- data/src/analyze/analyzed_ruby.c +1 -1
- data/src/analyze/builders.c +11 -8
- data/src/analyze/conditional_elements.c +6 -7
- data/src/analyze/conditional_open_tags.c +6 -7
- data/src/analyze/control_type.c +4 -2
- data/src/analyze/invalid_structures.c +5 -5
- data/src/analyze/missing_end.c +2 -2
- data/src/analyze/parse_errors.c +5 -5
- data/src/analyze/prism_annotate.c +7 -7
- data/src/analyze/render_nodes.c +6 -26
- data/src/analyze/strict_locals.c +637 -0
- data/src/analyze/transform.c +7 -0
- data/src/{ast_node.c → ast/ast_node.c} +8 -8
- data/src/{ast_nodes.c → ast/ast_nodes.c} +82 -11
- data/src/{ast_pretty_print.c → ast/ast_pretty_print.c} +113 -9
- data/src/{pretty_print.c → ast/pretty_print.c} +9 -9
- data/src/errors.c +398 -8
- data/src/extract.c +5 -5
- data/src/herb.c +15 -5
- data/src/include/analyze/action_view/attribute_extraction_helpers.h +3 -1
- data/src/include/analyze/action_view/tag_helper_handler.h +3 -3
- data/src/include/analyze/action_view/tag_helper_node_builders.h +34 -5
- data/src/include/analyze/action_view/tag_helpers.h +4 -3
- data/src/include/analyze/analyze.h +6 -4
- data/src/include/analyze/analyzed_ruby.h +2 -2
- data/src/include/analyze/builders.h +4 -4
- data/src/include/analyze/conditional_elements.h +2 -2
- data/src/include/analyze/conditional_open_tags.h +2 -2
- data/src/include/analyze/control_type.h +1 -1
- data/src/include/analyze/helpers.h +2 -2
- data/src/include/analyze/invalid_structures.h +1 -1
- data/src/include/analyze/prism_annotate.h +2 -2
- data/src/include/analyze/render_nodes.h +1 -1
- data/src/include/analyze/strict_locals.h +11 -0
- data/src/include/{ast_node.h → ast/ast_node.h} +4 -4
- data/src/include/{ast_nodes.h → ast/ast_nodes.h} +38 -14
- data/src/include/{ast_pretty_print.h → ast/ast_pretty_print.h} +3 -3
- data/src/include/{pretty_print.h → ast/pretty_print.h} +4 -4
- data/src/include/errors.h +65 -7
- data/src/include/extract.h +2 -2
- data/src/include/herb.h +5 -5
- data/src/include/{lex_helpers.h → lexer/lex_helpers.h} +5 -5
- data/src/include/{lexer.h → lexer/lexer.h} +1 -1
- data/src/include/{lexer_peek_helpers.h → lexer/lexer_peek_helpers.h} +2 -2
- data/src/include/{lexer_struct.h → lexer/lexer_struct.h} +2 -2
- data/src/include/{token.h → lexer/token.h} +3 -3
- data/src/include/{token_matchers.h → lexer/token_matchers.h} +1 -1
- data/src/include/{token_struct.h → lexer/token_struct.h} +3 -3
- data/src/include/{util → lib}/hb_foreach.h +1 -1
- data/src/include/{util → lib}/hb_string.h +5 -1
- data/src/include/{location.h → location/location.h} +1 -1
- data/src/include/parser/dot_notation.h +12 -0
- data/src/include/{parser.h → parser/parser.h} +11 -4
- data/src/include/{parser_helpers.h → parser/parser_helpers.h} +6 -6
- data/src/include/{prism_context.h → prism/prism_context.h} +2 -2
- data/src/include/{prism_helpers.h → prism/prism_helpers.h} +6 -6
- data/src/include/{html_util.h → util/html_util.h} +1 -1
- data/src/include/util/ruby_util.h +9 -0
- data/src/include/{utf8.h → util/utf8.h} +1 -1
- data/src/include/{util.h → util/util.h} +1 -1
- data/src/include/version.h +1 -1
- data/src/include/visitor.h +3 -3
- data/src/{lexer_peek_helpers.c → lexer/lexer_peek_helpers.c} +3 -3
- data/src/{token.c → lexer/token.c} +8 -8
- data/src/{token_matchers.c → lexer/token_matchers.c} +2 -2
- data/src/lexer.c +6 -6
- data/src/{util → lib}/hb_allocator.c +2 -2
- data/src/{util → lib}/hb_arena.c +1 -1
- data/src/{util → lib}/hb_arena_debug.c +2 -2
- data/src/{util → lib}/hb_array.c +2 -2
- data/src/{util → lib}/hb_buffer.c +2 -2
- data/src/{util → lib}/hb_narray.c +1 -1
- data/src/{util → lib}/hb_string.c +2 -2
- data/src/{location.c → location/location.c} +2 -2
- data/src/{position.c → location/position.c} +2 -2
- data/src/{range.c → location/range.c} +1 -1
- data/src/main.c +11 -11
- data/src/parser/dot_notation.c +100 -0
- data/src/{parser_match_tags.c → parser/match_tags.c} +34 -5
- data/src/{parser_helpers.c → parser/parser_helpers.c} +10 -10
- data/src/parser.c +68 -32
- data/src/{prism_helpers.c → prism/prism_helpers.c} +7 -7
- data/src/{ruby_parser.c → prism/ruby_parser.c} +1 -1
- data/src/{html_util.c → util/html_util.c} +4 -4
- data/src/{io.c → util/io.c} +3 -3
- data/src/util/ruby_util.c +42 -0
- data/src/{utf8.c → util/utf8.c} +2 -2
- data/src/{util.c → util/util.c} +4 -4
- data/src/visitor.c +35 -3
- data/templates/ext/herb/error_helpers.c.erb +2 -2
- data/templates/ext/herb/nodes.c.erb +1 -1
- data/templates/java/error_helpers.c.erb +1 -1
- data/templates/java/error_helpers.h.erb +2 -2
- data/templates/java/nodes.c.erb +4 -4
- data/templates/java/nodes.h.erb +1 -1
- data/templates/javascript/packages/node/extension/error_helpers.cpp.erb +4 -4
- data/templates/javascript/packages/node/extension/nodes.cpp.erb +4 -4
- data/templates/lib/herb/visitor.rb.erb +14 -0
- data/templates/src/analyze/missing_end.c.erb +2 -2
- data/templates/src/{ast_nodes.c.erb → ast/ast_nodes.c.erb} +9 -9
- data/templates/src/{ast_pretty_print.c.erb → ast/ast_pretty_print.c.erb} +8 -8
- data/templates/src/errors.c.erb +8 -8
- data/templates/src/include/{ast_nodes.h.erb → ast/ast_nodes.h.erb} +11 -12
- data/templates/src/include/{ast_pretty_print.h.erb → ast/ast_pretty_print.h.erb} +2 -2
- data/templates/src/include/errors.h.erb +7 -7
- data/templates/src/{parser_match_tags.c.erb → parser/match_tags.c.erb} +4 -4
- data/templates/src/visitor.c.erb +3 -3
- data/templates/wasm/error_helpers.cpp.erb +4 -4
- data/templates/wasm/nodes.cpp.erb +5 -5
- metadata +76 -68
- data/src/include/element_source.h +0 -10
- /data/src/include/{util → lib}/hb_allocator.h +0 -0
- /data/src/include/{util → lib}/hb_arena.h +0 -0
- /data/src/include/{util → lib}/hb_arena_debug.h +0 -0
- /data/src/include/{util → lib}/hb_array.h +0 -0
- /data/src/include/{util → lib}/hb_buffer.h +0 -0
- /data/src/include/{util → lib}/hb_narray.h +0 -0
- /data/src/include/{util → lib}/string.h +0 -0
- /data/src/include/{position.h → location/position.h} +0 -0
- /data/src/include/{range.h → location/range.h} +0 -0
- /data/src/include/{herb_prism_node.h → prism/herb_prism_node.h} +0 -0
- /data/src/include/{prism_serialized.h → prism/prism_serialized.h} +0 -0
- /data/src/include/{ruby_parser.h → prism/ruby_parser.h} +0 -0
- /data/src/include/{io.h → util/io.h} +0 -0
- /data/templates/src/include/{util → lib}/hb_foreach.h.erb +0 -0
data/lib/herb/ast/nodes.rb
CHANGED
|
@@ -2128,6 +2128,9 @@ module Herb
|
|
|
2128
2128
|
#| tag_closing: Herb::Token?,
|
|
2129
2129
|
#| prism_node: String?,
|
|
2130
2130
|
#| body: Array[Herb::AST::Node],
|
|
2131
|
+
#| rescue_clause: Herb::AST::ERBRescueNode?,
|
|
2132
|
+
#| else_clause: Herb::AST::ERBElseNode?,
|
|
2133
|
+
#| ensure_clause: Herb::AST::ERBEnsureNode?,
|
|
2131
2134
|
#| end_node: Herb::AST::ERBEndNode?,
|
|
2132
2135
|
#| }
|
|
2133
2136
|
class ERBBlockNode < Node
|
|
@@ -2138,16 +2141,22 @@ module Herb
|
|
|
2138
2141
|
attr_reader :tag_closing #: Herb::Token?
|
|
2139
2142
|
attr_reader :prism_node #: String?
|
|
2140
2143
|
attr_reader :body #: Array[Herb::AST::Node]
|
|
2144
|
+
attr_reader :rescue_clause #: Herb::AST::ERBRescueNode?
|
|
2145
|
+
attr_reader :else_clause #: Herb::AST::ERBElseNode?
|
|
2146
|
+
attr_reader :ensure_clause #: Herb::AST::ERBEnsureNode?
|
|
2141
2147
|
attr_reader :end_node #: Herb::AST::ERBEndNode?
|
|
2142
2148
|
|
|
2143
|
-
#: (String, Location, Array[Herb::Errors::Error], Herb::Token, Herb::Token, Herb::Token, String, Array[Herb::AST::Node], Herb::AST::ERBEndNode) -> void
|
|
2144
|
-
def initialize(type, location, errors, tag_opening, content, tag_closing, prism_node, body, end_node)
|
|
2149
|
+
#: (String, Location, Array[Herb::Errors::Error], Herb::Token, Herb::Token, Herb::Token, String, Array[Herb::AST::Node], Herb::AST::ERBRescueNode, Herb::AST::ERBElseNode, Herb::AST::ERBEnsureNode, Herb::AST::ERBEndNode) -> void
|
|
2150
|
+
def initialize(type, location, errors, tag_opening, content, tag_closing, prism_node, body, rescue_clause, else_clause, ensure_clause, end_node)
|
|
2145
2151
|
super(type, location, errors)
|
|
2146
2152
|
@tag_opening = tag_opening
|
|
2147
2153
|
@content = content
|
|
2148
2154
|
@tag_closing = tag_closing
|
|
2149
2155
|
@prism_node = prism_node
|
|
2150
2156
|
@body = body
|
|
2157
|
+
@rescue_clause = rescue_clause
|
|
2158
|
+
@else_clause = else_clause
|
|
2159
|
+
@ensure_clause = ensure_clause
|
|
2151
2160
|
@end_node = end_node
|
|
2152
2161
|
end
|
|
2153
2162
|
|
|
@@ -2175,6 +2184,9 @@ module Herb
|
|
|
2175
2184
|
tag_closing: tag_closing,
|
|
2176
2185
|
prism_node: prism_node,
|
|
2177
2186
|
body: body,
|
|
2187
|
+
rescue_clause: rescue_clause,
|
|
2188
|
+
else_clause: else_clause,
|
|
2189
|
+
ensure_clause: ensure_clause,
|
|
2178
2190
|
end_node: end_node,
|
|
2179
2191
|
}) #: Herb::serialized_erb_block_node
|
|
2180
2192
|
end
|
|
@@ -2186,7 +2198,7 @@ module Herb
|
|
|
2186
2198
|
|
|
2187
2199
|
#: () -> Array[Herb::AST::Node?]
|
|
2188
2200
|
def child_nodes
|
|
2189
|
-
[*(body || []), end_node]
|
|
2201
|
+
[*(body || []), rescue_clause, else_clause, ensure_clause, end_node]
|
|
2190
2202
|
end
|
|
2191
2203
|
|
|
2192
2204
|
#: () -> Array[Herb::AST::Node]
|
|
@@ -2230,6 +2242,30 @@ module Herb
|
|
|
2230
2242
|
end
|
|
2231
2243
|
output += white("├── body: ")
|
|
2232
2244
|
output += inspect_array(body, prefix: "│ ", indent: indent, depth: depth + 1, depth_limit: depth_limit)
|
|
2245
|
+
output += white("├── rescue_clause: ")
|
|
2246
|
+
if rescue_clause
|
|
2247
|
+
output += "\n"
|
|
2248
|
+
output += "│ └── "
|
|
2249
|
+
output += rescue_clause.tree_inspect(indent: indent, depth: depth + 1, depth_limit: depth_limit).gsub(/^/, " " * (indent + 1)).lstrip.gsub(/^/, "│ ").delete_prefix("│ ")
|
|
2250
|
+
else
|
|
2251
|
+
output += magenta("∅\n")
|
|
2252
|
+
end
|
|
2253
|
+
output += white("├── else_clause: ")
|
|
2254
|
+
if else_clause
|
|
2255
|
+
output += "\n"
|
|
2256
|
+
output += "│ └── "
|
|
2257
|
+
output += else_clause.tree_inspect(indent: indent, depth: depth + 1, depth_limit: depth_limit).gsub(/^/, " " * (indent + 1)).lstrip.gsub(/^/, "│ ").delete_prefix("│ ")
|
|
2258
|
+
else
|
|
2259
|
+
output += magenta("∅\n")
|
|
2260
|
+
end
|
|
2261
|
+
output += white("├── ensure_clause: ")
|
|
2262
|
+
if ensure_clause
|
|
2263
|
+
output += "\n"
|
|
2264
|
+
output += "│ └── "
|
|
2265
|
+
output += ensure_clause.tree_inspect(indent: indent, depth: depth + 1, depth_limit: depth_limit).gsub(/^/, " " * (indent + 1)).lstrip.gsub(/^/, "│ ").delete_prefix("│ ")
|
|
2266
|
+
else
|
|
2267
|
+
output += magenta("∅\n")
|
|
2268
|
+
end
|
|
2233
2269
|
output += white("└── end_node: ")
|
|
2234
2270
|
if end_node
|
|
2235
2271
|
output += "\n"
|
|
@@ -3773,6 +3809,211 @@ module Herb
|
|
|
3773
3809
|
end
|
|
3774
3810
|
end
|
|
3775
3811
|
|
|
3812
|
+
#: type serialized_ruby_strict_local_node = {
|
|
3813
|
+
#| name: Herb::Token?,
|
|
3814
|
+
#| default_value: Herb::AST::RubyLiteralNode?,
|
|
3815
|
+
#| required: bool,
|
|
3816
|
+
#| double_splat: bool,
|
|
3817
|
+
#| }
|
|
3818
|
+
class RubyStrictLocalNode < Node
|
|
3819
|
+
include Colors
|
|
3820
|
+
|
|
3821
|
+
attr_reader :name #: Herb::Token?
|
|
3822
|
+
attr_reader :default_value #: Herb::AST::RubyLiteralNode?
|
|
3823
|
+
attr_reader :required #: bool
|
|
3824
|
+
attr_reader :double_splat #: bool
|
|
3825
|
+
|
|
3826
|
+
#: (String, Location, Array[Herb::Errors::Error], Herb::Token, Herb::AST::RubyLiteralNode, bool, bool) -> void
|
|
3827
|
+
def initialize(type, location, errors, name, default_value, required, double_splat)
|
|
3828
|
+
super(type, location, errors)
|
|
3829
|
+
@name = name
|
|
3830
|
+
@default_value = default_value
|
|
3831
|
+
@required = required
|
|
3832
|
+
@double_splat = double_splat
|
|
3833
|
+
end
|
|
3834
|
+
|
|
3835
|
+
#: () -> serialized_ruby_strict_local_node
|
|
3836
|
+
def to_hash
|
|
3837
|
+
super.merge({
|
|
3838
|
+
name: name,
|
|
3839
|
+
default_value: default_value,
|
|
3840
|
+
required: required,
|
|
3841
|
+
double_splat: double_splat,
|
|
3842
|
+
}) #: Herb::serialized_ruby_strict_local_node
|
|
3843
|
+
end
|
|
3844
|
+
|
|
3845
|
+
#: (Visitor) -> void
|
|
3846
|
+
def accept(visitor)
|
|
3847
|
+
visitor.visit_ruby_strict_local_node(self)
|
|
3848
|
+
end
|
|
3849
|
+
|
|
3850
|
+
#: () -> Array[Herb::AST::Node?]
|
|
3851
|
+
def child_nodes
|
|
3852
|
+
[default_value]
|
|
3853
|
+
end
|
|
3854
|
+
|
|
3855
|
+
#: () -> Array[Herb::AST::Node]
|
|
3856
|
+
def compact_child_nodes
|
|
3857
|
+
child_nodes.compact
|
|
3858
|
+
end
|
|
3859
|
+
|
|
3860
|
+
#: () -> String
|
|
3861
|
+
def inspect
|
|
3862
|
+
tree_inspect.rstrip.gsub(/\s+$/, "")
|
|
3863
|
+
end
|
|
3864
|
+
|
|
3865
|
+
#: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
|
|
3866
|
+
def tree_inspect(indent: 0, depth: 0, depth_limit: 10)
|
|
3867
|
+
output = +""
|
|
3868
|
+
|
|
3869
|
+
output += white("@ #{bold(yellow(node_name.to_s))} #{dimmed("(location: #{location.tree_inspect})")}")
|
|
3870
|
+
output += "\n"
|
|
3871
|
+
|
|
3872
|
+
if depth >= depth_limit
|
|
3873
|
+
output += dimmed("└── [depth limit reached ...]\n\n")
|
|
3874
|
+
|
|
3875
|
+
return output.gsub(/^/, " " * indent)
|
|
3876
|
+
end
|
|
3877
|
+
|
|
3878
|
+
output += inspect_errors(prefix: "│ ")
|
|
3879
|
+
|
|
3880
|
+
output += white("├── name: ")
|
|
3881
|
+
output += name ? name.tree_inspect : magenta("∅")
|
|
3882
|
+
output += "\n"
|
|
3883
|
+
output += white("├── default_value: ")
|
|
3884
|
+
if default_value
|
|
3885
|
+
output += "\n"
|
|
3886
|
+
output += "│ └── "
|
|
3887
|
+
output += default_value.tree_inspect(indent: indent, depth: depth + 1, depth_limit: depth_limit).gsub(/^/, " " * (indent + 1)).lstrip.gsub(/^/, "│ ").delete_prefix("│ ")
|
|
3888
|
+
else
|
|
3889
|
+
output += magenta("∅\n")
|
|
3890
|
+
end
|
|
3891
|
+
output += white("├── required: ")
|
|
3892
|
+
output += [true, false].include?(required) ? bold(magenta(required.to_s)) : magenta("∅")
|
|
3893
|
+
output += "\n"
|
|
3894
|
+
output += white("└── double_splat: ")
|
|
3895
|
+
output += [true, false].include?(double_splat) ? bold(magenta(double_splat.to_s)) : magenta("∅")
|
|
3896
|
+
output += "\n"
|
|
3897
|
+
output += "\n"
|
|
3898
|
+
|
|
3899
|
+
output.gsub(/^/, " " * indent)
|
|
3900
|
+
end
|
|
3901
|
+
end
|
|
3902
|
+
|
|
3903
|
+
#: type serialized_erb_strict_locals_node = {
|
|
3904
|
+
#| tag_opening: Herb::Token?,
|
|
3905
|
+
#| content: Herb::Token?,
|
|
3906
|
+
#| tag_closing: Herb::Token?,
|
|
3907
|
+
#| analyzed_ruby: nil,
|
|
3908
|
+
#| prism_node: String?,
|
|
3909
|
+
#| locals: Array[Herb::AST::RubyStrictLocalNode]?,
|
|
3910
|
+
#| }
|
|
3911
|
+
class ERBStrictLocalsNode < Node
|
|
3912
|
+
include Colors
|
|
3913
|
+
|
|
3914
|
+
attr_reader :tag_opening #: Herb::Token?
|
|
3915
|
+
attr_reader :content #: Herb::Token?
|
|
3916
|
+
attr_reader :tag_closing #: Herb::Token?
|
|
3917
|
+
attr_reader :analyzed_ruby #: nil
|
|
3918
|
+
attr_reader :prism_node #: String?
|
|
3919
|
+
attr_reader :locals #: Array[Herb::AST::RubyStrictLocalNode]?
|
|
3920
|
+
|
|
3921
|
+
#: (String, Location, Array[Herb::Errors::Error], Herb::Token, Herb::Token, Herb::Token, nil, String, Array[Herb::AST::RubyStrictLocalNode]) -> void
|
|
3922
|
+
def initialize(type, location, errors, tag_opening, content, tag_closing, analyzed_ruby, prism_node, locals)
|
|
3923
|
+
super(type, location, errors)
|
|
3924
|
+
@tag_opening = tag_opening
|
|
3925
|
+
@content = content
|
|
3926
|
+
@tag_closing = tag_closing
|
|
3927
|
+
@analyzed_ruby = analyzed_ruby
|
|
3928
|
+
@prism_node = prism_node
|
|
3929
|
+
@locals = locals
|
|
3930
|
+
end
|
|
3931
|
+
|
|
3932
|
+
#: () -> Prism::node?
|
|
3933
|
+
def deserialized_prism_node
|
|
3934
|
+
prism_node = @prism_node
|
|
3935
|
+
return nil unless prism_node
|
|
3936
|
+
return nil unless source
|
|
3937
|
+
|
|
3938
|
+
begin
|
|
3939
|
+
require "prism"
|
|
3940
|
+
rescue LoadError
|
|
3941
|
+
warn "The 'prism' gem is required to deserialize Prism nodes. Add it to your Gemfile or install it with: gem install prism"
|
|
3942
|
+
return nil
|
|
3943
|
+
end
|
|
3944
|
+
|
|
3945
|
+
Prism.load(source, prism_node).value
|
|
3946
|
+
end
|
|
3947
|
+
|
|
3948
|
+
#: () -> serialized_erb_strict_locals_node
|
|
3949
|
+
def to_hash
|
|
3950
|
+
super.merge({
|
|
3951
|
+
tag_opening: tag_opening,
|
|
3952
|
+
content: content,
|
|
3953
|
+
tag_closing: tag_closing,
|
|
3954
|
+
analyzed_ruby: analyzed_ruby,
|
|
3955
|
+
prism_node: prism_node,
|
|
3956
|
+
locals: locals,
|
|
3957
|
+
}) #: Herb::serialized_erb_strict_locals_node
|
|
3958
|
+
end
|
|
3959
|
+
|
|
3960
|
+
#: (Visitor) -> void
|
|
3961
|
+
def accept(visitor)
|
|
3962
|
+
visitor.visit_erb_strict_locals_node(self)
|
|
3963
|
+
end
|
|
3964
|
+
|
|
3965
|
+
#: () -> Array[Herb::AST::Node?]
|
|
3966
|
+
def child_nodes
|
|
3967
|
+
[*(locals || [])]
|
|
3968
|
+
end
|
|
3969
|
+
|
|
3970
|
+
#: () -> Array[Herb::AST::Node]
|
|
3971
|
+
def compact_child_nodes
|
|
3972
|
+
child_nodes.compact
|
|
3973
|
+
end
|
|
3974
|
+
|
|
3975
|
+
#: () -> String
|
|
3976
|
+
def inspect
|
|
3977
|
+
tree_inspect.rstrip.gsub(/\s+$/, "")
|
|
3978
|
+
end
|
|
3979
|
+
|
|
3980
|
+
#: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
|
|
3981
|
+
def tree_inspect(indent: 0, depth: 0, depth_limit: 10)
|
|
3982
|
+
output = +""
|
|
3983
|
+
|
|
3984
|
+
output += white("@ #{bold(yellow(node_name.to_s))} #{dimmed("(location: #{location.tree_inspect})")}")
|
|
3985
|
+
output += "\n"
|
|
3986
|
+
|
|
3987
|
+
if depth >= depth_limit
|
|
3988
|
+
output += dimmed("└── [depth limit reached ...]\n\n")
|
|
3989
|
+
|
|
3990
|
+
return output.gsub(/^/, " " * indent)
|
|
3991
|
+
end
|
|
3992
|
+
|
|
3993
|
+
output += inspect_errors(prefix: "│ ")
|
|
3994
|
+
|
|
3995
|
+
output += white("├── tag_opening: ")
|
|
3996
|
+
output += tag_opening ? tag_opening.tree_inspect : magenta("∅")
|
|
3997
|
+
output += "\n"
|
|
3998
|
+
output += white("├── content: ")
|
|
3999
|
+
output += content ? content.tree_inspect : magenta("∅")
|
|
4000
|
+
output += "\n"
|
|
4001
|
+
output += white("├── tag_closing: ")
|
|
4002
|
+
output += tag_closing ? tag_closing.tree_inspect : magenta("∅")
|
|
4003
|
+
output += "\n"
|
|
4004
|
+
if prism_node && source
|
|
4005
|
+
output += white("├── prism_node: ")
|
|
4006
|
+
output += Herb::PrismInspect.inspect_prism_serialized(prism_node, source, "│ ")
|
|
4007
|
+
output += "\n"
|
|
4008
|
+
end
|
|
4009
|
+
output += white("└── locals: ")
|
|
4010
|
+
output += inspect_array(locals, prefix: " ", indent: indent, depth: depth + 1, depth_limit: depth_limit)
|
|
4011
|
+
output += "\n"
|
|
4012
|
+
|
|
4013
|
+
output.gsub(/^/, " " * indent)
|
|
4014
|
+
end
|
|
4015
|
+
end
|
|
4016
|
+
|
|
3776
4017
|
#: type serialized_erb_yield_node = {
|
|
3777
4018
|
#| tag_opening: Herb::Token?,
|
|
3778
4019
|
#| content: Herb::Token?,
|
data/lib/herb/cli.rb
CHANGED
|
@@ -8,7 +8,7 @@ require "optparse"
|
|
|
8
8
|
class Herb::CLI
|
|
9
9
|
include Herb::Colors
|
|
10
10
|
|
|
11
|
-
attr_accessor :json, :silent, :log_file, :no_timing, :local, :escape, :no_escape, :freeze, :debug, :tool, :strict, :analyze, :track_whitespace, :verbose, :isolate, :arena_stats, :leak_check
|
|
11
|
+
attr_accessor :json, :silent, :log_file, :no_timing, :local, :escape, :no_escape, :freeze, :debug, :tool, :strict, :analyze, :track_whitespace, :verbose, :isolate, :arena_stats, :leak_check, :action_view_helpers, :trim
|
|
12
12
|
|
|
13
13
|
def initialize(args)
|
|
14
14
|
@args = args
|
|
@@ -160,7 +160,7 @@ class Herb::CLI
|
|
|
160
160
|
show_config
|
|
161
161
|
exit(0)
|
|
162
162
|
when "parse"
|
|
163
|
-
Herb.parse(file_content, strict: strict.nil? || strict, analyze: analyze.nil? || analyze, track_whitespace: track_whitespace || false, arena_stats: arena_stats)
|
|
163
|
+
Herb.parse(file_content, strict: strict.nil? || strict, analyze: analyze.nil? || analyze, track_whitespace: track_whitespace || false, arena_stats: arena_stats, action_view_helpers: action_view_helpers || false)
|
|
164
164
|
when "compile"
|
|
165
165
|
compile_template
|
|
166
166
|
when "render"
|
|
@@ -298,6 +298,14 @@ class Herb::CLI
|
|
|
298
298
|
self.track_whitespace = true
|
|
299
299
|
end
|
|
300
300
|
|
|
301
|
+
parser.on("--action-view-helpers", "Enable Action View helper detection (for parse command) (default: false)") do
|
|
302
|
+
self.action_view_helpers = true
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
parser.on("--trim", "Enable trimming of leading/trailing whitespace (for compile/render commands)") do
|
|
306
|
+
self.trim = true
|
|
307
|
+
end
|
|
308
|
+
|
|
301
309
|
parser.on("--tool TOOL", "Show config for specific tool: linter, formatter (for config command)") do |t|
|
|
302
310
|
self.tool = t.to_sym
|
|
303
311
|
end
|
|
@@ -442,6 +450,7 @@ class Herb::CLI
|
|
|
442
450
|
options[:debug_filename] = @file if @file
|
|
443
451
|
end
|
|
444
452
|
|
|
453
|
+
options[:trim] = true if trim
|
|
445
454
|
options[:validate_ruby] = true
|
|
446
455
|
engine = Herb::Engine.new(file_content, options)
|
|
447
456
|
|
|
@@ -529,6 +538,7 @@ class Herb::CLI
|
|
|
529
538
|
options[:debug_filename] = @file if @file
|
|
530
539
|
end
|
|
531
540
|
|
|
541
|
+
options[:trim] = true if trim
|
|
532
542
|
engine = Herb::Engine.new(file_content, options)
|
|
533
543
|
compiled_code = engine.src
|
|
534
544
|
|
data/lib/herb/engine/compiler.rb
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
module Herb
|
|
4
4
|
class Engine
|
|
5
5
|
class Compiler < ::Herb::Visitor
|
|
6
|
+
EXPRESSION_TOKEN_TYPES = [:expr, :expr_escaped, :expr_block, :expr_block_escaped].freeze
|
|
7
|
+
|
|
6
8
|
attr_reader :tokens
|
|
7
9
|
|
|
8
10
|
def initialize(engine, options = {})
|
|
@@ -14,6 +16,9 @@ module Herb
|
|
|
14
16
|
@element_stack = [] #: Array[String]
|
|
15
17
|
@context_stack = [:html_content]
|
|
16
18
|
@trim_next_whitespace = false
|
|
19
|
+
@last_trim_consumed_newline = false
|
|
20
|
+
@pending_leading_whitespace = nil
|
|
21
|
+
@pending_leading_whitespace_insert_index = 0
|
|
17
22
|
end
|
|
18
23
|
|
|
19
24
|
def generate_output
|
|
@@ -46,43 +51,19 @@ module Herb
|
|
|
46
51
|
end
|
|
47
52
|
|
|
48
53
|
def visit_html_element_node(node)
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
if tag_name == "script"
|
|
54
|
-
push_context(:script_content)
|
|
55
|
-
elsif tag_name == "style"
|
|
56
|
-
push_context(:style_content)
|
|
54
|
+
with_element_context(node) do
|
|
55
|
+
visit(node.open_tag)
|
|
56
|
+
visit_all(node.body)
|
|
57
|
+
visit(node.close_tag)
|
|
57
58
|
end
|
|
58
|
-
|
|
59
|
-
visit(node.open_tag)
|
|
60
|
-
visit_all(node.body)
|
|
61
|
-
visit(node.close_tag)
|
|
62
|
-
|
|
63
|
-
pop_context if ["script", "style"].include?(tag_name)
|
|
64
|
-
|
|
65
|
-
@element_stack.pop if tag_name
|
|
66
59
|
end
|
|
67
60
|
|
|
68
61
|
def visit_html_conditional_element_node(node)
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
if tag_name == "script"
|
|
74
|
-
push_context(:script_content)
|
|
75
|
-
elsif tag_name == "style"
|
|
76
|
-
push_context(:style_content)
|
|
62
|
+
with_element_context(node) do
|
|
63
|
+
visit(node.open_conditional)
|
|
64
|
+
visit_all(node.body)
|
|
65
|
+
visit(node.close_conditional)
|
|
77
66
|
end
|
|
78
|
-
|
|
79
|
-
visit(node.open_conditional)
|
|
80
|
-
visit_all(node.body)
|
|
81
|
-
visit(node.close_conditional)
|
|
82
|
-
|
|
83
|
-
pop_context if ["script", "style"].include?(tag_name)
|
|
84
|
-
|
|
85
|
-
@element_stack.pop if tag_name
|
|
86
67
|
end
|
|
87
68
|
|
|
88
69
|
def visit_html_open_tag_node(node)
|
|
@@ -270,12 +251,16 @@ module Herb
|
|
|
270
251
|
else
|
|
271
252
|
[:expr_block, code, current_context]
|
|
272
253
|
end
|
|
254
|
+
@last_trim_consumed_newline = false
|
|
273
255
|
|
|
274
256
|
visit_all(node.body)
|
|
275
257
|
visit_erb_block_end_node(node.end_node, escaped: should_escape)
|
|
276
258
|
else
|
|
277
259
|
visit_erb_control_node(node) do
|
|
278
260
|
visit_all(node.body)
|
|
261
|
+
visit(node.rescue_clause)
|
|
262
|
+
visit(node.else_clause)
|
|
263
|
+
visit(node.ensure_clause)
|
|
279
264
|
visit(node.end_node)
|
|
280
265
|
end
|
|
281
266
|
end
|
|
@@ -289,10 +274,10 @@ module Herb
|
|
|
289
274
|
code = node.content.value.strip
|
|
290
275
|
|
|
291
276
|
if at_line_start?
|
|
292
|
-
|
|
293
|
-
|
|
277
|
+
leading_space = extract_and_remove_leading_space!
|
|
278
|
+
right_space = " \n"
|
|
294
279
|
|
|
295
|
-
@tokens << [:expr_block_end, "#{
|
|
280
|
+
@tokens << [:expr_block_end, "#{leading_space}#{code}#{right_space}", current_context, escaped]
|
|
296
281
|
@trim_next_whitespace = true
|
|
297
282
|
else
|
|
298
283
|
@tokens << [:expr_block_end, code, current_context, escaped]
|
|
@@ -329,16 +314,37 @@ module Herb
|
|
|
329
314
|
@context_stack.pop
|
|
330
315
|
end
|
|
331
316
|
|
|
317
|
+
#: (untyped node) { () -> untyped } -> untyped
|
|
318
|
+
def with_element_context(node)
|
|
319
|
+
tag_name = node.tag_name&.value&.downcase
|
|
320
|
+
|
|
321
|
+
@element_stack.push(tag_name) if tag_name
|
|
322
|
+
|
|
323
|
+
if tag_name == "script"
|
|
324
|
+
push_context(:script_content)
|
|
325
|
+
elsif tag_name == "style"
|
|
326
|
+
push_context(:style_content)
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
yield
|
|
330
|
+
|
|
331
|
+
pop_context if ["script", "style"].include?(tag_name)
|
|
332
|
+
|
|
333
|
+
@element_stack.pop if tag_name
|
|
334
|
+
end
|
|
335
|
+
|
|
332
336
|
def process_erb_tag(node, skip_comment_check: false)
|
|
333
337
|
opening = node.tag_opening.value
|
|
334
338
|
|
|
335
339
|
if !skip_comment_check && erb_comment?(opening)
|
|
336
340
|
has_left_trim = opening.start_with?("<%-")
|
|
341
|
+
follows_newline = leading_space_follows_newline?
|
|
337
342
|
remove_trailing_whitespace_from_last_token! if has_left_trim
|
|
338
343
|
|
|
339
344
|
if at_line_start?
|
|
340
|
-
|
|
345
|
+
leading_space = extract_and_remove_leading_space!
|
|
341
346
|
@trim_next_whitespace = true
|
|
347
|
+
save_pending_leading_whitespace!(leading_space) if !leading_space.empty? && follows_newline
|
|
342
348
|
end
|
|
343
349
|
return
|
|
344
350
|
end
|
|
@@ -357,10 +363,17 @@ module Herb
|
|
|
357
363
|
return if text.empty?
|
|
358
364
|
|
|
359
365
|
if @trim_next_whitespace
|
|
366
|
+
@last_trim_consumed_newline = text.match?(/\A[ \t]*\r?\n/)
|
|
360
367
|
text = text.sub(/\A[ \t]*\r?\n/, "")
|
|
361
368
|
@trim_next_whitespace = false
|
|
369
|
+
|
|
370
|
+
restore_pending_leading_whitespace! unless @last_trim_consumed_newline
|
|
371
|
+
else
|
|
372
|
+
@last_trim_consumed_newline = false
|
|
362
373
|
end
|
|
363
374
|
|
|
375
|
+
@pending_leading_whitespace = nil
|
|
376
|
+
|
|
364
377
|
return if text.empty?
|
|
365
378
|
|
|
366
379
|
@tokens << [:text, text, current_context]
|
|
@@ -376,10 +389,12 @@ module Herb
|
|
|
376
389
|
|
|
377
390
|
def add_expression(code)
|
|
378
391
|
@tokens << [:expr, code, current_context]
|
|
392
|
+
@last_trim_consumed_newline = false
|
|
379
393
|
end
|
|
380
394
|
|
|
381
395
|
def add_expression_escaped(code)
|
|
382
396
|
@tokens << [:expr_escaped, code, current_context]
|
|
397
|
+
@last_trim_consumed_newline = false
|
|
383
398
|
end
|
|
384
399
|
|
|
385
400
|
def optimize_tokens(tokens)
|
|
@@ -463,6 +478,13 @@ module Herb
|
|
|
463
478
|
end
|
|
464
479
|
|
|
465
480
|
def process_erb_output(node, opening, code)
|
|
481
|
+
if @trim_next_whitespace && @pending_leading_whitespace
|
|
482
|
+
restore_pending_leading_whitespace!
|
|
483
|
+
@pending_leading_whitespace = nil
|
|
484
|
+
@trim_next_whitespace = false
|
|
485
|
+
@last_trim_consumed_newline = false
|
|
486
|
+
end
|
|
487
|
+
|
|
466
488
|
has_right_trim = node.tag_closing&.value == "-%>"
|
|
467
489
|
should_escape = should_escape_output?(opening)
|
|
468
490
|
add_expression_with_escaping(code, should_escape)
|
|
@@ -493,78 +515,119 @@ module Herb
|
|
|
493
515
|
end
|
|
494
516
|
|
|
495
517
|
def at_line_start?
|
|
496
|
-
@tokens.empty?
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
518
|
+
return true if @tokens.empty?
|
|
519
|
+
|
|
520
|
+
last_type = @tokens.last[0]
|
|
521
|
+
last_value = @tokens.last[1]
|
|
522
|
+
|
|
523
|
+
if last_type == :text
|
|
524
|
+
last_value.empty? || last_value.end_with?("\n") || (last_value =~ /\A[ \t]+\z/ && preceding_token_ends_with_newline?) || last_value =~ /\n[ \t]+\z/
|
|
525
|
+
elsif EXPRESSION_TOKEN_TYPES.include?(last_type)
|
|
526
|
+
@last_trim_consumed_newline
|
|
527
|
+
else
|
|
528
|
+
last_value.end_with?("\n")
|
|
529
|
+
end
|
|
502
530
|
end
|
|
503
531
|
|
|
504
532
|
def preceding_token_ends_with_newline?
|
|
505
533
|
return true unless @tokens.length >= 2
|
|
506
534
|
|
|
507
535
|
preceding = @tokens[-2]
|
|
508
|
-
return
|
|
536
|
+
return @last_trim_consumed_newline if EXPRESSION_TOKEN_TYPES.include?(preceding[0])
|
|
537
|
+
return preceding[1].end_with?("\n") if preceding[0] == :expr_block_end
|
|
509
538
|
return true unless preceding[0] == :text
|
|
510
539
|
|
|
511
540
|
preceding[1].end_with?("\n")
|
|
512
541
|
end
|
|
513
542
|
|
|
514
|
-
def
|
|
515
|
-
return
|
|
543
|
+
def last_text_token
|
|
544
|
+
return unless @tokens.last && @tokens.last[0] == :text
|
|
516
545
|
|
|
517
|
-
|
|
546
|
+
@tokens.last
|
|
547
|
+
end
|
|
548
|
+
|
|
549
|
+
def extract_leading_space
|
|
550
|
+
token = last_text_token
|
|
551
|
+
return "" unless token
|
|
552
|
+
|
|
553
|
+
text = token[1]
|
|
518
554
|
|
|
519
555
|
return Regexp.last_match(1) if text =~ /\n([ \t]+)\z/ || text =~ /\A([ \t]+)\z/
|
|
520
556
|
|
|
521
557
|
""
|
|
522
558
|
end
|
|
523
559
|
|
|
524
|
-
def
|
|
525
|
-
|
|
526
|
-
return
|
|
560
|
+
def leading_space_follows_newline?
|
|
561
|
+
token = last_text_token
|
|
562
|
+
return false unless token
|
|
563
|
+
|
|
564
|
+
token[1].match?(/\n[ \t]+\z/)
|
|
565
|
+
end
|
|
566
|
+
|
|
567
|
+
def extract_and_remove_leading_space!
|
|
568
|
+
leading_space = extract_leading_space
|
|
569
|
+
return leading_space if leading_space.empty?
|
|
527
570
|
|
|
528
571
|
text = @tokens.last[1]
|
|
572
|
+
|
|
529
573
|
if text =~ /\n[ \t]+\z/
|
|
530
574
|
text.sub!(/[ \t]+\z/, "")
|
|
531
575
|
elsif text =~ /\A[ \t]+\z/
|
|
532
576
|
text.replace("")
|
|
533
577
|
end
|
|
578
|
+
|
|
534
579
|
@tokens.last[1] = text
|
|
535
580
|
|
|
536
|
-
|
|
581
|
+
leading_space
|
|
537
582
|
end
|
|
538
583
|
|
|
539
584
|
def apply_trim(node, code)
|
|
540
585
|
has_left_trim = node.tag_opening.value.start_with?("<%-")
|
|
541
|
-
node.tag_closing&.value
|
|
542
586
|
|
|
543
|
-
|
|
587
|
+
follows_newline = leading_space_follows_newline?
|
|
588
|
+
removed_whitespace = has_left_trim ? remove_trailing_whitespace_from_last_token! : ""
|
|
544
589
|
|
|
545
590
|
if at_line_start?
|
|
546
|
-
|
|
547
|
-
|
|
591
|
+
leading_space = extract_and_remove_leading_space!
|
|
592
|
+
effective_leading_space = leading_space.empty? ? removed_whitespace : leading_space
|
|
593
|
+
right_space = Herb::Engine.heredoc?(code) ? "\n" : " \n"
|
|
548
594
|
|
|
549
|
-
@
|
|
595
|
+
@pending_leading_whitespace_insert_index = @tokens.length
|
|
596
|
+
@pending_leading_whitespace = effective_leading_space if !effective_leading_space.empty? && follows_newline
|
|
597
|
+
@tokens << [:code, "#{effective_leading_space}#{code}#{right_space}", current_context]
|
|
550
598
|
@trim_next_whitespace = true
|
|
551
599
|
else
|
|
552
600
|
@tokens << [:code, code, current_context]
|
|
553
601
|
end
|
|
554
602
|
end
|
|
555
603
|
|
|
604
|
+
def save_pending_leading_whitespace!(whitespace)
|
|
605
|
+
@pending_leading_whitespace = whitespace
|
|
606
|
+
@pending_leading_whitespace_insert_index = @tokens.length
|
|
607
|
+
end
|
|
608
|
+
|
|
609
|
+
def restore_pending_leading_whitespace!
|
|
610
|
+
return unless @pending_leading_whitespace
|
|
611
|
+
|
|
612
|
+
@tokens.insert(@pending_leading_whitespace_insert_index, [:text, @pending_leading_whitespace, current_context])
|
|
613
|
+
end
|
|
614
|
+
|
|
556
615
|
def remove_trailing_whitespace_from_last_token!
|
|
557
|
-
|
|
616
|
+
token = last_text_token
|
|
617
|
+
return "" unless token
|
|
558
618
|
|
|
559
|
-
text =
|
|
619
|
+
text = token[1]
|
|
620
|
+
removed = text[/[ \t]+\z/] || ""
|
|
560
621
|
|
|
561
622
|
if text =~ /\n[ \t]+\z/
|
|
562
623
|
text.sub!(/[ \t]+\z/, "")
|
|
563
|
-
|
|
624
|
+
token[1] = text
|
|
564
625
|
elsif text =~ /\A[ \t]+\z/
|
|
565
626
|
text.replace("")
|
|
566
|
-
|
|
627
|
+
token[1] = text
|
|
567
628
|
end
|
|
629
|
+
|
|
630
|
+
removed
|
|
568
631
|
end
|
|
569
632
|
end
|
|
570
633
|
end
|