herb 0.9.1 → 0.9.2
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/lib/herb/engine/compiler.rb +17 -41
- data/lib/herb/engine.rb +21 -0
- data/lib/herb/version.rb +1 -1
- data/sig/herb/engine/compiler.rbs +4 -2
- data/sig/herb/engine.rbs +4 -0
- data/src/analyze/action_view/attribute_extraction_helpers.c +9 -0
- data/src/analyze/action_view/javascript_include_tag.c +92 -0
- data/src/analyze/action_view/javascript_tag.c +55 -0
- data/src/analyze/action_view/registry.c +6 -2
- data/src/analyze/action_view/tag_helper_node_builders.c +16 -3
- data/src/analyze/action_view/tag_helpers.c +254 -1
- data/src/html_util.c +50 -0
- data/src/include/html_util.h +1 -0
- data/src/include/version.h +1 -1
- data/src/util/hb_arena.c +3 -7
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3a1a61c49f642abaffb189b7ff947009cc5c2e808d4eaa5e8103f0086b6b6b78
|
|
4
|
+
data.tar.gz: d26cdc1d962413d92cc6a2800f4c183777e690aa95c7ab918b5ef4f6591e9278
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: df024e2b48dadab5af063c54e614948dc46efed4aed24f813476ca491164db7b863692a6617f580bf2e9523701a2f76a8c392935a6fb40cb4b269eece5fb1737
|
|
7
|
+
data.tar.gz: cfb04158d09179dc2a5d08b96a3300b1748ba1ec66afb71c5004e1eaabf1c4cfcfc8b1296d0fb613152eafd0927c400df43622463d5850b341ddfcb6aa03fae4
|
data/lib/herb/engine/compiler.rb
CHANGED
|
@@ -10,9 +10,6 @@ module Herb
|
|
|
10
10
|
|
|
11
11
|
@engine = engine
|
|
12
12
|
@escape = options.fetch(:escape) { options.fetch(:escape_html, false) }
|
|
13
|
-
@attrfunc = options.fetch(:attrfunc, @escape ? "__herb.attr" : "::Herb::Engine.attr")
|
|
14
|
-
@jsfunc = options.fetch(:jsfunc, @escape ? "__herb.js" : "::Herb::Engine.js")
|
|
15
|
-
@cssfunc = options.fetch(:cssfunc, @escape ? "__herb.css" : "::Herb::Engine.css")
|
|
16
13
|
@tokens = [] #: Array[untyped]
|
|
17
14
|
@element_stack = [] #: Array[String]
|
|
18
15
|
@context_stack = [:html_content]
|
|
@@ -28,26 +25,16 @@ module Herb
|
|
|
28
25
|
@engine.send(:add_text, value)
|
|
29
26
|
when :code
|
|
30
27
|
@engine.send(:add_code, value)
|
|
31
|
-
when :expr
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
@engine.send(:add_expression, indicator, value)
|
|
37
|
-
end
|
|
38
|
-
when :expr_escaped
|
|
39
|
-
if [:attribute_value, :script_content, :style_content].include?(context)
|
|
40
|
-
add_context_aware_expression(value, context)
|
|
28
|
+
when :expr, :expr_escaped
|
|
29
|
+
indicator = indicator_for(type)
|
|
30
|
+
|
|
31
|
+
if context_aware_context?(context)
|
|
32
|
+
@engine.send(:add_context_aware_expression, indicator, value, context)
|
|
41
33
|
else
|
|
42
|
-
indicator = @escape ? "=" : "=="
|
|
43
34
|
@engine.send(:add_expression, indicator, value)
|
|
44
35
|
end
|
|
45
|
-
when :expr_block
|
|
46
|
-
|
|
47
|
-
@engine.send(:add_expression_block, indicator, value)
|
|
48
|
-
when :expr_block_escaped
|
|
49
|
-
indicator = @escape ? "=" : "=="
|
|
50
|
-
@engine.send(:add_expression_block, indicator, value)
|
|
36
|
+
when :expr_block, :expr_block_escaped
|
|
37
|
+
@engine.send(:add_expression_block, indicator_for(type), value)
|
|
51
38
|
when :expr_block_end
|
|
52
39
|
@engine.send(:add_expression_block_end, value, escaped: escaped)
|
|
53
40
|
end
|
|
@@ -342,27 +329,6 @@ module Herb
|
|
|
342
329
|
@context_stack.pop
|
|
343
330
|
end
|
|
344
331
|
|
|
345
|
-
def add_context_aware_expression(code, context)
|
|
346
|
-
closing = code.include?("#") ? "\n))" : "))"
|
|
347
|
-
|
|
348
|
-
case context
|
|
349
|
-
when :attribute_value
|
|
350
|
-
@engine.send(:with_buffer) {
|
|
351
|
-
@engine.src << " << #{@attrfunc}((" << code << closing
|
|
352
|
-
}
|
|
353
|
-
when :script_content
|
|
354
|
-
@engine.send(:with_buffer) {
|
|
355
|
-
@engine.src << " << #{@jsfunc}((" << code << closing
|
|
356
|
-
}
|
|
357
|
-
when :style_content
|
|
358
|
-
@engine.send(:with_buffer) {
|
|
359
|
-
@engine.src << " << #{@cssfunc}((" << code << closing
|
|
360
|
-
}
|
|
361
|
-
else
|
|
362
|
-
@engine.send(:add_expression_result_escaped, code)
|
|
363
|
-
end
|
|
364
|
-
end
|
|
365
|
-
|
|
366
332
|
def process_erb_tag(node, skip_comment_check: false)
|
|
367
333
|
opening = node.tag_opening.value
|
|
368
334
|
|
|
@@ -503,6 +469,16 @@ module Herb
|
|
|
503
469
|
@trim_next_whitespace = true if has_right_trim
|
|
504
470
|
end
|
|
505
471
|
|
|
472
|
+
def indicator_for(type)
|
|
473
|
+
escaped = [:expr_escaped, :expr_block_escaped].include?(type)
|
|
474
|
+
|
|
475
|
+
escaped ^ @escape ? "==" : "="
|
|
476
|
+
end
|
|
477
|
+
|
|
478
|
+
def context_aware_context?(context)
|
|
479
|
+
[:attribute_value, :script_content, :style_content].include?(context)
|
|
480
|
+
end
|
|
481
|
+
|
|
506
482
|
def should_escape_output?(opening)
|
|
507
483
|
is_double_equals = opening == "<%=="
|
|
508
484
|
is_double_equals ? !@escape : @escape
|
data/lib/herb/engine.rb
CHANGED
|
@@ -55,6 +55,9 @@ module Herb
|
|
|
55
55
|
@bufvar = properties[:bufvar] || properties[:outvar] || "_buf"
|
|
56
56
|
@escape = properties.fetch(:escape) { properties.fetch(:escape_html, false) }
|
|
57
57
|
@escapefunc = properties.fetch(:escapefunc, @escape ? "__herb.h" : "::Herb::Engine.h")
|
|
58
|
+
@attrfunc = properties.fetch(:attrfunc, @escape ? "__herb.attr" : "::Herb::Engine.attr")
|
|
59
|
+
@jsfunc = properties.fetch(:jsfunc, @escape ? "__herb.js" : "::Herb::Engine.js")
|
|
60
|
+
@cssfunc = properties.fetch(:cssfunc, @escape ? "__herb.css" : "::Herb::Engine.css")
|
|
58
61
|
@src = properties[:src] || String.new
|
|
59
62
|
@chain_appends = properties[:chain_appends]
|
|
60
63
|
@buffer_on_stack = false
|
|
@@ -244,6 +247,24 @@ module Herb
|
|
|
244
247
|
end
|
|
245
248
|
end
|
|
246
249
|
|
|
250
|
+
def add_context_aware_expression(indicator, code, context)
|
|
251
|
+
escapefunc = context_escape_function(context)
|
|
252
|
+
|
|
253
|
+
if escapefunc.nil?
|
|
254
|
+
add_expression(indicator, code)
|
|
255
|
+
else
|
|
256
|
+
with_buffer { @src << " << #{escapefunc}((" << code << trailing_newline(code) << "))" }
|
|
257
|
+
end
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
def context_escape_function(context)
|
|
261
|
+
case context
|
|
262
|
+
when :attribute_value then @attrfunc
|
|
263
|
+
when :script_content then @jsfunc
|
|
264
|
+
when :style_content then @cssfunc
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
|
|
247
268
|
def add_expression_result(code)
|
|
248
269
|
with_buffer {
|
|
249
270
|
@src << " << (" << code << trailing_newline(code) << ").to_s"
|
data/lib/herb/version.rb
CHANGED
|
@@ -89,8 +89,6 @@ module Herb
|
|
|
89
89
|
|
|
90
90
|
def pop_context: () -> untyped
|
|
91
91
|
|
|
92
|
-
def add_context_aware_expression: (untyped code, untyped context) -> untyped
|
|
93
|
-
|
|
94
92
|
def process_erb_tag: (untyped node, ?skip_comment_check: untyped) -> untyped
|
|
95
93
|
|
|
96
94
|
def add_text: (untyped text) -> untyped
|
|
@@ -119,6 +117,10 @@ module Herb
|
|
|
119
117
|
|
|
120
118
|
def process_erb_output: (untyped node, untyped opening, untyped code) -> untyped
|
|
121
119
|
|
|
120
|
+
def indicator_for: (untyped type) -> untyped
|
|
121
|
+
|
|
122
|
+
def context_aware_context?: (untyped context) -> untyped
|
|
123
|
+
|
|
122
124
|
def should_escape_output?: (untyped opening) -> untyped
|
|
123
125
|
|
|
124
126
|
def add_expression_with_escaping: (untyped code, untyped should_escape) -> untyped
|
data/sig/herb/engine.rbs
CHANGED
|
@@ -55,6 +55,10 @@ module Herb
|
|
|
55
55
|
|
|
56
56
|
def add_expression: (untyped indicator, untyped code) -> untyped
|
|
57
57
|
|
|
58
|
+
def add_context_aware_expression: (untyped indicator, untyped code, untyped context) -> untyped
|
|
59
|
+
|
|
60
|
+
def context_escape_function: (untyped context) -> untyped
|
|
61
|
+
|
|
58
62
|
def add_expression_result: (untyped code) -> untyped
|
|
59
63
|
|
|
60
64
|
def add_expression_result_escaped: (untyped code) -> untyped
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#include "../../include/analyze/action_view/attribute_extraction_helpers.h"
|
|
2
2
|
#include "../../include/analyze/action_view/tag_helper_node_builders.h"
|
|
3
|
+
#include "../../include/html_util.h"
|
|
3
4
|
#include "../../include/util.h"
|
|
4
5
|
#include "../../include/util/hb_allocator.h"
|
|
5
6
|
#include "../../include/util/hb_array.h"
|
|
@@ -58,6 +59,14 @@ static AST_HTML_ATTRIBUTE_NODE_T* create_attribute_from_value(
|
|
|
58
59
|
hb_allocator_dealloc(allocator, value_string);
|
|
59
60
|
|
|
60
61
|
return attribute;
|
|
62
|
+
} else if (value_node->type == PM_TRUE_NODE) {
|
|
63
|
+
if (is_boolean_attribute(hb_string((char*) name_string))) {
|
|
64
|
+
return create_html_attribute_node(name_string, NULL, start_position, end_position, allocator);
|
|
65
|
+
}
|
|
66
|
+
return create_html_attribute_node(name_string, "true", start_position, end_position, allocator);
|
|
67
|
+
} else if (value_node->type == PM_FALSE_NODE) {
|
|
68
|
+
if (is_boolean_attribute(hb_string((char*) name_string))) { return NULL; }
|
|
69
|
+
return create_html_attribute_node(name_string, "false", start_position, end_position, allocator);
|
|
61
70
|
} else if (value_node->type == PM_INTERPOLATED_STRING_NODE) {
|
|
62
71
|
return create_html_attribute_with_interpolated_value(
|
|
63
72
|
name_string,
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
#include "../../include/analyze/action_view/tag_helper_handler.h"
|
|
2
|
+
|
|
3
|
+
#include <prism.h>
|
|
4
|
+
#include <stdbool.h>
|
|
5
|
+
#include <stdlib.h>
|
|
6
|
+
#include <string.h>
|
|
7
|
+
|
|
8
|
+
bool detect_javascript_include_tag(pm_call_node_t* call_node, pm_parser_t* parser) {
|
|
9
|
+
if (!call_node || !call_node->name) { return false; }
|
|
10
|
+
|
|
11
|
+
pm_constant_t* constant = pm_constant_pool_id_to_constant(&parser->constant_pool, call_node->name);
|
|
12
|
+
return constant && constant->length == 22
|
|
13
|
+
&& strncmp((const char*) constant->start, "javascript_include_tag", 22) == 0;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
char* extract_javascript_include_tag_name(pm_call_node_t* call_node, pm_parser_t* parser, hb_allocator_T* allocator) {
|
|
17
|
+
(void) call_node;
|
|
18
|
+
(void) parser;
|
|
19
|
+
|
|
20
|
+
return hb_allocator_strdup(allocator, "script");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
char* extract_javascript_include_tag_content(
|
|
24
|
+
pm_call_node_t* call_node,
|
|
25
|
+
pm_parser_t* parser,
|
|
26
|
+
hb_allocator_T* allocator
|
|
27
|
+
) {
|
|
28
|
+
(void) call_node;
|
|
29
|
+
(void) parser;
|
|
30
|
+
(void) allocator;
|
|
31
|
+
|
|
32
|
+
return NULL;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
char* extract_javascript_include_tag_src(pm_call_node_t* call_node, pm_parser_t* parser, hb_allocator_T* allocator) {
|
|
36
|
+
(void) parser;
|
|
37
|
+
|
|
38
|
+
if (!call_node || !call_node->arguments) { return NULL; }
|
|
39
|
+
|
|
40
|
+
pm_arguments_node_t* arguments = call_node->arguments;
|
|
41
|
+
if (!arguments->arguments.size) { return NULL; }
|
|
42
|
+
|
|
43
|
+
pm_node_t* first_argument = arguments->arguments.nodes[0];
|
|
44
|
+
|
|
45
|
+
if (first_argument->type == PM_STRING_NODE) {
|
|
46
|
+
pm_string_node_t* string_node = (pm_string_node_t*) first_argument;
|
|
47
|
+
size_t length = pm_string_length(&string_node->unescaped);
|
|
48
|
+
|
|
49
|
+
return hb_allocator_strndup(allocator, (const char*) pm_string_source(&string_node->unescaped), length);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
size_t source_length = first_argument->location.end - first_argument->location.start;
|
|
53
|
+
return hb_allocator_strndup(allocator, (const char*) first_argument->location.start, source_length);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
bool javascript_include_tag_source_is_url(const char* source, size_t length) {
|
|
57
|
+
if (!source || length == 0) { return false; }
|
|
58
|
+
|
|
59
|
+
if (length >= 2 && source[0] == '/' && source[1] == '/') { return true; }
|
|
60
|
+
if (strstr(source, "://") != NULL) { return true; }
|
|
61
|
+
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
char* wrap_in_javascript_path(const char* source, size_t source_length, hb_allocator_T* allocator) {
|
|
66
|
+
const char* prefix = "javascript_path(";
|
|
67
|
+
const char* suffix = ")";
|
|
68
|
+
size_t prefix_length = strlen(prefix);
|
|
69
|
+
size_t suffix_length = strlen(suffix);
|
|
70
|
+
size_t total_length = prefix_length + source_length + suffix_length;
|
|
71
|
+
char* result = hb_allocator_alloc(allocator, total_length + 1);
|
|
72
|
+
|
|
73
|
+
memcpy(result, prefix, prefix_length);
|
|
74
|
+
memcpy(result + prefix_length, source, source_length);
|
|
75
|
+
memcpy(result + prefix_length + source_length, suffix, suffix_length);
|
|
76
|
+
result[total_length] = '\0';
|
|
77
|
+
|
|
78
|
+
return result;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
bool javascript_include_tag_supports_block(void) {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const tag_helper_handler_T javascript_include_tag_handler = {
|
|
86
|
+
.name = "javascript_include_tag",
|
|
87
|
+
.source = HB_STRING_LITERAL("ActionView::Helpers::AssetTagHelper#javascript_include_tag"),
|
|
88
|
+
.detect = detect_javascript_include_tag,
|
|
89
|
+
.extract_tag_name = extract_javascript_include_tag_name,
|
|
90
|
+
.extract_content = extract_javascript_include_tag_content,
|
|
91
|
+
.supports_block = javascript_include_tag_supports_block
|
|
92
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
#include "../../include/analyze/action_view/tag_helper_handler.h"
|
|
2
|
+
|
|
3
|
+
#include <prism.h>
|
|
4
|
+
#include <stdbool.h>
|
|
5
|
+
#include <stdlib.h>
|
|
6
|
+
#include <string.h>
|
|
7
|
+
|
|
8
|
+
bool detect_javascript_tag(pm_call_node_t* call_node, pm_parser_t* parser) {
|
|
9
|
+
if (!call_node || !call_node->name) { return false; }
|
|
10
|
+
|
|
11
|
+
pm_constant_t* constant = pm_constant_pool_id_to_constant(&parser->constant_pool, call_node->name);
|
|
12
|
+
return constant && constant->length == 14 && strncmp((const char*) constant->start, "javascript_tag", 14) == 0;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
char* extract_javascript_tag_name(pm_call_node_t* call_node, pm_parser_t* parser, hb_allocator_T* allocator) {
|
|
16
|
+
(void) call_node;
|
|
17
|
+
(void) parser;
|
|
18
|
+
|
|
19
|
+
return hb_allocator_strdup(allocator, "script");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
char* extract_javascript_tag_content(pm_call_node_t* call_node, pm_parser_t* parser, hb_allocator_T* allocator) {
|
|
23
|
+
(void) parser;
|
|
24
|
+
|
|
25
|
+
if (!call_node || !call_node->arguments) { return NULL; }
|
|
26
|
+
|
|
27
|
+
pm_arguments_node_t* arguments = call_node->arguments;
|
|
28
|
+
if (!arguments->arguments.size) { return NULL; }
|
|
29
|
+
|
|
30
|
+
pm_node_t* first_argument = arguments->arguments.nodes[0];
|
|
31
|
+
|
|
32
|
+
if (first_argument->type == PM_KEYWORD_HASH_NODE) { return NULL; }
|
|
33
|
+
|
|
34
|
+
if (first_argument->type == PM_STRING_NODE) {
|
|
35
|
+
pm_string_node_t* string_node = (pm_string_node_t*) first_argument;
|
|
36
|
+
size_t length = pm_string_length(&string_node->unescaped);
|
|
37
|
+
return hb_allocator_strndup(allocator, (const char*) pm_string_source(&string_node->unescaped), length);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
size_t source_length = first_argument->location.end - first_argument->location.start;
|
|
41
|
+
return hb_allocator_strndup(allocator, (const char*) first_argument->location.start, source_length);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
bool javascript_tag_supports_block(void) {
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const tag_helper_handler_T javascript_tag_handler = {
|
|
49
|
+
.name = "javascript_tag",
|
|
50
|
+
.source = HB_STRING_LITERAL("ActionView::Helpers::JavaScriptHelper#javascript_tag"),
|
|
51
|
+
.detect = detect_javascript_tag,
|
|
52
|
+
.extract_tag_name = extract_javascript_tag_name,
|
|
53
|
+
.extract_content = extract_javascript_tag_content,
|
|
54
|
+
.supports_block = javascript_tag_supports_block
|
|
55
|
+
};
|
|
@@ -8,8 +8,10 @@ extern const tag_helper_handler_T content_tag_handler;
|
|
|
8
8
|
extern const tag_helper_handler_T tag_dot_handler;
|
|
9
9
|
extern const tag_helper_handler_T link_to_handler;
|
|
10
10
|
extern const tag_helper_handler_T turbo_frame_tag_handler;
|
|
11
|
+
extern const tag_helper_handler_T javascript_tag_handler;
|
|
12
|
+
extern const tag_helper_handler_T javascript_include_tag_handler;
|
|
11
13
|
|
|
12
|
-
static size_t handlers_count =
|
|
14
|
+
static size_t handlers_count = 6;
|
|
13
15
|
|
|
14
16
|
tag_helper_info_T* tag_helper_info_init(hb_allocator_T* allocator) {
|
|
15
17
|
tag_helper_info_T* info = hb_allocator_alloc(allocator, sizeof(tag_helper_info_T));
|
|
@@ -41,7 +43,7 @@ void tag_helper_info_free(tag_helper_info_T** info) {
|
|
|
41
43
|
}
|
|
42
44
|
|
|
43
45
|
tag_helper_handler_T* get_tag_helper_handlers(void) {
|
|
44
|
-
static tag_helper_handler_T static_handlers[
|
|
46
|
+
static tag_helper_handler_T static_handlers[6];
|
|
45
47
|
static bool initialized = false;
|
|
46
48
|
|
|
47
49
|
if (!initialized) {
|
|
@@ -49,6 +51,8 @@ tag_helper_handler_T* get_tag_helper_handlers(void) {
|
|
|
49
51
|
static_handlers[1] = tag_dot_handler;
|
|
50
52
|
static_handlers[2] = link_to_handler;
|
|
51
53
|
static_handlers[3] = turbo_frame_tag_handler;
|
|
54
|
+
static_handlers[4] = javascript_tag_handler;
|
|
55
|
+
static_handlers[5] = javascript_include_tag_handler;
|
|
52
56
|
initialized = true;
|
|
53
57
|
}
|
|
54
58
|
|
|
@@ -88,7 +88,8 @@ AST_HTML_ATTRIBUTE_NODE_T* create_html_attribute_node(
|
|
|
88
88
|
AST_HTML_ATTRIBUTE_NAME_NODE_T* name_node =
|
|
89
89
|
create_attribute_name_node(name_string, start_position, end_position, allocator);
|
|
90
90
|
|
|
91
|
-
token_T* equals_token =
|
|
91
|
+
token_T* equals_token =
|
|
92
|
+
value_string ? create_synthetic_token(allocator, "=", TOKEN_EQUALS, start_position, end_position) : NULL;
|
|
92
93
|
AST_HTML_ATTRIBUTE_VALUE_NODE_T* value_node = NULL;
|
|
93
94
|
|
|
94
95
|
if (value_string) {
|
|
@@ -210,8 +211,20 @@ static AST_HTML_ATTRIBUTE_VALUE_NODE_T* create_interpolated_attribute_value(
|
|
|
210
211
|
}
|
|
211
212
|
}
|
|
212
213
|
} else if (part->type == PM_EMBEDDED_STATEMENTS_NODE) {
|
|
213
|
-
|
|
214
|
-
|
|
214
|
+
pm_embedded_statements_node_t* embedded = (pm_embedded_statements_node_t*) part;
|
|
215
|
+
const uint8_t* content_start;
|
|
216
|
+
const uint8_t* content_end;
|
|
217
|
+
|
|
218
|
+
if (embedded->statements) {
|
|
219
|
+
content_start = embedded->statements->base.location.start;
|
|
220
|
+
content_end = embedded->statements->base.location.end;
|
|
221
|
+
} else {
|
|
222
|
+
content_start = part->location.start;
|
|
223
|
+
content_end = part->location.end;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
size_t ruby_length = content_end - content_start;
|
|
227
|
+
char* ruby_content = hb_allocator_strndup(allocator, (const char*) content_start, ruby_length);
|
|
215
228
|
|
|
216
229
|
if (ruby_content) {
|
|
217
230
|
AST_RUBY_LITERAL_NODE_T* ruby_node = ast_ruby_literal_node_init(
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
#include "../../include/analyze/analyze.h"
|
|
6
6
|
#include "../../include/ast_nodes.h"
|
|
7
7
|
#include "../../include/html_util.h"
|
|
8
|
+
#include "../../include/parser_helpers.h"
|
|
8
9
|
#include "../../include/position.h"
|
|
9
10
|
#include "../../include/util/hb_allocator.h"
|
|
10
11
|
#include "../../include/util/hb_array.h"
|
|
@@ -22,6 +23,10 @@ extern char* wrap_in_url_for(const char*, size_t, hb_allocator_T*);
|
|
|
22
23
|
extern char* extract_link_to_href(pm_call_node_t*, pm_parser_t*, hb_allocator_T*);
|
|
23
24
|
extern bool detect_turbo_frame_tag(pm_call_node_t*, pm_parser_t*);
|
|
24
25
|
extern char* extract_turbo_frame_tag_id(pm_call_node_t*, pm_parser_t*, hb_allocator_T*);
|
|
26
|
+
extern bool detect_javascript_include_tag(pm_call_node_t*, pm_parser_t*);
|
|
27
|
+
extern char* extract_javascript_include_tag_src(pm_call_node_t*, pm_parser_t*, hb_allocator_T*);
|
|
28
|
+
extern char* wrap_in_javascript_path(const char*, size_t, hb_allocator_T*);
|
|
29
|
+
extern bool javascript_include_tag_source_is_url(const char*, size_t);
|
|
25
30
|
|
|
26
31
|
typedef struct {
|
|
27
32
|
pm_parser_t parser;
|
|
@@ -310,6 +315,47 @@ static AST_NODE_T* transform_tag_helper_with_attributes(
|
|
|
310
315
|
}
|
|
311
316
|
}
|
|
312
317
|
|
|
318
|
+
if (detect_javascript_include_tag(parse_context->info->call_node, &parse_context->parser)
|
|
319
|
+
&& parse_context->info->call_node->arguments && parse_context->info->call_node->arguments->arguments.size >= 1) {
|
|
320
|
+
char* source_value =
|
|
321
|
+
extract_javascript_include_tag_src(parse_context->info->call_node, &parse_context->parser, allocator);
|
|
322
|
+
|
|
323
|
+
if (source_value) {
|
|
324
|
+
if (!attributes) { attributes = hb_array_init(4, allocator); }
|
|
325
|
+
|
|
326
|
+
pm_node_t* first_argument = parse_context->info->call_node->arguments->arguments.nodes[0];
|
|
327
|
+
position_T source_start, source_end;
|
|
328
|
+
prism_node_location_to_positions(&first_argument->location, parse_context, &source_start, &source_end);
|
|
329
|
+
bool source_is_string = (first_argument->type == PM_STRING_NODE);
|
|
330
|
+
|
|
331
|
+
size_t source_length = strlen(source_value);
|
|
332
|
+
bool is_url = javascript_include_tag_source_is_url(source_value, source_length);
|
|
333
|
+
|
|
334
|
+
char* source_attribute_value = source_value;
|
|
335
|
+
if (source_is_string && !is_url) {
|
|
336
|
+
size_t quoted_length = source_length + 2;
|
|
337
|
+
char* quoted_source = hb_allocator_alloc(allocator, quoted_length + 1);
|
|
338
|
+
quoted_source[0] = '"';
|
|
339
|
+
memcpy(quoted_source + 1, source_value, source_length);
|
|
340
|
+
quoted_source[quoted_length - 1] = '"';
|
|
341
|
+
quoted_source[quoted_length] = '\0';
|
|
342
|
+
|
|
343
|
+
source_attribute_value = wrap_in_javascript_path(quoted_source, quoted_length, allocator);
|
|
344
|
+
hb_allocator_dealloc(allocator, quoted_source);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
AST_HTML_ATTRIBUTE_NODE_T* source_attribute =
|
|
348
|
+
is_url
|
|
349
|
+
? create_html_attribute_node("src", source_attribute_value, source_start, source_end, allocator)
|
|
350
|
+
: create_html_attribute_with_ruby_literal("src", source_attribute_value, source_start, source_end, allocator);
|
|
351
|
+
|
|
352
|
+
if (source_attribute) { attributes = prepend_attribute(attributes, (AST_NODE_T*) source_attribute, allocator); }
|
|
353
|
+
|
|
354
|
+
if (source_attribute_value != source_value) { hb_allocator_dealloc(allocator, source_attribute_value); }
|
|
355
|
+
hb_allocator_dealloc(allocator, source_value);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
313
359
|
token_T* tag_name_token =
|
|
314
360
|
tag_name ? create_synthetic_token(allocator, tag_name, TOKEN_IDENTIFIER, tag_name_start, tag_name_end) : NULL;
|
|
315
361
|
|
|
@@ -372,6 +418,166 @@ static AST_NODE_T* transform_tag_helper_with_attributes(
|
|
|
372
418
|
return (AST_NODE_T*) element;
|
|
373
419
|
}
|
|
374
420
|
|
|
421
|
+
static size_t count_javascript_include_tag_sources(pm_call_node_t* call_node) {
|
|
422
|
+
if (!call_node || !call_node->arguments) { return 0; }
|
|
423
|
+
|
|
424
|
+
size_t count = 0;
|
|
425
|
+
pm_arguments_node_t* arguments = call_node->arguments;
|
|
426
|
+
|
|
427
|
+
for (size_t i = 0; i < arguments->arguments.size; i++) {
|
|
428
|
+
pm_node_t* arg = arguments->arguments.nodes[i];
|
|
429
|
+
if (arg->type == PM_KEYWORD_HASH_NODE) { break; }
|
|
430
|
+
count++;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
return count;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
static AST_NODE_T* create_javascript_include_tag_element(
|
|
437
|
+
AST_ERB_CONTENT_NODE_T* erb_node,
|
|
438
|
+
tag_helper_parse_context_T* parse_context,
|
|
439
|
+
pm_node_t* source_argument,
|
|
440
|
+
hb_array_T* shared_attributes,
|
|
441
|
+
hb_allocator_T* allocator
|
|
442
|
+
) {
|
|
443
|
+
position_T tag_name_start, tag_name_end;
|
|
444
|
+
calculate_tag_name_positions(
|
|
445
|
+
parse_context,
|
|
446
|
+
erb_node->base.location.start,
|
|
447
|
+
erb_node->base.location.end,
|
|
448
|
+
&tag_name_start,
|
|
449
|
+
&tag_name_end
|
|
450
|
+
);
|
|
451
|
+
|
|
452
|
+
token_T* tag_name_token = create_synthetic_token(allocator, "script", TOKEN_IDENTIFIER, tag_name_start, tag_name_end);
|
|
453
|
+
|
|
454
|
+
char* source_value = NULL;
|
|
455
|
+
bool source_is_string = (source_argument->type == PM_STRING_NODE);
|
|
456
|
+
|
|
457
|
+
if (source_is_string) {
|
|
458
|
+
pm_string_node_t* string_node = (pm_string_node_t*) source_argument;
|
|
459
|
+
size_t length = pm_string_length(&string_node->unescaped);
|
|
460
|
+
source_value = hb_allocator_strndup(allocator, (const char*) pm_string_source(&string_node->unescaped), length);
|
|
461
|
+
} else {
|
|
462
|
+
size_t source_length = source_argument->location.end - source_argument->location.start;
|
|
463
|
+
source_value = hb_allocator_strndup(allocator, (const char*) source_argument->location.start, source_length);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
position_T source_start, source_end;
|
|
467
|
+
prism_node_location_to_positions(&source_argument->location, parse_context, &source_start, &source_end);
|
|
468
|
+
|
|
469
|
+
size_t source_length = strlen(source_value);
|
|
470
|
+
bool is_url = javascript_include_tag_source_is_url(source_value, source_length);
|
|
471
|
+
|
|
472
|
+
char* source_attribute_value = source_value;
|
|
473
|
+
if (source_is_string && !is_url) {
|
|
474
|
+
size_t quoted_length = source_length + 2;
|
|
475
|
+
char* quoted_source = hb_allocator_alloc(allocator, quoted_length + 1);
|
|
476
|
+
quoted_source[0] = '"';
|
|
477
|
+
memcpy(quoted_source + 1, source_value, source_length);
|
|
478
|
+
quoted_source[quoted_length - 1] = '"';
|
|
479
|
+
quoted_source[quoted_length] = '\0';
|
|
480
|
+
|
|
481
|
+
source_attribute_value = wrap_in_javascript_path(quoted_source, quoted_length, allocator);
|
|
482
|
+
hb_allocator_dealloc(allocator, quoted_source);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
hb_array_T* attributes = hb_array_init(hb_array_size(shared_attributes) + 1, allocator);
|
|
486
|
+
|
|
487
|
+
AST_HTML_ATTRIBUTE_NODE_T* source_attribute =
|
|
488
|
+
is_url
|
|
489
|
+
? create_html_attribute_node("src", source_attribute_value, source_start, source_end, allocator)
|
|
490
|
+
: create_html_attribute_with_ruby_literal("src", source_attribute_value, source_start, source_end, allocator);
|
|
491
|
+
if (source_attribute) { hb_array_append(attributes, source_attribute); }
|
|
492
|
+
|
|
493
|
+
for (size_t i = 0; i < hb_array_size(shared_attributes); i++) {
|
|
494
|
+
hb_array_append(attributes, hb_array_get(shared_attributes, i));
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
if (source_attribute_value != source_value) { hb_allocator_dealloc(allocator, source_attribute_value); }
|
|
498
|
+
hb_allocator_dealloc(allocator, source_value);
|
|
499
|
+
|
|
500
|
+
AST_ERB_OPEN_TAG_NODE_T* open_tag_node = ast_erb_open_tag_node_init(
|
|
501
|
+
erb_node->tag_opening,
|
|
502
|
+
erb_node->content,
|
|
503
|
+
erb_node->tag_closing,
|
|
504
|
+
tag_name_token,
|
|
505
|
+
attributes,
|
|
506
|
+
erb_node->base.location.start,
|
|
507
|
+
erb_node->base.location.end,
|
|
508
|
+
hb_array_init(0, allocator),
|
|
509
|
+
allocator
|
|
510
|
+
);
|
|
511
|
+
|
|
512
|
+
AST_HTML_VIRTUAL_CLOSE_TAG_NODE_T* virtual_close = ast_html_virtual_close_tag_node_init(
|
|
513
|
+
tag_name_token,
|
|
514
|
+
erb_node->base.location.end,
|
|
515
|
+
erb_node->base.location.end,
|
|
516
|
+
hb_array_init(0, allocator),
|
|
517
|
+
allocator
|
|
518
|
+
);
|
|
519
|
+
|
|
520
|
+
return (AST_NODE_T*) ast_html_element_node_init(
|
|
521
|
+
(AST_NODE_T*) open_tag_node,
|
|
522
|
+
tag_name_token,
|
|
523
|
+
hb_array_init(0, allocator),
|
|
524
|
+
(AST_NODE_T*) virtual_close,
|
|
525
|
+
false,
|
|
526
|
+
parse_context->matched_handler->source,
|
|
527
|
+
erb_node->base.location.start,
|
|
528
|
+
erb_node->base.location.end,
|
|
529
|
+
hb_array_init(0, allocator),
|
|
530
|
+
allocator
|
|
531
|
+
);
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
static hb_array_T* transform_javascript_include_tag_multi_source(
|
|
535
|
+
AST_ERB_CONTENT_NODE_T* erb_node,
|
|
536
|
+
analyze_ruby_context_T* context,
|
|
537
|
+
tag_helper_parse_context_T* parse_context
|
|
538
|
+
) {
|
|
539
|
+
hb_allocator_T* allocator = context->allocator;
|
|
540
|
+
pm_call_node_t* call_node = parse_context->info->call_node;
|
|
541
|
+
size_t source_count = count_javascript_include_tag_sources(call_node);
|
|
542
|
+
|
|
543
|
+
if (source_count == 0) { return NULL; }
|
|
544
|
+
|
|
545
|
+
hb_array_T* shared_attributes = extract_html_attributes_from_call_node(
|
|
546
|
+
call_node,
|
|
547
|
+
parse_context->prism_source,
|
|
548
|
+
parse_context->original_source,
|
|
549
|
+
parse_context->erb_content_offset,
|
|
550
|
+
allocator
|
|
551
|
+
);
|
|
552
|
+
if (!shared_attributes) { shared_attributes = hb_array_init(0, allocator); }
|
|
553
|
+
|
|
554
|
+
hb_array_T* elements = hb_array_init(source_count * 2, allocator);
|
|
555
|
+
|
|
556
|
+
for (size_t i = 0; i < source_count; i++) {
|
|
557
|
+
pm_node_t* source_arg = call_node->arguments->arguments.nodes[i];
|
|
558
|
+
AST_NODE_T* element =
|
|
559
|
+
create_javascript_include_tag_element(erb_node, parse_context, source_arg, shared_attributes, allocator);
|
|
560
|
+
|
|
561
|
+
if (element) {
|
|
562
|
+
if (hb_array_size(elements) > 0) {
|
|
563
|
+
position_T position = erb_node->base.location.start;
|
|
564
|
+
AST_HTML_TEXT_NODE_T* newline = ast_html_text_node_init(
|
|
565
|
+
hb_string_from_c_string("\n"),
|
|
566
|
+
position,
|
|
567
|
+
position,
|
|
568
|
+
hb_array_init(0, allocator),
|
|
569
|
+
allocator
|
|
570
|
+
);
|
|
571
|
+
if (newline) { hb_array_append(elements, (AST_NODE_T*) newline); }
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
hb_array_append(elements, element);
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
return elements;
|
|
579
|
+
}
|
|
580
|
+
|
|
375
581
|
static AST_NODE_T* transform_erb_block_to_tag_helper(
|
|
376
582
|
AST_ERB_BLOCK_NODE_T* block_node,
|
|
377
583
|
analyze_ruby_context_T* context,
|
|
@@ -478,6 +684,27 @@ static AST_NODE_T* transform_erb_block_to_tag_helper(
|
|
|
478
684
|
hb_array_T* body = block_node->body ? block_node->body : hb_array_init(0, allocator);
|
|
479
685
|
AST_NODE_T* close_tag = (AST_NODE_T*) block_node->end_node;
|
|
480
686
|
|
|
687
|
+
if (tag_name && parser_is_foreign_content_tag(hb_string_from_c_string(tag_name)) && context->source
|
|
688
|
+
&& block_node->body && hb_array_size(block_node->body) > 0) {
|
|
689
|
+
size_t start_offset = block_node->tag_closing->range.to;
|
|
690
|
+
size_t end_offset = block_node->end_node->tag_opening->range.from;
|
|
691
|
+
|
|
692
|
+
if (end_offset > start_offset) {
|
|
693
|
+
position_T body_start = block_node->tag_closing->location.end;
|
|
694
|
+
position_T body_end = block_node->end_node->tag_opening->location.start;
|
|
695
|
+
|
|
696
|
+
size_t content_length = end_offset - start_offset;
|
|
697
|
+
char* raw_copy = hb_allocator_strndup(allocator, context->source + start_offset, content_length);
|
|
698
|
+
hb_string_T raw_content = { .data = raw_copy, .length = content_length };
|
|
699
|
+
|
|
700
|
+
AST_LITERAL_NODE_T* literal_node =
|
|
701
|
+
ast_literal_node_init(raw_content, body_start, body_end, hb_array_init(0, allocator), allocator);
|
|
702
|
+
|
|
703
|
+
body = hb_array_init(1, allocator);
|
|
704
|
+
hb_array_append(body, literal_node);
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
|
|
481
708
|
AST_HTML_ELEMENT_NODE_T* element = ast_html_element_node_init(
|
|
482
709
|
(AST_NODE_T*) open_tag_node,
|
|
483
710
|
tag_name_token,
|
|
@@ -787,7 +1014,33 @@ void transform_tag_helper_blocks(const AST_NODE_T* node, analyze_ruby_context_T*
|
|
|
787
1014
|
parse_tag_helper_content(erb_string, context->source, erb_content_offset, context->allocator);
|
|
788
1015
|
|
|
789
1016
|
if (parse_context) {
|
|
790
|
-
if (strcmp(parse_context->matched_handler->name, "
|
|
1017
|
+
if (strcmp(parse_context->matched_handler->name, "javascript_include_tag") == 0
|
|
1018
|
+
&& parse_context->info->call_node
|
|
1019
|
+
&& count_javascript_include_tag_sources(parse_context->info->call_node) > 1) {
|
|
1020
|
+
hb_array_T* multi = transform_javascript_include_tag_multi_source(erb_node, context, parse_context);
|
|
1021
|
+
|
|
1022
|
+
if (multi && hb_array_size(multi) > 0) {
|
|
1023
|
+
size_t old_size = hb_array_size(array);
|
|
1024
|
+
size_t multi_size = hb_array_size(multi);
|
|
1025
|
+
hb_array_T* new_array = hb_array_init(old_size - 1 + multi_size, context->allocator);
|
|
1026
|
+
|
|
1027
|
+
for (size_t j = 0; j < old_size; j++) {
|
|
1028
|
+
if (j == i) {
|
|
1029
|
+
for (size_t k = 0; k < multi_size; k++) {
|
|
1030
|
+
hb_array_append(new_array, hb_array_get(multi, k));
|
|
1031
|
+
}
|
|
1032
|
+
} else {
|
|
1033
|
+
hb_array_append(new_array, hb_array_get(array, j));
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
array->items = new_array->items;
|
|
1038
|
+
array->size = new_array->size;
|
|
1039
|
+
array->capacity = new_array->capacity;
|
|
1040
|
+
|
|
1041
|
+
i += multi_size - 1;
|
|
1042
|
+
}
|
|
1043
|
+
} else if (strcmp(parse_context->matched_handler->name, "link_to") == 0) {
|
|
791
1044
|
replacement = transform_link_to_helper(erb_node, context, parse_context);
|
|
792
1045
|
} else {
|
|
793
1046
|
replacement = transform_tag_helper_with_attributes(erb_node, context, parse_context);
|
data/src/html_util.c
CHANGED
|
@@ -27,6 +27,46 @@ static hb_string_T void_tags[] = HB_STRING_LIST(
|
|
|
27
27
|
"wbr"
|
|
28
28
|
);
|
|
29
29
|
|
|
30
|
+
// https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#boolean-attributes
|
|
31
|
+
static hb_string_T boolean_attributes[] = HB_STRING_LIST(
|
|
32
|
+
"allowfullscreen",
|
|
33
|
+
"async",
|
|
34
|
+
"autofocus",
|
|
35
|
+
"autoplay",
|
|
36
|
+
"checked",
|
|
37
|
+
"compact",
|
|
38
|
+
"controls",
|
|
39
|
+
"declare",
|
|
40
|
+
"default",
|
|
41
|
+
"defer",
|
|
42
|
+
"disabled",
|
|
43
|
+
"formnovalidate",
|
|
44
|
+
"hidden",
|
|
45
|
+
"inert",
|
|
46
|
+
"ismap",
|
|
47
|
+
"itemscope",
|
|
48
|
+
"loop",
|
|
49
|
+
"multiple",
|
|
50
|
+
"muted",
|
|
51
|
+
"nomodule",
|
|
52
|
+
"nohref",
|
|
53
|
+
"noresize",
|
|
54
|
+
"noshade",
|
|
55
|
+
"novalidate",
|
|
56
|
+
"nowrap",
|
|
57
|
+
"open",
|
|
58
|
+
"playsinline",
|
|
59
|
+
"readonly",
|
|
60
|
+
"required",
|
|
61
|
+
"reversed",
|
|
62
|
+
"scoped",
|
|
63
|
+
"seamless",
|
|
64
|
+
"selected",
|
|
65
|
+
"sortable",
|
|
66
|
+
"truespeed",
|
|
67
|
+
"typemustmatch"
|
|
68
|
+
);
|
|
69
|
+
|
|
30
70
|
// https://html.spec.whatwg.org/multipage/syntax.html#optional-tags
|
|
31
71
|
static hb_string_T optional_end_tags[] = HB_STRING_LIST(
|
|
32
72
|
"li",
|
|
@@ -112,6 +152,16 @@ bool is_void_element(hb_string_T tag_name) {
|
|
|
112
152
|
return false;
|
|
113
153
|
}
|
|
114
154
|
|
|
155
|
+
bool is_boolean_attribute(hb_string_T attribute_name) {
|
|
156
|
+
if (hb_string_is_empty(attribute_name)) { return false; }
|
|
157
|
+
|
|
158
|
+
for (size_t i = 0; i < sizeof(boolean_attributes) / sizeof(boolean_attributes[0]); i++) {
|
|
159
|
+
if (hb_string_equals_case_insensitive(attribute_name, boolean_attributes[i])) { return true; }
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
|
|
115
165
|
bool has_optional_end_tag(hb_string_T tag_name) {
|
|
116
166
|
if (hb_string_is_empty(tag_name)) { return false; }
|
|
117
167
|
|
data/src/include/html_util.h
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
struct hb_allocator;
|
|
8
8
|
|
|
9
9
|
bool is_void_element(hb_string_T tag_name);
|
|
10
|
+
bool is_boolean_attribute(hb_string_T attribute_name);
|
|
10
11
|
bool has_optional_end_tag(hb_string_T tag_name);
|
|
11
12
|
bool should_implicitly_close(hb_string_T open_tag_name, hb_string_T next_tag_name);
|
|
12
13
|
bool parent_closes_element(hb_string_T open_tag_name, hb_string_T parent_close_tag_name);
|
data/src/include/version.h
CHANGED
data/src/util/hb_arena.c
CHANGED
|
@@ -70,16 +70,12 @@ static bool hb_arena_append_page(hb_arena_T* allocator, size_t page_size) {
|
|
|
70
70
|
page->position = 0;
|
|
71
71
|
|
|
72
72
|
if (allocator->head == NULL) {
|
|
73
|
+
assert(allocator->tail == NULL);
|
|
74
|
+
|
|
73
75
|
allocator->head = page;
|
|
74
76
|
allocator->tail = page;
|
|
75
77
|
} else {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
while (last->next != NULL) {
|
|
79
|
-
last = last->next;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
last->next = page;
|
|
78
|
+
allocator->tail->next = page;
|
|
83
79
|
allocator->tail = page;
|
|
84
80
|
}
|
|
85
81
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: herb
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.9.
|
|
4
|
+
version: 0.9.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Marco Roth
|
|
@@ -111,6 +111,8 @@ files:
|
|
|
111
111
|
- sig/serialized_ast_nodes.rbs
|
|
112
112
|
- src/analyze/action_view/attribute_extraction_helpers.c
|
|
113
113
|
- src/analyze/action_view/content_tag.c
|
|
114
|
+
- src/analyze/action_view/javascript_include_tag.c
|
|
115
|
+
- src/analyze/action_view/javascript_tag.c
|
|
114
116
|
- src/analyze/action_view/link_to.c
|
|
115
117
|
- src/analyze/action_view/registry.c
|
|
116
118
|
- src/analyze/action_view/tag.c
|