ripple 0.2.215 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (157) hide show
  1. package/CHANGELOG.md +86 -0
  2. package/package.json +16 -7
  3. package/src/compiler/errors.js +1 -1
  4. package/src/compiler/identifier-utils.js +2 -0
  5. package/src/compiler/index.d.ts +2 -6
  6. package/src/compiler/phases/1-parse/index.js +171 -233
  7. package/src/compiler/phases/2-analyze/index.js +216 -16
  8. package/src/compiler/phases/2-analyze/prune.js +2 -2
  9. package/src/compiler/phases/3-transform/client/index.js +326 -94
  10. package/src/compiler/phases/3-transform/segments.js +43 -15
  11. package/src/compiler/phases/3-transform/server/index.js +71 -21
  12. package/src/compiler/scope.js +31 -12
  13. package/src/compiler/source-map-utils.js +4 -6
  14. package/src/compiler/types/acorn.d.ts +11 -0
  15. package/src/compiler/types/estree-jsx.d.ts +11 -0
  16. package/src/compiler/types/estree.d.ts +11 -0
  17. package/src/compiler/types/import.d.ts +32 -18
  18. package/src/compiler/types/index.d.ts +75 -23
  19. package/src/compiler/types/parse.d.ts +7 -10
  20. package/src/compiler/utils.js +48 -0
  21. package/src/runtime/array.js +53 -22
  22. package/src/runtime/date.js +15 -5
  23. package/src/runtime/index-client.js +41 -7
  24. package/src/runtime/index-server.js +7 -7
  25. package/src/runtime/internal/client/bindings.js +2 -2
  26. package/src/runtime/internal/client/blocks.js +40 -1
  27. package/src/runtime/internal/client/context.js +8 -0
  28. package/src/runtime/internal/client/for.js +3 -3
  29. package/src/runtime/internal/client/index.js +32 -5
  30. package/src/runtime/internal/client/render.js +20 -8
  31. package/src/runtime/internal/client/runtime.js +9 -7
  32. package/src/runtime/internal/client/template.js +1 -1
  33. package/src/runtime/internal/client/try.js +15 -22
  34. package/src/runtime/internal/client/utils.js +1 -1
  35. package/src/runtime/internal/server/context.js +8 -0
  36. package/src/runtime/internal/server/index.js +99 -6
  37. package/src/runtime/map.js +7 -7
  38. package/src/runtime/media-query.js +10 -1
  39. package/src/runtime/object.js +6 -6
  40. package/src/runtime/proxy.js +6 -6
  41. package/src/runtime/set.js +11 -11
  42. package/src/runtime/url-search-params.js +13 -2
  43. package/src/runtime/url.js +15 -5
  44. package/src/utils/builders.js +13 -3
  45. package/tests/client/array/array.copy-within.test.ripple +11 -11
  46. package/tests/client/array/array.derived.test.ripple +42 -42
  47. package/tests/client/array/array.iteration.test.ripple +12 -12
  48. package/tests/client/array/array.mutations.test.ripple +25 -25
  49. package/tests/client/array/array.static.test.ripple +103 -106
  50. package/tests/client/array/array.to-methods.test.ripple +8 -8
  51. package/tests/client/async-suspend.test.ripple +94 -0
  52. package/tests/client/basic/basic.attributes.test.ripple +31 -31
  53. package/tests/client/basic/basic.collections.test.ripple +7 -7
  54. package/tests/client/basic/basic.components.test.ripple +48 -10
  55. package/tests/client/basic/basic.errors.test.ripple +111 -30
  56. package/tests/client/basic/basic.events.test.ripple +11 -11
  57. package/tests/client/basic/basic.get-set.test.ripple +18 -18
  58. package/tests/client/basic/basic.reactivity.test.ripple +47 -42
  59. package/tests/client/basic/basic.rendering.test.ripple +7 -7
  60. package/tests/client/basic/basic.utilities.test.ripple +4 -4
  61. package/tests/client/boundaries.test.ripple +7 -7
  62. package/tests/client/compiler/__snapshots__/compiler.assignments.test.ripple.snap +2 -2
  63. package/tests/client/compiler/compiler.assignments.test.ripple +21 -21
  64. package/tests/client/compiler/compiler.basic.test.ripple +223 -82
  65. package/tests/client/compiler/compiler.tracked-access.test.ripple +8 -9
  66. package/tests/client/composite/composite.dynamic-components.test.ripple +8 -8
  67. package/tests/client/composite/composite.generics.test.ripple +4 -4
  68. package/tests/client/composite/composite.props.test.ripple +9 -9
  69. package/tests/client/composite/composite.reactivity.test.ripple +32 -26
  70. package/tests/client/composite/composite.render.test.ripple +13 -4
  71. package/tests/client/computed-properties.test.ripple +3 -3
  72. package/tests/client/context.test.ripple +3 -3
  73. package/tests/client/css/global-additional-cases.test.ripple +4 -4
  74. package/tests/client/css/style-identifier.test.ripple +49 -41
  75. package/tests/client/date.test.ripple +40 -40
  76. package/tests/client/dynamic-elements.test.ripple +165 -30
  77. package/tests/client/events.test.ripple +25 -25
  78. package/tests/client/for.test.ripple +76 -8
  79. package/tests/client/function-overload.test.ripple +0 -1
  80. package/tests/client/head.test.ripple +7 -7
  81. package/tests/client/html.test.ripple +2 -2
  82. package/tests/client/input-value.test.ripple +174 -176
  83. package/tests/client/map.test.ripple +21 -21
  84. package/tests/client/media-query.test.ripple +4 -4
  85. package/tests/client/object.test.ripple +12 -12
  86. package/tests/client/portal.test.ripple +4 -4
  87. package/tests/client/ref.test.ripple +5 -5
  88. package/tests/client/return.test.ripple +17 -17
  89. package/tests/client/set.test.ripple +16 -16
  90. package/tests/client/svg.test.ripple +6 -7
  91. package/tests/client/switch.test.ripple +10 -10
  92. package/tests/client/tracked-expression.test.ripple +1 -3
  93. package/tests/client/try.test.ripple +56 -4
  94. package/tests/client/url/url.derived.test.ripple +10 -9
  95. package/tests/client/url/url.parsing.test.ripple +10 -10
  96. package/tests/client/url/url.partial-removal.test.ripple +10 -10
  97. package/tests/client/url/url.reactivity.test.ripple +17 -17
  98. package/tests/client/url/url.serialization.test.ripple +4 -4
  99. package/tests/client/url-search-params/url-search-params.derived.test.ripple +11 -10
  100. package/tests/client/url-search-params/url-search-params.initialization.test.ripple +5 -7
  101. package/tests/client/url-search-params/url-search-params.iteration.test.ripple +13 -13
  102. package/tests/client/url-search-params/url-search-params.mutation.test.ripple +19 -19
  103. package/tests/client/url-search-params/url-search-params.retrieval.test.ripple +17 -17
  104. package/tests/client/url-search-params/url-search-params.serialization.test.ripple +5 -5
  105. package/tests/client/url-search-params/url-search-params.tracked-url.test.ripple +5 -5
  106. package/tests/hydration/compiled/client/events.js +8 -11
  107. package/tests/hydration/compiled/client/for.js +20 -23
  108. package/tests/hydration/compiled/client/head.js +17 -19
  109. package/tests/hydration/compiled/client/hmr.js +84 -0
  110. package/tests/hydration/compiled/client/html.js +1 -15
  111. package/tests/hydration/compiled/client/if-children.js +7 -9
  112. package/tests/hydration/compiled/client/if.js +5 -7
  113. package/tests/hydration/compiled/client/mixed-control-flow.js +3 -5
  114. package/tests/hydration/compiled/client/portal.js +1 -1
  115. package/tests/hydration/compiled/client/reactivity.js +9 -11
  116. package/tests/hydration/compiled/client/return.js +11 -13
  117. package/tests/hydration/compiled/client/switch.js +4 -6
  118. package/tests/hydration/compiled/server/basic.js +0 -1
  119. package/tests/hydration/compiled/server/composite.js +0 -3
  120. package/tests/hydration/compiled/server/events.js +8 -12
  121. package/tests/hydration/compiled/server/for.js +20 -23
  122. package/tests/hydration/compiled/server/head.js +17 -19
  123. package/tests/hydration/compiled/server/hmr.js +107 -0
  124. package/tests/hydration/compiled/server/html.js +1 -35
  125. package/tests/hydration/compiled/server/if-children.js +7 -11
  126. package/tests/hydration/compiled/server/if.js +5 -7
  127. package/tests/hydration/compiled/server/mixed-control-flow.js +3 -5
  128. package/tests/hydration/compiled/server/portal.js +1 -9
  129. package/tests/hydration/compiled/server/reactivity.js +9 -11
  130. package/tests/hydration/compiled/server/return.js +11 -13
  131. package/tests/hydration/compiled/server/switch.js +4 -6
  132. package/tests/hydration/components/events.ripple +8 -9
  133. package/tests/hydration/components/for.ripple +20 -21
  134. package/tests/hydration/components/head.ripple +6 -8
  135. package/tests/hydration/components/hmr.ripple +34 -0
  136. package/tests/hydration/components/html.ripple +1 -3
  137. package/tests/hydration/components/if-children.ripple +7 -8
  138. package/tests/hydration/components/if.ripple +5 -6
  139. package/tests/hydration/components/mixed-control-flow.ripple +4 -6
  140. package/tests/hydration/components/portal.ripple +1 -1
  141. package/tests/hydration/components/reactivity.ripple +9 -10
  142. package/tests/hydration/components/return.ripple +11 -12
  143. package/tests/hydration/components/switch.ripple +6 -8
  144. package/tests/hydration/hmr.test.js +74 -0
  145. package/tests/server/await.test.ripple +2 -2
  146. package/tests/server/basic.attributes.test.ripple +19 -21
  147. package/tests/server/basic.components.test.ripple +13 -7
  148. package/tests/server/basic.test.ripple +20 -21
  149. package/tests/server/compiler.test.ripple +5 -5
  150. package/tests/server/composite.props.test.ripple +6 -7
  151. package/tests/server/composite.test.ripple +4 -4
  152. package/tests/server/context.test.ripple +1 -3
  153. package/tests/server/dynamic-elements.test.ripple +24 -24
  154. package/tests/server/head.test.ripple +5 -7
  155. package/tests/server/style-identifier.test.ripple +16 -17
  156. package/types/index.d.ts +266 -62
  157. package/types/server.d.ts +6 -6
@@ -1,5 +1,8 @@
1
1
  import { parse, compile, compile_to_volar_mappings } from 'ripple/compiler';
2
- import { obfuscate_identifier } from 'ripple/compiler/internal/identifier/utils';
2
+ import {
3
+ obfuscate_identifier,
4
+ RIPPLE_NAMESPACE_IDENTIFIER,
5
+ } from 'ripple/compiler/internal/identifier/utils';
3
6
  import type * as AST from 'estree';
4
7
 
5
8
  function count_occurrences(string: string, subString: string): number {
@@ -19,25 +22,25 @@ describe('compiler > basics', () => {
19
22
  const source = `export component App() {
20
23
  <div id="myid" class="myclass">{"Hello World"}</div>
21
24
 
22
- <style>#style</style>
25
+ <style>#ripple.style</style>
23
26
  }`;
24
27
  const style1 = '.myid {color: green }';
25
28
  const style2 = '#myid {color: green }';
26
29
  const style3 = 'div {color: green }';
27
30
 
28
- let input = source.replace('#style', style1);
31
+ let input = source.replace('#ripple.style', style1);
29
32
  let ast = parse(input);
30
33
  expect(
31
34
  ((ast.body[0] as AST.ExportNamedDeclaration).declaration as unknown as AST.Component)?.css.source,
32
35
  ).toEqual(style1);
33
36
 
34
- input = source.replace('#style', style2);
37
+ input = source.replace('#ripple.style', style2);
35
38
  ast = parse(input);
36
39
  expect(
37
40
  ((ast.body[0] as AST.ExportNamedDeclaration).declaration as unknown as AST.Component)?.css.source,
38
41
  ).toEqual(style2);
39
42
 
40
- input = source.replace('#style', style3);
43
+ input = source.replace('#ripple.style', style3);
41
44
  ast = parse(input);
42
45
  expect(
43
46
  ((ast.body[0] as AST.ExportNamedDeclaration).declaration as unknown as AST.Component)?.css.source,
@@ -197,8 +200,8 @@ describe('compiler > basics', () => {
197
200
  class Box<T> {
198
201
  value: T;
199
202
 
200
- method<T>(): T {
201
- return this.value;
203
+ method<U extends T>(): U {
204
+ return this.value as U;
202
205
  }
203
206
 
204
207
  constructor(value: T) {
@@ -247,91 +250,152 @@ describe('compiler > basics', () => {
247
250
  expect(js.code).not.toMatch(/_\$_\.template\(`<!><!>`,\s*1,\s*3\)/);
248
251
  });
249
252
 
250
- it(
251
- 'imports and uses only obfuscated Tracked imports when encountering only shorthand syntax',
252
- () => {
253
- const source = `
254
- import {
255
- TrackedArray,
256
- TrackedObject,
257
- TrackedSet,
258
- TrackedMap,
259
- createRefKey,
260
- } from 'ripple';
253
+ // it(
254
+ // 'imports and uses only obfuscated Tracked imports when encountering only shorthand syntax',
255
+ // () => {
256
+ // const source = `
257
+ // import { RippleArray, RippleObject, RippleSet, RippleMap, createRefKey } from 'ripple';
258
+ // component App() {
259
+ // const items = #ripple[1, 2, 3];
260
+ // const obj = #ripple{ a: 1, b: 2, c: 3 };
261
+ // const set = #ripple.set([1, 2, 3]);
262
+ // const map = #ripple.map([['a', 1], ['b', 2], ['c', 3]]);
263
+
264
+ // <div {ref () => {}} />
265
+ // }
266
+ // `;
267
+ // const result = compile_to_volar_mappings(source, 'test.ripple').code;
268
+
269
+ // expect(count_occurrences(result, 'RippleArray')).toBe(1);
270
+ // expect(count_occurrences(result, 'RippleObject')).toBe(1);
271
+ // expect(count_occurrences(result, 'RippleSet')).toBe(1);
272
+ // expect(count_occurrences(result, 'RippleMap')).toBe(1);
273
+ // expect(count_occurrences(result, 'createRefKey')).toBe(1);
274
+ // },
275
+ // );
276
+
277
+ // it(
278
+ // 'adds obfuscated imports and keeps renamed Tracked imports intact when encountering shorthand syntax',
279
+ // () => {
280
+ // const source = `
281
+ // import { RippleArray as TA, RippleObject as TO, RippleSet as TS, RippleMap as TM, createRefKey as crk } from 'ripple';
282
+ // component App() {
283
+ // const items = #ripple[1, 2, 3];
284
+ // const obj = #ripple{ a: 1, b: 2, c: 3 };
285
+ // const set = #ripple.set([1, 2, 3]);
286
+ // const map = #ripple.map([['a', 1], ['b', 2], ['c', 3]]);
287
+
288
+ // <div {ref () => {}} />
289
+ // }
290
+ // `;
291
+ // const result = compile_to_volar_mappings(source, 'test.ripple').code;
292
+
293
+ // expect(count_occurrences(result, obfuscate_identifier('RippleArray'))).toBe(2);
294
+ // expect(count_occurrences(result, 'TA')).toBe(1);
295
+ // expect(count_occurrences(result, obfuscate_identifier('RippleObject'))).toBe(2);
296
+ // expect(count_occurrences(result, 'TO')).toBe(1);
297
+ // expect(count_occurrences(result, obfuscate_identifier('RippleSet'))).toBe(2);
298
+ // expect(count_occurrences(result, 'TS')).toBe(1);
299
+ // expect(count_occurrences(result, obfuscate_identifier('RippleMap'))).toBe(2);
300
+ // expect(count_occurrences(result, 'TM')).toBe(1);
301
+ // expect(count_occurrences(result, obfuscate_identifier('createRefKey'))).toBe(2);
302
+ // expect(count_occurrences(result, 'crk')).toBe(1);
303
+ // },
304
+ // );
305
+
306
+ // it('adds hidden obfuscated imports for shorthand syntax', () => {
307
+ // const source = `
308
+ // component App() {
309
+ // const items = #ripple[1, 2, 3];
310
+ // const obj = #ripple{ a: 1, b: 2, c: 3 };
311
+ // const set = #ripple.set([1, 2, 3]);
312
+ // const map = #ripple.map([['a', 1], ['b', 2], ['c', 3]]);
313
+
314
+ // <div {ref () => {}} />
315
+ // }
316
+ // `;
317
+ // const result = compile_to_volar_mappings(source, 'test.ripple').code;
318
+
319
+ // expect(count_occurrences(result, obfuscate_identifier('RippleArray'))).toBe(2);
320
+ // expect(count_occurrences(result, obfuscate_identifier('RippleObject'))).toBe(2);
321
+ // expect(count_occurrences(result, obfuscate_identifier('RippleSet'))).toBe(2);
322
+ // expect(count_occurrences(result, obfuscate_identifier('RippleMap'))).toBe(2);
323
+ // expect(count_occurrences(result, obfuscate_identifier('createRefKey'))).toBe(2);
324
+ // });
325
+
326
+ it('prints longhand tracked property values in to_ts output while preserving [\'#v\']', () => {
327
+ const source = `
328
+ import { createRefKey } from 'ripple';
261
329
 
262
330
  component App() {
263
- const items = #[1, 2, 3];
264
- const obj = #{ a: 1, b: 2, c: 3 };
265
- const set = new #Set([1, 2, 3]);
266
- const map = new #Map([['a', 1], ['b', 2], ['c', 3]]);
267
-
268
- <div {ref () => {}} />
331
+ let value = #ripple.track('test');
332
+ function inputRef(node) {}
333
+
334
+ const props = {
335
+ id: 'example',
336
+ @value,
337
+ [createRefKey()]: inputRef,
338
+ };
269
339
  }
270
340
  `;
271
- const result = compile_to_volar_mappings(source, 'test.ripple').code;
272
-
273
- expect(count_occurrences(result, 'TrackedArray')).toBe(1);
274
- expect(count_occurrences(result, 'TrackedObject')).toBe(1);
275
- expect(count_occurrences(result, 'TrackedSet')).toBe(1);
276
- expect(count_occurrences(result, 'TrackedMap')).toBe(1);
277
- expect(count_occurrences(result, 'createRefKey')).toBe(1);
278
- },
279
- );
280
-
281
- it(
282
- 'adds obfuscated imports and keeps renamed Tracked imports intact when encountering shorthand syntax',
283
- () => {
284
- const source = `
285
- import {
286
- TrackedArray as TA,
287
- TrackedObject as TO,
288
- TrackedSet as TS,
289
- TrackedMap as TM,
290
- createRefKey as crk,
291
- } from 'ripple';
292
341
 
293
- component App() {
294
- const items = #[1, 2, 3];
295
- const obj = #{ a: 1, b: 2, c: 3 };
296
- const set = new #Set([1, 2, 3]);
297
- const map = new #Map([['a', 1], ['b', 2], ['c', 3]]);
342
+ const result = compile_to_volar_mappings(source, 'test.ripple').code;
343
+
344
+ expect(result).toMatch(/value:\s*value\??\.\['#v'\]/);
345
+ });
298
346
 
299
- <div {ref () => {}} />
347
+ it('rewrites standalone #ripple to the hidden namespace identifier in to_ts output', () => {
348
+ const source = `
349
+ export component App() {
350
+ #ripple
300
351
  }
301
352
  `;
302
- const result = compile_to_volar_mappings(source, 'test.ripple').code;
303
-
304
- expect(count_occurrences(result, obfuscate_identifier('TrackedArray'))).toBe(2);
305
- expect(count_occurrences(result, 'TA')).toBe(1);
306
- expect(count_occurrences(result, obfuscate_identifier('TrackedObject'))).toBe(2);
307
- expect(count_occurrences(result, 'TO')).toBe(1);
308
- expect(count_occurrences(result, obfuscate_identifier('TrackedSet'))).toBe(2);
309
- expect(count_occurrences(result, 'TS')).toBe(1);
310
- expect(count_occurrences(result, obfuscate_identifier('TrackedMap'))).toBe(2);
311
- expect(count_occurrences(result, 'TM')).toBe(1);
312
- expect(count_occurrences(result, obfuscate_identifier('createRefKey'))).toBe(2);
313
- expect(count_occurrences(result, 'crk')).toBe(1);
314
- },
315
- );
316
-
317
- it('adds hidden obfuscated imports for shorthand syntax', () => {
353
+
354
+ const result = compile_to_volar_mappings(source, 'test.ripple', { loose: true }).code;
355
+
356
+ expect(result).toContain(RIPPLE_NAMESPACE_IDENTIFIER);
357
+ expect(result).toContain(`${RIPPLE_NAMESPACE_IDENTIFIER};`);
358
+ expect(result).not.toContain('#ripple;');
359
+ });
360
+
361
+ it('preserves generic type args in interface extends for Volar mappings', () => {
318
362
  const source = `
319
- component App() {
320
- const items = #[1, 2, 3];
321
- const obj = #{ a: 1, b: 2, c: 3 };
322
- const set = new #Set([1, 2, 3]);
323
- const map = new #Map([['a', 1], ['b', 2], ['c', 3]]);
363
+ interface PolymorphicProps<T extends keyof HTMLElementTagNameMap> {
364
+ as?: T;
365
+ }
324
366
 
325
- <div {ref () => {}} />
367
+ interface Props extends PolymorphicProps<'div'> {
368
+ id: string;
369
+ }
370
+
371
+ export component App(props: Props) {
372
+ <div id={props.id} />
326
373
  }
327
374
  `;
375
+
328
376
  const result = compile_to_volar_mappings(source, 'test.ripple').code;
329
377
 
330
- expect(count_occurrences(result, obfuscate_identifier('TrackedArray'))).toBe(2);
331
- expect(count_occurrences(result, obfuscate_identifier('TrackedObject'))).toBe(2);
332
- expect(count_occurrences(result, obfuscate_identifier('TrackedSet'))).toBe(2);
333
- expect(count_occurrences(result, obfuscate_identifier('TrackedMap'))).toBe(2);
334
- expect(count_occurrences(result, obfuscate_identifier('createRefKey'))).toBe(2);
378
+ expect(result).toContain('extends PolymorphicProps<\'div\'>');
379
+ });
380
+
381
+ it('handles if-else expression statements in Volar mappings', () => {
382
+ const source = `
383
+ export component App() {
384
+ let level = #ripple.track(1);
385
+
386
+ <button
387
+ onClick={() => {
388
+ if (@level === 1) @level = 2;
389
+ else if (@level === 2) @level = 3;
390
+ else @level = 1;
391
+ }}
392
+ >
393
+ {'Toggle'}
394
+ </button>
395
+ }
396
+ `;
397
+
398
+ expect(() => compile_to_volar_mappings(source, 'test.ripple')).not.toThrow();
335
399
  });
336
400
 
337
401
  it('should not error on having js below markup in the same scope', () => {
@@ -357,6 +421,23 @@ export component App() {
357
421
  expect(() => compile(code, 'test.ripple')).not.toThrow();
358
422
  });
359
423
 
424
+ it('converts nested component children into props in Volar mappings', () => {
425
+ const source = `
426
+ export component App() {
427
+ <ark.div class="host-class" data-value="42">
428
+ component asChild({ children, href, ...rest }: { href: string; [key: string]: any }) {
429
+ <a id="aschild-anchor" {href} {...rest} data-extra="yes">{'Link'}</a>
430
+ }
431
+ </ark.div>
432
+ }
433
+ `;
434
+ const result = compile_to_volar_mappings(source, 'test.ripple').code;
435
+
436
+ expect(result).toContain('<ark.div class="host-class" data-value="42" asChild={');
437
+ expect(result).not.toContain('children={() =>');
438
+ expect(result).not.toContain('function asChild');
439
+ });
440
+
360
441
  it('should not error on `this` MemberExpression with a UpdateExpression', () => {
361
442
  const code = `
362
443
  class Test {
@@ -374,14 +455,13 @@ export component App() {
374
455
  expect(() => compile(code, 'test.ripple')).not.toThrow();
375
456
  });
376
457
 
377
- it('should inject __block for track() calls inside class constructors', () => {
458
+ it('should inject __block for #ripple.track() calls inside class constructors', () => {
378
459
  const source = `
379
- import { track } from 'ripple';
380
460
 
381
461
  class Store {
382
462
  constructor() {
383
- this.count = track(0);
384
- this.items = #[1, 2, 3];
463
+ this.count = #ripple.track(0);
464
+ this.items = #ripple[1, 2, 3];
385
465
  }
386
466
  }
387
467
 
@@ -397,4 +477,65 @@ export component App() {
397
477
  expect(code).toContain('__block');
398
478
  expect(code).toContain('_$_.scope()');
399
479
  });
480
+
481
+ it('parses #ripple.effect and #ripple.untrack calls', () => {
482
+ const source = `
483
+ component App() {
484
+ let count = #ripple.track(0);
485
+
486
+ #ripple.effect(() => {
487
+ const snapshot = #ripple.untrack(() => @count);
488
+ console.log(snapshot);
489
+ });
490
+ }
491
+ `;
492
+
493
+ const ast = parse(source);
494
+ const ast_json = JSON.stringify(ast);
495
+
496
+ expect(ast_json).toContain('"source_name":"#ripple.effect"');
497
+ expect(ast_json).toContain('"source_name":"#ripple.untrack"');
498
+ });
499
+
500
+ it('collects duplicate declaration parser errors in loose mode', () => {
501
+ const source = `
502
+ export component App() {
503
+ let test = #ripple.track(false);
504
+ let test = 'hey';
505
+ }
506
+ `;
507
+
508
+ expect(() => compile(source, 'test.ripple')).toThrow(
509
+ 'Identifier \'test\' has already been declared',
510
+ );
511
+
512
+ const result = compile_to_volar_mappings(source, 'test.ripple', { loose: true });
513
+
514
+ expect(
515
+ result.errors.some(
516
+ (item) => item.message.includes('Identifier \'test\' has already been declared'),
517
+ ),
518
+ ).toBe(true);
519
+ });
520
+
521
+ it('parses bare #ripple in loose mode for autocomplete recovery', () => {
522
+ const source = `
523
+ export component App() {
524
+ #ripple
525
+ }
526
+ `;
527
+
528
+ expect(() => compile_to_volar_mappings(source, 'test.ripple', { loose: true })).not.toThrow();
529
+
530
+ const result = compile_to_volar_mappings(source, 'test.ripple', { loose: true });
531
+ expect(result.errors).toEqual([]);
532
+
533
+ const ripple_offset = source.indexOf('#ripple');
534
+ const mapping = result.mappings.find(
535
+ (mapping) => mapping.sourceOffsets[0] === ripple_offset &&
536
+ mapping.lengths[0] === '#ripple'.length,
537
+ );
538
+
539
+ expect(mapping?.data.customData.suppressedDiagnostics).toContain(18016);
540
+ });
400
541
  });
@@ -1,11 +1,10 @@
1
1
  import { compile } from 'ripple/compiler';
2
- import { track } from 'ripple';
3
2
 
4
3
  describe('Compiler: Tracked Object Direct Access Checks', () => {
5
4
  it('should error on direct access to __v of a tracked object', () => {
6
5
  const code = `
7
6
  export default component App() {
8
- let count = track(0);
7
+ let count = #ripple.track(0);
9
8
  console.log(count.__v);
10
9
  }
11
10
  `;
@@ -17,7 +16,7 @@ describe('Compiler: Tracked Object Direct Access Checks', () => {
17
16
  it('should error on direct access to "a" (get/set config) of a tracked object', () => {
18
17
  const code = `
19
18
  export default component App() {
20
- let myTracked = track(0);
19
+ let myTracked = #ripple.track(0);
21
20
  console.log(myTracked.a);
22
21
  }
23
22
  `;
@@ -29,7 +28,7 @@ describe('Compiler: Tracked Object Direct Access Checks', () => {
29
28
  it('should error on direct access to "b" (block) of a tracked object', () => {
30
29
  const code = `
31
30
  export default component App() {
32
- let myTracked = track(0);
31
+ let myTracked = #ripple.track(0);
33
32
  console.log(myTracked.b);
34
33
  }
35
34
  `;
@@ -41,7 +40,7 @@ describe('Compiler: Tracked Object Direct Access Checks', () => {
41
40
  it('should error on direct access to "c" (clock) of a tracked object', () => {
42
41
  const code = `
43
42
  export default component App() {
44
- let myTracked = track(0);
43
+ let myTracked = #ripple.track(0);
45
44
  console.log(myTracked.c);
46
45
  }
47
46
  `;
@@ -53,7 +52,7 @@ describe('Compiler: Tracked Object Direct Access Checks', () => {
53
52
  it('should error on direct access to "f" (flags) of a tracked object', () => {
54
53
  const code = `
55
54
  export default component App() {
56
- let myTracked = track(0);
55
+ let myTracked = #ripple.track(0);
57
56
  console.log(myTracked.f);
58
57
  }
59
58
  `;
@@ -65,7 +64,7 @@ describe('Compiler: Tracked Object Direct Access Checks', () => {
65
64
  it('should compile successfully with correct @ syntax access', () => {
66
65
  const code = `
67
66
  export default component App() {
68
- let count = track(0);
67
+ let count = #ripple.track(0);
69
68
  console.log(@count);
70
69
  }
71
70
  `;
@@ -74,9 +73,9 @@ describe('Compiler: Tracked Object Direct Access Checks', () => {
74
73
 
75
74
  it('should compile successfully with correct get() function access', () => {
76
75
  const code = `
77
- import { get, track } from 'ripple';
76
+ import { get } from 'ripple';
78
77
  export default component App() {
79
- let count = track(0);
78
+ let count = #ripple.track(0);
80
79
  console.log(get(count));
81
80
  }
82
81
  `;
@@ -1,13 +1,13 @@
1
- import { track, flushSync } from 'ripple';
1
+ import { flushSync } from 'ripple';
2
2
 
3
3
  describe('composite > dynamic components', () => {
4
- it('supports rendering compositie components using <@component> syntax', () => {
4
+ it('supports rendering composite components using <@component> syntax', () => {
5
5
  component App() {
6
6
  component basic() {
7
7
  <div>{'Basic Component'}</div>
8
8
  }
9
9
 
10
- const tracked_basic = track(() => basic);
10
+ const tracked_basic = #ripple.track(() => basic);
11
11
 
12
12
  <@tracked_basic />
13
13
  }
@@ -24,7 +24,7 @@ describe('composite > dynamic components', () => {
24
24
  <div>{'Basic Component'}</div>
25
25
  }
26
26
 
27
- const tracked_basic = track(() => basic);
27
+ const tracked_basic = #ripple.track(() => basic);
28
28
 
29
29
  const obj = {
30
30
  tracked_basic,
@@ -45,15 +45,15 @@ describe('composite > dynamic components', () => {
45
45
  <div>{'Basic Component'}</div>
46
46
  }
47
47
 
48
- const tracked_basic = track(() => basic);
48
+ const tracked_basic = #ripple.track(() => basic);
49
49
 
50
50
  const obj = {
51
51
  tracked_basic,
52
52
  };
53
53
 
54
- const tracked_object = track(obj);
54
+ const ripple_object = #ripple.track(obj);
55
55
 
56
- <@tracked_object.@tracked_basic />
56
+ <@ripple_object.@tracked_basic />
57
57
  }
58
58
 
59
59
  render(App);
@@ -72,7 +72,7 @@ describe('composite > dynamic components', () => {
72
72
  }
73
73
 
74
74
  component App() {
75
- let thing = track(() => Child1);
75
+ let thing = #ripple.track(() => Child1);
76
76
 
77
77
  <div id="container">
78
78
  <@thing />
@@ -1,4 +1,4 @@
1
- import { TrackedArray, TrackedMap } from 'ripple';
1
+ import { RippleArray, RippleMap } from 'ripple';
2
2
 
3
3
  describe('composite > generics', () => {
4
4
  it('handles advanced generic ambiguity and edge cases', () => {
@@ -143,10 +143,10 @@ describe('composite > generics', () => {
143
143
  const s = flag ? new Box<number>(1) : new Box<string>('string');
144
144
 
145
145
  // 20. Generic inside template expression
146
- const t = `length=${new TrackedArray<number>().length}`;
146
+ const t = `length=${new RippleArray<number>().length}`;
147
147
 
148
148
  // 21. Optional chaining + generic + property access
149
- const registry = new TrackedMap<string, number>();
149
+ const registry = new RippleMap<string, number>();
150
150
  const u = registry.get('id')?.toString();
151
151
 
152
152
  // 22. Generic call used as callee for another call
@@ -208,7 +208,7 @@ describe('composite > generics', () => {
208
208
  >();
209
209
 
210
210
  // 33. Map of generic instance as key
211
- const mm = new Map<TrackedArray<number>, TrackedArray<string>>();
211
+ const mm = new Map<RippleArray<number>, RippleArray<string>>();
212
212
  }
213
213
 
214
214
  render(App);
@@ -1,5 +1,5 @@
1
1
  import type { Tracked, Props } from 'ripple';
2
- import { track, trackSplit, effect, flushSync } from 'ripple';
2
+ import { flushSync } from 'ripple';
3
3
 
4
4
  describe('composite > props', () => {
5
5
  it('correctly handles default prop values', () => {
@@ -8,7 +8,7 @@ describe('composite > props', () => {
8
8
  }
9
9
 
10
10
  component App() {
11
- let foo = track(123);
11
+ let foo = #ripple.track(123);
12
12
 
13
13
  <Child />
14
14
  <Child {@foo} />
@@ -44,7 +44,7 @@ describe('composite > props', () => {
44
44
  }
45
45
 
46
46
  component App() {
47
- let foo = track(123);
47
+ let foo = #ripple.track(123);
48
48
 
49
49
  <Child />
50
50
  <Child {foo} />
@@ -62,7 +62,7 @@ describe('composite > props', () => {
62
62
  }
63
63
 
64
64
  component App() {
65
- let foo = track(123);
65
+ let foo = #ripple.track(123);
66
66
 
67
67
  <Child />
68
68
  <Child {@foo} />
@@ -78,7 +78,7 @@ describe('composite > props', () => {
78
78
  const logs: number[] = [];
79
79
 
80
80
  component Counter({ count }: { count: Tracked<number> }) {
81
- effect(() => {
81
+ #ripple.effect(() => {
82
82
  logs.push(@count);
83
83
  });
84
84
 
@@ -86,7 +86,7 @@ describe('composite > props', () => {
86
86
  }
87
87
 
88
88
  component App() {
89
- const count = track(0);
89
+ const count = #ripple.track(0);
90
90
 
91
91
  <div>
92
92
  <Counter {count} />
@@ -107,7 +107,7 @@ describe('composite > props', () => {
107
107
 
108
108
  it('correctly retains prop accessors and reactivity when using rest props', () => {
109
109
  component Button(props: Props) {
110
- const [children, rest] = trackSplit(props, ['children']);
110
+ const [children, rest] = #ripple.trackSplit(props, ['children']);
111
111
  <button {...@rest}>
112
112
  <@children />
113
113
  </button>
@@ -122,14 +122,14 @@ describe('composite > props', () => {
122
122
  }
123
123
 
124
124
  component Toggle(props: { pressed: Tracked<boolean> }) {
125
- const [pressed, rest] = trackSplit(props, ['pressed']);
125
+ const [pressed, rest] = #ripple.trackSplit(props, ['pressed']);
126
126
  const onClick = () => (@pressed = !@pressed);
127
127
  <Button {...@rest} class={@pressed ? 'on' : 'off'} {onClick}>{'button 1'}</Button>
128
128
  <Button class={@pressed ? 'on' : 'off'} {onClick}>{'button 2'}</Button>
129
129
  }
130
130
 
131
131
  component App() {
132
- const pressed = track(true);
132
+ const pressed = #ripple.track(true);
133
133
  <Toggle {pressed} />
134
134
  }
135
135