herb 0.8.9-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.
- checksums.yaml +4 -4
- data/Makefile +33 -11
- data/README.md +64 -34
- data/Rakefile +48 -40
- data/config.yml +323 -33
- data/ext/herb/error_helpers.c +384 -132
- data/ext/herb/error_helpers.h +1 -0
- data/ext/herb/extconf.rb +67 -28
- data/ext/herb/extension.c +317 -51
- data/ext/herb/extension.h +1 -0
- data/ext/herb/extension_helpers.c +23 -14
- data/ext/herb/extension_helpers.h +2 -2
- data/ext/herb/nodes.c +537 -270
- data/ext/herb/nodes.h +1 -0
- data/herb.gemspec +3 -2
- 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/helpers.rb +3 -3
- data/lib/herb/ast/node.rb +15 -2
- data/lib/herb/ast/nodes.rb +1132 -157
- data/lib/herb/bootstrap.rb +87 -0
- data/lib/herb/cli.rb +341 -31
- data/lib/herb/configuration.rb +248 -0
- data/lib/herb/defaults.yml +32 -0
- data/lib/herb/engine/compiler.rb +83 -14
- data/lib/herb/engine/debug_visitor.rb +51 -6
- data/lib/herb/engine/error_formatter.rb +13 -9
- data/lib/herb/engine/parser_error_overlay.rb +10 -6
- data/lib/herb/engine/validator.rb +8 -3
- data/lib/herb/engine/validators/nesting_validator.rb +2 -2
- data/lib/herb/engine.rb +92 -33
- data/lib/herb/errors.rb +582 -87
- data/lib/herb/lex_result.rb +1 -0
- data/lib/herb/location.rb +7 -3
- data/lib/herb/parse_result.rb +12 -2
- data/lib/herb/parser_options.rb +57 -0
- data/lib/herb/position.rb +1 -0
- data/lib/herb/prism_inspect.rb +116 -0
- data/lib/herb/project.rb +923 -331
- data/lib/herb/range.rb +1 -0
- data/lib/herb/token.rb +7 -1
- data/lib/herb/version.rb +1 -1
- data/lib/herb/visitor.rb +37 -2
- data/lib/herb/warnings.rb +6 -1
- data/lib/herb.rb +35 -3
- data/sig/herb/ast/helpers.rbs +2 -2
- data/sig/herb/ast/node.rbs +12 -2
- data/sig/herb/ast/nodes.rbs +641 -128
- data/sig/herb/bootstrap.rbs +31 -0
- data/sig/herb/configuration.rbs +89 -0
- data/sig/herb/engine/compiler.rbs +9 -1
- data/sig/herb/engine/debug_visitor.rbs +8 -0
- data/sig/herb/engine/validator.rbs +5 -1
- data/sig/herb/engine.rbs +18 -2
- data/sig/herb/errors.rbs +268 -63
- data/sig/herb/location.rbs +4 -0
- data/sig/herb/parse_result.rbs +4 -2
- data/sig/herb/parser_options.rbs +42 -0
- data/sig/herb/position.rbs +1 -0
- data/sig/herb/prism_inspect.rbs +28 -0
- data/sig/herb/range.rbs +1 -0
- data/sig/herb/token.rbs +6 -0
- data/sig/herb/visitor.rbs +25 -4
- data/sig/herb/warnings.rbs +6 -1
- data/sig/herb.rbs +14 -0
- data/sig/herb_c_extension.rbs +5 -2
- data/sig/serialized_ast_errors.rbs +57 -6
- data/sig/serialized_ast_nodes.rbs +60 -6
- data/src/analyze/action_view/attribute_extraction_helpers.c +290 -0
- data/src/analyze/action_view/content_tag.c +70 -0
- data/src/analyze/action_view/link_to.c +143 -0
- data/src/analyze/action_view/registry.c +60 -0
- data/src/analyze/action_view/tag.c +64 -0
- data/src/analyze/action_view/tag_helper_node_builders.c +305 -0
- data/src/analyze/action_view/tag_helpers.c +748 -0
- data/src/analyze/action_view/turbo_frame_tag.c +88 -0
- data/src/analyze/analyze.c +882 -0
- data/src/{analyzed_ruby.c → analyze/analyzed_ruby.c} +13 -11
- data/src/analyze/builders.c +343 -0
- data/src/analyze/conditional_elements.c +594 -0
- data/src/analyze/conditional_open_tags.c +640 -0
- data/src/analyze/control_type.c +250 -0
- data/src/{analyze_helpers.c → analyze/helpers.c} +79 -31
- data/src/analyze/invalid_structures.c +193 -0
- data/src/{analyze_missing_end.c → analyze/missing_end.c} +33 -22
- data/src/analyze/parse_errors.c +84 -0
- data/src/analyze/prism_annotate.c +397 -0
- data/src/{analyze_transform.c → analyze/transform.c} +17 -3
- data/src/ast_node.c +17 -7
- data/src/ast_nodes.c +662 -387
- data/src/ast_pretty_print.c +190 -6
- data/src/errors.c +1099 -506
- data/src/extract.c +148 -49
- data/src/herb.c +52 -34
- data/src/html_util.c +241 -12
- data/src/include/analyze/action_view/attribute_extraction_helpers.h +36 -0
- data/src/include/analyze/action_view/tag_helper_handler.h +41 -0
- data/src/include/analyze/action_view/tag_helper_node_builders.h +70 -0
- data/src/include/analyze/action_view/tag_helpers.h +38 -0
- data/src/include/{analyze.h → analyze/analyze.h} +14 -4
- data/src/include/{analyzed_ruby.h → analyze/analyzed_ruby.h} +3 -3
- data/src/include/analyze/builders.h +27 -0
- data/src/include/analyze/conditional_elements.h +9 -0
- data/src/include/analyze/conditional_open_tags.h +9 -0
- data/src/include/analyze/control_type.h +14 -0
- data/src/include/{analyze_helpers.h → analyze/helpers.h} +22 -17
- data/src/include/analyze/invalid_structures.h +11 -0
- data/src/include/analyze/prism_annotate.h +16 -0
- data/src/include/ast_node.h +11 -5
- data/src/include/ast_nodes.h +117 -38
- data/src/include/ast_pretty_print.h +5 -0
- data/src/include/element_source.h +3 -8
- data/src/include/errors.h +154 -53
- data/src/include/extract.h +21 -5
- data/src/include/herb.h +18 -6
- data/src/include/herb_prism_node.h +13 -0
- data/src/include/html_util.h +7 -2
- data/src/include/io.h +3 -1
- data/src/include/lex_helpers.h +29 -0
- data/src/include/lexer.h +1 -1
- data/src/include/lexer_peek_helpers.h +87 -13
- data/src/include/lexer_struct.h +2 -0
- data/src/include/location.h +2 -1
- data/src/include/parser.h +27 -2
- data/src/include/parser_helpers.h +19 -3
- data/src/include/pretty_print.h +10 -5
- data/src/include/prism_context.h +45 -0
- data/src/include/prism_helpers.h +10 -7
- data/src/include/prism_serialized.h +12 -0
- data/src/include/token.h +16 -4
- data/src/include/token_struct.h +10 -3
- data/src/include/utf8.h +2 -1
- data/src/include/util/hb_allocator.h +78 -0
- data/src/include/util/hb_arena.h +6 -1
- data/src/include/util/hb_arena_debug.h +12 -1
- data/src/include/util/hb_array.h +7 -3
- data/src/include/util/hb_buffer.h +6 -4
- data/src/include/util/hb_foreach.h +79 -0
- data/src/include/util/hb_narray.h +8 -4
- data/src/include/util/hb_string.h +56 -9
- data/src/include/util/string.h +11 -0
- data/src/include/util.h +6 -3
- data/src/include/version.h +1 -1
- data/src/io.c +3 -2
- data/src/lexer.c +42 -30
- data/src/lexer_peek_helpers.c +12 -74
- data/src/location.c +2 -2
- data/src/main.c +79 -66
- data/src/parser.c +784 -247
- data/src/parser_helpers.c +110 -23
- data/src/parser_match_tags.c +109 -48
- data/src/pretty_print.c +29 -24
- data/src/prism_helpers.c +30 -27
- data/src/ruby_parser.c +2 -0
- data/src/token.c +151 -66
- data/src/token_matchers.c +0 -1
- data/src/utf8.c +7 -6
- data/src/util/hb_allocator.c +341 -0
- data/src/util/hb_arena.c +81 -56
- data/src/util/hb_arena_debug.c +32 -17
- data/src/util/hb_array.c +30 -15
- data/src/util/hb_buffer.c +17 -21
- data/src/util/hb_narray.c +22 -7
- data/src/util/hb_string.c +49 -35
- data/src/util.c +21 -11
- data/src/visitor.c +47 -0
- data/templates/ext/herb/error_helpers.c.erb +24 -11
- data/templates/ext/herb/error_helpers.h.erb +1 -0
- data/templates/ext/herb/nodes.c.erb +50 -16
- data/templates/ext/herb/nodes.h.erb +1 -0
- data/templates/java/error_helpers.c.erb +1 -1
- data/templates/java/nodes.c.erb +30 -8
- data/templates/java/org/herb/ast/Errors.java.erb +24 -1
- data/templates/java/org/herb/ast/Nodes.java.erb +80 -21
- data/templates/javascript/packages/core/src/errors.ts.erb +16 -3
- data/templates/javascript/packages/core/src/node-type-guards.ts.erb +3 -1
- data/templates/javascript/packages/core/src/nodes.ts.erb +109 -32
- data/templates/javascript/packages/node/extension/error_helpers.cpp.erb +13 -4
- data/templates/javascript/packages/node/extension/nodes.cpp.erb +43 -4
- data/templates/lib/herb/ast/nodes.rb.erb +88 -31
- data/templates/lib/herb/errors.rb.erb +15 -3
- data/templates/lib/herb/visitor.rb.erb +2 -2
- data/templates/rust/src/ast/nodes.rs.erb +97 -44
- data/templates/rust/src/errors.rs.erb +2 -1
- data/templates/rust/src/nodes.rs.erb +167 -15
- data/templates/rust/src/union_types.rs.erb +60 -0
- data/templates/rust/src/visitor.rs.erb +81 -0
- data/templates/src/{analyze_missing_end.c.erb → analyze/missing_end.c.erb} +9 -6
- data/templates/src/{analyze_transform.c.erb → analyze/transform.c.erb} +2 -2
- data/templates/src/ast_nodes.c.erb +34 -26
- data/templates/src/ast_pretty_print.c.erb +24 -5
- data/templates/src/errors.c.erb +60 -54
- data/templates/src/include/ast_nodes.h.erb +6 -2
- data/templates/src/include/ast_pretty_print.h.erb +5 -0
- data/templates/src/include/errors.h.erb +15 -11
- data/templates/src/include/util/hb_foreach.h.erb +20 -0
- data/templates/src/parser_match_tags.c.erb +10 -4
- data/templates/src/visitor.c.erb +2 -2
- data/templates/template.rb +204 -29
- data/templates/wasm/error_helpers.cpp.erb +9 -5
- data/templates/wasm/nodes.cpp.erb +41 -4
- data/vendor/prism/config.yml +4 -4
- data/vendor/prism/include/prism/ast.h +4 -4
- data/vendor/prism/include/prism/version.h +2 -2
- data/vendor/prism/src/prism.c +1 -1
- data/vendor/prism/templates/java/org/prism/Loader.java.erb +1 -1
- data/vendor/prism/templates/javascript/src/deserialize.js.erb +1 -1
- data/vendor/prism/templates/lib/prism/node.rb.erb +23 -15
- data/vendor/prism/templates/lib/prism/serialize.rb.erb +1 -1
- data/vendor/prism/templates/rbi/prism/node.rbi.erb +3 -0
- data/vendor/prism/templates/sig/prism/node.rbs.erb +3 -0
- data/vendor/prism/templates/sig/prism.rbs.erb +9 -10
- metadata +58 -16
- data/src/analyze.c +0 -1594
- data/src/element_source.c +0 -12
- data/src/include/util/hb_system.h +0 -9
- data/src/util/hb_system.c +0 -30
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "yaml"
|
|
4
|
+
require "pathname"
|
|
5
|
+
|
|
6
|
+
module Herb
|
|
7
|
+
class Configuration
|
|
8
|
+
CONFIG_FILENAMES = [".herb.yml"].freeze
|
|
9
|
+
|
|
10
|
+
PROJECT_INDICATORS = [
|
|
11
|
+
".git",
|
|
12
|
+
".herb",
|
|
13
|
+
".herb.yml",
|
|
14
|
+
"Gemfile",
|
|
15
|
+
"package.json",
|
|
16
|
+
"Rakefile",
|
|
17
|
+
"README.md",
|
|
18
|
+
"*.gemspec",
|
|
19
|
+
"config/application.rb"
|
|
20
|
+
].freeze
|
|
21
|
+
|
|
22
|
+
DEFAULTS_PATH = File.expand_path("defaults.yml", __dir__ || __FILE__).freeze
|
|
23
|
+
DEFAULTS = YAML.safe_load_file(DEFAULTS_PATH).freeze
|
|
24
|
+
|
|
25
|
+
attr_reader :config, :config_path, :project_root
|
|
26
|
+
|
|
27
|
+
def initialize(project_path = nil)
|
|
28
|
+
@start_path = project_path ? Pathname.new(project_path) : Pathname.pwd
|
|
29
|
+
@config_path, @project_root = find_config_file
|
|
30
|
+
@config = load_config
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def [](key)
|
|
34
|
+
@config[key.to_s]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def dig(*keys)
|
|
38
|
+
@config.dig(*keys.map(&:to_s))
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def version
|
|
42
|
+
@config["version"]
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def files
|
|
46
|
+
@config["files"] || {}
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def file_include_patterns
|
|
50
|
+
files["include"] || DEFAULTS.dig("files", "include") || []
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def file_exclude_patterns
|
|
54
|
+
files["exclude"] || DEFAULTS.dig("files", "exclude") || []
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def linter
|
|
58
|
+
@config["linter"] || {}
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def engine
|
|
62
|
+
@config["engine"] || {}
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def enabled_validators(overrides = {})
|
|
66
|
+
config = dig("engine", "validators") || {}
|
|
67
|
+
|
|
68
|
+
{
|
|
69
|
+
security: config.fetch("security", true),
|
|
70
|
+
nesting: config.fetch("nesting", true),
|
|
71
|
+
accessibility: config.fetch("accessibility", true),
|
|
72
|
+
}.merge(
|
|
73
|
+
overrides.to_h { |key, value| [key.to_sym, !!value] }
|
|
74
|
+
)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def formatter
|
|
78
|
+
@config["formatter"] || {}
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def include_patterns_for(tool)
|
|
82
|
+
tool_config = send(tool.to_s)
|
|
83
|
+
file_include_patterns + (tool_config["include"] || [])
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def exclude_patterns_for(tool)
|
|
87
|
+
tool_config = send(tool.to_s)
|
|
88
|
+
file_exclude_patterns + (tool_config["exclude"] || [])
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def linter_include_patterns
|
|
92
|
+
include_patterns_for(:linter)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def linter_exclude_patterns
|
|
96
|
+
exclude_patterns_for(:linter)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def formatter_include_patterns
|
|
100
|
+
include_patterns_for(:formatter)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def formatter_exclude_patterns
|
|
104
|
+
exclude_patterns_for(:formatter)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def enabled_for_path?(path, tool)
|
|
108
|
+
tool_config = send(tool.to_s)
|
|
109
|
+
tool_include = tool_config["include"] || []
|
|
110
|
+
tool_exclude = tool_config["exclude"] || []
|
|
111
|
+
|
|
112
|
+
if tool_include.any? && path_included?(path, tool_include)
|
|
113
|
+
return !path_excluded?(path, tool_exclude)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
exclude_patterns = exclude_patterns_for(tool)
|
|
117
|
+
|
|
118
|
+
!path_excluded?(path, exclude_patterns)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def linter_enabled_for_path?(path)
|
|
122
|
+
enabled_for_path?(path, :linter)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def formatter_enabled_for_path?(path)
|
|
126
|
+
enabled_for_path?(path, :formatter)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def path_excluded?(path, patterns)
|
|
130
|
+
patterns.any? { |pattern| File.fnmatch?(pattern, path, File::FNM_PATHNAME) }
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def path_included?(path, patterns)
|
|
134
|
+
patterns.any? { |pattern| File.fnmatch?(pattern, path, File::FNM_PATHNAME) }
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def find_files(search_path = nil)
|
|
138
|
+
search_path ||= @project_root || @start_path
|
|
139
|
+
expanded_path = File.expand_path(search_path.to_s)
|
|
140
|
+
|
|
141
|
+
all_files = file_include_patterns.flat_map do |pattern|
|
|
142
|
+
Dir[File.join(expanded_path, pattern)]
|
|
143
|
+
end.uniq
|
|
144
|
+
|
|
145
|
+
all_files.reject do |file|
|
|
146
|
+
relative = file.sub("#{expanded_path}/", "")
|
|
147
|
+
path_excluded?(relative, file_exclude_patterns)
|
|
148
|
+
end.sort
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def find_files_for_tool(tool, search_path = nil)
|
|
152
|
+
search_path ||= @project_root || @start_path
|
|
153
|
+
expanded_path = File.expand_path(search_path.to_s)
|
|
154
|
+
|
|
155
|
+
include_patterns = include_patterns_for(tool)
|
|
156
|
+
exclude_patterns = exclude_patterns_for(tool)
|
|
157
|
+
|
|
158
|
+
all_files = include_patterns.flat_map do |pattern|
|
|
159
|
+
Dir[File.join(expanded_path, pattern)]
|
|
160
|
+
end.uniq
|
|
161
|
+
|
|
162
|
+
all_files.reject do |file|
|
|
163
|
+
relative = file.sub("#{expanded_path}/", "")
|
|
164
|
+
path_excluded?(relative, exclude_patterns)
|
|
165
|
+
end.sort
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def find_files_for_linter(search_path = nil)
|
|
169
|
+
find_files_for_tool(:linter, search_path)
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def find_files_for_formatter(search_path = nil)
|
|
173
|
+
find_files_for_tool(:formatter, search_path)
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
class << self
|
|
177
|
+
def load(project_path = nil)
|
|
178
|
+
new(project_path)
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def default
|
|
182
|
+
@default ||= new
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def default_file_patterns
|
|
186
|
+
DEFAULTS.dig("files", "include") || []
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def default_exclude_patterns
|
|
190
|
+
DEFAULTS.dig("files", "exclude") || []
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
private
|
|
195
|
+
|
|
196
|
+
def find_config_file
|
|
197
|
+
search_path = @start_path
|
|
198
|
+
search_path = search_path.parent if search_path.file?
|
|
199
|
+
|
|
200
|
+
while search_path.to_s != "/"
|
|
201
|
+
CONFIG_FILENAMES.each do |filename|
|
|
202
|
+
config_file = search_path / filename
|
|
203
|
+
return [config_file, search_path] if config_file.exist?
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
return [nil, search_path] if project_root?(search_path)
|
|
207
|
+
|
|
208
|
+
search_path = search_path.parent
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
[nil, @start_path]
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
def project_root?(path)
|
|
215
|
+
PROJECT_INDICATORS.any? do |indicator|
|
|
216
|
+
if indicator.include?("*")
|
|
217
|
+
Dir.glob(path / indicator).any?
|
|
218
|
+
else
|
|
219
|
+
(path / indicator).exist?
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def load_config
|
|
225
|
+
return deep_merge(DEFAULTS, {}) unless @config_path&.exist?
|
|
226
|
+
|
|
227
|
+
begin
|
|
228
|
+
user_config = YAML.safe_load_file(@config_path, permitted_classes: [Symbol]) || {}
|
|
229
|
+
deep_merge(DEFAULTS, user_config)
|
|
230
|
+
rescue Psych::SyntaxError => e
|
|
231
|
+
warn "Warning: Invalid YAML in #{@config_path}: #{e.message}"
|
|
232
|
+
deep_merge(DEFAULTS, {})
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
def deep_merge(base, override, additive_keys: ["include", "exclude"])
|
|
237
|
+
base.merge(override) do |key, old_val, new_val|
|
|
238
|
+
if old_val.is_a?(Hash) && new_val.is_a?(Hash)
|
|
239
|
+
deep_merge(old_val, new_val, additive_keys: additive_keys)
|
|
240
|
+
elsif old_val.is_a?(Array) && new_val.is_a?(Array) && additive_keys.include?(key)
|
|
241
|
+
old_val + new_val
|
|
242
|
+
else
|
|
243
|
+
new_val
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
files:
|
|
2
|
+
include:
|
|
3
|
+
- "**/*.herb"
|
|
4
|
+
- "**/*.html.erb"
|
|
5
|
+
- "**/*.html.herb"
|
|
6
|
+
- "**/*.html"
|
|
7
|
+
- "**/*.html+*.erb"
|
|
8
|
+
- "**/*.rhtml"
|
|
9
|
+
- "**/*.turbo_stream.erb"
|
|
10
|
+
|
|
11
|
+
exclude:
|
|
12
|
+
- "coverage/**/*"
|
|
13
|
+
- "log/**/*"
|
|
14
|
+
- "node_modules/**/*"
|
|
15
|
+
- "storage/**/*"
|
|
16
|
+
- "tmp/**/*"
|
|
17
|
+
- "vendor/**/*"
|
|
18
|
+
|
|
19
|
+
engine:
|
|
20
|
+
validators:
|
|
21
|
+
security: true
|
|
22
|
+
nesting: true
|
|
23
|
+
accessibility: true
|
|
24
|
+
|
|
25
|
+
linter:
|
|
26
|
+
enabled: true
|
|
27
|
+
rules: {}
|
|
28
|
+
|
|
29
|
+
formatter:
|
|
30
|
+
enabled: false
|
|
31
|
+
indentWidth: 2
|
|
32
|
+
maxLineLength: 80
|
data/lib/herb/engine/compiler.rb
CHANGED
|
@@ -22,7 +22,7 @@ module Herb
|
|
|
22
22
|
def generate_output
|
|
23
23
|
optimized_tokens = optimize_tokens(@tokens)
|
|
24
24
|
|
|
25
|
-
optimized_tokens.each do |type, value, context|
|
|
25
|
+
optimized_tokens.each do |type, value, context, escaped|
|
|
26
26
|
case type
|
|
27
27
|
when :text
|
|
28
28
|
@engine.send(:add_text, value)
|
|
@@ -48,6 +48,8 @@ module Herb
|
|
|
48
48
|
when :expr_block_escaped
|
|
49
49
|
indicator = @escape ? "=" : "=="
|
|
50
50
|
@engine.send(:add_expression_block, indicator, value)
|
|
51
|
+
when :expr_block_end
|
|
52
|
+
@engine.send(:add_expression_block_end, value, escaped: escaped)
|
|
51
53
|
end
|
|
52
54
|
end
|
|
53
55
|
end
|
|
@@ -71,7 +73,27 @@ module Herb
|
|
|
71
73
|
visit_all(node.body)
|
|
72
74
|
visit(node.close_tag)
|
|
73
75
|
|
|
74
|
-
pop_context if
|
|
76
|
+
pop_context if ["script", "style"].include?(tag_name)
|
|
77
|
+
|
|
78
|
+
@element_stack.pop if tag_name
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def visit_html_conditional_element_node(node)
|
|
82
|
+
tag_name = node.tag_name&.value&.downcase
|
|
83
|
+
|
|
84
|
+
@element_stack.push(tag_name) if tag_name
|
|
85
|
+
|
|
86
|
+
if tag_name == "script"
|
|
87
|
+
push_context(:script_content)
|
|
88
|
+
elsif tag_name == "style"
|
|
89
|
+
push_context(:style_content)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
visit(node.open_conditional)
|
|
93
|
+
visit_all(node.body)
|
|
94
|
+
visit(node.close_conditional)
|
|
95
|
+
|
|
96
|
+
pop_context if ["script", "style"].include?(tag_name)
|
|
75
97
|
|
|
76
98
|
@element_stack.pop if tag_name
|
|
77
99
|
end
|
|
@@ -125,6 +147,10 @@ module Herb
|
|
|
125
147
|
add_text(node.tag_closing&.value)
|
|
126
148
|
end
|
|
127
149
|
|
|
150
|
+
def visit_html_omitted_close_tag_node(node)
|
|
151
|
+
# no-op
|
|
152
|
+
end
|
|
153
|
+
|
|
128
154
|
def visit_html_text_node(node)
|
|
129
155
|
add_text(node.content)
|
|
130
156
|
end
|
|
@@ -168,7 +194,9 @@ module Herb
|
|
|
168
194
|
end
|
|
169
195
|
|
|
170
196
|
def visit_erb_control_node(node, &_block)
|
|
171
|
-
|
|
197
|
+
if node.content
|
|
198
|
+
apply_trim(node, node.content.value.strip)
|
|
199
|
+
end
|
|
172
200
|
|
|
173
201
|
yield if block_given?
|
|
174
202
|
end
|
|
@@ -257,7 +285,7 @@ module Herb
|
|
|
257
285
|
end
|
|
258
286
|
|
|
259
287
|
visit_all(node.body)
|
|
260
|
-
|
|
288
|
+
visit_erb_block_end_node(node.end_node, escaped: should_escape)
|
|
261
289
|
else
|
|
262
290
|
visit_erb_control_node(node) do
|
|
263
291
|
visit_all(node.body)
|
|
@@ -266,6 +294,24 @@ module Herb
|
|
|
266
294
|
end
|
|
267
295
|
end
|
|
268
296
|
|
|
297
|
+
def visit_erb_block_end_node(node, escaped: false)
|
|
298
|
+
has_left_trim = node.tag_opening.value.start_with?("<%-")
|
|
299
|
+
|
|
300
|
+
remove_trailing_whitespace_from_last_token! if has_left_trim
|
|
301
|
+
|
|
302
|
+
code = node.content.value.strip
|
|
303
|
+
|
|
304
|
+
if at_line_start?
|
|
305
|
+
lspace = extract_and_remove_lspace!
|
|
306
|
+
rspace = " \n"
|
|
307
|
+
|
|
308
|
+
@tokens << [:expr_block_end, "#{lspace}#{code}#{rspace}", current_context, escaped]
|
|
309
|
+
@trim_next_whitespace = true
|
|
310
|
+
else
|
|
311
|
+
@tokens << [:expr_block_end, code, current_context, escaped]
|
|
312
|
+
end
|
|
313
|
+
end
|
|
314
|
+
|
|
269
315
|
def visit_erb_control_with_parts(node, *parts)
|
|
270
316
|
visit_erb_control_node(node) do
|
|
271
317
|
parts.each do |part|
|
|
@@ -297,18 +343,20 @@ module Herb
|
|
|
297
343
|
end
|
|
298
344
|
|
|
299
345
|
def add_context_aware_expression(code, context)
|
|
346
|
+
closing = code.include?("#") ? "\n))" : "))"
|
|
347
|
+
|
|
300
348
|
case context
|
|
301
349
|
when :attribute_value
|
|
302
350
|
@engine.send(:with_buffer) {
|
|
303
|
-
@engine.src << " << #{@attrfunc}((" << code <<
|
|
351
|
+
@engine.src << " << #{@attrfunc}((" << code << closing
|
|
304
352
|
}
|
|
305
353
|
when :script_content
|
|
306
354
|
@engine.send(:with_buffer) {
|
|
307
|
-
@engine.src << " << #{@jsfunc}((" << code <<
|
|
355
|
+
@engine.src << " << #{@jsfunc}((" << code << closing
|
|
308
356
|
}
|
|
309
357
|
when :style_content
|
|
310
358
|
@engine.send(:with_buffer) {
|
|
311
|
-
@engine.src << " << #{@cssfunc}((" << code <<
|
|
359
|
+
@engine.src << " << #{@cssfunc}((" << code << closing
|
|
312
360
|
}
|
|
313
361
|
else
|
|
314
362
|
@engine.send(:add_expression_result_escaped, code)
|
|
@@ -318,13 +366,22 @@ module Herb
|
|
|
318
366
|
def process_erb_tag(node, skip_comment_check: false)
|
|
319
367
|
opening = node.tag_opening.value
|
|
320
368
|
|
|
321
|
-
|
|
369
|
+
if !skip_comment_check && erb_comment?(opening)
|
|
370
|
+
has_left_trim = opening.start_with?("<%-")
|
|
371
|
+
remove_trailing_whitespace_from_last_token! if has_left_trim
|
|
372
|
+
|
|
373
|
+
if at_line_start?
|
|
374
|
+
extract_and_remove_lspace!
|
|
375
|
+
@trim_next_whitespace = true
|
|
376
|
+
end
|
|
377
|
+
return
|
|
378
|
+
end
|
|
322
379
|
return if erb_graphql?(opening)
|
|
323
380
|
|
|
324
381
|
code = node.content.value.strip
|
|
325
382
|
|
|
326
383
|
if erb_output?(opening)
|
|
327
|
-
process_erb_output(opening, code)
|
|
384
|
+
process_erb_output(node, opening, code)
|
|
328
385
|
else
|
|
329
386
|
apply_trim(node, code)
|
|
330
387
|
end
|
|
@@ -368,7 +425,7 @@ module Herb
|
|
|
368
425
|
current_text = ""
|
|
369
426
|
current_context = nil
|
|
370
427
|
|
|
371
|
-
compacted.each do |type, value, context|
|
|
428
|
+
compacted.each do |type, value, context, escaped|
|
|
372
429
|
if type == :text
|
|
373
430
|
current_text += value
|
|
374
431
|
current_context ||= context
|
|
@@ -380,7 +437,7 @@ module Herb
|
|
|
380
437
|
current_context = nil
|
|
381
438
|
end
|
|
382
439
|
|
|
383
|
-
optimized << [type, value, context]
|
|
440
|
+
optimized << [type, value, context, escaped]
|
|
384
441
|
end
|
|
385
442
|
end
|
|
386
443
|
|
|
@@ -439,9 +496,11 @@ module Herb
|
|
|
439
496
|
search_index >= 0 ? tokens[search_index] : nil
|
|
440
497
|
end
|
|
441
498
|
|
|
442
|
-
def process_erb_output(opening, code)
|
|
499
|
+
def process_erb_output(node, opening, code)
|
|
500
|
+
has_right_trim = node.tag_closing&.value == "-%>"
|
|
443
501
|
should_escape = should_escape_output?(opening)
|
|
444
502
|
add_expression_with_escaping(code, should_escape)
|
|
503
|
+
@trim_next_whitespace = true if has_right_trim
|
|
445
504
|
end
|
|
446
505
|
|
|
447
506
|
def should_escape_output?(opening)
|
|
@@ -462,10 +521,20 @@ module Herb
|
|
|
462
521
|
@tokens.last[0] != :text ||
|
|
463
522
|
@tokens.last[1].empty? ||
|
|
464
523
|
@tokens.last[1].end_with?("\n") ||
|
|
465
|
-
@tokens.last[1] =~ /\A[ \t]+\z/ ||
|
|
524
|
+
(@tokens.last[1] =~ /\A[ \t]+\z/ && preceding_token_ends_with_newline?) ||
|
|
466
525
|
@tokens.last[1] =~ /\n[ \t]+\z/
|
|
467
526
|
end
|
|
468
527
|
|
|
528
|
+
def preceding_token_ends_with_newline?
|
|
529
|
+
return true unless @tokens.length >= 2
|
|
530
|
+
|
|
531
|
+
preceding = @tokens[-2]
|
|
532
|
+
return false if [:expr, :expr_escaped, :expr_block, :expr_block_escaped, :expr_block_end].include?(preceding[0])
|
|
533
|
+
return true unless preceding[0] == :text
|
|
534
|
+
|
|
535
|
+
preceding[1].end_with?("\n")
|
|
536
|
+
end
|
|
537
|
+
|
|
469
538
|
def extract_lspace
|
|
470
539
|
return "" unless @tokens.last && @tokens.last[0] == :text
|
|
471
540
|
|
|
@@ -499,7 +568,7 @@ module Herb
|
|
|
499
568
|
|
|
500
569
|
if at_line_start?
|
|
501
570
|
lspace = extract_and_remove_lspace!
|
|
502
|
-
rspace = " \n"
|
|
571
|
+
rspace = Herb::Engine.heredoc?(code) ? "\n" : " \n"
|
|
503
572
|
|
|
504
573
|
@tokens << [:code, "#{lspace}#{code}#{rspace}", current_context]
|
|
505
574
|
@trim_next_whitespace = true
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
# typed: false
|
|
2
3
|
|
|
3
4
|
module Herb
|
|
4
5
|
class Engine
|
|
@@ -31,6 +32,7 @@ module Herb
|
|
|
31
32
|
@in_html_comment = false
|
|
32
33
|
@in_html_doctype = false
|
|
33
34
|
@erb_nodes_to_wrap = [] #: Array[Herb::AST::ERBContentNode]
|
|
35
|
+
@top_level_elements = [] #: Array[Herb::AST::HTMLElementNode]
|
|
34
36
|
end
|
|
35
37
|
|
|
36
38
|
def visit_document_node(node)
|
|
@@ -149,8 +151,6 @@ module Herb
|
|
|
149
151
|
end
|
|
150
152
|
|
|
151
153
|
def find_top_level_elements(document_node)
|
|
152
|
-
@top_level_elements = [] #: Array[Herb::AST::HTMLElementNode]
|
|
153
|
-
|
|
154
154
|
document_node.children.each do |child|
|
|
155
155
|
@top_level_elements << child if child.is_a?(Herb::AST::HTMLElementNode)
|
|
156
156
|
end
|
|
@@ -178,7 +178,7 @@ module Herb
|
|
|
178
178
|
|
|
179
179
|
debug_attributes = [
|
|
180
180
|
create_debug_attribute("data-herb-debug-outline-type", view_type),
|
|
181
|
-
create_debug_attribute("data-herb-debug-file-name",
|
|
181
|
+
create_debug_attribute("data-herb-debug-file-name", component_display_name),
|
|
182
182
|
create_debug_attribute("data-herb-debug-file-relative-path", @relative_file_path || "unknown"),
|
|
183
183
|
create_debug_attribute("data-herb-debug-file-full-path", @filename&.to_s || "unknown")
|
|
184
184
|
]
|
|
@@ -231,7 +231,7 @@ module Herb
|
|
|
231
231
|
debug_attributes = [
|
|
232
232
|
create_debug_attribute("data-herb-debug-outline-type", outline_type),
|
|
233
233
|
create_debug_attribute("data-herb-debug-erb", escaped_erb),
|
|
234
|
-
create_debug_attribute("data-herb-debug-file-name",
|
|
234
|
+
create_debug_attribute("data-herb-debug-file-name", component_display_name),
|
|
235
235
|
create_debug_attribute("data-herb-debug-file-relative-path", @relative_file_path || "unknown"),
|
|
236
236
|
create_debug_attribute("data-herb-debug-file-full-path", @filename&.to_s || "unknown"),
|
|
237
237
|
create_debug_attribute("data-herb-debug-inserted", "true")
|
|
@@ -288,8 +288,43 @@ module Herb
|
|
|
288
288
|
def component?
|
|
289
289
|
return false unless @filename
|
|
290
290
|
|
|
291
|
+
@filename.to_s.match?(%r{(^|/)app/components/})
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
def sidecar_component?
|
|
295
|
+
return false unless component?
|
|
296
|
+
return false unless @filename
|
|
297
|
+
|
|
298
|
+
@filename.basename.to_s.match?(/\Acomponent\.(html\.erb|html\.herb|erb|herb)\z/)
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
def component_display_name
|
|
302
|
+
return @filename&.basename&.to_s || "unknown" unless @filename
|
|
303
|
+
|
|
304
|
+
basename = @filename.basename.to_s
|
|
291
305
|
path = @filename.to_s
|
|
292
|
-
|
|
306
|
+
|
|
307
|
+
if sidecar_component? && (match = path.match(%r{/components/(.+)/component\.[^/]+\z}))
|
|
308
|
+
return match[1].split("/").map { |s| classify(s) }.join("::")
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
if component?
|
|
312
|
+
path_without_ext = path.sub(/\.(?:html\.erb|html\.herb|erb|herb)\z/, "")
|
|
313
|
+
|
|
314
|
+
if (match = path_without_ext.match(%r{/components/(.+)\z}))
|
|
315
|
+
return match[1].split("/").map { |s| classify(s) }.join("::")
|
|
316
|
+
end
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
basename
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
def classify(name)
|
|
323
|
+
if name.respond_to?(:camelize)
|
|
324
|
+
name.camelize
|
|
325
|
+
else
|
|
326
|
+
name.split(/[_-]/).map(&:capitalize).join
|
|
327
|
+
end
|
|
293
328
|
end
|
|
294
329
|
|
|
295
330
|
def in_head_context?
|
|
@@ -304,7 +339,9 @@ module Herb
|
|
|
304
339
|
excluded_tags = ["script", "style", "head", "textarea", "pre", "svg", "math"]
|
|
305
340
|
return true if excluded_tags.any? { |tag| @element_stack.include?(tag) }
|
|
306
341
|
|
|
307
|
-
|
|
342
|
+
if @erb_block_stack.any? { |node| javascript_tag?(node.content.value.strip) || include_debug_disable_comment?(node.content.value.strip) }
|
|
343
|
+
return true
|
|
344
|
+
end
|
|
308
345
|
|
|
309
346
|
false
|
|
310
347
|
end
|
|
@@ -355,6 +392,14 @@ module Herb
|
|
|
355
392
|
|
|
356
393
|
false
|
|
357
394
|
end
|
|
395
|
+
|
|
396
|
+
def include_debug_disable_comment?(code)
|
|
397
|
+
cleaned_code = code.strip.gsub(/\s+/, " ")
|
|
398
|
+
|
|
399
|
+
return true if cleaned_code.match?(/#\s*herb:debug\sdisable\s*$/)
|
|
400
|
+
|
|
401
|
+
false
|
|
402
|
+
end
|
|
358
403
|
end
|
|
359
404
|
end
|
|
360
405
|
end
|
|
@@ -214,12 +214,9 @@ module Herb
|
|
|
214
214
|
output << " Details: #{error.error_message}\n"
|
|
215
215
|
output << " Suggestion: Check your Ruby syntax inside the ERB tag\n"
|
|
216
216
|
|
|
217
|
-
when Herb::Errors::
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
output << " Closing quote: #{error.closing_quote.value}\n"
|
|
221
|
-
output << " Suggestion: Use matching quotes for attribute values\n"
|
|
222
|
-
end
|
|
217
|
+
when Herb::Errors::MissingAttributeValueError
|
|
218
|
+
output << " Attribute: #{error.attribute_name}\n"
|
|
219
|
+
output << " Suggestion: Add a value after the equals sign or remove the equals sign\n"
|
|
223
220
|
end
|
|
224
221
|
|
|
225
222
|
output
|
|
@@ -229,7 +226,8 @@ module Herb
|
|
|
229
226
|
case error
|
|
230
227
|
when Herb::Errors::MissingClosingTagError,
|
|
231
228
|
Herb::Errors::TagNamesMismatchError,
|
|
232
|
-
Herb::Errors::UnclosedElementError
|
|
229
|
+
Herb::Errors::UnclosedElementError,
|
|
230
|
+
Herb::Errors::MissingAttributeValueError
|
|
233
231
|
true
|
|
234
232
|
else
|
|
235
233
|
false
|
|
@@ -244,6 +242,8 @@ module Herb
|
|
|
244
242
|
"← Tag mismatch"
|
|
245
243
|
when Herb::Errors::UnclosedElementError
|
|
246
244
|
"← Unclosed element"
|
|
245
|
+
when Herb::Errors::MissingAttributeValueError
|
|
246
|
+
"← Missing attribute value"
|
|
247
247
|
else
|
|
248
248
|
""
|
|
249
249
|
end
|
|
@@ -419,8 +419,12 @@ module Herb
|
|
|
419
419
|
end
|
|
420
420
|
when Herb::Errors::RubyParseError
|
|
421
421
|
"Check your Ruby syntax inside the ERB tag"
|
|
422
|
-
when Herb::Errors::
|
|
423
|
-
|
|
422
|
+
when Herb::Errors::MissingAttributeValueError
|
|
423
|
+
if error.attribute_name
|
|
424
|
+
"Add a value after the equals sign for '#{error.attribute_name}' or remove the equals sign"
|
|
425
|
+
else
|
|
426
|
+
"Add a value after the equals sign or remove the equals sign"
|
|
427
|
+
end
|
|
424
428
|
end
|
|
425
429
|
end
|
|
426
430
|
end
|
|
@@ -9,12 +9,12 @@ module Herb
|
|
|
9
9
|
Herb::Errors::UnexpectedTokenError,
|
|
10
10
|
Herb::Errors::UnexpectedError,
|
|
11
11
|
Herb::Errors::RubyParseError,
|
|
12
|
-
Herb::Errors::QuotesMismatchError,
|
|
13
12
|
Herb::Errors::TagNamesMismatchError,
|
|
14
13
|
Herb::Errors::VoidElementClosingTagError,
|
|
15
14
|
Herb::Errors::UnclosedElementError,
|
|
16
15
|
Herb::Errors::MissingClosingTagError,
|
|
17
|
-
Herb::Errors::MissingOpeningTagError
|
|
16
|
+
Herb::Errors::MissingOpeningTagError,
|
|
17
|
+
Herb::Errors::MissingAttributeValueError
|
|
18
18
|
].freeze
|
|
19
19
|
|
|
20
20
|
def initialize(source, errors, filename: nil)
|
|
@@ -729,8 +729,12 @@ module Herb
|
|
|
729
729
|
end
|
|
730
730
|
when Herb::Errors::RubyParseError
|
|
731
731
|
"Fix Ruby syntax: Check your Ruby syntax inside the ERB tag"
|
|
732
|
-
when Herb::Errors::
|
|
733
|
-
|
|
732
|
+
when Herb::Errors::MissingAttributeValueError
|
|
733
|
+
if error.respond_to?(:attribute_name) && error.attribute_name
|
|
734
|
+
"Add attribute value: Add a value after the equals sign for '#{error.attribute_name}' or remove the equals sign"
|
|
735
|
+
else
|
|
736
|
+
"Add attribute value: Add a value after the equals sign or remove the equals sign"
|
|
737
|
+
end
|
|
734
738
|
else
|
|
735
739
|
message = error.respond_to?(:message) ? error.message : error.to_s
|
|
736
740
|
"Fix error: #{message}"
|
|
@@ -747,10 +751,10 @@ module Herb
|
|
|
747
751
|
"← Unclosed element"
|
|
748
752
|
when Herb::Errors::VoidElementClosingTagError
|
|
749
753
|
"← Void element cannot be closed"
|
|
750
|
-
when Herb::Errors::QuotesMismatchError
|
|
751
|
-
"← Quote mismatch"
|
|
752
754
|
when Herb::Errors::RubyParseError
|
|
753
755
|
"← Ruby syntax error"
|
|
756
|
+
when Herb::Errors::MissingAttributeValueError
|
|
757
|
+
"← Missing attribute value"
|
|
754
758
|
end
|
|
755
759
|
end
|
|
756
760
|
|