ripple 0.2.216 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. package/CHANGELOG.md +52 -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 +27 -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
@@ -183,6 +183,53 @@ function RipplePlugin(config) {
183
183
  this.#filename = options?.rippleOptions.filename || null;
184
184
  }
185
185
 
186
+ /**
187
+ * @param {number} position
188
+ * @param {string} message
189
+ */
190
+ #report_recoverable_error(position, message) {
191
+ const start = Math.max(0, Math.min(position, this.input.length));
192
+ const end = Math.min(this.input.length, start + 1);
193
+ const start_loc = acorn.getLineInfo(this.input, start);
194
+ const end_loc = acorn.getLineInfo(this.input, end);
195
+
196
+ error(
197
+ message,
198
+ this.#filename,
199
+ /** @type {AST.NodeWithLocation} */ ({
200
+ start,
201
+ end,
202
+ loc: {
203
+ start: start_loc,
204
+ end: end_loc,
205
+ },
206
+ }),
207
+ this.#loose ? this.#errors : undefined,
208
+ );
209
+ }
210
+
211
+ /**
212
+ * In loose mode, keep parsing after duplicate declaration diagnostics so
213
+ * editor tooling can continue producing AST and mappings.
214
+ * @param {number} position
215
+ * @param {string | { message?: string }} message
216
+ */
217
+ raiseRecoverable(position, message) {
218
+ const error_message =
219
+ typeof message === 'string'
220
+ ? message
221
+ : typeof message?.message === 'string'
222
+ ? message.message
223
+ : String(message);
224
+
225
+ if (error_message.includes('has already been declared')) {
226
+ this.#report_recoverable_error(position, error_message);
227
+ return;
228
+ }
229
+
230
+ return super.raiseRecoverable(position, error_message);
231
+ }
232
+
186
233
  /**
187
234
  * Override to allow single-parameter generic arrow functions without trailing comma.
188
235
  * By default, @sveltejs/acorn-typescript throws an error for `<T>() => {}` when JSX is enabled
@@ -593,141 +640,72 @@ function RipplePlugin(config) {
593
640
 
594
641
  if (code === 35) {
595
642
  // # character
596
- // Look ahead to see if this is followed by [ for tuple syntax or 'server' keyword
597
643
  if (this.pos + 1 < this.input.length) {
598
- const nextChar = this.input.charCodeAt(this.pos + 1);
599
- if (nextChar === 91 || nextChar === 123) {
600
- // [ or { character
601
- // This is a tuple literal #[ or #{
602
- // Consume both # and [ or {
603
- ++this.pos; // consume #
604
- ++this.pos; // consume [ or {
605
- if (nextChar === 123) {
606
- return this.finishToken(tt.braceL, '#{');
607
- } else {
608
- return this.finishToken(tt.bracketL, '#[');
609
- }
644
+ /** @param {string} value */
645
+ const startsWith = (value) =>
646
+ this.input.slice(this.pos, this.pos + value.length) === value;
647
+ /** @param {number} length */
648
+ const char_after = (length) =>
649
+ this.pos + length < this.input.length ? this.input.charCodeAt(this.pos + length) : -1;
650
+ /** @param {number} ch */
651
+ const is_ripple_delimiter = (ch) =>
652
+ ch === 40 || // (
653
+ ch === 41 || // )
654
+ ch === 60 || // <
655
+ ch === 46 || // .
656
+ ch === 44 || // ,
657
+ ch === 59 || // ;
658
+ ch === 91 || // [
659
+ ch === 93 || // ]
660
+ ch === 123 || // {
661
+ ch === 125 || // }
662
+ ch === 32 || // space
663
+ ch === 9 || // tab
664
+ ch === 10 || // newline
665
+ ch === 13 || // carriage return
666
+ ch === -1; // EOF
667
+
668
+ if (startsWith('#ripple[')) {
669
+ this.pos += 8;
670
+ return this.finishToken(tt.bracketL, '#ripple[');
610
671
  }
611
672
 
612
- // Check if this is #Map or #Set
613
- if (this.input.slice(this.pos, this.pos + 4) === '#Map') {
614
- const charAfter =
615
- this.pos + 4 < this.input.length ? this.input.charCodeAt(this.pos + 4) : -1;
616
- if (charAfter === 40 || charAfter === 60) {
617
- // ( or < character (for generics like #Map<string, number>)
618
- this.pos += 4; // consume '#Map'
619
- return this.finishToken(tt.name, '#Map');
620
- } else if (this.#loose) {
621
- // In loose mode, produce token even without parens (incomplete syntax)
622
- this.pos += 4; // consume '#Map'
623
- return this.finishToken(tt.name, '#Map');
624
- }
625
- }
626
- if (this.input.slice(this.pos, this.pos + 4) === '#Set') {
627
- const charAfter =
628
- this.pos + 4 < this.input.length ? this.input.charCodeAt(this.pos + 4) : -1;
629
- if (charAfter === 40 || charAfter === 60) {
630
- // ( or < character (for generics like #Set<number>)
631
- this.pos += 4; // consume '#Set'
632
- return this.finishToken(tt.name, '#Set');
633
- } else if (this.#loose) {
634
- // In loose mode, produce token even without parens (incomplete syntax)
635
- this.pos += 4; // consume '#Set'
636
- return this.finishToken(tt.name, '#Set');
637
- }
673
+ if (startsWith('#ripple{')) {
674
+ this.pos += 8;
675
+ return this.finishToken(tt.braceL, '#ripple{');
638
676
  }
639
677
 
640
- // Check if this is #server
641
- if (this.input.slice(this.pos, this.pos + 7) === '#server') {
642
- // Check that next char after 'server' is whitespace, {, . (dot), or EOF
643
- const charAfter =
644
- this.pos + 7 < this.input.length ? this.input.charCodeAt(this.pos + 7) : -1;
645
- if (
646
- charAfter === 123 || // {
647
- charAfter === 46 || // . (dot)
648
- charAfter === 32 || // space
649
- charAfter === 9 || // tab
650
- charAfter === 10 || // newline
651
- charAfter === 13 || // carriage return
652
- charAfter === -1 // EOF
653
- ) {
654
- // { or . or whitespace or EOF
655
- this.pos += 7; // consume '#server'
656
- return this.finishToken(tt.name, '#server');
657
- }
658
- }
678
+ const ripple_keywords = [
679
+ '#ripple.map',
680
+ '#ripple.set',
681
+ '#ripple.array',
682
+ '#ripple.object',
683
+ '#ripple.track',
684
+ '#ripple.trackSplit',
685
+ '#ripple.untrack',
686
+ '#ripple.effect',
687
+ '#ripple.context',
688
+ '#ripple.date',
689
+ '#ripple.url',
690
+ '#ripple.urlSearchParams',
691
+ '#ripple.mediaQuery',
692
+ '#ripple.server',
693
+ '#ripple.style',
694
+ '#ripple.validate',
695
+ ];
659
696
 
660
- if (this.input.slice(this.pos, this.pos + 6) === '#style') {
661
- // Check that next char after 'style' is . (dot), [, whitespace, or EOF
662
- const charAfter =
663
- this.pos + 6 < this.input.length ? this.input.charCodeAt(this.pos + 6) : -1;
664
- if (
665
- charAfter === 46 || // . (dot)
666
- charAfter === 91 || // [
667
- charAfter === 32 || // space
668
- charAfter === 9 || // tab
669
- charAfter === 10 || // newline
670
- charAfter === 13 || // carriage return
671
- charAfter === -1 // EOF
672
- ) {
673
- // { or . or whitespace or EOF
674
- this.pos += 6; // consume '#style'
675
- return this.finishToken(tt.name, '#style');
697
+ for (let i = 0; i < ripple_keywords.length; i++) {
698
+ const keyword = ripple_keywords[i];
699
+ if (startsWith(keyword) && is_ripple_delimiter(char_after(keyword.length))) {
700
+ this.pos += keyword.length;
701
+ return this.finishToken(tt.name, keyword);
676
702
  }
677
703
  }
678
704
 
679
- // Check if this is an invalid #Identifier pattern
680
- // Valid patterns: #[, #{, #Map(, #Map<, #Set(, #Set<, #server, #style
681
- // If we see # followed by an uppercase letter that isn't Map or Set, it's an error
682
- // In loose mode, allow incomplete identifiers like #M, #Ma, #S, #Se for autocomplete
683
- if (nextChar >= 65 && nextChar <= 90) {
684
- // A-Z
685
- // Extract the identifier name
686
- let identEnd = this.pos + 1;
687
- while (identEnd < this.input.length) {
688
- const ch = this.input.charCodeAt(identEnd);
689
- if (
690
- (ch >= 65 && ch <= 90) ||
691
- (ch >= 97 && ch <= 122) ||
692
- (ch >= 48 && ch <= 57) ||
693
- ch === 95
694
- ) {
695
- // A-Z, a-z, 0-9, _
696
- identEnd++;
697
- } else {
698
- break;
699
- }
700
- }
701
- const identName = this.input.slice(this.pos + 1, identEnd);
702
- if (identName !== 'Map' && identName !== 'Set') {
703
- // In loose mode, allow incomplete identifiers (prefixes of Map/Set)
704
- // This supports autocomplete scenarios where user is still typing
705
- const isIncompleteMap = 'Map'.startsWith(identName);
706
- const isIncompleteSet = 'Set'.startsWith(identName);
707
-
708
- if (!this.#loose || (!isIncompleteMap && !isIncompleteSet)) {
709
- this.raise(
710
- this.pos,
711
- `Invalid tracked syntax '#${identName}'. Only #Map and #Set are currently supported using shorthand tracked syntax.`,
712
- );
713
- } else {
714
- // In loose mode with valid prefix, consume the token and return it
715
- // This allows the parser to handle incomplete syntax gracefully
716
- this.pos = identEnd; // consume '#' + identifier
717
- return this.finishToken(tt.name, '#' + identName);
718
- }
719
- }
720
- }
721
-
722
- // In loose mode, handle bare # or # followed by unrecognized characters
723
- if (this.#loose) {
724
- this.pos++; // consume '#'
725
- return this.finishToken(tt.name, '#');
705
+ if (this.#loose && startsWith('#ripple') && is_ripple_delimiter(char_after(7))) {
706
+ this.pos += 7;
707
+ return this.finishToken(tt.name, '#ripple');
726
708
  }
727
- } else if (this.#loose) {
728
- // In loose mode, handle bare # at EOF
729
- this.pos++; // consume '#'
730
- return this.finishToken(tt.name, '#');
731
709
  }
732
710
  }
733
711
  if (code === 64) {
@@ -927,40 +905,66 @@ function RipplePlugin(config) {
927
905
  }
928
906
 
929
907
  /**
930
- * Parse expression atom - handles TrackedArray and TrackedObject literals
908
+ * Parse expression atom - handles RippleArray and RippleObject literals
931
909
  * @type {Parse.Parser['parseExprAtom']}
932
910
  */
933
911
  parseExprAtom(refDestructuringErrors, forNew, forInit) {
912
+ const lookahead_type = this.lookahead().type;
913
+ const is_next_call_token = lookahead_type === tt.parenL || lookahead_type === tt.relational;
914
+
934
915
  // Check if this is @(expression) for unboxing tracked values
935
916
  if (this.type === tt.parenL && this.value === '@(') {
936
917
  return this.parseTrackedExpression();
937
918
  }
938
919
 
939
- // Check if this is #server identifier for server function calls
940
- if (this.type === tt.name && this.value === '#server') {
920
+ // Check if this is #ripple.server identifier for server function calls
921
+ if (this.type === tt.name && this.value === '#ripple.server') {
941
922
  const node = this.startNode();
942
923
  this.next();
943
924
  return /** @type {AST.ServerIdentifier} */ (this.finishNode(node, 'ServerIdentifier'));
944
925
  }
945
926
 
946
- if (this.type === tt.name && this.value === '#style') {
927
+ if (this.type === tt.name && this.value === '#ripple.style') {
947
928
  const node = this.startNode();
948
929
  this.next();
949
930
  return /** @type {AST.StyleIdentifier} */ (this.finishNode(node, 'StyleIdentifier'));
950
931
  }
951
932
 
952
- // Check if this is #Map( or #Set(
953
- if (this.type === tt.name && (this.value === '#Map' || this.value === '#Set')) {
954
- const type = this.value === '#Map' ? 'TrackedMapExpression' : 'TrackedSetExpression';
955
- return this.parseTrackedCollectionExpression(type);
933
+ if (this.type === tt.name && typeof this.value === 'string') {
934
+ const ripple_identifier_map = {
935
+ '#ripple.array': 'RippleArray',
936
+ '#ripple.object': 'RippleObject',
937
+ '#ripple.track': 'track',
938
+ '#ripple.trackSplit': 'trackSplit',
939
+ '#ripple.untrack': 'untrack',
940
+ '#ripple.effect': 'effect',
941
+ '#ripple.context': 'Context',
942
+ '#ripple.date': 'RippleDate',
943
+ '#ripple.map': 'RippleMap',
944
+ '#ripple.set': 'RippleSet',
945
+ '#ripple.url': 'RippleURL',
946
+ '#ripple.urlSearchParams': 'RippleURLSearchParams',
947
+ '#ripple.mediaQuery': 'MediaQuery',
948
+ };
949
+
950
+ const identifier_name =
951
+ ripple_identifier_map[/** @type {keyof typeof ripple_identifier_map} */ (this.value)];
952
+ if (identifier_name !== undefined) {
953
+ const node = /** @type {AST.Identifier} */ (this.startNode());
954
+ node.name = identifier_name;
955
+ node.metadata ??= { path: [] };
956
+ node.metadata.source_name = this.value;
957
+ this.next();
958
+ return this.finishNode(node, 'Identifier');
959
+ }
956
960
  }
957
961
 
958
- // In loose mode, handle incomplete #Map/#Set prefixes (e.g., #M, #Ma, #S, #Se)
962
+ // In loose mode, handle incomplete #ripple prefixes for autocomplete
959
963
  if (
960
964
  this.#loose &&
961
965
  this.type === tt.name &&
962
966
  typeof this.value === 'string' &&
963
- this.value.startsWith('#')
967
+ this.value === '#ripple'
964
968
  ) {
965
969
  // Return an Identifier node for incomplete tracked syntax
966
970
  const node = /** @type {AST.Identifier} */ (this.startNode());
@@ -969,11 +973,11 @@ function RipplePlugin(config) {
969
973
  return this.finishNode(node, 'Identifier');
970
974
  }
971
975
 
972
- // Check if this is a tuple literal starting with #[
973
- if (this.type === tt.bracketL && this.value === '#[') {
974
- return this.parseTrackedArrayExpression();
975
- } else if (this.type === tt.braceL && this.value === '#{') {
976
- return this.parseTrackedObjectExpression();
976
+ // Check if this is a tuple literal starting with #ripple[
977
+ if (this.type === tt.bracketL && this.value === '#ripple[') {
978
+ return this.parseRippleArrayExpression();
979
+ } else if (this.type === tt.braceL && this.value === '#ripple{') {
980
+ return this.parseRippleObjectExpression();
977
981
  }
978
982
 
979
983
  // Check if this is a component expression (e.g., in object literal values)
@@ -1077,79 +1081,11 @@ function RipplePlugin(config) {
1077
1081
  }
1078
1082
 
1079
1083
  /**
1080
- * Parse `#Map(...)` or `#Set(...)` syntax for tracked collections
1081
- * Creates a TrackedMap or TrackedSet node with the arguments property
1082
- * @type {Parse.Parser['parseTrackedCollectionExpression']}
1083
- */
1084
- parseTrackedCollectionExpression(type) {
1085
- const node =
1086
- /** @type {(AST.TrackedMapExpression | AST.TrackedSetExpression) & AST.NodeWithLocation} */ (
1087
- this.startNode()
1088
- );
1089
- this.next(); // consume '#Map' or '#Set'
1090
-
1091
- // Check if we should NOT consume the parentheses
1092
- // This happens when #Map/#Set appears as a callee in 'new #Map(...)'
1093
- // In this case, the parentheses and arguments belong to the NewExpression
1094
- // We detect this by checking if next token is '(' but we just consumed a token
1095
- // that came right after 'new' keyword (indicated by context or recent token)
1096
-
1097
- // Simple heuristic: if the input around our start position looks like 'new #Map('
1098
- // then don't consume the parens
1099
- const beforeStart = this.input.substring(Math.max(0, node.start - 5), node.start);
1100
- const isAfterNew = /new\s*$/.test(beforeStart);
1101
-
1102
- if (!isAfterNew) {
1103
- // If we reach here, it means #Map or #Set is being called without 'new'
1104
- // Throw a TypeError to match JavaScript class constructor behavior
1105
- const constructorName =
1106
- type === 'TrackedMapExpression' ? '#Map (TrackedMap)' : '#Set (TrackedSet)';
1107
- this.raise(
1108
- node.start,
1109
- `TypeError: Class constructor ${constructorName} cannot be invoked without 'new'`,
1110
- );
1111
- }
1112
-
1113
- // Don't consume parens or generics - they belong to NewExpression
1114
- // When used as "new #Map(...)" the next token is '('
1115
- // When used as "new #Map<K,V>(...)" the next token is '<' (relational)
1116
- if (this.type === tt.parenL || (this.type === tt.relational && this.value === '<')) {
1117
- node.arguments = [];
1118
- return this.finishNode(node, type);
1119
- }
1120
-
1121
- this.expect(tt.parenL); // expect '('
1122
-
1123
- node.arguments = [];
1124
- // Parse arguments similar to function call arguments
1125
- let first = true;
1126
- while (!this.eat(tt.parenR)) {
1127
- if (!first) {
1128
- this.expect(tt.comma);
1129
- if (this.afterTrailingComma(tt.parenR)) break;
1130
- } else {
1131
- first = false;
1132
- }
1133
-
1134
- if (this.type === tt.ellipsis) {
1135
- // Spread argument
1136
- const arg = this.parseSpread();
1137
- node.arguments.push(arg);
1138
- } else {
1139
- // Regular argument
1140
- node.arguments.push(this.parseMaybeAssign(false));
1141
- }
1142
- }
1143
-
1144
- return this.finishNode(node, type);
1145
- }
1146
-
1147
- /**
1148
- * @type {Parse.Parser['parseTrackedArrayExpression']}
1084
+ * @type {Parse.Parser['parseRippleArrayExpression']}
1149
1085
  */
1150
- parseTrackedArrayExpression() {
1151
- const node = /** @type {AST.TrackedArrayExpression} */ (this.startNode());
1152
- this.next(); // consume the '#['
1086
+ parseRippleArrayExpression() {
1087
+ const node = /** @type {AST.RippleArrayExpression} */ (this.startNode());
1088
+ this.next(); // consume the '#ripple['
1153
1089
 
1154
1090
  node.elements = [];
1155
1091
 
@@ -1179,15 +1115,15 @@ function RipplePlugin(config) {
1179
1115
  }
1180
1116
  }
1181
1117
 
1182
- return this.finishNode(node, 'TrackedArrayExpression');
1118
+ return this.finishNode(node, 'RippleArrayExpression');
1183
1119
  }
1184
1120
 
1185
1121
  /**
1186
- * @type {Parse.Parser['parseTrackedObjectExpression']}
1122
+ * @type {Parse.Parser['parseRippleObjectExpression']}
1187
1123
  */
1188
- parseTrackedObjectExpression() {
1189
- const node = /** @type {AST.TrackedObjectExpression} */ (this.startNode());
1190
- this.next(); // consume the '#{'
1124
+ parseRippleObjectExpression() {
1125
+ const node = /** @type {AST.RippleObjectExpression} */ (this.startNode());
1126
+ this.next(); // consume the '#ripple{'
1191
1127
 
1192
1128
  node.properties = [];
1193
1129
 
@@ -1214,7 +1150,7 @@ function RipplePlugin(config) {
1214
1150
  }
1215
1151
  }
1216
1152
 
1217
- return this.finishNode(node, 'TrackedObjectExpression');
1153
+ return this.finishNode(node, 'RippleObjectExpression');
1218
1154
  }
1219
1155
 
1220
1156
  /**
@@ -1247,7 +1183,7 @@ function RipplePlugin(config) {
1247
1183
  if (declareName) {
1248
1184
  this.declareName(
1249
1185
  node.id.name,
1250
- BINDING_TYPES.BIND_VAR,
1186
+ BINDING_TYPES.BIND_FUNCTION,
1251
1187
  /** @type {AST.NodeWithLocation} */ (node.id).start,
1252
1188
  );
1253
1189
  }
@@ -1256,7 +1192,7 @@ function RipplePlugin(config) {
1256
1192
  if (declareName && node.id) {
1257
1193
  this.declareName(
1258
1194
  node.id.name,
1259
- BINDING_TYPES.BIND_VAR,
1195
+ BINDING_TYPES.BIND_FUNCTION,
1260
1196
  /** @type {AST.NodeWithLocation} */ (node.id).start,
1261
1197
  );
1262
1198
  }
@@ -2098,13 +2034,15 @@ function RipplePlugin(config) {
2098
2034
  );
2099
2035
 
2100
2036
  element.children = [
2101
- /** @type {AST.ScriptContent} */ ({
2102
- type: 'ScriptContent',
2103
- content,
2104
- start,
2105
- end: closingStart,
2106
- loc: { start: contentStartLoc, end: contentEndLoc },
2107
- }),
2037
+ /** @type {AST.ScriptContent} */ (
2038
+ /** @type {unknown} */ ({
2039
+ type: 'ScriptContent',
2040
+ content,
2041
+ start,
2042
+ end: closingStart,
2043
+ loc: { start: contentStartLoc, end: contentEndLoc },
2044
+ })
2045
+ ),
2108
2046
  ];
2109
2047
 
2110
2048
  this.#path.pop();
@@ -2495,16 +2433,16 @@ function RipplePlugin(config) {
2495
2433
  );
2496
2434
  }
2497
2435
 
2498
- if (this.value === '#server') {
2499
- // Peek ahead to see if this is a server block (#server { ... }) vs
2500
- // a server identifier expression (#server.fn(), #server.fn().then())
2436
+ if (this.value === '#ripple.server') {
2437
+ // Peek ahead to see if this is a server block (#ripple.server { ... }) vs
2438
+ // a server identifier expression (#ripple.server.fn(), #ripple.server.fn().then())
2501
2439
  let peek_pos = this.end;
2502
2440
  while (peek_pos < this.input.length && /\s/.test(this.input[peek_pos])) peek_pos++;
2503
2441
  if (peek_pos < this.input.length && this.input.charCodeAt(peek_pos) === 123) {
2504
2442
  // Next non-whitespace character is '{' — parse as server block
2505
2443
  return this.parseServerBlock();
2506
2444
  }
2507
- // Otherwise fall through to parse as expression statement (e.g., #server.fn().then(...))
2445
+ // Otherwise fall through to parse as expression statement (e.g., #ripple.server.fn().then(...))
2508
2446
  }
2509
2447
 
2510
2448
  if (this.value === 'component') {
@@ -2967,12 +2905,12 @@ function get_comment_handlers(source, comments, index = 0) {
2967
2905
  node_array = parent.consequent;
2968
2906
  } else if (
2969
2907
  parent.type === 'ArrayExpression' ||
2970
- parent.type === 'TrackedArrayExpression'
2908
+ parent.type === 'RippleArrayExpression'
2971
2909
  ) {
2972
2910
  node_array = parent.elements;
2973
2911
  } else if (
2974
2912
  parent.type === 'ObjectExpression' ||
2975
- parent.type === 'TrackedObjectExpression'
2913
+ parent.type === 'RippleObjectExpression'
2976
2914
  ) {
2977
2915
  node_array = parent.properties;
2978
2916
  } else if (