ripple 0.3.2 → 0.3.4

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 (128) hide show
  1. package/CHANGELOG.md +85 -0
  2. package/package.json +2 -2
  3. package/src/compiler/identifier-utils.js +0 -2
  4. package/src/compiler/phases/1-parse/index.js +101 -195
  5. package/src/compiler/phases/2-analyze/index.js +82 -174
  6. package/src/compiler/phases/2-analyze/prune.js +2 -2
  7. package/src/compiler/phases/3-transform/client/index.js +174 -264
  8. package/src/compiler/phases/3-transform/segments.js +0 -22
  9. package/src/compiler/phases/3-transform/server/index.js +185 -42
  10. package/src/compiler/types/index.d.ts +14 -33
  11. package/src/compiler/utils.js +32 -20
  12. package/src/runtime/index-client.js +0 -17
  13. package/src/runtime/internal/client/bindings.js +118 -7
  14. package/src/runtime/internal/client/render.js +5 -1
  15. package/src/runtime/internal/client/runtime.js +1 -1
  16. package/src/runtime/internal/client/types.d.ts +4 -0
  17. package/tests/client/array/array.copy-within.test.ripple +7 -7
  18. package/tests/client/array/array.derived.test.ripple +24 -24
  19. package/tests/client/array/array.iteration.test.ripple +7 -7
  20. package/tests/client/array/array.mutations.test.ripple +17 -17
  21. package/tests/client/array/array.to-methods.test.ripple +4 -4
  22. package/tests/client/async-suspend.test.ripple +3 -3
  23. package/tests/client/basic/basic.attributes.test.ripple +31 -31
  24. package/tests/client/basic/basic.collections.test.ripple +6 -6
  25. package/tests/client/basic/basic.components.test.ripple +8 -8
  26. package/tests/client/basic/basic.errors.test.ripple +31 -34
  27. package/tests/client/basic/basic.events.test.ripple +11 -11
  28. package/tests/client/basic/basic.get-set.test.ripple +18 -18
  29. package/tests/client/basic/basic.reactivity.test.ripple +36 -36
  30. package/tests/client/basic/basic.rendering.test.ripple +7 -7
  31. package/tests/client/basic/basic.utilities.test.ripple +4 -4
  32. package/tests/client/boundaries.test.ripple +7 -7
  33. package/tests/client/compiler/__snapshots__/compiler.typescript.test.ripple.snap +24 -0
  34. package/tests/client/compiler/compiler.assignments.test.ripple +12 -10
  35. package/tests/client/compiler/compiler.basic.test.ripple +58 -60
  36. package/tests/client/compiler/compiler.tracked-access.test.ripple +14 -8
  37. package/tests/client/compiler/compiler.typescript.test.ripple +31 -0
  38. package/tests/client/composite/composite.dynamic-components.test.ripple +6 -6
  39. package/tests/client/composite/composite.props.test.ripple +9 -9
  40. package/tests/client/composite/composite.reactivity.test.ripple +23 -23
  41. package/tests/client/composite/composite.render.test.ripple +52 -4
  42. package/tests/client/computed-properties.test.ripple +3 -3
  43. package/tests/client/context.test.ripple +3 -3
  44. package/tests/client/css/global-additional-cases.test.ripple +5 -2
  45. package/tests/client/css/style-identifier.test.ripple +40 -49
  46. package/tests/client/date.test.ripple +39 -39
  47. package/tests/client/dynamic-elements.test.ripple +37 -37
  48. package/tests/client/events.test.ripple +25 -25
  49. package/tests/client/for.test.ripple +8 -8
  50. package/tests/client/head.test.ripple +7 -7
  51. package/tests/client/html.test.ripple +2 -2
  52. package/tests/client/input-value.test.ripple +376 -177
  53. package/tests/client/lazy-destructuring.test.ripple +185 -0
  54. package/tests/client/map.test.ripple +20 -20
  55. package/tests/client/media-query.test.ripple +4 -4
  56. package/tests/client/object.test.ripple +5 -5
  57. package/tests/client/portal.test.ripple +4 -4
  58. package/tests/client/ref.test.ripple +3 -3
  59. package/tests/client/return.test.ripple +17 -17
  60. package/tests/client/set.test.ripple +10 -10
  61. package/tests/client/svg.test.ripple +6 -5
  62. package/tests/client/switch.test.ripple +10 -10
  63. package/tests/client/tracked-expression.test.ripple +3 -1
  64. package/tests/client/try.test.ripple +4 -4
  65. package/tests/client/url/url.derived.test.ripple +6 -7
  66. package/tests/client/url/url.parsing.test.ripple +9 -9
  67. package/tests/client/url/url.partial-removal.test.ripple +9 -9
  68. package/tests/client/url/url.reactivity.test.ripple +16 -16
  69. package/tests/client/url/url.serialization.test.ripple +3 -3
  70. package/tests/client/url-search-params/url-search-params.derived.test.ripple +7 -8
  71. package/tests/client/url-search-params/url-search-params.initialization.test.ripple +6 -4
  72. package/tests/client/url-search-params/url-search-params.iteration.test.ripple +12 -12
  73. package/tests/client/url-search-params/url-search-params.mutation.test.ripple +18 -18
  74. package/tests/client/url-search-params/url-search-params.retrieval.test.ripple +16 -16
  75. package/tests/client/url-search-params/url-search-params.serialization.test.ripple +4 -4
  76. package/tests/client/url-search-params/url-search-params.tracked-url.test.ripple +3 -3
  77. package/tests/hydration/build-components.js +4 -10
  78. package/tests/hydration/compiled/client/basic.js +4 -4
  79. package/tests/hydration/compiled/client/events.js +2 -0
  80. package/tests/hydration/compiled/client/for.js +2 -0
  81. package/tests/hydration/compiled/client/head.js +13 -11
  82. package/tests/hydration/compiled/client/hmr.js +4 -2
  83. package/tests/hydration/compiled/client/html.js +82 -95
  84. package/tests/hydration/compiled/client/if-children.js +8 -9
  85. package/tests/hydration/compiled/client/if.js +2 -0
  86. package/tests/hydration/compiled/client/mixed-control-flow.js +4 -2
  87. package/tests/hydration/compiled/client/portal.js +1 -1
  88. package/tests/hydration/compiled/client/reactivity.js +2 -0
  89. package/tests/hydration/compiled/client/return.js +2 -0
  90. package/tests/hydration/compiled/client/switch.js +2 -0
  91. package/tests/hydration/compiled/server/composite.js +2 -2
  92. package/tests/hydration/compiled/server/events.js +2 -0
  93. package/tests/hydration/compiled/server/for.js +2 -0
  94. package/tests/hydration/compiled/server/head.js +13 -11
  95. package/tests/hydration/compiled/server/hmr.js +2 -0
  96. package/tests/hydration/compiled/server/html.js +2 -0
  97. package/tests/hydration/compiled/server/if-children.js +2 -0
  98. package/tests/hydration/compiled/server/if.js +2 -0
  99. package/tests/hydration/compiled/server/mixed-control-flow.js +2 -0
  100. package/tests/hydration/compiled/server/portal.js +1 -1
  101. package/tests/hydration/compiled/server/reactivity.js +2 -0
  102. package/tests/hydration/compiled/server/return.js +2 -0
  103. package/tests/hydration/compiled/server/switch.js +2 -0
  104. package/tests/hydration/components/composite.ripple +1 -1
  105. package/tests/hydration/components/events.ripple +10 -8
  106. package/tests/hydration/components/for.ripple +22 -20
  107. package/tests/hydration/components/head.ripple +8 -6
  108. package/tests/hydration/components/hmr.ripple +3 -1
  109. package/tests/hydration/components/html.ripple +3 -1
  110. package/tests/hydration/components/if-children.ripple +9 -7
  111. package/tests/hydration/components/if.ripple +7 -5
  112. package/tests/hydration/components/mixed-control-flow.ripple +5 -3
  113. package/tests/hydration/components/portal.ripple +2 -2
  114. package/tests/hydration/components/reactivity.ripple +11 -9
  115. package/tests/hydration/components/return.ripple +13 -11
  116. package/tests/hydration/components/switch.ripple +6 -4
  117. package/tests/server/__snapshots__/compiler.test.ripple.snap +22 -0
  118. package/tests/server/await.test.ripple +2 -2
  119. package/tests/server/basic.attributes.test.ripple +21 -19
  120. package/tests/server/basic.components.test.ripple +5 -4
  121. package/tests/server/basic.test.ripple +21 -20
  122. package/tests/server/compiler.test.ripple +36 -5
  123. package/tests/server/composite.props.test.ripple +7 -6
  124. package/tests/server/context.test.ripple +3 -1
  125. package/tests/server/dynamic-elements.test.ripple +24 -24
  126. package/tests/server/head.test.ripple +7 -5
  127. package/tests/server/style-identifier.test.ripple +95 -16
  128. package/types/index.d.ts +4 -1
@@ -57,10 +57,11 @@ import {
57
57
  hash,
58
58
  flatten_switch_consequent,
59
59
  get_ripple_namespace_call_name,
60
+ is_ripple_import,
61
+ ripple_import_requires_block,
60
62
  } from '../../../utils.js';
61
63
  import {
62
64
  CSS_HASH_IDENTIFIER,
63
- RIPPLE_NAMESPACE_IDENTIFIER,
64
65
  STYLE_IDENTIFIER,
65
66
  SERVER_IDENTIFIER,
66
67
  obfuscate_identifier,
@@ -107,6 +108,23 @@ function visit_function(node, context) {
107
108
  }
108
109
  }
109
110
 
111
+ // Replace lazy destructuring params with generated identifiers
112
+ const transformed_params = node.params.map((param) => {
113
+ const pattern = param.type === 'AssignmentPattern' ? param.left : param;
114
+ if (
115
+ (pattern.type === 'ObjectPattern' || pattern.type === 'ArrayPattern') &&
116
+ pattern.lazy &&
117
+ pattern.metadata?.lazy_id
118
+ ) {
119
+ const id = b.id(pattern.metadata.lazy_id);
120
+ if (param.type === 'AssignmentPattern') {
121
+ return /** @type {AST.AssignmentPattern} */ ({ ...param, left: id });
122
+ }
123
+ return id;
124
+ }
125
+ return param;
126
+ });
127
+
110
128
  let body = /** @type {AST.BlockStatement | AST.Expression} */ (
111
129
  context.visit(node.body, {
112
130
  ...state,
@@ -125,11 +143,28 @@ function visit_function(node, context) {
125
143
 
126
144
  return {
127
145
  ...node,
128
- params: node.params.map((param) => context.visit(param, state)),
146
+ params: transformed_params.map((param) => context.visit(param, state)),
129
147
  body,
130
148
  };
131
149
  }
132
150
 
151
+ /**
152
+ * @param {AST.ClassDeclaration | AST.ClassExpression} node
153
+ * @param {TransformClientContext} context
154
+ * @returns {void}
155
+ */
156
+ function strip_class_typescript_syntax(node, context) {
157
+ delete node.typeParameters;
158
+ delete node.superTypeParameters;
159
+ delete node.implements;
160
+
161
+ if (node.superClass?.type === 'TSInstantiationExpression') {
162
+ node.superClass = /** @type {AST.Expression} */ (context.visit(node.superClass.expression));
163
+ } else if (node.superClass && 'typeArguments' in node.superClass) {
164
+ delete node.superClass.typeArguments;
165
+ }
166
+ }
167
+
133
168
  /**
134
169
  * @param {AST.Element} node
135
170
  * @param {number} index
@@ -385,36 +420,12 @@ function slice_loc_info(loc_info, start_offset = 0, length) {
385
420
  };
386
421
  }
387
422
 
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
423
  /**
408
424
  * @param {string | undefined} name
409
425
  * @returns {boolean}
410
426
  */
411
427
  function ripple_namespace_requires_block(name) {
412
- return (
413
- name !== undefined &&
414
- name !== '#ripple.effect' &&
415
- name !== '#ripple.untrack' &&
416
- name !== '#ripple.context'
417
- );
428
+ return name !== undefined && ripple_import_requires_block(name);
418
429
  }
419
430
 
420
431
  /**
@@ -467,17 +478,6 @@ const visitors = {
467
478
 
468
479
  if (is_reference(node, parent)) {
469
480
  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
-
481
481
  if (node.tracked) {
482
482
  // Check if this identifier is used as a dynamic component/element
483
483
  // by checking if it has a capitalized name in metadata
@@ -525,6 +525,8 @@ const visitors = {
525
525
  binding?.kind === 'prop' ||
526
526
  binding?.kind === 'index' ||
527
527
  binding?.kind === 'prop_fallback' ||
528
+ binding?.kind === 'lazy' ||
529
+ binding?.kind === 'lazy_fallback' ||
528
530
  binding?.kind === 'for_pattern') &&
529
531
  binding?.node !== node
530
532
  ) {
@@ -541,41 +543,20 @@ const visitors = {
541
543
  },
542
544
 
543
545
  ServerIdentifier(node, context) {
544
- const id = b.id(SERVER_IDENTIFIER);
545
- id.metadata.source_name = '#ripple.server';
546
- return { ...node, ...id };
546
+ const id = b.id(SERVER_IDENTIFIER, /** @type {AST.NodeWithLocation} */ (node));
547
+ id.metadata.source_name = '#server';
548
+ return id;
547
549
  },
548
550
 
549
551
  StyleIdentifier(node, context) {
550
552
  if (context.state.to_ts) {
551
- const namespace_alias = set_hidden_import_from_ripple(
552
- RIPPLE_NAMESPACE_IDENTIFIER,
553
- context,
554
- true,
555
- );
553
+ const style_alias = set_hidden_import_from_ripple(STYLE_IDENTIFIER, context, true);
556
554
 
557
- // IMPORTANT! only add location to the ParenthesizedExpression
558
- // otherwise it will cause partial #ripple mapping
559
- const namespace_parens = b.parenthesized(
560
- b.ts_as(b.id(namespace_alias), b.ts_type_reference(b.id('RippleNamespaceWithStyle'))),
561
- slice_loc_info(/** @type {AST.NodeWithLocation} */ (node), 0, '#ripple'.length),
562
- );
563
- namespace_parens.metadata = {
564
- ...namespace_parens.metadata,
565
- forceMapping: true,
566
- skipParenthesisMapping: true,
567
- };
555
+ // IMPORTANT! only add location to the identifier
556
+ const style_id = b.id(style_alias, /** @type {AST.NodeWithLocation} */ (node));
557
+ style_id.metadata.source_name = '#style';
568
558
 
569
- return b.member(
570
- namespace_parens,
571
- b.id(
572
- 'style',
573
- slice_loc_info(/** @type {AST.NodeWithLocation} */ (node), '#ripple.'.length),
574
- ),
575
- false,
576
- false,
577
- /** @type {AST.NodeWithLocation} */ (node),
578
- );
559
+ return b.ts_as(style_id, b.ts_type_reference(b.id('RippleStyle')));
579
560
  }
580
561
 
581
562
  return { ...node, ...b.id(STYLE_IDENTIFIER) };
@@ -632,41 +613,25 @@ const visitors = {
632
613
  }
633
614
  const callee = node.callee;
634
615
  const parent = context.path.at(-1);
635
- const source_name = callee.type === 'Identifier' ? callee.metadata?.source_name : undefined;
636
- const ripple_runtime_method = get_ripple_namespace_call_name(source_name);
637
- const ripple_method_requires_block = ripple_namespace_requires_block(source_name);
638
616
 
639
617
  if (context.state.metadata?.tracking === false) {
640
618
  context.state.metadata.tracking = true;
641
619
  }
642
620
 
643
- if (!context.state.to_ts && ripple_runtime_method !== null) {
644
- return {
645
- ...node,
646
- callee: b.member(b.id('_$_'), b.id(ripple_runtime_method)),
647
- arguments: /** @type {(AST.Expression | AST.SpreadElement)[]} */ ([
648
- ...(ripple_method_requires_block ? [b.id('__block')] : []),
649
- ...node.arguments.map((arg) => context.visit(arg)),
650
- ]),
651
- };
652
- }
653
-
654
- if (context.state.to_ts && source_name?.startsWith('#ripple.')) {
655
- const property_name = source_name.replace('#ripple.', '');
656
- const namespace_member = build_ripple_namespace_member(
657
- property_name,
658
- source_name,
659
- /** @type {AST.NodeWithLocation} */ (callee),
660
- context,
661
- );
662
-
663
- return {
664
- ...node,
665
- callee: namespace_member,
666
- arguments: /** @type {(AST.Expression | AST.SpreadElement)[]} */ (
667
- node.arguments.map((arg) => context.visit(arg))
668
- ),
669
- };
621
+ // Handle direct calls to ripple-imported functions: effect(), untrack(), RippleArray(), etc.
622
+ if (!context.state.to_ts && callee.type === 'Identifier' && is_ripple_import(callee, context)) {
623
+ const ripple_runtime_method = get_ripple_namespace_call_name(callee.name);
624
+ if (ripple_runtime_method !== null) {
625
+ const requires_block = ripple_namespace_requires_block(callee.name);
626
+ return {
627
+ ...node,
628
+ callee: b.member(b.id('_$_'), b.id(ripple_runtime_method)),
629
+ arguments: /** @type {(AST.Expression | AST.SpreadElement)[]} */ ([
630
+ ...(requires_block ? [b.id('__block')] : []),
631
+ ...node.arguments.map((arg) => context.visit(arg)),
632
+ ]),
633
+ };
634
+ }
670
635
  }
671
636
 
672
637
  if (!context.state.to_ts && is_ripple_track_call(callee, context)) {
@@ -700,60 +665,32 @@ const visitors = {
700
665
  };
701
666
  }
702
667
 
703
- // Check for more than two nested level calls, like #ripple.array.from()
668
+ // Handle member calls on ripple imports, like RippleArray.from()
704
669
  if (
705
670
  callee.type === 'MemberExpression' &&
706
- callee.object.metadata?.source_name?.startsWith('#ripple.') &&
707
671
  callee.object.type === 'Identifier' &&
708
- callee.property.type === 'Identifier'
672
+ callee.property.type === 'Identifier' &&
673
+ is_ripple_import(callee, context)
709
674
  ) {
710
675
  const object = callee.object;
711
676
  const property = callee.property;
712
- const source_name = /** @type {string} */ (object.metadata?.source_name);
713
- const property_name = source_name.replace('#ripple.', '');
714
-
715
- if (context.state.to_ts) {
716
- // e.g. `#ripple.array`
717
- const namespace_member = build_ripple_namespace_member(
718
- property_name,
719
- source_name,
720
- /** @type {AST.NodeWithLocation} */ (node.callee),
721
- context,
722
- );
723
-
724
- return /** @type {AST.CallExpression} */ ({
725
- ...node,
726
- callee: {
727
- ...namespace_member,
728
- // e.g. `array.from`
729
- property: b.member(
730
- /** @type {AST.Identifier} */ (namespace_member.property),
731
- property,
732
- false,
733
- false,
734
- slice_loc_info(/** @type {AST.NodeWithLocation} */ (node.callee), '#ripple.'.length),
735
- ),
736
- },
737
- arguments: node.arguments.map((arg) => context.visit(arg)),
738
- });
739
- } else {
740
- const method_name = get_ripple_namespace_call_name(source_name);
741
- const requires_block = ripple_namespace_requires_block(source_name);
742
- if (method_name !== null) {
743
- return b.member(
744
- b.id('_$_'),
745
- b.member(
746
- b.id(method_name),
747
- b.call(
748
- b.id(property.name),
749
- .../** @type {(AST.Expression | AST.SpreadElement)[]} */ ([
750
- ...(requires_block ? [b.id('__block')] : []),
751
- ...node.arguments.map((arg) => context.visit(arg)),
752
- ]),
753
- ),
677
+ const method_name = get_ripple_namespace_call_name(object.name);
678
+
679
+ if (!context.state.to_ts && method_name !== null) {
680
+ const requires_block = ripple_namespace_requires_block(object.name);
681
+ return b.member(
682
+ b.id('_$_'),
683
+ b.member(
684
+ b.id(method_name),
685
+ b.call(
686
+ b.id(property.name),
687
+ .../** @type {(AST.Expression | AST.SpreadElement)[]} */ ([
688
+ ...(requires_block ? [b.id('__block')] : []),
689
+ ...node.arguments.map((arg) => context.visit(arg)),
690
+ ]),
754
691
  ),
755
- );
756
- }
692
+ ),
693
+ );
757
694
  }
758
695
  }
759
696
 
@@ -834,6 +771,21 @@ const visitors = {
834
771
  context.state.metadata.tracking = true;
835
772
  }
836
773
 
774
+ // Transform `new RippleArray(...)`, `new RippleMap(...)`, etc. imported from 'ripple'
775
+ if (!context.state.to_ts && callee.type === 'Identifier' && is_ripple_import(callee, context)) {
776
+ const ripple_runtime_method = get_ripple_namespace_call_name(callee.name);
777
+ if (ripple_runtime_method !== null) {
778
+ const requires_block = ripple_namespace_requires_block(callee.name);
779
+ return b.call(
780
+ '_$_.' + ripple_runtime_method,
781
+ ...(requires_block ? [b.id('__block')] : []),
782
+ .../** @type {(AST.Expression | AST.SpreadElement)[]} */ (
783
+ node.arguments.map((arg) => context.visit(arg))
784
+ ),
785
+ );
786
+ }
787
+ }
788
+
837
789
  if (
838
790
  context.state.to_ts ||
839
791
  !is_inside_component(context, true) ||
@@ -862,70 +814,6 @@ const visitors = {
862
814
  return b.call('_$_.with_scope', b.id('__block'), b.thunk(new_node));
863
815
  },
864
816
 
865
- RippleArrayExpression(node, context) {
866
- if (context.state.to_ts) {
867
- const arrayAlias = set_hidden_import_from_ripple('RippleArray', context);
868
- const id = b.id(
869
- arrayAlias,
870
- slice_loc_info(/** @type {AST.NodeWithLocation} */ (node), 0, '#ripple'.length),
871
- );
872
- id.metadata.source_name = '#ripple';
873
-
874
- return {
875
- type: 'NewExpression',
876
- callee: id,
877
- arguments: /** @type {(AST.Expression | AST.SpreadElement)[]} */ (
878
- node.elements.map((el) => context.visit(/** @type {AST.Node} */ (el)))
879
- ),
880
- metadata: { path: [] },
881
- };
882
- }
883
-
884
- return b.call(
885
- '_$_.ripple_array',
886
- b.id('__block'),
887
- .../** @type {(AST.Expression | AST.SpreadElement)[]} */ (
888
- node.elements.map((el) => context.visit(/** @type {AST.Node} */ (el)))
889
- ),
890
- );
891
- },
892
-
893
- RippleObjectExpression(node, context) {
894
- if (context.state.to_ts) {
895
- const objectAlias = set_hidden_import_from_ripple('RippleObject', context);
896
- const id = b.id(
897
- objectAlias,
898
- slice_loc_info(/** @type {AST.NodeWithLocation} */ (node), 0, '#ripple'.length),
899
- );
900
- id.metadata.source_name = '#ripple';
901
- const new_node = b.new(
902
- id,
903
- /** @type {AST.NodeWithLocation} */ (node),
904
- b.object(
905
- /** @type {(AST.Property | AST.SpreadElement)[]} */ (
906
- node.properties.map((prop) => context.visit(prop))
907
- ),
908
- slice_loc_info(/** @type {AST.NodeWithLocation} */ (node), '#ripple'.length),
909
- ),
910
- );
911
- // force the source mapping to skip the constructor,
912
- // otherwise the mapping will be off by 4 and the whole
913
- // new expression will be mapped to the source which doesn't have `new `
914
- new_node.metadata.skipNewMapping = true;
915
- return new_node;
916
- }
917
-
918
- return b.call(
919
- '_$_.ripple_object',
920
- b.id('__block'),
921
- b.object(
922
- /** @type {(AST.Property | AST.SpreadElement)[]} */ (
923
- node.properties.map((prop) => context.visit(prop))
924
- ),
925
- ),
926
- );
927
- },
928
-
929
817
  TrackedExpression(node, context) {
930
818
  if (context.state.to_ts) {
931
819
  const visited = /** @type {AST.Expression} */ (context.visit(node.argument));
@@ -1025,10 +913,33 @@ const visitors = {
1025
913
  return context.next();
1026
914
  },
1027
915
 
916
+ ClassDeclaration(node, context) {
917
+ if (!context.state.to_ts) {
918
+ strip_class_typescript_syntax(node, context);
919
+ }
920
+ return context.next();
921
+ },
922
+
923
+ ClassExpression(node, context) {
924
+ if (!context.state.to_ts) {
925
+ strip_class_typescript_syntax(node, context);
926
+ }
927
+ return context.next();
928
+ },
929
+
1028
930
  VariableDeclaration(node, context) {
1029
931
  for (const declarator of node.declarations) {
1030
932
  if (!context.state.to_ts) {
1031
933
  delete declarator.id.typeAnnotation;
934
+
935
+ // Replace lazy destructuring patterns with the generated identifier
936
+ if (
937
+ (declarator.id.type === 'ObjectPattern' || declarator.id.type === 'ArrayPattern') &&
938
+ declarator.id.lazy &&
939
+ declarator.id.metadata?.lazy_id
940
+ ) {
941
+ declarator.id = b.id(declarator.id.metadata.lazy_id);
942
+ }
1032
943
  }
1033
944
  }
1034
945
 
@@ -1369,7 +1280,8 @@ const visitors = {
1369
1280
  let style_attribute = null;
1370
1281
  /** @type {TransformClientState['update']} */
1371
1282
  const local_updates = [];
1372
- const is_void = is_void_element(/** @type {AST.Identifier} */ (node.id).name);
1283
+ const element_name = /** @type {AST.Identifier} */ (node.id).name;
1284
+ const is_void = is_void_element(element_name);
1373
1285
  /** @type {AST.CSS.StyleSheet['hash'] | null} */
1374
1286
  const scoping_hash =
1375
1287
  state.applyParentCssScope ??
@@ -1377,7 +1289,7 @@ const visitors = {
1377
1289
  ? /** @type {AST.CSS.StyleSheet} */ (state.component?.css).hash
1378
1290
  : null);
1379
1291
 
1380
- state.template?.push(`<${/** @type {AST.Identifier} */ (node.id).name}`);
1292
+ state.template?.push(`<${element_name}`);
1381
1293
 
1382
1294
  for (const attr of node.attributes) {
1383
1295
  if (attr.type === 'Attribute') {
@@ -1389,23 +1301,16 @@ const visitors = {
1389
1301
  continue;
1390
1302
  }
1391
1303
 
1392
- if (attr.value.type === 'Literal' && name !== 'class' && name !== 'style') {
1304
+ if (
1305
+ attr.value.type === 'Literal' &&
1306
+ name !== 'class' &&
1307
+ name !== 'style' &&
1308
+ !(name === 'value' && element_name === 'option')
1309
+ ) {
1393
1310
  handle_static_attr(name, attr.value.value);
1394
1311
  continue;
1395
1312
  }
1396
1313
 
1397
- if (name === 'class') {
1398
- class_attribute = attr;
1399
-
1400
- continue;
1401
- }
1402
-
1403
- if (name === 'style') {
1404
- style_attribute = attr;
1405
-
1406
- continue;
1407
- }
1408
-
1409
1314
  if (name === 'value') {
1410
1315
  const id = state.flush_node?.();
1411
1316
  const metadata = { tracking: false, await: false };
@@ -1427,6 +1332,18 @@ const visitors = {
1427
1332
  continue;
1428
1333
  }
1429
1334
 
1335
+ if (name === 'class') {
1336
+ class_attribute = attr;
1337
+
1338
+ continue;
1339
+ }
1340
+
1341
+ if (name === 'style') {
1342
+ style_attribute = attr;
1343
+
1344
+ continue;
1345
+ }
1346
+
1430
1347
  if (name === 'checked') {
1431
1348
  const id = state.flush_node?.();
1432
1349
  const metadata = { tracking: false, await: false };
@@ -1729,10 +1646,7 @@ const visitors = {
1729
1646
 
1730
1647
  state.template?.push('<!>');
1731
1648
 
1732
- if (state.applyParentCssScope) {
1733
- // We're inside a component, don't continue applying css hash to class
1734
- state.applyParentCssScope = undefined;
1735
- }
1649
+ const apply_parent_css_scope = state.applyParentCssScope;
1736
1650
 
1737
1651
  const is_dynamic_element = is_element_dynamic(node);
1738
1652
  const is_spreading = node.attributes.some((attr) => attr.type === 'SpreadAttribute');
@@ -1849,11 +1763,12 @@ const visitors = {
1849
1763
  const children = /** @type {AST.Expression} */ (
1850
1764
  visit(children_component, {
1851
1765
  ...state,
1852
- ...(state.applyParentCssScope ||
1766
+ ...(apply_parent_css_scope ||
1853
1767
  (is_dynamic_element && node.metadata.scoped && state.component?.css)
1854
1768
  ? {
1855
- applyParentCssScope: /** @type {AST.CSS.StyleSheet} */ (state.component?.css)
1856
- .hash,
1769
+ applyParentCssScope:
1770
+ apply_parent_css_scope ||
1771
+ /** @type {AST.CSS.StyleSheet} */ (state.component?.css).hash,
1857
1772
  }
1858
1773
  : {}),
1859
1774
  scope: /** @type {ScopeInterface} */ (component_scope),
@@ -1981,34 +1896,21 @@ const visitors = {
1981
1896
  }
1982
1897
  }
1983
1898
  if (context.state.to_ts) {
1984
- const namespace_alias = set_hidden_import_from_ripple(
1985
- RIPPLE_NAMESPACE_IDENTIFIER,
1986
- context,
1987
- true,
1988
- );
1989
-
1990
- // This will create a type that we'll use for type casting #ripple.style access, e.g.
1991
- // type RippleNamespaceWithStyle = Omit<typeof _$__u0023_ripple, 'style'> & { style: typeof _$__u0023_style };
1992
- const ripple_type_alias = b.ts_type_alias(
1993
- b.id('RippleNamespaceWithStyle'),
1994
- b.ts_intersection_type([
1995
- b.ts_type_reference(
1996
- b.id('Omit'),
1997
- b.ts_type_parameter_instantiation([
1998
- b.ts_type_query(b.id(namespace_alias)),
1999
- b.ts_literal_type(b.literal('style')),
2000
- ]),
2001
- ),
2002
- b.ts_type_literal([
1899
+ // For to_ts mode, we create a type alias for RippleStyle
1900
+ // that maps the scoped class names
1901
+ const ripple_style_type_alias = b.ts_type_alias(
1902
+ b.id('RippleStyle'),
1903
+ b.ts_type_literal(
1904
+ properties.map((prop) =>
2003
1905
  b.ts_property_signature(
2004
- b.id('style'),
2005
- b.ts_type_annotation(b.ts_type_query(b.id(STYLE_IDENTIFIER))),
1906
+ /** @type {AST.Expression} */ (prop.key),
1907
+ b.ts_type_annotation(b.ts_keyword_type('string')),
2006
1908
  ),
2007
- ]),
2008
- ]),
1909
+ ),
1910
+ ),
2009
1911
  );
2010
1912
  style_statements.push(
2011
- /** @type {AST.Statement} */ (/** @type {unknown} */ (ripple_type_alias)),
1913
+ /** @type {AST.Statement} */ (/** @type {unknown} */ (ripple_style_type_alias)),
2012
1914
  );
2013
1915
  }
2014
1916
  style_statements.push(b[var_method_type](b.id(STYLE_IDENTIFIER), b.object(properties)));
@@ -2049,6 +1951,7 @@ const visitors = {
2049
1951
  return func;
2050
1952
  }
2051
1953
 
1954
+ /** @type {AST.Identifier | AST.ObjectPattern | AST.ArrayPattern} */
2052
1955
  let props = b.id('__props');
2053
1956
 
2054
1957
  if (node.params.length > 0) {
@@ -2057,8 +1960,13 @@ const visitors = {
2057
1960
  if (props_param.type === 'Identifier') {
2058
1961
  delete props_param.typeAnnotation;
2059
1962
  props = props_param;
2060
- } else if (props_param.type === 'ObjectPattern') {
1963
+ } else if (props_param.type === 'ObjectPattern' || props_param.type === 'ArrayPattern') {
2061
1964
  delete props_param.typeAnnotation;
1965
+ if (!props_param.lazy) {
1966
+ // Non-lazy destructuring: use the pattern directly as the function param
1967
+ props = props_param;
1968
+ }
1969
+ // Lazy destructuring: props stays as __props, bindings resolved via transforms
2062
1970
  }
2063
1971
  }
2064
1972
 
@@ -2716,10 +2624,12 @@ const visitors = {
2716
2624
  );
2717
2625
  value.loc = node.loc;
2718
2626
 
2719
- const server_identifier = b.id(SERVER_IDENTIFIER);
2720
- server_identifier.loc = node.loc;
2721
- // Add source_name to properly map longer generated back to '#ripple.server'
2722
- server_identifier.metadata.source_name = '#ripple.server';
2627
+ const server_identifier = b.id(
2628
+ SERVER_IDENTIFIER,
2629
+ slice_loc_info(/** @type {AST.NodeWithLocation} */ (node), 0, '#server'.length),
2630
+ );
2631
+ // Add source_name to properly map longer generated back to '#server'
2632
+ server_identifier.metadata.source_name = '#server';
2723
2633
 
2724
2634
  const server_const = b.const(server_identifier, value);
2725
2635
  server_const.loc = node.loc;
@@ -2728,7 +2638,7 @@ const visitors = {
2728
2638
  }
2729
2639
 
2730
2640
  if (!context.state.serverIdentifierPresent) {
2731
- // no point printing the client-side block if #ripple.server.func is not used
2641
+ // no point printing the client-side block if #server.func is not used
2732
2642
  return b.empty;
2733
2643
  }
2734
2644
 
@@ -4943,8 +4853,8 @@ function create_tsx_with_typescript_support(comments) {
4943
4853
  if (node.superClass) {
4944
4854
  context.write(' extends ');
4945
4855
  context.visit(node.superClass);
4946
- if (node.superTypeArguments) {
4947
- context.visit(node.superTypeArguments);
4856
+ if (node.superTypeParameters) {
4857
+ context.visit(node.superTypeParameters);
4948
4858
  }
4949
4859
  }
4950
4860
  if (node.implements && node.implements.length > 0) {
@@ -4969,8 +4879,8 @@ function create_tsx_with_typescript_support(comments) {
4969
4879
  if (node.superClass) {
4970
4880
  context.write(' extends ');
4971
4881
  context.visit(node.superClass);
4972
- if (node.superTypeArguments) {
4973
- context.visit(node.superTypeArguments);
4882
+ if (node.superTypeParameters) {
4883
+ context.visit(node.superTypeParameters);
4974
4884
  }
4975
4885
  }
4976
4886
  if (node.implements && node.implements.length > 0) {
@@ -422,22 +422,6 @@ export function convert_source_map_to_mappings(
422
422
  loc: node.loc,
423
423
  metadata: {},
424
424
  };
425
-
426
- if (node.metadata.source_name === '#ripple') {
427
- // Suppress the private-identifier parse diagnostic while the user is
428
- // still typing a namespace access like `#ripple.` for completions.
429
- token.metadata.suppressedDiagnostics = [18016];
430
- }
431
-
432
- if (
433
- node.metadata.source_name === '#ripple.server' ||
434
- node.metadata.source_name === '#ripple.style'
435
- ) {
436
- // Let TextMate own the coloring for these namespace forms.
437
- // Their dedicated AST nodes otherwise cause semantic tokens to repaint
438
- // the full '#ripple.server' / '#ripple.style' span after TS attaches.
439
- token.mappingData = { ...mapping_data, semantic: false };
440
- }
441
425
  } else {
442
426
  token = {
443
427
  source: node.name,
@@ -445,12 +429,6 @@ export function convert_source_map_to_mappings(
445
429
  loc: node.loc,
446
430
  metadata: {},
447
431
  };
448
- if (node.name === '#ripple') {
449
- // Suppress the private-identifier parse diagnostic while the user is
450
- // still typing a namespace access like `#ripple.` for completions.
451
- token.metadata.suppressedDiagnostics = [18016];
452
- }
453
- // No transformation - source and generated names are the same
454
432
  }
455
433
 
456
434
  if (node.metadata?.is_component) {