@herb-tools/node 0.9.0 → 0.9.1

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 (44) hide show
  1. package/binding.gyp +1 -0
  2. package/dist/herb-node.cjs +1 -1
  3. package/dist/herb-node.esm.js +1 -1
  4. package/extension/error_helpers.cpp +204 -1
  5. package/extension/error_helpers.h +8 -1
  6. package/extension/extension_helpers.cpp +3 -1
  7. package/extension/herb.cpp +11 -0
  8. package/extension/libherb/analyze/action_view/attribute_extraction_helpers.c +14 -1
  9. package/extension/libherb/analyze/action_view/content_tag.c +19 -11
  10. package/extension/libherb/analyze/action_view/link_to.c +25 -1
  11. package/extension/libherb/analyze/action_view/registry.c +23 -0
  12. package/extension/libherb/analyze/action_view/tag.c +14 -8
  13. package/extension/libherb/analyze/action_view/tag_helper_handler.h +2 -0
  14. package/extension/libherb/analyze/action_view/tag_helpers.c +78 -11
  15. package/extension/libherb/analyze/analyze.c +3 -0
  16. package/extension/libherb/analyze/missing_end.c +1 -1
  17. package/extension/libherb/analyze/prism_annotate.c +4 -2
  18. package/extension/libherb/analyze/render_nodes.c +761 -0
  19. package/extension/libherb/analyze/render_nodes.h +11 -0
  20. package/extension/libherb/analyze/transform.c +8 -1
  21. package/extension/libherb/ast_nodes.c +98 -1
  22. package/extension/libherb/ast_nodes.h +38 -1
  23. package/extension/libherb/ast_pretty_print.c +75 -1
  24. package/extension/libherb/ast_pretty_print.h +1 -1
  25. package/extension/libherb/errors.c +380 -1
  26. package/extension/libherb/errors.h +59 -1
  27. package/extension/libherb/include/analyze/action_view/tag_helper_handler.h +2 -0
  28. package/extension/libherb/include/analyze/render_nodes.h +11 -0
  29. package/extension/libherb/include/ast_nodes.h +38 -1
  30. package/extension/libherb/include/ast_pretty_print.h +1 -1
  31. package/extension/libherb/include/errors.h +59 -1
  32. package/extension/libherb/include/parser.h +1 -0
  33. package/extension/libherb/include/util/hb_foreach.h +1 -1
  34. package/extension/libherb/include/version.h +1 -1
  35. package/extension/libherb/parser.c +1 -0
  36. package/extension/libherb/parser.h +1 -0
  37. package/extension/libherb/parser_match_tags.c +21 -1
  38. package/extension/libherb/util/hb_foreach.h +1 -1
  39. package/extension/libherb/version.h +1 -1
  40. package/extension/libherb/visitor.c +21 -1
  41. package/extension/nodes.cpp +143 -1
  42. package/extension/nodes.h +3 -1
  43. package/package.json +2 -2
  44. package/CHANGELOG.md +0 -19
package/binding.gyp CHANGED
@@ -21,6 +21,7 @@
21
21
  "./extension/libherb/analyze/missing_end.c",
22
22
  "./extension/libherb/analyze/parse_errors.c",
23
23
  "./extension/libherb/analyze/prism_annotate.c",
24
+ "./extension/libherb/analyze/render_nodes.c",
24
25
  "./extension/libherb/analyze/transform.c",
25
26
  "./extension/libherb/analyze/action_view/attribute_extraction_helpers.c",
26
27
  "./extension/libherb/analyze/action_view/content_tag.c",
@@ -9,7 +9,7 @@ var node_fs = require('node:fs');
9
9
 
10
10
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
11
11
  var name = "@herb-tools/node";
12
- var version = "0.9.0";
12
+ var version = "0.9.1";
13
13
  var packageJSON = {
14
14
  name: name,
15
15
  version: version};
@@ -7,7 +7,7 @@ import { fileURLToPath } from 'url';
7
7
  import { readFileSync } from 'node:fs';
8
8
 
9
9
  var name = "@herb-tools/node";
10
- var version = "0.9.0";
10
+ var version = "0.9.1";
11
11
  var packageJSON = {
12
12
  name: name,
13
13
  version: version};
@@ -1,5 +1,5 @@
1
1
  // NOTE: This file is generated by the templates/template.rb script and should not
2
- // be modified manually. See /Users/marcoroth/Development/herb-release-0.9.0/templates/javascript/packages/node/extension/error_helpers.cpp.erb
2
+ // be modified manually. See /Users/marcoroth/Development/herb-release-0.9.1/templates/javascript/packages/node/extension/error_helpers.cpp.erb
3
3
 
4
4
  #include <node_api.h>
5
5
  #include "error_helpers.h"
@@ -669,6 +669,188 @@ napi_value NestedERBTagErrorFromCStruct(napi_env env, NESTED_ERB_TAG_ERROR_T* ne
669
669
  return result;
670
670
  }
671
671
 
672
+ napi_value RenderAmbiguousLocalsErrorFromCStruct(napi_env env, RENDER_AMBIGUOUS_LOCALS_ERROR_T* render_ambiguous_locals_error) {
673
+ if (!render_ambiguous_locals_error) {
674
+ napi_value null_value;
675
+ napi_get_null(env, &null_value);
676
+ return null_value;
677
+ }
678
+
679
+ napi_value result;
680
+ napi_create_object(env, &result);
681
+
682
+ napi_value type = CreateStringFromHbString(env, error_type_to_string(&render_ambiguous_locals_error->base));
683
+ napi_set_named_property(env, result, "type", type);
684
+
685
+ napi_value message = CreateStringFromHbString(env, render_ambiguous_locals_error->base.message);
686
+ napi_set_named_property(env, result, "message", message);
687
+
688
+ napi_value location = CreateLocation(env, render_ambiguous_locals_error->base.location);
689
+ napi_set_named_property(env, result, "location", location);
690
+
691
+ napi_value partial = CreateStringFromHbString(env, render_ambiguous_locals_error->partial);
692
+ napi_set_named_property(env, result, "partial", partial);
693
+
694
+
695
+ return result;
696
+ }
697
+
698
+ napi_value RenderMissingLocalsErrorFromCStruct(napi_env env, RENDER_MISSING_LOCALS_ERROR_T* render_missing_locals_error) {
699
+ if (!render_missing_locals_error) {
700
+ napi_value null_value;
701
+ napi_get_null(env, &null_value);
702
+ return null_value;
703
+ }
704
+
705
+ napi_value result;
706
+ napi_create_object(env, &result);
707
+
708
+ napi_value type = CreateStringFromHbString(env, error_type_to_string(&render_missing_locals_error->base));
709
+ napi_set_named_property(env, result, "type", type);
710
+
711
+ napi_value message = CreateStringFromHbString(env, render_missing_locals_error->base.message);
712
+ napi_set_named_property(env, result, "message", message);
713
+
714
+ napi_value location = CreateLocation(env, render_missing_locals_error->base.location);
715
+ napi_set_named_property(env, result, "location", location);
716
+
717
+ napi_value partial = CreateStringFromHbString(env, render_missing_locals_error->partial);
718
+ napi_set_named_property(env, result, "partial", partial);
719
+
720
+ napi_value keywords = CreateStringFromHbString(env, render_missing_locals_error->keywords);
721
+ napi_set_named_property(env, result, "keywords", keywords);
722
+
723
+
724
+ return result;
725
+ }
726
+
727
+ napi_value RenderNoArgumentsErrorFromCStruct(napi_env env, RENDER_NO_ARGUMENTS_ERROR_T* render_no_arguments_error) {
728
+ if (!render_no_arguments_error) {
729
+ napi_value null_value;
730
+ napi_get_null(env, &null_value);
731
+ return null_value;
732
+ }
733
+
734
+ napi_value result;
735
+ napi_create_object(env, &result);
736
+
737
+ napi_value type = CreateStringFromHbString(env, error_type_to_string(&render_no_arguments_error->base));
738
+ napi_set_named_property(env, result, "type", type);
739
+
740
+ napi_value message = CreateStringFromHbString(env, render_no_arguments_error->base.message);
741
+ napi_set_named_property(env, result, "message", message);
742
+
743
+ napi_value location = CreateLocation(env, render_no_arguments_error->base.location);
744
+ napi_set_named_property(env, result, "location", location);
745
+
746
+
747
+ return result;
748
+ }
749
+
750
+ napi_value RenderConflictingPartialErrorFromCStruct(napi_env env, RENDER_CONFLICTING_PARTIAL_ERROR_T* render_conflicting_partial_error) {
751
+ if (!render_conflicting_partial_error) {
752
+ napi_value null_value;
753
+ napi_get_null(env, &null_value);
754
+ return null_value;
755
+ }
756
+
757
+ napi_value result;
758
+ napi_create_object(env, &result);
759
+
760
+ napi_value type = CreateStringFromHbString(env, error_type_to_string(&render_conflicting_partial_error->base));
761
+ napi_set_named_property(env, result, "type", type);
762
+
763
+ napi_value message = CreateStringFromHbString(env, render_conflicting_partial_error->base.message);
764
+ napi_set_named_property(env, result, "message", message);
765
+
766
+ napi_value location = CreateLocation(env, render_conflicting_partial_error->base.location);
767
+ napi_set_named_property(env, result, "location", location);
768
+
769
+ napi_value positional_partial = CreateStringFromHbString(env, render_conflicting_partial_error->positional_partial);
770
+ napi_set_named_property(env, result, "positional_partial", positional_partial);
771
+
772
+ napi_value keyword_partial = CreateStringFromHbString(env, render_conflicting_partial_error->keyword_partial);
773
+ napi_set_named_property(env, result, "keyword_partial", keyword_partial);
774
+
775
+
776
+ return result;
777
+ }
778
+
779
+ napi_value RenderInvalidAsOptionErrorFromCStruct(napi_env env, RENDER_INVALID_AS_OPTION_ERROR_T* render_invalid_as_option_error) {
780
+ if (!render_invalid_as_option_error) {
781
+ napi_value null_value;
782
+ napi_get_null(env, &null_value);
783
+ return null_value;
784
+ }
785
+
786
+ napi_value result;
787
+ napi_create_object(env, &result);
788
+
789
+ napi_value type = CreateStringFromHbString(env, error_type_to_string(&render_invalid_as_option_error->base));
790
+ napi_set_named_property(env, result, "type", type);
791
+
792
+ napi_value message = CreateStringFromHbString(env, render_invalid_as_option_error->base.message);
793
+ napi_set_named_property(env, result, "message", message);
794
+
795
+ napi_value location = CreateLocation(env, render_invalid_as_option_error->base.location);
796
+ napi_set_named_property(env, result, "location", location);
797
+
798
+ napi_value as_value = CreateStringFromHbString(env, render_invalid_as_option_error->as_value);
799
+ napi_set_named_property(env, result, "as_value", as_value);
800
+
801
+
802
+ return result;
803
+ }
804
+
805
+ napi_value RenderObjectAndCollectionErrorFromCStruct(napi_env env, RENDER_OBJECT_AND_COLLECTION_ERROR_T* render_object_and_collection_error) {
806
+ if (!render_object_and_collection_error) {
807
+ napi_value null_value;
808
+ napi_get_null(env, &null_value);
809
+ return null_value;
810
+ }
811
+
812
+ napi_value result;
813
+ napi_create_object(env, &result);
814
+
815
+ napi_value type = CreateStringFromHbString(env, error_type_to_string(&render_object_and_collection_error->base));
816
+ napi_set_named_property(env, result, "type", type);
817
+
818
+ napi_value message = CreateStringFromHbString(env, render_object_and_collection_error->base.message);
819
+ napi_set_named_property(env, result, "message", message);
820
+
821
+ napi_value location = CreateLocation(env, render_object_and_collection_error->base.location);
822
+ napi_set_named_property(env, result, "location", location);
823
+
824
+
825
+ return result;
826
+ }
827
+
828
+ napi_value RenderLayoutWithoutBlockErrorFromCStruct(napi_env env, RENDER_LAYOUT_WITHOUT_BLOCK_ERROR_T* render_layout_without_block_error) {
829
+ if (!render_layout_without_block_error) {
830
+ napi_value null_value;
831
+ napi_get_null(env, &null_value);
832
+ return null_value;
833
+ }
834
+
835
+ napi_value result;
836
+ napi_create_object(env, &result);
837
+
838
+ napi_value type = CreateStringFromHbString(env, error_type_to_string(&render_layout_without_block_error->base));
839
+ napi_set_named_property(env, result, "type", type);
840
+
841
+ napi_value message = CreateStringFromHbString(env, render_layout_without_block_error->base.message);
842
+ napi_set_named_property(env, result, "message", message);
843
+
844
+ napi_value location = CreateLocation(env, render_layout_without_block_error->base.location);
845
+ napi_set_named_property(env, result, "location", location);
846
+
847
+ napi_value layout = CreateStringFromHbString(env, render_layout_without_block_error->layout);
848
+ napi_set_named_property(env, result, "layout", layout);
849
+
850
+
851
+ return result;
852
+ }
853
+
672
854
 
673
855
  napi_value ErrorsArrayFromCArray(napi_env env, hb_array_T* array) {
674
856
  napi_value result;
@@ -764,6 +946,27 @@ napi_value ErrorFromCStruct(napi_env env, ERROR_T* error) {
764
946
  case NESTED_ERB_TAG_ERROR:
765
947
  return NestedERBTagErrorFromCStruct(env, (NESTED_ERB_TAG_ERROR_T*) error);
766
948
  break;
949
+ case RENDER_AMBIGUOUS_LOCALS_ERROR:
950
+ return RenderAmbiguousLocalsErrorFromCStruct(env, (RENDER_AMBIGUOUS_LOCALS_ERROR_T*) error);
951
+ break;
952
+ case RENDER_MISSING_LOCALS_ERROR:
953
+ return RenderMissingLocalsErrorFromCStruct(env, (RENDER_MISSING_LOCALS_ERROR_T*) error);
954
+ break;
955
+ case RENDER_NO_ARGUMENTS_ERROR:
956
+ return RenderNoArgumentsErrorFromCStruct(env, (RENDER_NO_ARGUMENTS_ERROR_T*) error);
957
+ break;
958
+ case RENDER_CONFLICTING_PARTIAL_ERROR:
959
+ return RenderConflictingPartialErrorFromCStruct(env, (RENDER_CONFLICTING_PARTIAL_ERROR_T*) error);
960
+ break;
961
+ case RENDER_INVALID_AS_OPTION_ERROR:
962
+ return RenderInvalidAsOptionErrorFromCStruct(env, (RENDER_INVALID_AS_OPTION_ERROR_T*) error);
963
+ break;
964
+ case RENDER_OBJECT_AND_COLLECTION_ERROR:
965
+ return RenderObjectAndCollectionErrorFromCStruct(env, (RENDER_OBJECT_AND_COLLECTION_ERROR_T*) error);
966
+ break;
967
+ case RENDER_LAYOUT_WITHOUT_BLOCK_ERROR:
968
+ return RenderLayoutWithoutBlockErrorFromCStruct(env, (RENDER_LAYOUT_WITHOUT_BLOCK_ERROR_T*) error);
969
+ break;
767
970
  default:
768
971
  napi_value null_value;
769
972
  napi_get_null(env, &null_value);
@@ -1,5 +1,5 @@
1
1
  // NOTE: This file is generated by the templates/template.rb script and should not
2
- // be modified manually. See /Users/marcoroth/Development/herb-release-0.9.0/templates/javascript/packages/node/extension/error_helpers.h.erb
2
+ // be modified manually. See /Users/marcoroth/Development/herb-release-0.9.1/templates/javascript/packages/node/extension/error_helpers.h.erb
3
3
 
4
4
  #ifndef HERB_EXTENSION_ERRORS_H
5
5
  #define HERB_EXTENSION_ERRORS_H
@@ -36,5 +36,12 @@ napi_value MissingAttributeValueErrorFromCStruct(napi_env env, MISSING_ATTRIBUTE
36
36
  napi_value UnclosedERBTagErrorFromCStruct(napi_env env, UNCLOSED_ERB_TAG_ERROR_T* unclosed_erb_tag_error);
37
37
  napi_value StrayERBClosingTagErrorFromCStruct(napi_env env, STRAY_ERB_CLOSING_TAG_ERROR_T* stray_erb_closing_tag_error);
38
38
  napi_value NestedERBTagErrorFromCStruct(napi_env env, NESTED_ERB_TAG_ERROR_T* nested_erb_tag_error);
39
+ napi_value RenderAmbiguousLocalsErrorFromCStruct(napi_env env, RENDER_AMBIGUOUS_LOCALS_ERROR_T* render_ambiguous_locals_error);
40
+ napi_value RenderMissingLocalsErrorFromCStruct(napi_env env, RENDER_MISSING_LOCALS_ERROR_T* render_missing_locals_error);
41
+ napi_value RenderNoArgumentsErrorFromCStruct(napi_env env, RENDER_NO_ARGUMENTS_ERROR_T* render_no_arguments_error);
42
+ napi_value RenderConflictingPartialErrorFromCStruct(napi_env env, RENDER_CONFLICTING_PARTIAL_ERROR_T* render_conflicting_partial_error);
43
+ napi_value RenderInvalidAsOptionErrorFromCStruct(napi_env env, RENDER_INVALID_AS_OPTION_ERROR_T* render_invalid_as_option_error);
44
+ napi_value RenderObjectAndCollectionErrorFromCStruct(napi_env env, RENDER_OBJECT_AND_COLLECTION_ERROR_T* render_object_and_collection_error);
45
+ napi_value RenderLayoutWithoutBlockErrorFromCStruct(napi_env env, RENDER_LAYOUT_WITHOUT_BLOCK_ERROR_T* render_layout_without_block_error);
39
46
 
40
47
  #endif
@@ -173,12 +173,13 @@ napi_value CreateParseResult(napi_env env, AST_DOCUMENT_NODE_T* root, napi_value
173
173
  napi_value options_object;
174
174
  napi_create_object(env, &options_object);
175
175
 
176
- napi_value strict_value, track_whitespace_value, analyze_value, action_view_helpers_value, prism_program_value, prism_nodes_value, prism_nodes_deep_value;
176
+ napi_value strict_value, track_whitespace_value, analyze_value, action_view_helpers_value, render_nodes_value, prism_program_value, prism_nodes_value, prism_nodes_deep_value;
177
177
 
178
178
  napi_get_boolean(env, options->strict, &strict_value);
179
179
  napi_get_boolean(env, options->track_whitespace, &track_whitespace_value);
180
180
  napi_get_boolean(env, options->analyze, &analyze_value);
181
181
  napi_get_boolean(env, options->action_view_helpers, &action_view_helpers_value);
182
+ napi_get_boolean(env, options->render_nodes, &render_nodes_value);
182
183
  napi_get_boolean(env, options->prism_program, &prism_program_value);
183
184
  napi_get_boolean(env, options->prism_nodes, &prism_nodes_value);
184
185
  napi_get_boolean(env, options->prism_nodes_deep, &prism_nodes_deep_value);
@@ -187,6 +188,7 @@ napi_value CreateParseResult(napi_env env, AST_DOCUMENT_NODE_T* root, napi_value
187
188
  napi_set_named_property(env, options_object, "track_whitespace", track_whitespace_value);
188
189
  napi_set_named_property(env, options_object, "analyze", analyze_value);
189
190
  napi_set_named_property(env, options_object, "action_view_helpers", action_view_helpers_value);
191
+ napi_set_named_property(env, options_object, "render_nodes", render_nodes_value);
190
192
  napi_set_named_property(env, options_object, "prism_program", prism_program_value);
191
193
  napi_set_named_property(env, options_object, "prism_nodes", prism_nodes_value);
192
194
  napi_set_named_property(env, options_object, "prism_nodes_deep", prism_nodes_deep_value);
@@ -119,6 +119,17 @@ napi_value Herb_parse(napi_env env, napi_callback_info info) {
119
119
  parser_options.action_view_helpers = action_view_helpers_value;
120
120
  }
121
121
 
122
+ napi_value render_nodes_prop;
123
+ bool has_render_nodes_prop;
124
+ napi_has_named_property(env, args[1], "render_nodes", &has_render_nodes_prop);
125
+
126
+ if (has_render_nodes_prop) {
127
+ napi_get_named_property(env, args[1], "render_nodes", &render_nodes_prop);
128
+ bool render_nodes_value;
129
+ napi_get_value_bool(env, render_nodes_prop, &render_nodes_value);
130
+ parser_options.render_nodes = render_nodes_value;
131
+ }
132
+
122
133
  napi_value prism_nodes_prop;
123
134
  bool has_prism_nodes_prop;
124
135
  napi_has_named_property(env, args[1], "prism_nodes", &has_prism_nodes_prop);
@@ -265,7 +265,7 @@ bool has_html_attributes_in_call(pm_call_node_t* call_node) {
265
265
 
266
266
  pm_node_t* last_argument = arguments->arguments.nodes[arguments->arguments.size - 1];
267
267
 
268
- return last_argument && last_argument->type == PM_KEYWORD_HASH_NODE;
268
+ return last_argument && (last_argument->type == PM_KEYWORD_HASH_NODE || last_argument->type == PM_HASH_NODE);
269
269
  }
270
270
 
271
271
  hb_array_T* extract_html_attributes_from_call_node(
@@ -280,6 +280,19 @@ hb_array_T* extract_html_attributes_from_call_node(
280
280
  pm_arguments_node_t* arguments = call_node->arguments;
281
281
  pm_node_t* last_argument = arguments->arguments.nodes[arguments->arguments.size - 1];
282
282
 
283
+ if (last_argument->type == PM_HASH_NODE) {
284
+ pm_hash_node_t* hash_node = (pm_hash_node_t*) last_argument;
285
+ pm_keyword_hash_node_t synthetic = { .base = hash_node->base, .elements = hash_node->elements };
286
+
287
+ return extract_html_attributes_from_keyword_hash(
288
+ &synthetic,
289
+ source,
290
+ original_source,
291
+ erb_content_offset,
292
+ allocator
293
+ );
294
+ }
295
+
283
296
  return extract_html_attributes_from_keyword_hash(
284
297
  (pm_keyword_hash_node_t*) last_argument,
285
298
  source,
@@ -38,23 +38,31 @@ char* extract_content_tag_name(pm_call_node_t* call_node, pm_parser_t* parser, h
38
38
  char* extract_content_tag_content(pm_call_node_t* call_node, pm_parser_t* parser, hb_allocator_T* allocator) {
39
39
  (void) parser;
40
40
 
41
- if (!call_node || !call_node->arguments) { return NULL; }
41
+ if (!call_node) { return NULL; }
42
42
 
43
- pm_arguments_node_t* arguments = call_node->arguments;
44
- if (arguments->arguments.size < 2) { return NULL; }
43
+ char* block_content = extract_inline_block_content(call_node, allocator);
44
+ if (block_content) { return block_content; }
45
45
 
46
- pm_node_t* second_argument = arguments->arguments.nodes[1];
46
+ if (call_node->arguments) {
47
+ pm_arguments_node_t* arguments = call_node->arguments;
47
48
 
48
- if (second_argument->type == PM_KEYWORD_HASH_NODE) { return NULL; }
49
+ if (arguments->arguments.size >= 2) {
50
+ pm_node_t* second_argument = arguments->arguments.nodes[1];
49
51
 
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);
52
+ if (second_argument->type != PM_KEYWORD_HASH_NODE) {
53
+ if (second_argument->type == PM_STRING_NODE) {
54
+ pm_string_node_t* string_node = (pm_string_node_t*) second_argument;
55
+ size_t length = pm_string_length(&string_node->unescaped);
56
+ return hb_allocator_strndup(allocator, (const char*) pm_string_source(&string_node->unescaped), length);
57
+ }
58
+
59
+ size_t source_length = second_argument->location.end - second_argument->location.start;
60
+ return hb_allocator_strndup(allocator, (const char*) second_argument->location.start, source_length);
61
+ }
62
+ }
54
63
  }
55
64
 
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);
65
+ return NULL;
58
66
  }
59
67
 
60
68
  bool content_tag_supports_block(void) {
@@ -56,7 +56,12 @@ char* extract_link_to_tag_name(pm_call_node_t* call_node, pm_parser_t* parser, h
56
56
  char* extract_link_to_content(pm_call_node_t* call_node, pm_parser_t* parser, hb_allocator_T* allocator) {
57
57
  (void) parser;
58
58
 
59
- if (!call_node || !call_node->arguments) { return NULL; }
59
+ if (!call_node) { return NULL; }
60
+
61
+ char* block_content = extract_inline_block_content(call_node, allocator);
62
+ if (block_content) { return block_content; }
63
+
64
+ if (!call_node->arguments) { return NULL; }
60
65
 
61
66
  pm_arguments_node_t* arguments = call_node->arguments;
62
67
  if (!arguments->arguments.size) { return NULL; }
@@ -96,6 +101,25 @@ char* extract_link_to_href(pm_call_node_t* call_node, pm_parser_t* parser, hb_al
96
101
  if (!call_node || !call_node->arguments) { return NULL; }
97
102
 
98
103
  pm_arguments_node_t* arguments = call_node->arguments;
104
+ bool has_inline_block = call_node->block && call_node->block->type == PM_BLOCK_NODE;
105
+
106
+ if (has_inline_block && arguments->arguments.size >= 1) {
107
+ pm_node_t* first_argument = arguments->arguments.nodes[0];
108
+
109
+ if (first_argument->type == PM_STRING_NODE) {
110
+ pm_string_node_t* string_node = (pm_string_node_t*) first_argument;
111
+ size_t length = pm_string_length(&string_node->unescaped);
112
+ return hb_allocator_strndup(allocator, (const char*) pm_string_source(&string_node->unescaped), length);
113
+ }
114
+
115
+ size_t source_length = first_argument->location.end - first_argument->location.start;
116
+
117
+ if (is_route_helper_node(first_argument, parser)) {
118
+ return hb_allocator_strndup(allocator, (const char*) first_argument->location.start, source_length);
119
+ }
120
+
121
+ return wrap_in_url_for((const char*) first_argument->location.start, source_length, allocator);
122
+ }
99
123
 
100
124
  // Format: "url_for(<expression>)"
101
125
  if (arguments->arguments.size == 1) {
@@ -58,3 +58,26 @@ tag_helper_handler_T* get_tag_helper_handlers(void) {
58
58
  size_t get_tag_helper_handlers_count(void) {
59
59
  return handlers_count;
60
60
  }
61
+
62
+ char* extract_inline_block_content(pm_call_node_t* call_node, hb_allocator_T* allocator) {
63
+ if (!call_node || !call_node->block || call_node->block->type != PM_BLOCK_NODE) { return NULL; }
64
+
65
+ pm_block_node_t* block_node = (pm_block_node_t*) call_node->block;
66
+
67
+ if (!block_node->body || block_node->body->type != PM_STATEMENTS_NODE) { return NULL; }
68
+
69
+ pm_statements_node_t* statements = (pm_statements_node_t*) block_node->body;
70
+
71
+ if (statements->body.size != 1) { return NULL; }
72
+
73
+ pm_node_t* statement = statements->body.nodes[0];
74
+
75
+ if (statement->type == PM_STRING_NODE) {
76
+ pm_string_node_t* string_node = (pm_string_node_t*) statement;
77
+ size_t length = pm_string_length(&string_node->unescaped);
78
+ return hb_allocator_strndup(allocator, (const char*) pm_string_source(&string_node->unescaped), length);
79
+ }
80
+
81
+ size_t source_length = statement->location.end - statement->location.start;
82
+ return hb_allocator_strndup(allocator, (const char*) statement->location.start, source_length);
83
+ }
@@ -36,17 +36,23 @@ char* extract_tag_dot_name(pm_call_node_t* call_node, pm_parser_t* parser, hb_al
36
36
  char* extract_tag_dot_content(pm_call_node_t* call_node, pm_parser_t* parser, hb_allocator_T* allocator) {
37
37
  (void) parser;
38
38
 
39
- if (!call_node || !call_node->arguments) { return NULL; }
39
+ if (!call_node) { return NULL; }
40
40
 
41
- pm_arguments_node_t* arguments = call_node->arguments;
42
- if (!arguments->arguments.size) { return NULL; }
41
+ char* block_content = extract_inline_block_content(call_node, allocator);
42
+ if (block_content) { return block_content; }
43
43
 
44
- pm_node_t* first_argument = arguments->arguments.nodes[0];
44
+ if (call_node->arguments) {
45
+ pm_arguments_node_t* arguments = call_node->arguments;
45
46
 
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);
47
+ if (arguments->arguments.size) {
48
+ pm_node_t* first_argument = arguments->arguments.nodes[0];
49
+
50
+ if (first_argument->type == PM_STRING_NODE) {
51
+ pm_string_node_t* string_node = (pm_string_node_t*) first_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
+ }
50
56
  }
51
57
 
52
58
  return NULL;
@@ -38,4 +38,6 @@ void tag_helper_info_free(tag_helper_info_T** info);
38
38
  tag_helper_handler_T* get_tag_helper_handlers(void);
39
39
  size_t get_tag_helper_handlers_count(void);
40
40
 
41
+ char* extract_inline_block_content(pm_call_node_t* call_node, hb_allocator_T* allocator);
42
+
41
43
  #endif
@@ -262,13 +262,29 @@ static AST_NODE_T* transform_tag_helper_with_attributes(
262
262
  if (parse_context->info->call_node && handler->extract_content) {
263
263
  helper_content = handler->extract_content(parse_context->info->call_node, &parse_context->parser, allocator);
264
264
 
265
- if (helper_content && parse_context->info->call_node->arguments) {
266
- if (strcmp(handler->name, "content_tag") == 0 && parse_context->info->call_node->arguments->arguments.size >= 2) {
267
- content_is_ruby_expression =
268
- (parse_context->info->call_node->arguments->arguments.nodes[1]->type != PM_STRING_NODE);
269
- } else if (parse_context->info->call_node->arguments->arguments.size >= 1) {
270
- content_is_ruby_expression =
271
- (parse_context->info->call_node->arguments->arguments.nodes[0]->type != PM_STRING_NODE);
265
+ if (helper_content) {
266
+ pm_call_node_t* call = parse_context->info->call_node;
267
+
268
+ if (call->arguments) {
269
+ if (strcmp(handler->name, "content_tag") == 0 && call->arguments->arguments.size >= 2
270
+ && call->arguments->arguments.nodes[1]->type != PM_KEYWORD_HASH_NODE) {
271
+ content_is_ruby_expression = (call->arguments->arguments.nodes[1]->type != PM_STRING_NODE);
272
+ } else if (strcmp(handler->name, "content_tag") != 0 && call->arguments->arguments.size >= 1
273
+ && call->arguments->arguments.nodes[0]->type != PM_KEYWORD_HASH_NODE) {
274
+ content_is_ruby_expression = (call->arguments->arguments.nodes[0]->type != PM_STRING_NODE);
275
+ }
276
+ }
277
+
278
+ if (!content_is_ruby_expression && call->block && call->block->type == PM_BLOCK_NODE) {
279
+ pm_block_node_t* block_node = (pm_block_node_t*) call->block;
280
+
281
+ if (block_node->body && block_node->body->type == PM_STATEMENTS_NODE) {
282
+ pm_statements_node_t* statements = (pm_statements_node_t*) block_node->body;
283
+
284
+ if (statements->body.size == 1) {
285
+ content_is_ruby_expression = (statements->body.nodes[0]->type != PM_STRING_NODE);
286
+ }
287
+ }
272
288
  }
273
289
  }
274
290
  }
@@ -492,8 +508,12 @@ static AST_NODE_T* transform_link_to_helper(
492
508
 
493
509
  hb_array_T* attributes = NULL;
494
510
  pm_arguments_node_t* link_arguments = info->call_node->arguments;
495
- bool keyword_hash_is_url = link_arguments && link_arguments->arguments.size == 2
496
- && link_arguments->arguments.nodes[1]->type == PM_KEYWORD_HASH_NODE;
511
+ bool has_inline_block = info->call_node->block && info->call_node->block->type == PM_BLOCK_NODE;
512
+
513
+ bool second_arg_is_hash = link_arguments && link_arguments->arguments.size == 2
514
+ && (link_arguments->arguments.nodes[1]->type == PM_KEYWORD_HASH_NODE
515
+ || link_arguments->arguments.nodes[1]->type == PM_HASH_NODE);
516
+ bool keyword_hash_is_url = !has_inline_block && second_arg_is_hash;
497
517
 
498
518
  if (!keyword_hash_is_url) {
499
519
  attributes = extract_html_attributes_from_call_node(
@@ -507,6 +527,38 @@ static AST_NODE_T* transform_link_to_helper(
507
527
 
508
528
  if (!attributes) { attributes = hb_array_init(4, allocator); }
509
529
 
530
+ if (has_inline_block && link_arguments && link_arguments->arguments.size >= 2) {
531
+ pm_node_t* second_arg = link_arguments->arguments.nodes[1];
532
+
533
+ if (second_arg->type != PM_KEYWORD_HASH_NODE && second_arg->type != PM_HASH_NODE
534
+ && second_arg->type != PM_STRING_NODE && second_arg->type != PM_SYMBOL_NODE) {
535
+ size_t source_length = second_arg->location.end - second_arg->location.start;
536
+ char* content = hb_allocator_strndup(allocator, (const char*) second_arg->location.start, source_length);
537
+
538
+ if (content) {
539
+ position_T position = prism_location_to_position_with_offset(
540
+ &second_arg->location,
541
+ parse_context->original_source,
542
+ parse_context->erb_content_offset,
543
+ parse_context->prism_source
544
+ );
545
+
546
+ AST_RUBY_HTML_ATTRIBUTES_SPLAT_NODE_T* splat_node = ast_ruby_html_attributes_splat_node_init(
547
+ hb_string_from_c_string(content),
548
+ HB_STRING_EMPTY,
549
+ position,
550
+ position,
551
+ hb_array_init(0, allocator),
552
+ allocator
553
+ );
554
+
555
+ if (splat_node) { hb_array_append(attributes, (AST_NODE_T*) splat_node); }
556
+
557
+ hb_allocator_dealloc(allocator, content);
558
+ }
559
+ }
560
+ }
561
+
510
562
  // `method:` implies `rel="nofollow"`
511
563
  bool has_data_method = false;
512
564
  hb_string_T data_method_string = hb_string("data-method");
@@ -549,7 +601,12 @@ static AST_NODE_T* transform_link_to_helper(
549
601
  pm_arguments_node_t* arguments = info->call_node->arguments;
550
602
  pm_node_t* href_argument = NULL;
551
603
 
552
- if (arguments->arguments.size >= 2) {
604
+ if (has_inline_block) {
605
+ if (arguments->arguments.size >= 1) {
606
+ href_argument = arguments->arguments.nodes[0];
607
+ href_is_ruby_expression = (href_argument->type != PM_STRING_NODE);
608
+ }
609
+ } else if (arguments->arguments.size >= 2) {
553
610
  href_argument = arguments->arguments.nodes[1];
554
611
  href_is_ruby_expression = (href_argument->type != PM_STRING_NODE);
555
612
  } else if (arguments->arguments.size == 1) {
@@ -602,7 +659,17 @@ static AST_NODE_T* transform_link_to_helper(
602
659
  if (info->content) {
603
660
  bool content_is_ruby_expression = false;
604
661
 
605
- if (info->call_node && info->call_node->arguments && info->call_node->arguments->arguments.size >= 1) {
662
+ if (has_inline_block && info->call_node->block && info->call_node->block->type == PM_BLOCK_NODE) {
663
+ pm_block_node_t* block_node = (pm_block_node_t*) info->call_node->block;
664
+
665
+ if (block_node->body && block_node->body->type == PM_STATEMENTS_NODE) {
666
+ pm_statements_node_t* statements = (pm_statements_node_t*) block_node->body;
667
+
668
+ if (statements->body.size == 1) {
669
+ content_is_ruby_expression = (statements->body.nodes[0]->type != PM_STRING_NODE);
670
+ }
671
+ }
672
+ } else if (info->call_node && info->call_node->arguments && info->call_node->arguments->arguments.size >= 1) {
606
673
  pm_node_t* first_argument = info->call_node->arguments->arguments.nodes[0];
607
674
  content_is_ruby_expression = (first_argument->type != PM_STRING_NODE);
608
675
  }
@@ -8,6 +8,7 @@
8
8
  #include "../include/analyze/control_type.h"
9
9
  #include "../include/analyze/helpers.h"
10
10
  #include "../include/analyze/invalid_structures.h"
11
+ #include "../include/analyze/render_nodes.h"
11
12
  #include "../include/ast_node.h"
12
13
  #include "../include/ast_nodes.h"
13
14
  #include "../include/errors.h"
@@ -859,6 +860,8 @@ void herb_analyze_parse_tree(
859
860
 
860
861
  herb_visit_node((AST_NODE_T*) document, transform_erb_nodes, &context);
861
862
 
863
+ if (options && options->render_nodes) { herb_visit_node((AST_NODE_T*) document, transform_render_nodes, &context); }
864
+
862
865
  if (options && options->action_view_helpers) {
863
866
  herb_visit_node((AST_NODE_T*) document, transform_tag_helper_nodes, &context);
864
867
  }
@@ -1,5 +1,5 @@
1
1
  // NOTE: This file is generated by the templates/template.rb script and should not
2
- // be modified manually. See /Users/marcoroth/Development/herb-release-0.9.0/templates/src/analyze/missing_end.c.erb
2
+ // be modified manually. See /Users/marcoroth/Development/herb-release-0.9.1/templates/src/analyze/missing_end.c.erb
3
3
 
4
4
  #include "../include/analyze/helpers.h"
5
5
  #include "../include/errors.h"