ripple 0.2.215 → 0.3.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 (157) hide show
  1. package/CHANGELOG.md +86 -0
  2. package/package.json +16 -7
  3. package/src/compiler/errors.js +1 -1
  4. package/src/compiler/identifier-utils.js +2 -0
  5. package/src/compiler/index.d.ts +2 -6
  6. package/src/compiler/phases/1-parse/index.js +171 -233
  7. package/src/compiler/phases/2-analyze/index.js +216 -16
  8. package/src/compiler/phases/2-analyze/prune.js +2 -2
  9. package/src/compiler/phases/3-transform/client/index.js +326 -94
  10. package/src/compiler/phases/3-transform/segments.js +43 -15
  11. package/src/compiler/phases/3-transform/server/index.js +71 -21
  12. package/src/compiler/scope.js +31 -12
  13. package/src/compiler/source-map-utils.js +4 -6
  14. package/src/compiler/types/acorn.d.ts +11 -0
  15. package/src/compiler/types/estree-jsx.d.ts +11 -0
  16. package/src/compiler/types/estree.d.ts +11 -0
  17. package/src/compiler/types/import.d.ts +32 -18
  18. package/src/compiler/types/index.d.ts +75 -23
  19. package/src/compiler/types/parse.d.ts +7 -10
  20. package/src/compiler/utils.js +48 -0
  21. package/src/runtime/array.js +53 -22
  22. package/src/runtime/date.js +15 -5
  23. package/src/runtime/index-client.js +41 -7
  24. package/src/runtime/index-server.js +7 -7
  25. package/src/runtime/internal/client/bindings.js +2 -2
  26. package/src/runtime/internal/client/blocks.js +40 -1
  27. package/src/runtime/internal/client/context.js +8 -0
  28. package/src/runtime/internal/client/for.js +3 -3
  29. package/src/runtime/internal/client/index.js +32 -5
  30. package/src/runtime/internal/client/render.js +20 -8
  31. package/src/runtime/internal/client/runtime.js +9 -7
  32. package/src/runtime/internal/client/template.js +1 -1
  33. package/src/runtime/internal/client/try.js +15 -22
  34. package/src/runtime/internal/client/utils.js +1 -1
  35. package/src/runtime/internal/server/context.js +8 -0
  36. package/src/runtime/internal/server/index.js +99 -6
  37. package/src/runtime/map.js +7 -7
  38. package/src/runtime/media-query.js +10 -1
  39. package/src/runtime/object.js +6 -6
  40. package/src/runtime/proxy.js +6 -6
  41. package/src/runtime/set.js +11 -11
  42. package/src/runtime/url-search-params.js +13 -2
  43. package/src/runtime/url.js +15 -5
  44. package/src/utils/builders.js +13 -3
  45. package/tests/client/array/array.copy-within.test.ripple +11 -11
  46. package/tests/client/array/array.derived.test.ripple +42 -42
  47. package/tests/client/array/array.iteration.test.ripple +12 -12
  48. package/tests/client/array/array.mutations.test.ripple +25 -25
  49. package/tests/client/array/array.static.test.ripple +103 -106
  50. package/tests/client/array/array.to-methods.test.ripple +8 -8
  51. package/tests/client/async-suspend.test.ripple +94 -0
  52. package/tests/client/basic/basic.attributes.test.ripple +31 -31
  53. package/tests/client/basic/basic.collections.test.ripple +7 -7
  54. package/tests/client/basic/basic.components.test.ripple +48 -10
  55. package/tests/client/basic/basic.errors.test.ripple +111 -30
  56. package/tests/client/basic/basic.events.test.ripple +11 -11
  57. package/tests/client/basic/basic.get-set.test.ripple +18 -18
  58. package/tests/client/basic/basic.reactivity.test.ripple +47 -42
  59. package/tests/client/basic/basic.rendering.test.ripple +7 -7
  60. package/tests/client/basic/basic.utilities.test.ripple +4 -4
  61. package/tests/client/boundaries.test.ripple +7 -7
  62. package/tests/client/compiler/__snapshots__/compiler.assignments.test.ripple.snap +2 -2
  63. package/tests/client/compiler/compiler.assignments.test.ripple +21 -21
  64. package/tests/client/compiler/compiler.basic.test.ripple +223 -82
  65. package/tests/client/compiler/compiler.tracked-access.test.ripple +8 -9
  66. package/tests/client/composite/composite.dynamic-components.test.ripple +8 -8
  67. package/tests/client/composite/composite.generics.test.ripple +4 -4
  68. package/tests/client/composite/composite.props.test.ripple +9 -9
  69. package/tests/client/composite/composite.reactivity.test.ripple +32 -26
  70. package/tests/client/composite/composite.render.test.ripple +13 -4
  71. package/tests/client/computed-properties.test.ripple +3 -3
  72. package/tests/client/context.test.ripple +3 -3
  73. package/tests/client/css/global-additional-cases.test.ripple +4 -4
  74. package/tests/client/css/style-identifier.test.ripple +49 -41
  75. package/tests/client/date.test.ripple +40 -40
  76. package/tests/client/dynamic-elements.test.ripple +165 -30
  77. package/tests/client/events.test.ripple +25 -25
  78. package/tests/client/for.test.ripple +76 -8
  79. package/tests/client/function-overload.test.ripple +0 -1
  80. package/tests/client/head.test.ripple +7 -7
  81. package/tests/client/html.test.ripple +2 -2
  82. package/tests/client/input-value.test.ripple +174 -176
  83. package/tests/client/map.test.ripple +21 -21
  84. package/tests/client/media-query.test.ripple +4 -4
  85. package/tests/client/object.test.ripple +12 -12
  86. package/tests/client/portal.test.ripple +4 -4
  87. package/tests/client/ref.test.ripple +5 -5
  88. package/tests/client/return.test.ripple +17 -17
  89. package/tests/client/set.test.ripple +16 -16
  90. package/tests/client/svg.test.ripple +6 -7
  91. package/tests/client/switch.test.ripple +10 -10
  92. package/tests/client/tracked-expression.test.ripple +1 -3
  93. package/tests/client/try.test.ripple +56 -4
  94. package/tests/client/url/url.derived.test.ripple +10 -9
  95. package/tests/client/url/url.parsing.test.ripple +10 -10
  96. package/tests/client/url/url.partial-removal.test.ripple +10 -10
  97. package/tests/client/url/url.reactivity.test.ripple +17 -17
  98. package/tests/client/url/url.serialization.test.ripple +4 -4
  99. package/tests/client/url-search-params/url-search-params.derived.test.ripple +11 -10
  100. package/tests/client/url-search-params/url-search-params.initialization.test.ripple +5 -7
  101. package/tests/client/url-search-params/url-search-params.iteration.test.ripple +13 -13
  102. package/tests/client/url-search-params/url-search-params.mutation.test.ripple +19 -19
  103. package/tests/client/url-search-params/url-search-params.retrieval.test.ripple +17 -17
  104. package/tests/client/url-search-params/url-search-params.serialization.test.ripple +5 -5
  105. package/tests/client/url-search-params/url-search-params.tracked-url.test.ripple +5 -5
  106. package/tests/hydration/compiled/client/events.js +8 -11
  107. package/tests/hydration/compiled/client/for.js +20 -23
  108. package/tests/hydration/compiled/client/head.js +17 -19
  109. package/tests/hydration/compiled/client/hmr.js +84 -0
  110. package/tests/hydration/compiled/client/html.js +1 -15
  111. package/tests/hydration/compiled/client/if-children.js +7 -9
  112. package/tests/hydration/compiled/client/if.js +5 -7
  113. package/tests/hydration/compiled/client/mixed-control-flow.js +3 -5
  114. package/tests/hydration/compiled/client/portal.js +1 -1
  115. package/tests/hydration/compiled/client/reactivity.js +9 -11
  116. package/tests/hydration/compiled/client/return.js +11 -13
  117. package/tests/hydration/compiled/client/switch.js +4 -6
  118. package/tests/hydration/compiled/server/basic.js +0 -1
  119. package/tests/hydration/compiled/server/composite.js +0 -3
  120. package/tests/hydration/compiled/server/events.js +8 -12
  121. package/tests/hydration/compiled/server/for.js +20 -23
  122. package/tests/hydration/compiled/server/head.js +17 -19
  123. package/tests/hydration/compiled/server/hmr.js +107 -0
  124. package/tests/hydration/compiled/server/html.js +1 -35
  125. package/tests/hydration/compiled/server/if-children.js +7 -11
  126. package/tests/hydration/compiled/server/if.js +5 -7
  127. package/tests/hydration/compiled/server/mixed-control-flow.js +3 -5
  128. package/tests/hydration/compiled/server/portal.js +1 -9
  129. package/tests/hydration/compiled/server/reactivity.js +9 -11
  130. package/tests/hydration/compiled/server/return.js +11 -13
  131. package/tests/hydration/compiled/server/switch.js +4 -6
  132. package/tests/hydration/components/events.ripple +8 -9
  133. package/tests/hydration/components/for.ripple +20 -21
  134. package/tests/hydration/components/head.ripple +6 -8
  135. package/tests/hydration/components/hmr.ripple +34 -0
  136. package/tests/hydration/components/html.ripple +1 -3
  137. package/tests/hydration/components/if-children.ripple +7 -8
  138. package/tests/hydration/components/if.ripple +5 -6
  139. package/tests/hydration/components/mixed-control-flow.ripple +4 -6
  140. package/tests/hydration/components/portal.ripple +1 -1
  141. package/tests/hydration/components/reactivity.ripple +9 -10
  142. package/tests/hydration/components/return.ripple +11 -12
  143. package/tests/hydration/components/switch.ripple +6 -8
  144. package/tests/hydration/hmr.test.js +74 -0
  145. package/tests/server/await.test.ripple +2 -2
  146. package/tests/server/basic.attributes.test.ripple +19 -21
  147. package/tests/server/basic.components.test.ripple +13 -7
  148. package/tests/server/basic.test.ripple +20 -21
  149. package/tests/server/compiler.test.ripple +5 -5
  150. package/tests/server/composite.props.test.ripple +6 -7
  151. package/tests/server/composite.test.ripple +4 -4
  152. package/tests/server/context.test.ripple +1 -3
  153. package/tests/server/dynamic-elements.test.ripple +24 -24
  154. package/tests/server/head.test.ripple +5 -7
  155. package/tests/server/style-identifier.test.ripple +16 -17
  156. package/types/index.d.ts +266 -62
  157. package/types/server.d.ts +6 -6
@@ -56,9 +56,11 @@ import {
56
56
  is_inside_left_side_assignment,
57
57
  hash,
58
58
  flatten_switch_consequent,
59
+ get_ripple_namespace_call_name,
59
60
  } from '../../../utils.js';
60
61
  import {
61
62
  CSS_HASH_IDENTIFIER,
63
+ RIPPLE_NAMESPACE_IDENTIFIER,
62
64
  STYLE_IDENTIFIER,
63
65
  SERVER_IDENTIFIER,
64
66
  obfuscate_identifier,
@@ -343,10 +345,13 @@ function visit_title_element(node, context) {
343
345
  /**
344
346
  * @param {string} name
345
347
  * @param {TransformClientContext} context
348
+ * @param {boolean} [is_obfuscated]
346
349
  * @returns {string}
347
350
  */
348
- function set_hidden_import_from_ripple(name, context) {
349
- name = obfuscate_identifier(name);
351
+ function set_hidden_import_from_ripple(name, context, is_obfuscated = false) {
352
+ if (!is_obfuscated) {
353
+ name = obfuscate_identifier(name);
354
+ }
350
355
  if (!context.state.imports.has(`import { ${name} } from 'ripple/compiler/internal/import'`)) {
351
356
  context.state.imports.add(`import { ${name} } from 'ripple/compiler/internal/import'`);
352
357
  }
@@ -354,6 +359,64 @@ function set_hidden_import_from_ripple(name, context) {
354
359
  return name;
355
360
  }
356
361
 
362
+ /**
363
+ * @param {AST.NodeWithLocation} loc_info
364
+ * @param {number} [start_offset]
365
+ * @param {number} [length]
366
+ * @returns {AST.NodeWithLocation}
367
+ */
368
+ function slice_loc_info(loc_info, start_offset = 0, length) {
369
+ if (length === undefined) {
370
+ length = loc_info.end - loc_info.start - start_offset;
371
+ }
372
+ return {
373
+ start: loc_info.start + start_offset,
374
+ end: loc_info.start + start_offset + length,
375
+ loc: {
376
+ start: {
377
+ line: loc_info.loc.start.line,
378
+ column: loc_info.loc.start.column + start_offset,
379
+ },
380
+ end: {
381
+ line: loc_info.loc.start.line,
382
+ column: loc_info.loc.start.column + start_offset + length,
383
+ },
384
+ },
385
+ };
386
+ }
387
+
388
+ /**
389
+ * @param {string} property_name
390
+ * @param {string} source_name
391
+ * @param {AST.NodeWithLocation} loc_info
392
+ * @param {TransformClientContext} context
393
+ * @returns {AST.MemberExpression}
394
+ */
395
+ function build_ripple_namespace_member(property_name, source_name, loc_info, context) {
396
+ const namespace_alias = set_hidden_import_from_ripple(RIPPLE_NAMESPACE_IDENTIFIER, context, true);
397
+ const namespace_loc = slice_loc_info(loc_info, 0, '#ripple'.length);
398
+ const namespace_id = b.id(namespace_alias, namespace_loc);
399
+ namespace_id.metadata.source_name = '#ripple';
400
+
401
+ const property_loc = slice_loc_info(loc_info, '#ripple.'.length, property_name.length);
402
+ const property_id = b.id(property_name, property_loc);
403
+
404
+ return b.member(namespace_id, property_id, false, false, loc_info);
405
+ }
406
+
407
+ /**
408
+ * @param {string | undefined} name
409
+ * @returns {boolean}
410
+ */
411
+ function ripple_namespace_requires_block(name) {
412
+ return (
413
+ name !== undefined &&
414
+ name !== '#ripple.effect' &&
415
+ name !== '#ripple.untrack' &&
416
+ name !== '#ripple.context'
417
+ );
418
+ }
419
+
357
420
  /**
358
421
  * @param {TransformClientContext} context
359
422
  * @param {Partial<TransformClientState>} [more_state]
@@ -404,6 +467,17 @@ const visitors = {
404
467
 
405
468
  if (is_reference(node, parent)) {
406
469
  if (context.state.to_ts) {
470
+ if (node.metadata?.source_name === '#ripple' || node.name === '#ripple') {
471
+ const namespace_alias = set_hidden_import_from_ripple(
472
+ RIPPLE_NAMESPACE_IDENTIFIER,
473
+ context,
474
+ true,
475
+ );
476
+ const namespace_id = b.id(namespace_alias, /** @type {AST.NodeWithLocation} */ (node));
477
+ namespace_id.metadata.source_name = '#ripple';
478
+ return namespace_id;
479
+ }
480
+
407
481
  if (node.tracked) {
408
482
  // Check if this identifier is used as a dynamic component/element
409
483
  // by checking if it has a capitalized name in metadata
@@ -468,13 +542,13 @@ const visitors = {
468
542
 
469
543
  ServerIdentifier(node, context) {
470
544
  const id = b.id(SERVER_IDENTIFIER);
471
- id.metadata.source_name = '#server';
545
+ id.metadata.source_name = '#ripple.server';
472
546
  return { ...node, ...id };
473
547
  },
474
548
 
475
549
  StyleIdentifier(node, context) {
476
550
  const id = b.id(STYLE_IDENTIFIER);
477
- id.metadata.source_name = '#style';
551
+ id.metadata.source_name = '#ripple.style';
478
552
  return { ...node, ...id };
479
553
  },
480
554
 
@@ -529,12 +603,55 @@ const visitors = {
529
603
  }
530
604
  const callee = node.callee;
531
605
  const parent = context.path.at(-1);
606
+ const source_name = callee.type === 'Identifier' ? callee.metadata?.source_name : undefined;
607
+ const ripple_runtime_method = get_ripple_namespace_call_name(source_name);
608
+ const ripple_method_requires_block = ripple_namespace_requires_block(source_name);
532
609
 
533
610
  if (context.state.metadata?.tracking === false) {
534
611
  context.state.metadata.tracking = true;
535
612
  }
536
613
 
614
+ if (!context.state.to_ts && ripple_runtime_method !== null) {
615
+ return {
616
+ ...node,
617
+ callee: b.member(b.id('_$_'), b.id(ripple_runtime_method)),
618
+ arguments: /** @type {(AST.Expression | AST.SpreadElement)[]} */ ([
619
+ ...(ripple_method_requires_block ? [b.id('__block')] : []),
620
+ ...node.arguments.map((arg) => context.visit(arg)),
621
+ ]),
622
+ };
623
+ }
624
+
625
+ if (context.state.to_ts && source_name?.startsWith('#ripple.')) {
626
+ const property_name = source_name.replace('#ripple.', '');
627
+ const namespace_member = build_ripple_namespace_member(
628
+ property_name,
629
+ source_name,
630
+ /** @type {AST.NodeWithLocation} */ (callee),
631
+ context,
632
+ );
633
+
634
+ return {
635
+ ...node,
636
+ callee: namespace_member,
637
+ arguments: /** @type {(AST.Expression | AST.SpreadElement)[]} */ (
638
+ node.arguments.map((arg) => context.visit(arg))
639
+ ),
640
+ };
641
+ }
642
+
537
643
  if (!context.state.to_ts && is_ripple_track_call(callee, context)) {
644
+ const track_method_name =
645
+ callee.type === 'Identifier'
646
+ ? callee.name === 'trackSplit'
647
+ ? 'track_split'
648
+ : 'track'
649
+ : callee.type === 'MemberExpression' && callee.property.type === 'Identifier'
650
+ ? callee.property.name === 'trackSplit'
651
+ ? 'track_split'
652
+ : 'track'
653
+ : 'track';
654
+
538
655
  if (callee.type === 'Identifier' && callee.name === 'track') {
539
656
  if (node.arguments.length === 0) {
540
657
  node.arguments.push(b.void0, b.void0, b.void0);
@@ -546,6 +663,7 @@ const visitors = {
546
663
  }
547
664
  return {
548
665
  ...node,
666
+ callee: b.member(b.id('_$_'), b.id(track_method_name)),
549
667
  arguments: /** @type {(AST.Expression | AST.SpreadElement)[]} */ ([
550
668
  ...node.arguments.map((arg) => context.visit(arg)),
551
669
  b.id('__block'),
@@ -553,6 +671,63 @@ const visitors = {
553
671
  };
554
672
  }
555
673
 
674
+ // Check for more than two nested level calls, like #ripple.array.from()
675
+ if (
676
+ callee.type === 'MemberExpression' &&
677
+ callee.object.metadata?.source_name?.startsWith('#ripple.') &&
678
+ callee.object.type === 'Identifier' &&
679
+ callee.property.type === 'Identifier'
680
+ ) {
681
+ const object = callee.object;
682
+ const property = callee.property;
683
+ const source_name = /** @type {string} */ (object.metadata?.source_name);
684
+ const property_name = source_name.replace('#ripple.', '');
685
+
686
+ if (context.state.to_ts) {
687
+ // e.g. `#ripple.array`
688
+ const namespace_member = build_ripple_namespace_member(
689
+ property_name,
690
+ source_name,
691
+ /** @type {AST.NodeWithLocation} */ (node.callee),
692
+ context,
693
+ );
694
+
695
+ return /** @type {AST.CallExpression} */ ({
696
+ ...node,
697
+ callee: {
698
+ ...namespace_member,
699
+ // e.g. `array.from`
700
+ property: b.member(
701
+ /** @type {AST.Identifier} */ (namespace_member.property),
702
+ property,
703
+ false,
704
+ false,
705
+ slice_loc_info(/** @type {AST.NodeWithLocation} */ (node.callee), '#ripple.'.length),
706
+ ),
707
+ },
708
+ arguments: node.arguments.map((arg) => context.visit(arg)),
709
+ });
710
+ } else {
711
+ const method_name = get_ripple_namespace_call_name(source_name);
712
+ const requires_block = ripple_namespace_requires_block(source_name);
713
+ if (method_name !== null) {
714
+ return b.member(
715
+ b.id('_$_'),
716
+ b.member(
717
+ b.id(method_name),
718
+ b.call(
719
+ b.id(property.name),
720
+ .../** @type {(AST.Expression | AST.SpreadElement)[]} */ ([
721
+ ...(requires_block ? [b.id('__block')] : []),
722
+ ...node.arguments.map((arg) => context.visit(arg)),
723
+ ]),
724
+ ),
725
+ ),
726
+ );
727
+ }
728
+ }
729
+ }
730
+
556
731
  if (
557
732
  !is_inside_component(context, true) ||
558
733
  context.state.to_ts ||
@@ -630,43 +805,6 @@ const visitors = {
630
805
  context.state.metadata.tracking = true;
631
806
  }
632
807
 
633
- // Special handling for TrackedMapExpression and TrackedSetExpression
634
- // When source is "new #Map(...)" or "new #Map<K,V>(...)", the callee is TrackedMapExpression
635
- // with empty arguments and the actual arguments are in NewExpression.arguments
636
- if (callee.type === 'TrackedMapExpression' || callee.type === 'TrackedSetExpression') {
637
- // Use NewExpression's arguments (the callee has empty arguments from parser)
638
- const argsToUse = node.arguments.length > 0 ? node.arguments : callee.arguments;
639
-
640
- if (context.state.to_ts) {
641
- const className = callee.type === 'TrackedMapExpression' ? 'TrackedMap' : 'TrackedSet';
642
- const alias = set_hidden_import_from_ripple(className, context);
643
- const calleeId = b.id(alias);
644
- calleeId.loc = callee.loc;
645
- calleeId.metadata = {
646
- source_name: callee.type === 'TrackedMapExpression' ? '#Map' : '#Set',
647
- path: [...context.path],
648
- };
649
- /** @type {AST.NewExpression} */
650
- const newExpr = b.new(
651
- calleeId,
652
- /** @type {AST.NodeWithLocation} */ (node),
653
- .../** @type {AST.Expression[]} */ (argsToUse.map((arg) => context.visit(arg))),
654
- );
655
- // Preserve typeArguments for generics syntax like new #Map<string, number>()
656
- if (node.typeArguments) {
657
- newExpr.typeArguments = node.typeArguments;
658
- }
659
- return newExpr;
660
- }
661
-
662
- const helperName = callee.type === 'TrackedMapExpression' ? 'tracked_map' : 'tracked_set';
663
- return b.call(
664
- `_$_.${helperName}`,
665
- b.id('__block'),
666
- .../** @type {AST.Expression[]} */ (argsToUse.map((arg) => context.visit(arg))),
667
- );
668
- }
669
-
670
808
  if (
671
809
  context.state.to_ts ||
672
810
  !is_inside_component(context, true) ||
@@ -695,54 +833,67 @@ const visitors = {
695
833
  return b.call('_$_.with_scope', b.id('__block'), b.thunk(new_node));
696
834
  },
697
835
 
698
- TrackedArrayExpression(node, context) {
836
+ RippleArrayExpression(node, context) {
699
837
  if (context.state.to_ts) {
700
- const arrayAlias = set_hidden_import_from_ripple('TrackedArray', context);
838
+ const arrayAlias = set_hidden_import_from_ripple('RippleArray', context);
839
+ const id = b.id(
840
+ arrayAlias,
841
+ slice_loc_info(/** @type {AST.NodeWithLocation} */ (node), 0, '#ripple'.length),
842
+ );
843
+ id.metadata.source_name = '#ripple';
701
844
 
702
- return b.call(
703
- b.member(b.id(arrayAlias), b.id('from')),
704
- b.array(
705
- /** @type {(AST.Expression | AST.SpreadElement)[]} */ (
706
- node.elements.map((el) => context.visit(/** @type {AST.Node} */ (el)))
707
- ),
845
+ return {
846
+ type: 'NewExpression',
847
+ callee: id,
848
+ arguments: /** @type {(AST.Expression | AST.SpreadElement)[]} */ (
849
+ node.elements.map((el) => context.visit(/** @type {AST.Node} */ (el)))
708
850
  ),
709
- );
851
+ metadata: { path: [] },
852
+ };
710
853
  }
711
854
 
712
855
  return b.call(
713
- '_$_.tracked_array',
714
- b.array(
715
- /** @type {(AST.Expression | AST.SpreadElement)[]} */ (
716
- node.elements.map((el) => context.visit(/** @type {AST.Node} */ (el)))
717
- ),
718
- ),
856
+ '_$_.ripple_array',
719
857
  b.id('__block'),
858
+ .../** @type {(AST.Expression | AST.SpreadElement)[]} */ (
859
+ node.elements.map((el) => context.visit(/** @type {AST.Node} */ (el)))
860
+ ),
720
861
  );
721
862
  },
722
863
 
723
- TrackedObjectExpression(node, context) {
864
+ RippleObjectExpression(node, context) {
724
865
  if (context.state.to_ts) {
725
- const objectAlias = set_hidden_import_from_ripple('TrackedObject', context);
726
-
727
- return b.new(
728
- b.id(objectAlias),
866
+ const objectAlias = set_hidden_import_from_ripple('RippleObject', context);
867
+ const id = b.id(
868
+ objectAlias,
869
+ slice_loc_info(/** @type {AST.NodeWithLocation} */ (node), 0, '#ripple'.length),
870
+ );
871
+ id.metadata.source_name = '#ripple';
872
+ const new_node = b.new(
873
+ id,
729
874
  /** @type {AST.NodeWithLocation} */ (node),
730
875
  b.object(
731
876
  /** @type {(AST.Property | AST.SpreadElement)[]} */ (
732
877
  node.properties.map((prop) => context.visit(prop))
733
878
  ),
879
+ slice_loc_info(/** @type {AST.NodeWithLocation} */ (node), '#ripple'.length),
734
880
  ),
735
881
  );
882
+ // force the source mapping to skip the constructor,
883
+ // otherwise the mapping will be off by 4 and the whole
884
+ // new expression will be mapped to the source which doesn't have `new `
885
+ new_node.metadata.skipNewMapping = true;
886
+ return new_node;
736
887
  }
737
888
 
738
889
  return b.call(
739
- '_$_.tracked_object',
890
+ '_$_.ripple_object',
891
+ b.id('__block'),
740
892
  b.object(
741
893
  /** @type {(AST.Property | AST.SpreadElement)[]} */ (
742
894
  node.properties.map((prop) => context.visit(prop))
743
895
  ),
744
896
  ),
745
- b.id('__block'),
746
897
  );
747
898
  },
748
899
 
@@ -1380,12 +1531,15 @@ const visitors = {
1380
1531
  );
1381
1532
  } else if (attr.type === 'RefAttribute') {
1382
1533
  const id = state.flush_node?.();
1534
+ const metadata = { tracking: false, await: false };
1383
1535
  state.init?.push(
1384
1536
  b.stmt(
1385
1537
  b.call(
1386
1538
  '_$_.ref',
1387
1539
  id,
1388
- b.thunk(/** @type {AST.Expression} */ (visit(attr.argument, state))),
1540
+ b.thunk(
1541
+ /** @type {AST.Expression} */ (visit(attr.argument, { ...state, metadata })),
1542
+ ),
1389
1543
  ),
1390
1544
  ),
1391
1545
  );
@@ -1609,12 +1763,13 @@ const visitors = {
1609
1763
  );
1610
1764
  } else if (attr.type === 'RefAttribute') {
1611
1765
  const ref_id = state.scope.generate('ref');
1766
+ const metadata = { tracking: false, await: false };
1612
1767
  state.init?.push(b.var(ref_id, b.call('_$_.ref_prop')));
1613
1768
  props.push(
1614
1769
  b.prop(
1615
1770
  'init',
1616
1771
  b.id(ref_id),
1617
- /** @type {AST.Expression} */ (visit(attr.argument, state)),
1772
+ /** @type {AST.Expression} */ (visit(attr.argument, { ...state, metadata })),
1618
1773
  true,
1619
1774
  ),
1620
1775
  );
@@ -2162,7 +2317,12 @@ const visitors = {
2162
2317
  callback_body.push(b.stmt(b.call('_$_.set', b.id(info.name), b.false)));
2163
2318
  callback_body.push(
2164
2319
  b.if(
2165
- /** @type {AST.Expression} */ (context.visit(node.test)),
2320
+ /** @type {AST.Expression} */ (
2321
+ context.visit(node.test, {
2322
+ ...context.state,
2323
+ metadata: { ...context.state.metadata, await: false },
2324
+ })
2325
+ ),
2166
2326
  b.stmt(b.call('_$_.set', b.id(info.name), b.true)),
2167
2327
  ),
2168
2328
  );
@@ -2170,7 +2330,12 @@ const visitors = {
2170
2330
  callback_body.push(b.stmt(b.assignment('=', b.id(info.name), b.false)));
2171
2331
  callback_body.push(
2172
2332
  b.if(
2173
- /** @type {AST.Expression} */ (context.visit(node.test)),
2333
+ /** @type {AST.Expression} */ (
2334
+ context.visit(node.test, {
2335
+ ...context.state,
2336
+ metadata: { ...context.state.metadata, await: false },
2337
+ })
2338
+ ),
2174
2339
  b.stmt(b.assignment('=', b.id(info.name), b.true)),
2175
2340
  ),
2176
2341
  );
@@ -2203,10 +2368,15 @@ const visitors = {
2203
2368
  let alternate_id;
2204
2369
 
2205
2370
  if (node.alternate !== null) {
2206
- const alternate = /** @type {AST.BlockStatement | AST.IfStatement} */ (node.alternate);
2371
+ const alternate = /** @type {AST.Statement} */ (node.alternate);
2207
2372
  const alternate_scope = context.state.scopes.get(alternate) || context.state.scope;
2208
2373
  /** @type {AST.Node[]} */
2209
- let alternate_body = alternate.type === 'IfStatement' ? [alternate] : alternate.body;
2374
+ let alternate_body =
2375
+ alternate.type === 'IfStatement'
2376
+ ? [alternate]
2377
+ : alternate.type === 'BlockStatement'
2378
+ ? alternate.body
2379
+ : [alternate];
2210
2380
  const alternate_block = b.block(
2211
2381
  transform_body(alternate_body, {
2212
2382
  ...context,
@@ -2237,7 +2407,12 @@ const visitors = {
2237
2407
 
2238
2408
  callback_body.push(
2239
2409
  b.if(
2240
- /** @type {AST.Expression} */ (context.visit(node.test)),
2410
+ /** @type {AST.Expression} */ (
2411
+ context.visit(node.test, {
2412
+ ...context.state,
2413
+ metadata: { ...context.state.metadata, await: false },
2414
+ })
2415
+ ),
2241
2416
  b.stmt(b.call(b.id('__render'), b.id(consequent_id))),
2242
2417
  alternate_id
2243
2418
  ? b.stmt(
@@ -2479,8 +2654,8 @@ const visitors = {
2479
2654
 
2480
2655
  const server_identifier = b.id(SERVER_IDENTIFIER);
2481
2656
  server_identifier.loc = node.loc;
2482
- // Add source_name to properly map longer generated back to '#server'
2483
- server_identifier.metadata.source_name = '#server';
2657
+ // Add source_name to properly map longer generated back to '#ripple.server'
2658
+ server_identifier.metadata.source_name = '#ripple.server';
2484
2659
 
2485
2660
  const server_const = b.const(server_identifier, value);
2486
2661
  server_const.loc = node.loc;
@@ -2489,7 +2664,7 @@ const visitors = {
2489
2664
  }
2490
2665
 
2491
2666
  if (!context.state.serverIdentifierPresent) {
2492
- // no point printing the client-side block if #server.func is not used
2667
+ // no point printing the client-side block if #ripple.server.func is not used
2493
2668
  return b.empty;
2494
2669
  }
2495
2670
 
@@ -2692,12 +2867,42 @@ function transform_ts_child(node, context) {
2692
2867
  if (!node.selfClosing && !node.unclosed && !has_children_props && node.children.length > 0) {
2693
2868
  const is_dom_element = is_element_dom_element(node);
2694
2869
  const component_scope = /** @type {ScopeInterface} */ (context.state.scopes.get(node));
2870
+ /** @type {AST.Node[]} */
2871
+ const non_component_children = [];
2872
+
2873
+ for (let i = 0; i < node.children.length; i++) {
2874
+ const child = node.children[i];
2875
+ if (!is_dom_element && child.type === 'Component' && child.id) {
2876
+ const transformed_component = /** @type {AST.FunctionDeclaration} */ (
2877
+ visit(child, {
2878
+ ...state,
2879
+ scope: component_scope,
2880
+ metadata: { await: false },
2881
+ })
2882
+ );
2883
+ const func = b.arrow(
2884
+ transformed_component.params,
2885
+ transformed_component.body,
2886
+ transformed_component.async,
2887
+ );
2888
+ func.metadata = { ...func.metadata, is_component: true };
2889
+ const id = b.jsx_id(
2890
+ /** @type {AST.Identifier} */ (child.id).name,
2891
+ /** @type {AST.NodeWithLocation} */ (child.id),
2892
+ );
2893
+ id.metadata = { ...id.metadata, is_component: true };
2894
+ attributes.push(b.jsx_attribute(id, b.jsx_expression_container(func)));
2895
+ } else {
2896
+ non_component_children.push(child);
2897
+ }
2898
+ }
2695
2899
  const thunk =
2696
- /** @type {AST.Identifier} */ (node.id).name === 'style'
2900
+ /** @type {AST.Identifier} */ (node.id).name === 'style' ||
2901
+ non_component_children.length === 0
2697
2902
  ? null
2698
2903
  : b.thunk(
2699
2904
  b.block(
2700
- transform_body(node.children, {
2905
+ transform_body(non_component_children, {
2701
2906
  ...context,
2702
2907
  state: {
2703
2908
  ...state,
@@ -2780,10 +2985,14 @@ function transform_ts_child(node, context) {
2780
2985
  let alternate;
2781
2986
 
2782
2987
  if (node.alternate !== null) {
2783
- const alternate_node = /** @type {AST.BlockStatement | AST.IfStatement} */ (node.alternate);
2988
+ const alternate_node = /** @type {AST.Statement} */ (node.alternate);
2784
2989
  const alternate_scope = context.state.scopes.get(alternate_node) || context.state.scope;
2785
2990
  const alternate_body =
2786
- alternate_node.type === 'IfStatement' ? [alternate_node] : alternate_node.body;
2991
+ alternate_node.type === 'IfStatement'
2992
+ ? [alternate_node]
2993
+ : alternate_node.type === 'BlockStatement'
2994
+ ? alternate_node.body
2995
+ : [alternate_node];
2787
2996
  alternate = b.block(
2788
2997
  transform_body(alternate_body, {
2789
2998
  ...context,
@@ -3926,6 +4135,12 @@ function create_tsx_with_typescript_support(comments) {
3926
4135
 
3927
4136
  visit(node);
3928
4137
  },
4138
+ TSExpressionWithTypeArguments(node, context) {
4139
+ context.visit(node.expression);
4140
+ if (node.typeParameters) {
4141
+ context.visit(node.typeParameters);
4142
+ }
4143
+ },
3929
4144
  AssignmentPattern(node, context) {
3930
4145
  // We need to make sure that the whole AssignmentPattern has a start and end mapping
3931
4146
  // Acorn only maps pieces but not the whole thing
@@ -4028,14 +4243,30 @@ function create_tsx_with_typescript_support(comments) {
4028
4243
  }
4029
4244
  },
4030
4245
  NewExpression(node, context) {
4031
- if (!node.loc) {
4032
- base_tsx.NewExpression?.(node, context);
4033
- return;
4246
+ const loc = /** @type {AST.SourceLocation} */ (node.loc) ?? null;
4247
+
4248
+ if (loc && !node?.metadata?.skipNewMapping) {
4249
+ context.location(loc.start.line, loc.start.column);
4250
+ }
4251
+ context.write('new ');
4252
+
4253
+ if (loc && node?.metadata?.skipNewMapping) {
4254
+ context.location(loc.start.line, loc.start.column);
4255
+ }
4256
+
4257
+ context.visit(node.callee);
4258
+ if (node.typeArguments) {
4259
+ context.visit(node.typeArguments);
4260
+ }
4261
+ context.write('(');
4262
+ for (let i = 0; i < node.arguments.length; i++) {
4263
+ if (i > 0) context.write(', ');
4264
+ context.visit(node.arguments[i]);
4265
+ }
4266
+ context.write(')');
4267
+ if (loc) {
4268
+ context.location(loc.end.line, loc.end.column);
4034
4269
  }
4035
- const loc = /** @type {AST.SourceLocation} */ (node.loc);
4036
- context.location(loc.start.line, loc.start.column);
4037
- base_tsx.NewExpression?.(node, context);
4038
- context.location(loc.end.line, loc.end.column);
4039
4270
  },
4040
4271
  TemplateLiteral(node, context) {
4041
4272
  if (!node.loc) {
@@ -4159,6 +4390,15 @@ function create_tsx_with_typescript_support(comments) {
4159
4390
  }
4160
4391
  } else {
4161
4392
  if (node.shorthand) {
4393
+ // Shorthand object properties require an Identifier value. When the
4394
+ // transformed value is a tracked MemberExpression (for example
4395
+ // @value), emit longhand to keep valid output.
4396
+ if (node.value.type === 'MemberExpression' && node.value.tracked) {
4397
+ context.visit(node.key);
4398
+ context.write(': ');
4399
+ context.visit(node.value);
4400
+ return;
4401
+ }
4162
4402
  // only visit value since key and value are the same
4163
4403
  // or the value will contain the key like in AssignmentPattern: { foo = 1 }
4164
4404
  context.visit(node.value);
@@ -4403,11 +4643,7 @@ function create_tsx_with_typescript_support(comments) {
4403
4643
  if (node.loc) {
4404
4644
  context.location(node.loc.start.line, node.loc.start.column);
4405
4645
  }
4406
- if (typeof node.name === 'string') {
4407
- context.write(node.name);
4408
- } else if (node.name && node.name.name) {
4409
- context.write(node.name.name);
4410
- }
4646
+ context.write(node.name);
4411
4647
  if (node.constraint) {
4412
4648
  context.write(' extends ');
4413
4649
  context.visit(node.constraint);
@@ -4548,11 +4784,7 @@ function create_tsx_with_typescript_support(comments) {
4548
4784
  context.location(tp.loc.start.line, tp.loc.start.column);
4549
4785
  }
4550
4786
  // Write the parameter name
4551
- if (typeof tp.name === 'string') {
4552
- context.write(tp.name);
4553
- } else if (tp.name && tp.name.name) {
4554
- context.write(tp.name.name);
4555
- }
4787
+ context.write(tp.name);
4556
4788
  // In mapped types, constraint uses 'in' instead of 'extends'
4557
4789
  if (tp.constraint) {
4558
4790
  context.write(' in ');