ripple 0.2.216 → 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 (155) hide show
  1. package/CHANGELOG.md +39 -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 +192 -16
  8. package/src/compiler/phases/2-analyze/prune.js +2 -2
  9. package/src/compiler/phases/3-transform/client/index.js +308 -91
  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/try.js +15 -22
  33. package/src/runtime/internal/client/utils.js +1 -1
  34. package/src/runtime/internal/server/context.js +8 -0
  35. package/src/runtime/internal/server/index.js +99 -6
  36. package/src/runtime/map.js +7 -7
  37. package/src/runtime/media-query.js +10 -1
  38. package/src/runtime/object.js +6 -6
  39. package/src/runtime/proxy.js +6 -6
  40. package/src/runtime/set.js +11 -11
  41. package/src/runtime/url-search-params.js +13 -2
  42. package/src/runtime/url.js +15 -5
  43. package/src/utils/builders.js +13 -3
  44. package/tests/client/array/array.copy-within.test.ripple +11 -11
  45. package/tests/client/array/array.derived.test.ripple +42 -42
  46. package/tests/client/array/array.iteration.test.ripple +12 -12
  47. package/tests/client/array/array.mutations.test.ripple +25 -25
  48. package/tests/client/array/array.static.test.ripple +103 -106
  49. package/tests/client/array/array.to-methods.test.ripple +8 -8
  50. package/tests/client/async-suspend.test.ripple +94 -0
  51. package/tests/client/basic/basic.attributes.test.ripple +31 -31
  52. package/tests/client/basic/basic.collections.test.ripple +7 -7
  53. package/tests/client/basic/basic.components.test.ripple +48 -10
  54. package/tests/client/basic/basic.errors.test.ripple +46 -31
  55. package/tests/client/basic/basic.events.test.ripple +11 -11
  56. package/tests/client/basic/basic.get-set.test.ripple +18 -18
  57. package/tests/client/basic/basic.reactivity.test.ripple +47 -42
  58. package/tests/client/basic/basic.rendering.test.ripple +7 -7
  59. package/tests/client/basic/basic.utilities.test.ripple +4 -4
  60. package/tests/client/boundaries.test.ripple +7 -7
  61. package/tests/client/compiler/__snapshots__/compiler.assignments.test.ripple.snap +2 -2
  62. package/tests/client/compiler/compiler.assignments.test.ripple +21 -21
  63. package/tests/client/compiler/compiler.basic.test.ripple +223 -82
  64. package/tests/client/compiler/compiler.tracked-access.test.ripple +8 -9
  65. package/tests/client/composite/composite.dynamic-components.test.ripple +8 -8
  66. package/tests/client/composite/composite.generics.test.ripple +4 -4
  67. package/tests/client/composite/composite.props.test.ripple +9 -9
  68. package/tests/client/composite/composite.reactivity.test.ripple +32 -26
  69. package/tests/client/composite/composite.render.test.ripple +13 -4
  70. package/tests/client/computed-properties.test.ripple +3 -3
  71. package/tests/client/context.test.ripple +3 -3
  72. package/tests/client/css/global-additional-cases.test.ripple +4 -4
  73. package/tests/client/css/style-identifier.test.ripple +49 -41
  74. package/tests/client/date.test.ripple +40 -40
  75. package/tests/client/dynamic-elements.test.ripple +165 -30
  76. package/tests/client/events.test.ripple +25 -25
  77. package/tests/client/for.test.ripple +76 -8
  78. package/tests/client/function-overload.test.ripple +0 -1
  79. package/tests/client/head.test.ripple +7 -7
  80. package/tests/client/html.test.ripple +2 -2
  81. package/tests/client/input-value.test.ripple +174 -176
  82. package/tests/client/map.test.ripple +21 -21
  83. package/tests/client/media-query.test.ripple +4 -4
  84. package/tests/client/object.test.ripple +12 -12
  85. package/tests/client/portal.test.ripple +4 -4
  86. package/tests/client/ref.test.ripple +5 -5
  87. package/tests/client/return.test.ripple +17 -17
  88. package/tests/client/set.test.ripple +16 -16
  89. package/tests/client/svg.test.ripple +6 -7
  90. package/tests/client/switch.test.ripple +10 -10
  91. package/tests/client/tracked-expression.test.ripple +1 -3
  92. package/tests/client/try.test.ripple +33 -4
  93. package/tests/client/url/url.derived.test.ripple +10 -9
  94. package/tests/client/url/url.parsing.test.ripple +10 -10
  95. package/tests/client/url/url.partial-removal.test.ripple +10 -10
  96. package/tests/client/url/url.reactivity.test.ripple +17 -17
  97. package/tests/client/url/url.serialization.test.ripple +4 -4
  98. package/tests/client/url-search-params/url-search-params.derived.test.ripple +11 -10
  99. package/tests/client/url-search-params/url-search-params.initialization.test.ripple +5 -7
  100. package/tests/client/url-search-params/url-search-params.iteration.test.ripple +13 -13
  101. package/tests/client/url-search-params/url-search-params.mutation.test.ripple +19 -19
  102. package/tests/client/url-search-params/url-search-params.retrieval.test.ripple +17 -17
  103. package/tests/client/url-search-params/url-search-params.serialization.test.ripple +5 -5
  104. package/tests/client/url-search-params/url-search-params.tracked-url.test.ripple +5 -5
  105. package/tests/hydration/compiled/client/events.js +8 -11
  106. package/tests/hydration/compiled/client/for.js +20 -23
  107. package/tests/hydration/compiled/client/head.js +17 -19
  108. package/tests/hydration/compiled/client/hmr.js +1 -3
  109. package/tests/hydration/compiled/client/html.js +1 -15
  110. package/tests/hydration/compiled/client/if-children.js +7 -9
  111. package/tests/hydration/compiled/client/if.js +5 -7
  112. package/tests/hydration/compiled/client/mixed-control-flow.js +3 -5
  113. package/tests/hydration/compiled/client/portal.js +1 -1
  114. package/tests/hydration/compiled/client/reactivity.js +9 -11
  115. package/tests/hydration/compiled/client/return.js +11 -13
  116. package/tests/hydration/compiled/client/switch.js +4 -6
  117. package/tests/hydration/compiled/server/basic.js +0 -1
  118. package/tests/hydration/compiled/server/composite.js +0 -3
  119. package/tests/hydration/compiled/server/events.js +8 -12
  120. package/tests/hydration/compiled/server/for.js +20 -23
  121. package/tests/hydration/compiled/server/head.js +17 -19
  122. package/tests/hydration/compiled/server/hmr.js +1 -4
  123. package/tests/hydration/compiled/server/html.js +1 -35
  124. package/tests/hydration/compiled/server/if-children.js +7 -11
  125. package/tests/hydration/compiled/server/if.js +5 -7
  126. package/tests/hydration/compiled/server/mixed-control-flow.js +3 -5
  127. package/tests/hydration/compiled/server/portal.js +1 -9
  128. package/tests/hydration/compiled/server/reactivity.js +9 -11
  129. package/tests/hydration/compiled/server/return.js +11 -13
  130. package/tests/hydration/compiled/server/switch.js +4 -6
  131. package/tests/hydration/components/events.ripple +8 -9
  132. package/tests/hydration/components/for.ripple +20 -21
  133. package/tests/hydration/components/head.ripple +6 -8
  134. package/tests/hydration/components/hmr.ripple +1 -2
  135. package/tests/hydration/components/html.ripple +1 -3
  136. package/tests/hydration/components/if-children.ripple +7 -8
  137. package/tests/hydration/components/if.ripple +5 -6
  138. package/tests/hydration/components/mixed-control-flow.ripple +4 -6
  139. package/tests/hydration/components/portal.ripple +1 -1
  140. package/tests/hydration/components/reactivity.ripple +9 -10
  141. package/tests/hydration/components/return.ripple +11 -12
  142. package/tests/hydration/components/switch.ripple +6 -8
  143. package/tests/server/await.test.ripple +2 -2
  144. package/tests/server/basic.attributes.test.ripple +19 -21
  145. package/tests/server/basic.components.test.ripple +13 -7
  146. package/tests/server/basic.test.ripple +20 -21
  147. package/tests/server/compiler.test.ripple +5 -5
  148. package/tests/server/composite.props.test.ripple +6 -7
  149. package/tests/server/composite.test.ripple +4 -4
  150. package/tests/server/context.test.ripple +1 -3
  151. package/tests/server/dynamic-elements.test.ripple +24 -24
  152. package/tests/server/head.test.ripple +5 -7
  153. package/tests/server/style-identifier.test.ripple +16 -17
  154. package/types/index.d.ts +266 -62
  155. 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
  );
@@ -2213,10 +2368,15 @@ const visitors = {
2213
2368
  let alternate_id;
2214
2369
 
2215
2370
  if (node.alternate !== null) {
2216
- const alternate = /** @type {AST.BlockStatement | AST.IfStatement} */ (node.alternate);
2371
+ const alternate = /** @type {AST.Statement} */ (node.alternate);
2217
2372
  const alternate_scope = context.state.scopes.get(alternate) || context.state.scope;
2218
2373
  /** @type {AST.Node[]} */
2219
- 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];
2220
2380
  const alternate_block = b.block(
2221
2381
  transform_body(alternate_body, {
2222
2382
  ...context,
@@ -2494,8 +2654,8 @@ const visitors = {
2494
2654
 
2495
2655
  const server_identifier = b.id(SERVER_IDENTIFIER);
2496
2656
  server_identifier.loc = node.loc;
2497
- // Add source_name to properly map longer generated back to '#server'
2498
- 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';
2499
2659
 
2500
2660
  const server_const = b.const(server_identifier, value);
2501
2661
  server_const.loc = node.loc;
@@ -2504,7 +2664,7 @@ const visitors = {
2504
2664
  }
2505
2665
 
2506
2666
  if (!context.state.serverIdentifierPresent) {
2507
- // 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
2508
2668
  return b.empty;
2509
2669
  }
2510
2670
 
@@ -2707,12 +2867,42 @@ function transform_ts_child(node, context) {
2707
2867
  if (!node.selfClosing && !node.unclosed && !has_children_props && node.children.length > 0) {
2708
2868
  const is_dom_element = is_element_dom_element(node);
2709
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
+ }
2710
2899
  const thunk =
2711
- /** @type {AST.Identifier} */ (node.id).name === 'style'
2900
+ /** @type {AST.Identifier} */ (node.id).name === 'style' ||
2901
+ non_component_children.length === 0
2712
2902
  ? null
2713
2903
  : b.thunk(
2714
2904
  b.block(
2715
- transform_body(node.children, {
2905
+ transform_body(non_component_children, {
2716
2906
  ...context,
2717
2907
  state: {
2718
2908
  ...state,
@@ -2795,10 +2985,14 @@ function transform_ts_child(node, context) {
2795
2985
  let alternate;
2796
2986
 
2797
2987
  if (node.alternate !== null) {
2798
- const alternate_node = /** @type {AST.BlockStatement | AST.IfStatement} */ (node.alternate);
2988
+ const alternate_node = /** @type {AST.Statement} */ (node.alternate);
2799
2989
  const alternate_scope = context.state.scopes.get(alternate_node) || context.state.scope;
2800
2990
  const alternate_body =
2801
- 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];
2802
2996
  alternate = b.block(
2803
2997
  transform_body(alternate_body, {
2804
2998
  ...context,
@@ -3941,6 +4135,12 @@ function create_tsx_with_typescript_support(comments) {
3941
4135
 
3942
4136
  visit(node);
3943
4137
  },
4138
+ TSExpressionWithTypeArguments(node, context) {
4139
+ context.visit(node.expression);
4140
+ if (node.typeParameters) {
4141
+ context.visit(node.typeParameters);
4142
+ }
4143
+ },
3944
4144
  AssignmentPattern(node, context) {
3945
4145
  // We need to make sure that the whole AssignmentPattern has a start and end mapping
3946
4146
  // Acorn only maps pieces but not the whole thing
@@ -4043,14 +4243,30 @@ function create_tsx_with_typescript_support(comments) {
4043
4243
  }
4044
4244
  },
4045
4245
  NewExpression(node, context) {
4046
- if (!node.loc) {
4047
- base_tsx.NewExpression?.(node, context);
4048
- 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);
4049
4269
  }
4050
- const loc = /** @type {AST.SourceLocation} */ (node.loc);
4051
- context.location(loc.start.line, loc.start.column);
4052
- base_tsx.NewExpression?.(node, context);
4053
- context.location(loc.end.line, loc.end.column);
4054
4270
  },
4055
4271
  TemplateLiteral(node, context) {
4056
4272
  if (!node.loc) {
@@ -4174,6 +4390,15 @@ function create_tsx_with_typescript_support(comments) {
4174
4390
  }
4175
4391
  } else {
4176
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
+ }
4177
4402
  // only visit value since key and value are the same
4178
4403
  // or the value will contain the key like in AssignmentPattern: { foo = 1 }
4179
4404
  context.visit(node.value);
@@ -4418,11 +4643,7 @@ function create_tsx_with_typescript_support(comments) {
4418
4643
  if (node.loc) {
4419
4644
  context.location(node.loc.start.line, node.loc.start.column);
4420
4645
  }
4421
- if (typeof node.name === 'string') {
4422
- context.write(node.name);
4423
- } else if (node.name && node.name.name) {
4424
- context.write(node.name.name);
4425
- }
4646
+ context.write(node.name);
4426
4647
  if (node.constraint) {
4427
4648
  context.write(' extends ');
4428
4649
  context.visit(node.constraint);
@@ -4563,11 +4784,7 @@ function create_tsx_with_typescript_support(comments) {
4563
4784
  context.location(tp.loc.start.line, tp.loc.start.column);
4564
4785
  }
4565
4786
  // Write the parameter name
4566
- if (typeof tp.name === 'string') {
4567
- context.write(tp.name);
4568
- } else if (tp.name && tp.name.name) {
4569
- context.write(tp.name.name);
4570
- }
4787
+ context.write(tp.name);
4571
4788
  // In mapped types, constraint uses 'in' instead of 'extends'
4572
4789
  if (tp.constraint) {
4573
4790
  context.write(' in ');