ripple 0.2.199 → 0.2.201

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 (55) hide show
  1. package/package.json +5 -4
  2. package/src/compiler/index.d.ts +1 -5
  3. package/src/compiler/phases/1-parse/index.js +145 -11
  4. package/src/compiler/phases/2-analyze/index.js +24 -8
  5. package/src/compiler/phases/2-analyze/prune.js +5 -3
  6. package/src/compiler/phases/3-transform/client/index.js +312 -165
  7. package/src/compiler/phases/3-transform/segments.js +220 -70
  8. package/src/compiler/phases/3-transform/server/index.js +227 -77
  9. package/src/compiler/source-map-utils.js +74 -10
  10. package/src/compiler/types/index.d.ts +63 -21
  11. package/src/compiler/types/parse.d.ts +3 -1
  12. package/src/compiler/utils.js +34 -0
  13. package/src/helpers.d.ts +5 -0
  14. package/src/runtime/index-server.js +27 -47
  15. package/src/runtime/internal/client/composite.js +5 -0
  16. package/src/runtime/internal/client/events.js +1 -9
  17. package/src/runtime/internal/client/for.js +6 -4
  18. package/src/runtime/internal/client/hydration.js +2 -2
  19. package/src/runtime/internal/client/index.js +1 -1
  20. package/src/runtime/internal/client/operations.js +4 -4
  21. package/src/runtime/internal/client/render.js +0 -2
  22. package/src/runtime/internal/client/template.js +9 -1
  23. package/src/runtime/internal/client/types.d.ts +18 -0
  24. package/src/runtime/internal/client/utils.js +1 -1
  25. package/src/runtime/internal/server/index.js +106 -3
  26. package/src/utils/builders.js +25 -5
  27. package/tests/client/basic/basic.attributes.test.ripple +1 -1
  28. package/tests/client/basic/basic.components.test.ripple +47 -0
  29. package/tests/client/basic/basic.rendering.test.ripple +1 -1
  30. package/tests/client/composite/composite.props.test.ripple +49 -4
  31. package/tests/client/dynamic-elements.test.ripple +44 -0
  32. package/tests/client/switch.test.ripple +40 -0
  33. package/tests/client/tsconfig.json +11 -0
  34. package/tests/client.d.ts +5 -22
  35. package/tests/common.d.ts +24 -0
  36. package/tests/hydration/compiled/server/basic.js +109 -24
  37. package/tests/hydration/compiled/server/events.js +161 -72
  38. package/tests/hydration/compiled/server/for.js +202 -102
  39. package/tests/hydration/compiled/server/if.js +130 -50
  40. package/tests/hydration/compiled/server/reactivity.js +51 -12
  41. package/tests/server/__snapshots__/compiler.test.ripple.snap +11 -4
  42. package/tests/server/basic.attributes.test.ripple +459 -0
  43. package/tests/server/basic.components.test.ripple +237 -0
  44. package/tests/server/basic.test.ripple +25 -0
  45. package/tests/server/compiler.test.ripple +2 -3
  46. package/tests/server/composite.props.test.ripple +161 -0
  47. package/tests/server/dynamic-elements.test.ripple +438 -0
  48. package/tests/server/head.test.ripple +102 -0
  49. package/tests/server/switch.test.ripple +40 -0
  50. package/tests/server/tsconfig.json +11 -0
  51. package/tests/server.d.ts +7 -0
  52. package/tests/setup-client.js +6 -2
  53. package/tests/setup-server.js +16 -0
  54. package/types/index.d.ts +2 -2
  55. package/types/server.d.ts +4 -3
@@ -24,6 +24,8 @@ import {
24
24
  is_void_element,
25
25
  normalize_children,
26
26
  is_binding_function,
27
+ build_getter,
28
+ is_element_dynamic,
27
29
  } from '../../../utils.js';
28
30
  import { escape } from '../../../../utils/escaping.js';
29
31
  import { is_event_attribute } from '../../../../utils/events.js';
@@ -69,9 +71,27 @@ function transform_children(children, context) {
69
71
  }
70
72
  }
71
73
  } else {
72
- visit(node, { ...state });
74
+ visit(node, state);
73
75
  }
74
76
  }
77
+
78
+ const head_elements = /** @type {AST.Element[]} */ (
79
+ children.filter(
80
+ (node) => node.type === 'Element' && node.id.type === 'Identifier' && node.id.name === 'head',
81
+ )
82
+ );
83
+
84
+ if (head_elements.length) {
85
+ state.init?.push(
86
+ b.stmt(b.assignment('=', b.member(b.id('__output'), b.id('target')), b.literal('head'))),
87
+ );
88
+ for (const head_element of head_elements) {
89
+ transform_children(head_element.children, context);
90
+ }
91
+ state.init?.push(
92
+ b.stmt(b.assignment('=', b.member(b.id('__output'), b.id('target')), b.literal(null))),
93
+ );
94
+ }
75
95
  }
76
96
 
77
97
  /**
@@ -108,15 +128,19 @@ const visitors = {
108
128
  Identifier(node, context) {
109
129
  const parent = /** @type {AST.Node} */ (context.path.at(-1));
110
130
 
111
- if (is_reference(node, parent) && node.tracked) {
112
- const is_right_side_of_assignment =
113
- parent.type === 'AssignmentExpression' && parent.right === node;
114
- if (
115
- (parent.type !== 'AssignmentExpression' && parent.type !== 'UpdateExpression') ||
116
- is_right_side_of_assignment
117
- ) {
118
- return b.call('_$_.get', node);
131
+ if (is_reference(node, parent)) {
132
+ if (node.tracked) {
133
+ const is_right_side_of_assignment =
134
+ parent.type === 'AssignmentExpression' && parent.right === node;
135
+ if (
136
+ (parent.type !== 'AssignmentExpression' && parent.type !== 'UpdateExpression') ||
137
+ is_right_side_of_assignment
138
+ ) {
139
+ return b.call('_$_.get', node);
140
+ }
119
141
  }
142
+
143
+ return node;
120
144
  }
121
145
  },
122
146
 
@@ -459,41 +483,73 @@ const visitors = {
459
483
  Element(node, context) {
460
484
  const { state, visit } = context;
461
485
 
462
- const is_dom_element = is_element_dom_element(node);
486
+ const dynamic_name = state.dynamicElementName;
487
+ if (dynamic_name) {
488
+ state.dynamicElementName = undefined;
489
+ }
490
+
491
+ const is_dom_element = !!dynamic_name || is_element_dom_element(node);
463
492
  const is_spreading = node.attributes.some((attr) => attr.type === 'SpreadAttribute');
464
493
  /** @type {(AST.Property | AST.SpreadElement)[] | null} */
465
494
  const spread_attributes = is_spreading ? [] : null;
466
- const child_namespace = is_dom_element
467
- ? determine_namespace_for_children(node.id.name, state.namespace)
468
- : state.namespace;
495
+ const child_namespace =
496
+ !dynamic_name && is_dom_element
497
+ ? determine_namespace_for_children(
498
+ /** @type {AST.Identifier} */ (node.id).name,
499
+ state.namespace,
500
+ )
501
+ : state.namespace;
469
502
 
470
503
  if (is_dom_element) {
471
- const is_void = is_void_element(node.id.name);
504
+ const is_void = dynamic_name
505
+ ? false
506
+ : is_void_element(/** @type {AST.Identifier} */ (node.id).name);
507
+ const tag_name = dynamic_name
508
+ ? dynamic_name
509
+ : b.literal(/** @type {AST.Identifier} */ (node.id).name);
510
+ /** @type {AST.CSS.StyleSheet['hash'] | null} */
511
+ const scoping_hash =
512
+ state.applyParentCssScope ??
513
+ (node.metadata.scoped && state.component?.css
514
+ ? /** @type {AST.CSS.StyleSheet} */ (state.component?.css).hash
515
+ : null);
472
516
 
473
517
  state.init?.push(
474
- b.stmt(b.call(b.member(b.id('__output'), b.id('push')), b.literal(`<${node.id.name}`))),
518
+ b.stmt(
519
+ b.call(
520
+ b.member(b.id('__output'), b.id('push')),
521
+ dynamic_name
522
+ ? b.template([b.quasi('<', false), b.quasi('', false)], [tag_name])
523
+ : b.literal('<' + /** @type {AST.Literal} */ (tag_name).value),
524
+ ),
525
+ ),
475
526
  );
476
527
  let class_attribute = null;
477
528
 
478
529
  /**
479
530
  * @param {string} name
480
- * @param {string | number | bigint | boolean | RegExp | null | undefined} value
531
+ * @param {string | number | bigint | boolean | RegExp | null | undefined} value
532
+ * @param {'push' | 'unshift'} [spread_method]
481
533
  */
482
- const handle_static_attr = (name, value) => {
483
- const attr_str = ` ${name}${
484
- is_boolean_attribute(name) && value === true
485
- ? ''
486
- : `="${value === true ? '' : escape_html(value, true)}"`
487
- }`;
488
-
534
+ const handle_static_attr = (name, value, spread_method = 'push') => {
489
535
  if (is_spreading) {
490
536
  // For spread attributes, store just the actual value, not the full attribute string
491
537
  const actual_value =
492
538
  is_boolean_attribute(name) && value === true
493
539
  ? b.literal(true)
494
540
  : b.literal(value === true ? '' : value);
495
- spread_attributes?.push(b.prop('init', b.literal(name), actual_value));
541
+
542
+ // spread_attributes cannot be null based on is_spreading === true
543
+ /** @type {(AST.Property | AST.SpreadElement)[]} */ (spread_attributes)[spread_method](
544
+ b.prop('init', b.literal(name), actual_value),
545
+ );
496
546
  } else {
547
+ const attr_str = ` ${name}${
548
+ is_boolean_attribute(name) && value === true
549
+ ? ''
550
+ : `="${value === true ? '' : escape_html(value, true)}"`
551
+ }`;
552
+
497
553
  state.init?.push(
498
554
  b.stmt(b.call(b.member(b.id('__output'), b.id('push')), b.literal(attr_str))),
499
555
  );
@@ -533,7 +589,12 @@ const visitors = {
533
589
  b.stmt(
534
590
  b.call(
535
591
  b.member(b.id('__output'), b.id('push')),
536
- b.call('_$_.attr', b.literal(name), expression),
592
+ b.call(
593
+ '_$_.attr',
594
+ b.literal(name),
595
+ expression,
596
+ b.literal(is_boolean_attribute(name)),
597
+ ),
537
598
  ),
538
599
  ),
539
600
  );
@@ -550,8 +611,8 @@ const visitors = {
550
611
  if (attr_value.type === 'Literal') {
551
612
  let value = attr_value.value;
552
613
 
553
- if (node.metadata.scoped && state.component?.css) {
554
- value = `${state.component.css.hash} ${value}`;
614
+ if (scoping_hash) {
615
+ value = `${scoping_hash} ${value}`;
555
616
  }
556
617
 
557
618
  handle_static_attr(class_attribute.name.name, value);
@@ -561,9 +622,9 @@ const visitors = {
561
622
  visit(attr_value, { ...state, metadata })
562
623
  );
563
624
 
564
- if (node.metadata.scoped && state.component?.css) {
625
+ if (scoping_hash) {
565
626
  // Pass array to clsx so it can handle objects properly
566
- expression = b.array([expression, b.literal(state.component.css.hash)]);
627
+ expression = b.array([expression, b.literal(scoping_hash)]);
567
628
  }
568
629
 
569
630
  state.init?.push(
@@ -575,10 +636,8 @@ const visitors = {
575
636
  ),
576
637
  );
577
638
  }
578
- } else if (node.metadata.scoped && state.component?.css) {
579
- const value = state.component.css.hash;
580
-
581
- handle_static_attr('class', value);
639
+ } else if (scoping_hash) {
640
+ handle_static_attr('class', scoping_hash, is_spreading ? 'unshift' : 'push');
582
641
  }
583
642
 
584
643
  if (spread_attributes !== null && spread_attributes.length > 0) {
@@ -589,26 +648,60 @@ const visitors = {
589
648
  b.call(
590
649
  '_$_.spread_attrs',
591
650
  b.object(spread_attributes),
592
- node.metadata.scoped && state.component?.css
593
- ? b.literal(state.component.css.hash)
594
- : undefined,
651
+ scoping_hash ? b.literal(scoping_hash) : undefined,
595
652
  ),
596
653
  ),
597
654
  ),
598
655
  );
599
656
  }
600
657
 
601
- state.init?.push(b.stmt(b.call(b.member(b.id('__output'), b.id('push')), b.literal(`>`))));
658
+ state.init?.push(
659
+ b.stmt(
660
+ b.call(
661
+ b.member(b.id('__output'), b.id('push')),
662
+ b.literal(!node.selfClosing ? '>' : ' />'),
663
+ ),
664
+ ),
665
+ );
602
666
 
603
667
  if (!is_void) {
668
+ /** @type {AST.Statement[]} */
669
+ const init = [];
604
670
  transform_children(
605
671
  node.children,
606
- /** @type {TransformServerContext} */ ({ visit, state: { ...state } }),
672
+ /** @type {TransformServerContext} */ ({
673
+ visit,
674
+ state: {
675
+ ...state,
676
+ init,
677
+ ...(state.applyParentCssScope ||
678
+ (dynamic_name && node.metadata.scoped && state.component?.css)
679
+ ? {
680
+ applyParentCssScope:
681
+ state.applyParentCssScope ||
682
+ /** @type {AST.CSS.StyleSheet} */ (state.component?.css).hash,
683
+ }
684
+ : {}),
685
+ },
686
+ }),
607
687
  );
608
688
 
609
- state.init?.push(
610
- b.stmt(b.call(b.member(b.id('__output'), b.id('push')), b.literal(`</${node.id.name}>`))),
611
- );
689
+ if (init.length > 0) {
690
+ state.init?.push(b.block(init));
691
+ }
692
+
693
+ if (!node.selfClosing) {
694
+ state.init?.push(
695
+ b.stmt(
696
+ b.call(
697
+ b.member(b.id('__output'), b.id('push')),
698
+ dynamic_name
699
+ ? b.template([b.quasi('</', false), b.quasi('>', false)], [tag_name])
700
+ : b.literal('</' + /** @type {AST.Literal} */ (tag_name).value + '>'),
701
+ ),
702
+ ),
703
+ );
704
+ }
612
705
  }
613
706
  } else {
614
707
  /** @type {(AST.Property | AST.SpreadElement)[]} */
@@ -616,23 +709,31 @@ const visitors = {
616
709
  /** @type {AST.Expression | null} */
617
710
  let children_prop = null;
618
711
 
712
+ if (state.applyParentCssScope) {
713
+ // We're inside a component, don't continue applying css hash to class
714
+ state.applyParentCssScope = undefined;
715
+ }
716
+
619
717
  for (const attr of node.attributes) {
620
718
  if (attr.type === 'Attribute') {
621
719
  if (attr.name.type === 'Identifier') {
622
720
  const metadata = { tracking: false, await: false };
623
- let property = /** @type {AST.Expression} */ (
624
- visit(/** @type {AST.Expression} */ (attr.value), {
625
- ...state,
626
- metadata,
627
- })
628
- );
721
+ let property =
722
+ attr.value === null
723
+ ? b.literal(true)
724
+ : /** @type {AST.Expression} */ (
725
+ visit(/** @type {AST.Expression} */ (attr.value), {
726
+ ...state,
727
+ metadata,
728
+ })
729
+ );
629
730
 
630
731
  if (attr.name.name === 'children') {
631
- children_prop = b.thunk(property);
732
+ children_prop = attr.name.tracked ? b.thunk(property) : property;
632
733
  continue;
633
734
  }
634
735
 
635
- props.push(b.prop('init', attr.name, property));
736
+ props.push(b.prop('init', b.key(attr.name.name), property));
636
737
  }
637
738
  } else if (attr.type === 'SpreadAttribute') {
638
739
  props.push(
@@ -666,6 +767,10 @@ const visitors = {
666
767
  }
667
768
  }
668
769
 
770
+ if (children_prop) {
771
+ props.push(b.prop('init', b.id('children'), children_prop));
772
+ }
773
+
669
774
  if (children_filtered.length > 0) {
670
775
  const component_scope = /** @type {ScopeInterface} */ (context.state.scopes.get(node));
671
776
  const children = /** @type {AST.Expression} */ (
@@ -676,55 +781,96 @@ const visitors = {
676
781
  })
677
782
  );
678
783
 
679
- if (children_prop) {
680
- /** @type {AST.ArrowFunctionExpression} */ (children_prop).body = b.logical(
681
- '??',
682
- /** @type {AST.Expression} */ (
683
- /** @type {AST.ArrowFunctionExpression} */ (children_prop).body
684
- ),
685
- children,
686
- );
687
- } else {
688
- props.push(b.prop('init', b.id('children'), children));
689
- }
784
+ props.push(b.prop('init', b.id('children'), children));
690
785
  }
691
786
 
692
787
  // For SSR, determine if we should await based on component metadata
693
- const component_call = b.call(
694
- /** @type {AST.Expression} */ (visit(node.id, state)),
695
- b.id('__output'),
696
- b.object(props),
697
- );
788
+ const args = [b.id('__output'), b.object(props)];
698
789
 
699
790
  // Check if this is a locally defined component and if it's async
700
791
  const component_name = node.id.type === 'Identifier' ? node.id.name : null;
701
792
  const local_metadata = component_name
702
793
  ? state.component_metadata.find((m) => m.id === component_name)
703
794
  : null;
795
+ const comp_id = b.id('comp');
796
+ const args_id = b.id('args');
797
+ const comp_call = b.call(comp_id, b.spread(args_id));
798
+ const comp_call_regular = b.stmt(comp_call);
799
+ const comp_call_await = b.stmt(b.await(comp_call));
800
+
801
+ /** @type {AST.Statement[]} */
802
+ const init = [];
803
+ /** @type {AST.Statement[]} */
804
+ const statements = [
805
+ b.const(comp_id, /** @type {AST.Expression} */ (visit(node.id, state))),
806
+ b.const(args_id, b.array(args)),
807
+ ];
704
808
 
705
809
  if (local_metadata) {
706
- // Component is defined locally - we know if it's async or not
707
- if (local_metadata.async) {
708
- state.init?.push(b.stmt(b.await(component_call)));
810
+ if (local_metadata?.async) {
811
+ statements.push(comp_call_await);
812
+
813
+ if (state.metadata?.await === false) {
814
+ state.metadata.await = true;
815
+ }
709
816
  } else {
710
- state.init?.push(b.stmt(component_call));
817
+ statements.push(comp_call_regular);
818
+ }
819
+ } else if (!is_element_dynamic(node)) {
820
+ // it's imported element, so it could be async
821
+ statements.push(
822
+ b.if(
823
+ b.member(comp_id, b.id('async'), false, true),
824
+ b.block([comp_call_await]),
825
+ b.if(comp_id, b.block([comp_call_regular])),
826
+ ),
827
+ );
828
+
829
+ if (state.metadata?.await === false) {
830
+ state.metadata.await = true;
711
831
  }
712
832
  } else {
713
- // Component is imported or dynamic - check .async property at runtime
714
- // Use if-statement instead of ternary to avoid parser issues with await in conditionals
715
- state.init?.push(
833
+ // if it's a dynamic element, build the element output
834
+ // and store the results in the `init` array
835
+ visit(
836
+ node,
837
+ /** @type {TransformServerState} */ ({
838
+ ...state,
839
+ dynamicElementName: b.template([b.quasi('', false), b.quasi('', false)], [comp_id]),
840
+ init,
841
+ }),
842
+ );
843
+
844
+ statements.push(
845
+ b.stmt(b.call(b.member(b.id('__output'), b.id('push')), b.literal(BLOCK_OPEN))),
846
+ );
847
+
848
+ statements.push(
716
849
  b.if(
717
- b.member(/** @type {AST.Expression} */ (visit(node.id, state)), b.id('async')),
718
- b.block([b.stmt(b.await(component_call))]),
719
- b.block([b.stmt(component_call)]),
850
+ b.binary('===', b.unary('typeof', comp_id), b.literal('function')),
851
+ b.block([
852
+ b.if(
853
+ b.member(comp_id, b.id('async')),
854
+ b.block([comp_call_await]),
855
+ b.block([comp_call_regular]),
856
+ ),
857
+ ]),
858
+ // make sure that falsy values for dynamic element or component don't get rendered
859
+ b.if(comp_id, b.block(init)),
720
860
  ),
721
861
  );
722
862
 
723
- // Mark parent component as async since we're using await
863
+ statements.push(
864
+ b.stmt(b.call(b.member(b.id('__output'), b.id('push')), b.literal(BLOCK_CLOSE))),
865
+ );
866
+
867
+ // Mark parent component as async since this child component could potentially be async
724
868
  if (state.metadata?.await === false) {
725
869
  state.metadata.await = true;
726
870
  }
727
871
  }
872
+
873
+ state.init?.push(b.block(statements));
728
874
  }
729
875
  },
730
876
 
@@ -1105,7 +1251,11 @@ const visitors = {
1105
1251
  },
1106
1252
 
1107
1253
  MemberExpression(node, context) {
1108
- if (node.tracked || (node.property.type === 'Identifier' && node.property.tracked)) {
1254
+ if (
1255
+ node.tracked ||
1256
+ ((node.property.type === 'Identifier' || node.property.type === 'Literal') &&
1257
+ node.property.tracked)
1258
+ ) {
1109
1259
  return b.call(
1110
1260
  '_$_.get_property',
1111
1261
  /** @type {AST.Expression} */ (context.visit(node.object)),
@@ -39,6 +39,12 @@ export const mapping_data_verify_only = {
39
39
  verification: true,
40
40
  };
41
41
 
42
+ /** @type {Partial<VolarCodeMapping['data']>} */
43
+ export const mapping_data_verify_complete = {
44
+ verification: true,
45
+ completion: true,
46
+ };
47
+
42
48
  /**
43
49
  * Convert byte offset to line/column
44
50
  * @param {number} offset
@@ -204,25 +210,42 @@ export function build_src_to_gen_map(
204
210
  }
205
211
 
206
212
  /**
207
- * Look up generated position for a given source position
213
+ * Look up generated position for a given source position if it exists
208
214
  * @param {number} src_line - 1-based line number in source
209
215
  * @param {number} src_column - 0-based column number in source
210
216
  * @param {CodeToGeneratedMap} src_to_gen_map - Lookup map
211
- * @returns {CodePosition} Generated position
217
+ * @returns {CodePosition | Error} Generated position
212
218
  */
213
- export function get_generated_position(src_line, src_column, src_to_gen_map) {
219
+ function maybe_get_generated_position(src_line, src_column, src_to_gen_map) {
214
220
  const key = `${src_line}:${src_column}`;
215
221
  const positions = src_to_gen_map.get(key);
216
222
 
217
223
  if (!positions || positions.length === 0) {
218
- // No mapping found in source map - this shouldn't happen since all tokens should have mappings
219
- throw new Error(`No source map entry for position "${src_line}:${src_column}"`);
224
+ return new Error(`No source map entry for position "${src_line}:${src_column}"`);
220
225
  }
221
226
 
222
227
  // If multiple generated positions map to same source, return the first
223
228
  return positions[0];
224
229
  }
225
230
 
231
+ /**
232
+ * Look up generated position for a given source position
233
+ * @param {number} src_line - 1-based line number in source
234
+ * @param {number} src_column - 0-based column number in source
235
+ * @param {CodeToGeneratedMap} src_to_gen_map - Lookup map
236
+ * @returns {CodePosition} Generated position
237
+ */
238
+ export function get_generated_position(src_line, src_column, src_to_gen_map) {
239
+ const maybe_position = maybe_get_generated_position(src_line, src_column, src_to_gen_map);
240
+
241
+ if (maybe_position instanceof Error) {
242
+ // No mapping found in source map - this shouldn't happen since all tokens should have mappings
243
+ throw maybe_position;
244
+ }
245
+
246
+ return maybe_position;
247
+ }
248
+
226
249
  /**
227
250
  * Convert line/column to byte offset
228
251
  * @param {number} line
@@ -255,15 +278,15 @@ export function build_line_offsets(text) {
255
278
  }
256
279
 
257
280
  /**
258
- * @param {AST.Node} node
281
+ * @param {AST.Node | AST.NodeWithLocation} node
259
282
  * @param {CodeToGeneratedMap} src_to_gen_map
260
283
  * @param {number[]} gen_line_offsets
261
284
  * @param {Partial<VolarCodeMapping['data']>} [filtered_data]
262
285
  * @param {number} [src_max_len]
263
286
  * @param {number} [gen_max_len]
264
- * @returns {CodeMapping}
287
+ * @returns {CodeMapping | Error}
265
288
  */
266
- export function get_mapping_from_node(
289
+ function maybe_get_mapping_from_node(
267
290
  node,
268
291
  src_to_gen_map,
269
292
  gen_line_offsets,
@@ -276,10 +299,18 @@ export function get_mapping_from_node(
276
299
  const src_length = src_max_len || src_end_offset - src_start_offset;
277
300
  const loc = /** @type {AST.SourceLocation} */ (node.loc);
278
301
 
279
- const gen_loc = get_generated_position(loc.start.line, loc.start.column, src_to_gen_map);
302
+ const gen_loc = maybe_get_generated_position(loc.start.line, loc.start.column, src_to_gen_map);
303
+ if (gen_loc instanceof Error) {
304
+ return gen_loc;
305
+ }
280
306
  const gen_start_offset = loc_to_offset(gen_loc.line, gen_loc.column, gen_line_offsets);
281
- const gen_end_loc = get_generated_position(loc.end.line, loc.end.column, src_to_gen_map);
307
+
308
+ const gen_end_loc = maybe_get_generated_position(loc.end.line, loc.end.column, src_to_gen_map);
309
+ if (gen_end_loc instanceof Error) {
310
+ return gen_end_loc;
311
+ }
282
312
  const gen_end_offset = loc_to_offset(gen_end_loc.line, gen_end_loc.column, gen_line_offsets);
313
+
283
314
  const gen_length = gen_max_len || gen_end_offset - gen_start_offset;
284
315
  return {
285
316
  sourceOffsets: [src_start_offset],
@@ -294,3 +325,36 @@ export function get_mapping_from_node(
294
325
  },
295
326
  };
296
327
  }
328
+
329
+ /**
330
+ * @param {AST.Node | AST.NodeWithLocation} node
331
+ * @param {CodeToGeneratedMap} src_to_gen_map
332
+ * @param {number[]} gen_line_offsets
333
+ * @param {Partial<VolarCodeMapping['data']>} [filtered_data]
334
+ * @param {number} [src_max_len]
335
+ * @param {number} [gen_max_len]
336
+ * @returns {CodeMapping}
337
+ */
338
+ export function get_mapping_from_node(
339
+ node,
340
+ src_to_gen_map,
341
+ gen_line_offsets,
342
+ filtered_data,
343
+ src_max_len,
344
+ gen_max_len,
345
+ ) {
346
+ const mapping = maybe_get_mapping_from_node(
347
+ node,
348
+ src_to_gen_map,
349
+ gen_line_offsets,
350
+ filtered_data,
351
+ src_max_len,
352
+ gen_max_len,
353
+ );
354
+
355
+ if (mapping instanceof Error) {
356
+ throw mapping;
357
+ }
358
+
359
+ return mapping;
360
+ }