@herb-tools/node 0.8.10 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/binding.gyp +26 -8
  3. package/dist/herb-node.cjs +41 -12
  4. package/dist/herb-node.cjs.map +1 -1
  5. package/dist/herb-node.esm.js +8 -1
  6. package/dist/herb-node.esm.js.map +1 -1
  7. package/dist/types/node-backend.d.ts +3 -1
  8. package/extension/error_helpers.cpp +395 -73
  9. package/extension/error_helpers.h +13 -3
  10. package/extension/extension_helpers.cpp +38 -35
  11. package/extension/extension_helpers.h +2 -2
  12. package/extension/herb.cpp +183 -64
  13. package/extension/libherb/analyze/action_view/attribute_extraction_helpers.c +290 -0
  14. package/extension/libherb/analyze/action_view/attribute_extraction_helpers.h +36 -0
  15. package/extension/libherb/analyze/action_view/content_tag.c +70 -0
  16. package/extension/libherb/analyze/action_view/link_to.c +143 -0
  17. package/extension/libherb/analyze/action_view/registry.c +60 -0
  18. package/extension/libherb/analyze/action_view/tag.c +64 -0
  19. package/extension/libherb/analyze/action_view/tag_helper_handler.h +41 -0
  20. package/extension/libherb/analyze/action_view/tag_helper_node_builders.c +305 -0
  21. package/extension/libherb/analyze/action_view/tag_helper_node_builders.h +70 -0
  22. package/extension/libherb/analyze/action_view/tag_helpers.c +748 -0
  23. package/extension/libherb/analyze/action_view/tag_helpers.h +38 -0
  24. package/extension/libherb/analyze/action_view/turbo_frame_tag.c +88 -0
  25. package/extension/libherb/analyze/analyze.c +882 -0
  26. package/extension/libherb/{include → analyze}/analyze.h +14 -4
  27. package/extension/libherb/{analyzed_ruby.c → analyze/analyzed_ruby.c} +13 -11
  28. package/extension/libherb/{analyzed_ruby.h → analyze/analyzed_ruby.h} +3 -3
  29. package/extension/libherb/analyze/builders.c +343 -0
  30. package/extension/libherb/analyze/builders.h +27 -0
  31. package/extension/libherb/analyze/conditional_elements.c +594 -0
  32. package/extension/libherb/analyze/conditional_elements.h +9 -0
  33. package/extension/libherb/analyze/conditional_open_tags.c +640 -0
  34. package/extension/libherb/analyze/conditional_open_tags.h +9 -0
  35. package/extension/libherb/analyze/control_type.c +250 -0
  36. package/extension/libherb/analyze/control_type.h +14 -0
  37. package/extension/libherb/{analyze_helpers.c → analyze/helpers.c} +48 -23
  38. package/extension/libherb/{analyze_helpers.h → analyze/helpers.h} +4 -2
  39. package/extension/libherb/analyze/invalid_structures.c +193 -0
  40. package/extension/libherb/analyze/invalid_structures.h +11 -0
  41. package/extension/libherb/{analyze_missing_end.c → analyze/missing_end.c} +33 -22
  42. package/extension/libherb/analyze/parse_errors.c +84 -0
  43. package/extension/libherb/analyze/prism_annotate.c +397 -0
  44. package/extension/libherb/analyze/prism_annotate.h +16 -0
  45. package/extension/libherb/{analyze_transform.c → analyze/transform.c} +17 -3
  46. package/extension/libherb/ast_node.c +17 -7
  47. package/extension/libherb/ast_node.h +11 -5
  48. package/extension/libherb/ast_nodes.c +663 -388
  49. package/extension/libherb/ast_nodes.h +118 -39
  50. package/extension/libherb/ast_pretty_print.c +191 -7
  51. package/extension/libherb/ast_pretty_print.h +6 -1
  52. package/extension/libherb/element_source.h +3 -8
  53. package/extension/libherb/errors.c +1077 -521
  54. package/extension/libherb/errors.h +149 -56
  55. package/extension/libherb/extract.c +145 -49
  56. package/extension/libherb/extract.h +21 -5
  57. package/extension/libherb/herb.c +52 -34
  58. package/extension/libherb/herb.h +18 -6
  59. package/extension/libherb/herb_prism_node.h +13 -0
  60. package/extension/libherb/html_util.c +241 -12
  61. package/extension/libherb/html_util.h +7 -2
  62. package/extension/libherb/include/analyze/action_view/attribute_extraction_helpers.h +36 -0
  63. package/extension/libherb/include/analyze/action_view/tag_helper_handler.h +41 -0
  64. package/extension/libherb/include/analyze/action_view/tag_helper_node_builders.h +70 -0
  65. package/extension/libherb/include/analyze/action_view/tag_helpers.h +38 -0
  66. package/extension/libherb/{analyze.h → include/analyze/analyze.h} +14 -4
  67. package/extension/libherb/include/{analyzed_ruby.h → analyze/analyzed_ruby.h} +3 -3
  68. package/extension/libherb/include/analyze/builders.h +27 -0
  69. package/extension/libherb/include/analyze/conditional_elements.h +9 -0
  70. package/extension/libherb/include/analyze/conditional_open_tags.h +9 -0
  71. package/extension/libherb/include/analyze/control_type.h +14 -0
  72. package/extension/libherb/include/{analyze_helpers.h → analyze/helpers.h} +4 -2
  73. package/extension/libherb/include/analyze/invalid_structures.h +11 -0
  74. package/extension/libherb/include/analyze/prism_annotate.h +16 -0
  75. package/extension/libherb/include/ast_node.h +11 -5
  76. package/extension/libherb/include/ast_nodes.h +118 -39
  77. package/extension/libherb/include/ast_pretty_print.h +6 -1
  78. package/extension/libherb/include/element_source.h +3 -8
  79. package/extension/libherb/include/errors.h +149 -56
  80. package/extension/libherb/include/extract.h +21 -5
  81. package/extension/libherb/include/herb.h +18 -6
  82. package/extension/libherb/include/herb_prism_node.h +13 -0
  83. package/extension/libherb/include/html_util.h +7 -2
  84. package/extension/libherb/include/io.h +3 -1
  85. package/extension/libherb/include/lex_helpers.h +29 -0
  86. package/extension/libherb/include/lexer.h +1 -1
  87. package/extension/libherb/include/lexer_peek_helpers.h +87 -13
  88. package/extension/libherb/include/lexer_struct.h +2 -0
  89. package/extension/libherb/include/location.h +2 -1
  90. package/extension/libherb/include/parser.h +27 -2
  91. package/extension/libherb/include/parser_helpers.h +19 -3
  92. package/extension/libherb/include/pretty_print.h +10 -5
  93. package/extension/libherb/include/prism_context.h +45 -0
  94. package/extension/libherb/include/prism_helpers.h +10 -7
  95. package/extension/libherb/include/prism_serialized.h +12 -0
  96. package/extension/libherb/include/token.h +16 -4
  97. package/extension/libherb/include/token_struct.h +10 -3
  98. package/extension/libherb/include/utf8.h +2 -1
  99. package/extension/libherb/include/util/hb_allocator.h +78 -0
  100. package/extension/libherb/include/util/hb_arena.h +6 -1
  101. package/extension/libherb/include/util/hb_arena_debug.h +12 -1
  102. package/extension/libherb/include/util/hb_array.h +7 -3
  103. package/extension/libherb/include/util/hb_buffer.h +6 -4
  104. package/extension/libherb/include/util/hb_foreach.h +79 -0
  105. package/extension/libherb/include/util/hb_narray.h +8 -4
  106. package/extension/libherb/include/util/hb_string.h +56 -9
  107. package/extension/libherb/include/util.h +6 -3
  108. package/extension/libherb/include/version.h +1 -1
  109. package/extension/libherb/io.c +3 -2
  110. package/extension/libherb/io.h +3 -1
  111. package/extension/libherb/lex_helpers.h +29 -0
  112. package/extension/libherb/lexer.c +42 -30
  113. package/extension/libherb/lexer.h +1 -1
  114. package/extension/libherb/lexer_peek_helpers.c +12 -74
  115. package/extension/libherb/lexer_peek_helpers.h +87 -13
  116. package/extension/libherb/lexer_struct.h +2 -0
  117. package/extension/libherb/location.c +2 -2
  118. package/extension/libherb/location.h +2 -1
  119. package/extension/libherb/main.c +53 -28
  120. package/extension/libherb/parser.c +783 -247
  121. package/extension/libherb/parser.h +27 -2
  122. package/extension/libherb/parser_helpers.c +110 -23
  123. package/extension/libherb/parser_helpers.h +19 -3
  124. package/extension/libherb/parser_match_tags.c +110 -49
  125. package/extension/libherb/pretty_print.c +29 -24
  126. package/extension/libherb/pretty_print.h +10 -5
  127. package/extension/libherb/prism_context.h +45 -0
  128. package/extension/libherb/prism_helpers.c +30 -27
  129. package/extension/libherb/prism_helpers.h +10 -7
  130. package/extension/libherb/prism_serialized.h +12 -0
  131. package/extension/libherb/ruby_parser.c +2 -0
  132. package/extension/libherb/token.c +151 -66
  133. package/extension/libherb/token.h +16 -4
  134. package/extension/libherb/token_matchers.c +0 -1
  135. package/extension/libherb/token_struct.h +10 -3
  136. package/extension/libherb/utf8.c +7 -6
  137. package/extension/libherb/utf8.h +2 -1
  138. package/extension/libherb/util/hb_allocator.c +341 -0
  139. package/extension/libherb/util/hb_allocator.h +78 -0
  140. package/extension/libherb/util/hb_arena.c +81 -56
  141. package/extension/libherb/util/hb_arena.h +6 -1
  142. package/extension/libherb/util/hb_arena_debug.c +32 -17
  143. package/extension/libherb/util/hb_arena_debug.h +12 -1
  144. package/extension/libherb/util/hb_array.c +30 -15
  145. package/extension/libherb/util/hb_array.h +7 -3
  146. package/extension/libherb/util/hb_buffer.c +17 -21
  147. package/extension/libherb/util/hb_buffer.h +6 -4
  148. package/extension/libherb/util/hb_foreach.h +79 -0
  149. package/extension/libherb/util/hb_narray.c +22 -7
  150. package/extension/libherb/util/hb_narray.h +8 -4
  151. package/extension/libherb/util/hb_string.c +49 -35
  152. package/extension/libherb/util/hb_string.h +56 -9
  153. package/extension/libherb/util.c +21 -11
  154. package/extension/libherb/util.h +6 -3
  155. package/extension/libherb/version.h +1 -1
  156. package/extension/libherb/visitor.c +48 -1
  157. package/extension/nodes.cpp +451 -6
  158. package/extension/nodes.h +8 -1
  159. package/package.json +12 -8
  160. package/src/node-backend.ts +11 -1
  161. package/dist/types/index-cjs.d.cts +0 -1
  162. package/extension/libherb/analyze.c +0 -1608
  163. package/extension/libherb/element_source.c +0 -12
  164. package/extension/libherb/include/util/hb_system.h +0 -9
  165. package/extension/libherb/util/hb_system.c +0 -30
  166. package/extension/libherb/util/hb_system.h +0 -9
  167. package/src/index-cjs.cts +0 -22
  168. /package/dist/types/{index-esm.d.mts → index.d.ts} +0 -0
  169. /package/src/{index-esm.mts → index.ts} +0 -0
@@ -0,0 +1,290 @@
1
+ #include "../../include/analyze/action_view/attribute_extraction_helpers.h"
2
+ #include "../../include/analyze/action_view/tag_helper_node_builders.h"
3
+ #include "../../include/util.h"
4
+ #include "../../include/util/hb_allocator.h"
5
+ #include "../../include/util/hb_array.h"
6
+ #include "../../include/util/hb_string.h"
7
+
8
+ #include <prism.h>
9
+ #include <stdlib.h>
10
+ #include <string.h>
11
+
12
+ static char* extract_string_from_prism_node(pm_node_t* node, hb_allocator_T* allocator) {
13
+ const uint8_t* source = NULL;
14
+ size_t length = 0;
15
+
16
+ if (node->type == PM_SYMBOL_NODE) {
17
+ pm_symbol_node_t* symbol = (pm_symbol_node_t*) node;
18
+ source = pm_string_source(&symbol->unescaped);
19
+ length = pm_string_length(&symbol->unescaped);
20
+ } else if (node->type == PM_STRING_NODE) {
21
+ pm_string_node_t* string_node = (pm_string_node_t*) node;
22
+ source = pm_string_source(&string_node->unescaped);
23
+ length = pm_string_length(&string_node->unescaped);
24
+ } else {
25
+ return NULL;
26
+ }
27
+
28
+ return hb_allocator_strndup(allocator, (const char*) source, length);
29
+ }
30
+
31
+ static char* build_prefixed_key(const char* prefix, const char* raw_key, hb_allocator_T* allocator) {
32
+ char* dashed_key = convert_underscores_to_dashes(raw_key);
33
+ const char* key = dashed_key ? dashed_key : raw_key;
34
+ size_t prefix_len = strlen(prefix);
35
+ size_t key_len = strlen(key);
36
+ size_t total = prefix_len + 1 + key_len + 1;
37
+ char* result = hb_allocator_alloc(allocator, total);
38
+
39
+ if (result) { snprintf(result, total, "%s-%s", prefix, key); }
40
+ if (dashed_key) { free(dashed_key); }
41
+
42
+ return result;
43
+ }
44
+
45
+ static AST_HTML_ATTRIBUTE_NODE_T* create_attribute_from_value(
46
+ const char* name_string,
47
+ pm_node_t* value_node,
48
+ position_T start_position,
49
+ position_T end_position,
50
+ hb_allocator_T* allocator
51
+ ) {
52
+ if (value_node->type == PM_SYMBOL_NODE || value_node->type == PM_STRING_NODE) {
53
+ char* value_string = extract_string_from_prism_node(value_node, allocator);
54
+ if (!value_string) { return NULL; }
55
+
56
+ AST_HTML_ATTRIBUTE_NODE_T* attribute =
57
+ create_html_attribute_node(name_string, value_string, start_position, end_position, allocator);
58
+ hb_allocator_dealloc(allocator, value_string);
59
+
60
+ return attribute;
61
+ } else if (value_node->type == PM_INTERPOLATED_STRING_NODE) {
62
+ return create_html_attribute_with_interpolated_value(
63
+ name_string,
64
+ (pm_interpolated_string_node_t*) value_node,
65
+ start_position,
66
+ end_position,
67
+ allocator
68
+ );
69
+ } else {
70
+ size_t value_length = value_node->location.end - value_node->location.start;
71
+ char* ruby_content = hb_allocator_strndup(allocator, (const char*) value_node->location.start, value_length);
72
+
73
+ if (ruby_content && value_node->location.start) {
74
+ AST_HTML_ATTRIBUTE_NODE_T* attribute =
75
+ create_html_attribute_with_ruby_literal(name_string, ruby_content, start_position, end_position, allocator);
76
+ hb_allocator_dealloc(allocator, ruby_content);
77
+ return attribute;
78
+ }
79
+
80
+ return NULL;
81
+ }
82
+ }
83
+
84
+ AST_HTML_ATTRIBUTE_NODE_T* extract_html_attribute_from_assoc(
85
+ pm_assoc_node_t* assoc,
86
+ const uint8_t* source,
87
+ const char* original_source,
88
+ size_t erb_content_offset,
89
+ hb_allocator_T* allocator
90
+ ) {
91
+ if (!assoc) { return NULL; }
92
+
93
+ char* name_string = extract_string_from_prism_node(assoc->key, allocator);
94
+ if (!name_string) { return NULL; }
95
+
96
+ position_T start_position =
97
+ prism_location_to_position_with_offset(&assoc->key->location, original_source, erb_content_offset, source);
98
+ position_T end_position =
99
+ prism_location_to_position_with_offset(&assoc->value->location, original_source, erb_content_offset, source);
100
+
101
+ // Rails converts `method:` and `remote:` to `data-*` attributes
102
+ if (strcmp(name_string, "method") == 0 || strcmp(name_string, "remote") == 0) {
103
+ size_t name_len = strlen(name_string);
104
+ size_t prefixed_len = 5 + name_len + 1;
105
+ char* prefixed = hb_allocator_alloc(allocator, prefixed_len);
106
+ snprintf(prefixed, prefixed_len, "data-%s", name_string);
107
+ hb_allocator_dealloc(allocator, name_string);
108
+ name_string = prefixed;
109
+ }
110
+
111
+ if ((strcmp(name_string, "data") == 0 || strcmp(name_string, "aria") == 0) && assoc->value->type == PM_HASH_NODE) {
112
+ hb_allocator_dealloc(allocator, name_string);
113
+ return NULL;
114
+ }
115
+
116
+ char* dashed_name = convert_underscores_to_dashes(name_string);
117
+ AST_HTML_ATTRIBUTE_NODE_T* attribute_node = create_attribute_from_value(
118
+ dashed_name ? dashed_name : name_string,
119
+ assoc->value,
120
+ start_position,
121
+ end_position,
122
+ allocator
123
+ );
124
+
125
+ if (dashed_name) { free(dashed_name); }
126
+ hb_allocator_dealloc(allocator, name_string);
127
+
128
+ return attribute_node;
129
+ }
130
+
131
+ hb_array_T* extract_html_attributes_from_keyword_hash(
132
+ pm_keyword_hash_node_t* kw_hash,
133
+ const uint8_t* source,
134
+ const char* original_source,
135
+ size_t erb_content_offset,
136
+ hb_allocator_T* allocator
137
+ ) {
138
+ if (!kw_hash) { return NULL; }
139
+
140
+ hb_array_T* attributes = hb_array_init(8, allocator);
141
+ if (!attributes) { return NULL; }
142
+
143
+ for (size_t i = 0; i < kw_hash->elements.size; i++) {
144
+ pm_node_t* element = kw_hash->elements.nodes[i];
145
+
146
+ if (element->type == PM_ASSOC_SPLAT_NODE) {
147
+ pm_assoc_splat_node_t* splat = (pm_assoc_splat_node_t*) element;
148
+ size_t splat_length = splat->base.location.end - splat->base.location.start;
149
+ char* splat_content = hb_allocator_strndup(allocator, (const char*) splat->base.location.start, splat_length);
150
+
151
+ if (splat_content) {
152
+ position_T splat_start =
153
+ prism_location_to_position_with_offset(&splat->base.location, original_source, erb_content_offset, source);
154
+
155
+ AST_RUBY_HTML_ATTRIBUTES_SPLAT_NODE_T* splat_node = ast_ruby_html_attributes_splat_node_init(
156
+ hb_string_from_c_string(splat_content),
157
+ HB_STRING_EMPTY,
158
+ splat_start,
159
+ splat_start,
160
+ hb_array_init(0, allocator),
161
+ allocator
162
+ );
163
+
164
+ if (splat_node) { hb_array_append(attributes, (AST_NODE_T*) splat_node); }
165
+
166
+ hb_allocator_dealloc(allocator, splat_content);
167
+ }
168
+ } else if (element->type == PM_ASSOC_NODE) {
169
+ pm_assoc_node_t* assoc = (pm_assoc_node_t*) element;
170
+
171
+ char* key_string = extract_string_from_prism_node(assoc->key, allocator);
172
+
173
+ if (key_string && (strcmp(key_string, "data") == 0 || strcmp(key_string, "aria") == 0)
174
+ && assoc->value->type == PM_HASH_NODE) {
175
+ pm_hash_node_t* hash = (pm_hash_node_t*) assoc->value;
176
+
177
+ for (size_t j = 0; j < hash->elements.size; j++) {
178
+ pm_node_t* hash_element = hash->elements.nodes[j];
179
+
180
+ if (hash_element->type == PM_ASSOC_SPLAT_NODE) {
181
+ pm_assoc_splat_node_t* splat = (pm_assoc_splat_node_t*) hash_element;
182
+ size_t splat_length = splat->base.location.end - splat->base.location.start;
183
+ char* splat_content =
184
+ hb_allocator_strndup(allocator, (const char*) splat->base.location.start, splat_length);
185
+
186
+ if (splat_content) {
187
+ position_T splat_start = prism_location_to_position_with_offset(
188
+ &splat->base.location,
189
+ original_source,
190
+ erb_content_offset,
191
+ source
192
+ );
193
+
194
+ AST_RUBY_HTML_ATTRIBUTES_SPLAT_NODE_T* splat_node = ast_ruby_html_attributes_splat_node_init(
195
+ hb_string_from_c_string(splat_content),
196
+ hb_string_from_c_string(key_string),
197
+ splat_start,
198
+ splat_start,
199
+ hb_array_init(0, allocator),
200
+ allocator
201
+ );
202
+
203
+ if (splat_node) { hb_array_append(attributes, (AST_NODE_T*) splat_node); }
204
+
205
+ hb_allocator_dealloc(allocator, splat_content);
206
+ }
207
+
208
+ continue;
209
+ }
210
+
211
+ if (hash_element->type != PM_ASSOC_NODE) { continue; }
212
+
213
+ pm_assoc_node_t* hash_assoc = (pm_assoc_node_t*) hash_element;
214
+ char* raw_key = extract_string_from_prism_node(hash_assoc->key, allocator);
215
+ if (!raw_key) { continue; }
216
+
217
+ char* attribute_key_string = build_prefixed_key(key_string, raw_key, allocator);
218
+ hb_allocator_dealloc(allocator, raw_key);
219
+
220
+ if (attribute_key_string) {
221
+ position_T attribute_start = prism_location_to_position_with_offset(
222
+ &hash_assoc->key->location,
223
+ original_source,
224
+ erb_content_offset,
225
+ source
226
+ );
227
+ position_T attribute_end = prism_location_to_position_with_offset(
228
+ &hash_assoc->value->location,
229
+ original_source,
230
+ erb_content_offset,
231
+ source
232
+ );
233
+
234
+ AST_HTML_ATTRIBUTE_NODE_T* attribute = create_attribute_from_value(
235
+ attribute_key_string,
236
+ hash_assoc->value,
237
+ attribute_start,
238
+ attribute_end,
239
+ allocator
240
+ );
241
+
242
+ if (attribute) { hb_array_append(attributes, attribute); }
243
+ hb_allocator_dealloc(allocator, attribute_key_string);
244
+ }
245
+ }
246
+ } else {
247
+ AST_HTML_ATTRIBUTE_NODE_T* attribute =
248
+ extract_html_attribute_from_assoc(assoc, source, original_source, erb_content_offset, allocator);
249
+
250
+ if (attribute) { hb_array_append(attributes, attribute); }
251
+ }
252
+
253
+ if (key_string) { hb_allocator_dealloc(allocator, key_string); }
254
+ }
255
+ }
256
+
257
+ return attributes;
258
+ }
259
+
260
+ bool has_html_attributes_in_call(pm_call_node_t* call_node) {
261
+ if (!call_node || !call_node->arguments) { return false; }
262
+
263
+ pm_arguments_node_t* arguments = call_node->arguments;
264
+ if (arguments->arguments.size == 0) { return false; }
265
+
266
+ pm_node_t* last_argument = arguments->arguments.nodes[arguments->arguments.size - 1];
267
+
268
+ return last_argument && last_argument->type == PM_KEYWORD_HASH_NODE;
269
+ }
270
+
271
+ hb_array_T* extract_html_attributes_from_call_node(
272
+ pm_call_node_t* call_node,
273
+ const uint8_t* source,
274
+ const char* original_source,
275
+ size_t erb_content_offset,
276
+ hb_allocator_T* allocator
277
+ ) {
278
+ if (!has_html_attributes_in_call(call_node)) { return NULL; }
279
+
280
+ pm_arguments_node_t* arguments = call_node->arguments;
281
+ pm_node_t* last_argument = arguments->arguments.nodes[arguments->arguments.size - 1];
282
+
283
+ return extract_html_attributes_from_keyword_hash(
284
+ (pm_keyword_hash_node_t*) last_argument,
285
+ source,
286
+ original_source,
287
+ erb_content_offset,
288
+ allocator
289
+ );
290
+ }
@@ -0,0 +1,36 @@
1
+ #ifndef ATTRIBUTE_EXTRACTION_HELPERS_H
2
+ #define ATTRIBUTE_EXTRACTION_HELPERS_H
3
+
4
+ #include "../../util/hb_allocator.h"
5
+ #include "tag_helper_handler.h"
6
+ #include "tag_helpers.h"
7
+
8
+ #include <prism.h>
9
+
10
+ AST_HTML_ATTRIBUTE_NODE_T* extract_html_attribute_from_assoc(
11
+ pm_assoc_node_t* assoc,
12
+ const uint8_t* source,
13
+ const char* original_source,
14
+ size_t erb_content_offset,
15
+ hb_allocator_T* allocator
16
+ );
17
+
18
+ hb_array_T* extract_html_attributes_from_keyword_hash(
19
+ pm_keyword_hash_node_t* kw_hash,
20
+ const uint8_t* source,
21
+ const char* original_source,
22
+ size_t erb_content_offset,
23
+ hb_allocator_T* allocator
24
+ );
25
+
26
+ bool has_html_attributes_in_call(pm_call_node_t* call_node);
27
+
28
+ hb_array_T* extract_html_attributes_from_call_node(
29
+ pm_call_node_t* call_node,
30
+ const uint8_t* source,
31
+ const char* original_source,
32
+ size_t erb_content_offset,
33
+ hb_allocator_T* allocator
34
+ );
35
+
36
+ #endif
@@ -0,0 +1,70 @@
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_content_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 == 11 && strncmp((const char*) constant->start, "content_tag", 11) == 0;
13
+ }
14
+
15
+ char* extract_content_tag_name(pm_call_node_t* call_node, pm_parser_t* parser, hb_allocator_T* allocator) {
16
+ (void) parser;
17
+
18
+ if (!call_node || !call_node->arguments) { return NULL; }
19
+
20
+ pm_arguments_node_t* arguments = call_node->arguments;
21
+ if (!arguments->arguments.size) { return NULL; }
22
+
23
+ pm_node_t* first_argument = arguments->arguments.nodes[0];
24
+
25
+ if (first_argument->type == PM_STRING_NODE) {
26
+ pm_string_node_t* string_node = (pm_string_node_t*) first_argument;
27
+ size_t length = pm_string_length(&string_node->unescaped);
28
+ return hb_allocator_strndup(allocator, (const char*) pm_string_source(&string_node->unescaped), length);
29
+ } else if (first_argument->type == PM_SYMBOL_NODE) {
30
+ pm_symbol_node_t* symbol_node = (pm_symbol_node_t*) first_argument;
31
+ size_t length = pm_string_length(&symbol_node->unescaped);
32
+ return hb_allocator_strndup(allocator, (const char*) pm_string_source(&symbol_node->unescaped), length);
33
+ }
34
+
35
+ return NULL;
36
+ }
37
+
38
+ char* extract_content_tag_content(pm_call_node_t* call_node, pm_parser_t* parser, hb_allocator_T* allocator) {
39
+ (void) parser;
40
+
41
+ if (!call_node || !call_node->arguments) { return NULL; }
42
+
43
+ pm_arguments_node_t* arguments = call_node->arguments;
44
+ if (arguments->arguments.size < 2) { return NULL; }
45
+
46
+ pm_node_t* second_argument = arguments->arguments.nodes[1];
47
+
48
+ if (second_argument->type == PM_KEYWORD_HASH_NODE) { return NULL; }
49
+
50
+ if (second_argument->type == PM_STRING_NODE) {
51
+ pm_string_node_t* string_node = (pm_string_node_t*) second_argument;
52
+ size_t length = pm_string_length(&string_node->unescaped);
53
+ return hb_allocator_strndup(allocator, (const char*) pm_string_source(&string_node->unescaped), length);
54
+ }
55
+
56
+ size_t source_length = second_argument->location.end - second_argument->location.start;
57
+ return hb_allocator_strndup(allocator, (const char*) second_argument->location.start, source_length);
58
+ }
59
+
60
+ bool content_tag_supports_block(void) {
61
+ return true;
62
+ }
63
+
64
+ const tag_helper_handler_T content_tag_handler = { .name = "content_tag",
65
+ .source =
66
+ HB_STRING_LITERAL("ActionView::Helpers::TagHelper#content_tag"),
67
+ .detect = detect_content_tag,
68
+ .extract_tag_name = extract_content_tag_name,
69
+ .extract_content = extract_content_tag_content,
70
+ .supports_block = content_tag_supports_block };
@@ -0,0 +1,143 @@
1
+ #include "../../include/analyze/action_view/tag_helper_handler.h"
2
+
3
+ #include <prism.h>
4
+ #include <stdbool.h>
5
+ #include <stdio.h>
6
+ #include <stdlib.h>
7
+ #include <string.h>
8
+
9
+ char* wrap_in_url_for(const char* source, size_t source_length, hb_allocator_T* allocator) {
10
+ const char* prefix = "url_for(";
11
+ const char* suffix = ")";
12
+ size_t prefix_len = strlen(prefix);
13
+ size_t suffix_len = strlen(suffix);
14
+ size_t total_length = prefix_len + source_length + suffix_len;
15
+ char* result = hb_allocator_alloc(allocator, total_length + 1);
16
+
17
+ memcpy(result, prefix, prefix_len);
18
+ memcpy(result + prefix_len, source, source_length);
19
+ memcpy(result + prefix_len + source_length, suffix, suffix_len);
20
+ result[total_length] = '\0';
21
+
22
+ return result;
23
+ }
24
+
25
+ bool detect_link_to(pm_call_node_t* call_node, pm_parser_t* parser) {
26
+ if (!call_node || !call_node->name) { return false; }
27
+
28
+ pm_constant_t* constant = pm_constant_pool_id_to_constant(&parser->constant_pool, call_node->name);
29
+
30
+ return constant && constant->length == 7 && strncmp((const char*) constant->start, "link_to", 7) == 0;
31
+ }
32
+
33
+ bool is_route_helper_node(pm_node_t* node, pm_parser_t* parser) {
34
+ if (!node || node->type != PM_CALL_NODE) { return false; }
35
+
36
+ pm_call_node_t* call = (pm_call_node_t*) node;
37
+ if (!call->name) { return false; }
38
+
39
+ pm_constant_t* constant = pm_constant_pool_id_to_constant(&parser->constant_pool, call->name);
40
+ if (!constant || constant->length < 5) { return false; }
41
+
42
+ const char* name = (const char*) constant->start;
43
+ size_t length = constant->length;
44
+
45
+ return (length >= 5 && strncmp(name + length - 5, "_path", 5) == 0)
46
+ || (length >= 4 && strncmp(name + length - 4, "_url", 4) == 0);
47
+ }
48
+
49
+ char* extract_link_to_tag_name(pm_call_node_t* call_node, pm_parser_t* parser, hb_allocator_T* allocator) {
50
+ (void) call_node;
51
+ (void) parser;
52
+
53
+ return hb_allocator_strdup(allocator, "a");
54
+ }
55
+
56
+ char* extract_link_to_content(pm_call_node_t* call_node, pm_parser_t* parser, hb_allocator_T* allocator) {
57
+ (void) parser;
58
+
59
+ if (!call_node || !call_node->arguments) { return NULL; }
60
+
61
+ pm_arguments_node_t* arguments = call_node->arguments;
62
+ if (!arguments->arguments.size) { return NULL; }
63
+
64
+ pm_node_t* first_argument = arguments->arguments.nodes[0];
65
+
66
+ if (first_argument->type == PM_NIL_NODE) { return NULL; }
67
+
68
+ if (first_argument->type == PM_STRING_NODE) {
69
+ pm_string_node_t* string_node = (pm_string_node_t*) first_argument;
70
+ size_t length = pm_string_length(&string_node->unescaped);
71
+ return hb_allocator_strndup(allocator, (const char*) pm_string_source(&string_node->unescaped), length);
72
+ }
73
+
74
+ size_t source_length = first_argument->location.end - first_argument->location.start;
75
+
76
+ // Format: "<expression>.to_s"
77
+ if (arguments->arguments.size == 1) {
78
+ const char* suffix = ".to_s";
79
+ size_t suffix_len = strlen(suffix);
80
+ size_t total_length = source_length + suffix_len;
81
+ char* ruby_expression = hb_allocator_alloc(allocator, total_length + 1);
82
+
83
+ memcpy(ruby_expression, (const char*) first_argument->location.start, source_length);
84
+ memcpy(ruby_expression + source_length, suffix, suffix_len);
85
+ ruby_expression[total_length] = '\0';
86
+
87
+ return ruby_expression;
88
+ }
89
+
90
+ return hb_allocator_strndup(allocator, (const char*) first_argument->location.start, source_length);
91
+ }
92
+
93
+ char* extract_link_to_href(pm_call_node_t* call_node, pm_parser_t* parser, hb_allocator_T* allocator) {
94
+ (void) parser;
95
+
96
+ if (!call_node || !call_node->arguments) { return NULL; }
97
+
98
+ pm_arguments_node_t* arguments = call_node->arguments;
99
+
100
+ // Format: "url_for(<expression>)"
101
+ if (arguments->arguments.size == 1) {
102
+ pm_node_t* first_argument = arguments->arguments.nodes[0];
103
+
104
+ if (first_argument->type == PM_STRING_NODE || first_argument->type == PM_NIL_NODE) { return NULL; }
105
+
106
+ size_t source_length = first_argument->location.end - first_argument->location.start;
107
+
108
+ if (is_route_helper_node(first_argument, parser)) {
109
+ return hb_allocator_strndup(allocator, (const char*) first_argument->location.start, source_length);
110
+ }
111
+
112
+ return wrap_in_url_for((const char*) first_argument->location.start, source_length, allocator);
113
+ }
114
+
115
+ if (arguments->arguments.size < 2) { return NULL; }
116
+
117
+ pm_node_t* second_argument = arguments->arguments.nodes[1];
118
+
119
+ if (second_argument->type == PM_STRING_NODE) {
120
+ pm_string_node_t* string_node = (pm_string_node_t*) second_argument;
121
+ size_t length = pm_string_length(&string_node->unescaped);
122
+ return hb_allocator_strndup(allocator, (const char*) pm_string_source(&string_node->unescaped), length);
123
+ }
124
+
125
+ size_t source_length = second_argument->location.end - second_argument->location.start;
126
+
127
+ if (!is_route_helper_node(second_argument, parser)) {
128
+ return wrap_in_url_for((const char*) second_argument->location.start, source_length, allocator);
129
+ }
130
+
131
+ return hb_allocator_strndup(allocator, (const char*) second_argument->location.start, source_length);
132
+ }
133
+
134
+ bool link_to_supports_block(void) {
135
+ return true;
136
+ }
137
+
138
+ const tag_helper_handler_T link_to_handler = { .name = "link_to",
139
+ .source = HB_STRING_LITERAL("ActionView::Helpers::UrlHelper#link_to"),
140
+ .detect = detect_link_to,
141
+ .extract_tag_name = extract_link_to_tag_name,
142
+ .extract_content = extract_link_to_content,
143
+ .supports_block = link_to_supports_block };
@@ -0,0 +1,60 @@
1
+ #include "../../include/analyze/action_view/tag_helper_handler.h"
2
+ #include "../../include/util/hb_allocator.h"
3
+
4
+ #include <stdbool.h>
5
+ #include <stdlib.h>
6
+
7
+ extern const tag_helper_handler_T content_tag_handler;
8
+ extern const tag_helper_handler_T tag_dot_handler;
9
+ extern const tag_helper_handler_T link_to_handler;
10
+ extern const tag_helper_handler_T turbo_frame_tag_handler;
11
+
12
+ static size_t handlers_count = 4;
13
+
14
+ tag_helper_info_T* tag_helper_info_init(hb_allocator_T* allocator) {
15
+ tag_helper_info_T* info = hb_allocator_alloc(allocator, sizeof(tag_helper_info_T));
16
+
17
+ if (info) {
18
+ info->tag_name = NULL;
19
+ info->call_node = NULL;
20
+ info->attributes = NULL;
21
+ info->content = NULL;
22
+ info->has_block = false;
23
+ info->allocator = allocator;
24
+ }
25
+
26
+ return info;
27
+ }
28
+
29
+ void tag_helper_info_free(tag_helper_info_T** info) {
30
+ if (!info || !*info) { return; }
31
+
32
+ hb_allocator_T* allocator = (*info)->allocator;
33
+
34
+ if ((*info)->tag_name) { hb_allocator_dealloc(allocator, (*info)->tag_name); }
35
+ if ((*info)->content) { hb_allocator_dealloc(allocator, (*info)->content); }
36
+ if ((*info)->attributes) { hb_array_free(&(*info)->attributes); }
37
+
38
+ hb_allocator_dealloc(allocator, *info);
39
+
40
+ *info = NULL;
41
+ }
42
+
43
+ tag_helper_handler_T* get_tag_helper_handlers(void) {
44
+ static tag_helper_handler_T static_handlers[4];
45
+ static bool initialized = false;
46
+
47
+ if (!initialized) {
48
+ static_handlers[0] = content_tag_handler;
49
+ static_handlers[1] = tag_dot_handler;
50
+ static_handlers[2] = link_to_handler;
51
+ static_handlers[3] = turbo_frame_tag_handler;
52
+ initialized = true;
53
+ }
54
+
55
+ return static_handlers;
56
+ }
57
+
58
+ size_t get_tag_helper_handlers_count(void) {
59
+ return handlers_count;
60
+ }
@@ -0,0 +1,64 @@
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_tag_dot(pm_call_node_t* call_node, pm_parser_t* parser) {
9
+ if (!call_node || !call_node->receiver) { return false; }
10
+ if (call_node->receiver->type != PM_CALL_NODE) { return false; }
11
+
12
+ pm_call_node_t* receiver_node = (pm_call_node_t*) call_node->receiver;
13
+ if (!receiver_node->name) { return false; }
14
+
15
+ pm_constant_t* constant = pm_constant_pool_id_to_constant(&parser->constant_pool, receiver_node->name);
16
+
17
+ return constant && constant->length == 3 && strncmp((const char*) constant->start, "tag", 3) == 0;
18
+ }
19
+
20
+ char* extract_tag_dot_name(pm_call_node_t* call_node, pm_parser_t* parser, hb_allocator_T* allocator) {
21
+ if (!call_node || !call_node->name) { return NULL; }
22
+
23
+ pm_constant_t* constant = pm_constant_pool_id_to_constant(&parser->constant_pool, call_node->name);
24
+ if (!constant) { return NULL; }
25
+
26
+ char* name = hb_allocator_strndup(allocator, (const char*) constant->start, constant->length);
27
+
28
+ for (size_t i = 0; i < constant->length && name[i] != '\0'; i++) {
29
+ if (name[i] == '_') { name[i] = '-'; }
30
+ }
31
+
32
+ return name;
33
+ }
34
+
35
+ // TODO: this should probably be an array of nodes
36
+ char* extract_tag_dot_content(pm_call_node_t* call_node, pm_parser_t* parser, hb_allocator_T* allocator) {
37
+ (void) parser;
38
+
39
+ if (!call_node || !call_node->arguments) { return NULL; }
40
+
41
+ pm_arguments_node_t* arguments = call_node->arguments;
42
+ if (!arguments->arguments.size) { return NULL; }
43
+
44
+ pm_node_t* first_argument = arguments->arguments.nodes[0];
45
+
46
+ if (first_argument->type == PM_STRING_NODE) {
47
+ pm_string_node_t* string_node = (pm_string_node_t*) first_argument;
48
+ size_t length = pm_string_length(&string_node->unescaped);
49
+ return hb_allocator_strndup(allocator, (const char*) pm_string_source(&string_node->unescaped), length);
50
+ }
51
+
52
+ return NULL;
53
+ }
54
+
55
+ bool tag_dot_supports_block(void) {
56
+ return true;
57
+ }
58
+
59
+ const tag_helper_handler_T tag_dot_handler = { .name = "tag",
60
+ .source = HB_STRING_LITERAL("ActionView::Helpers::TagHelper#tag"),
61
+ .detect = detect_tag_dot,
62
+ .extract_tag_name = extract_tag_dot_name,
63
+ .extract_content = extract_tag_dot_content,
64
+ .supports_block = tag_dot_supports_block };