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
@@ -1,19 +1,19 @@
1
1
  import type { PropsWithChildren, Tracked } from 'ripple';
2
- import { flushSync } from 'ripple';
2
+ import { effect, flushSync, track, trackSplit, untrack } from 'ripple';
3
3
 
4
4
  describe('basic client > reactivity', () => {
5
5
  it('renders multiple reactive lexical blocks', () => {
6
6
  component Basic() {
7
7
  <div>
8
8
  let obj = {
9
- count: #ripple.track(0),
9
+ count: track(0),
10
10
  };
11
11
 
12
12
  <span>{obj.@count}</span>
13
13
  </div>
14
14
  <div>
15
15
  let b = {
16
- count: #ripple.track(0),
16
+ count: track(0),
17
17
  };
18
18
 
19
19
  <button
@@ -54,14 +54,14 @@ describe('basic client > reactivity', () => {
54
54
 
55
55
  <div>
56
56
  let obj = {
57
- count: #ripple.track(0),
57
+ count: track(0),
58
58
  };
59
59
 
60
60
  <span>{obj.@[count]}</span>
61
61
  </div>
62
62
  <div>
63
63
  let b = {
64
- count: #ripple.track(0),
64
+ count: track(0),
65
65
  };
66
66
 
67
67
  <button
@@ -98,7 +98,7 @@ describe('basic client > reactivity', () => {
98
98
 
99
99
  it('renders with computed reactive state', () => {
100
100
  component Basic() {
101
- let count = #ripple.track(5);
101
+ let count = track(5);
102
102
 
103
103
  <div class="count">{@count}</div>
104
104
  <div class="doubled">{@count * 2}</div>
@@ -135,11 +135,11 @@ describe('basic client > reactivity', () => {
135
135
  let logs: string[] = [];
136
136
 
137
137
  component App() {
138
- let first = #ripple.track(0);
139
- let second = #ripple.track(0);
138
+ let first = track(0);
139
+ let second = track(0);
140
140
  const arr = [first, second];
141
141
 
142
- const total = #ripple.track(() => arr.reduce((a, b) => a + @b, 0));
142
+ const total = track(() => arr.reduce((a, b) => a + @b, 0));
143
143
 
144
144
  <button
145
145
  onClick={() => {
@@ -156,7 +156,7 @@ describe('basic client > reactivity', () => {
156
156
  {'second: ' + @second}
157
157
  </button>
158
158
 
159
- #ripple.effect(() => {
159
+ effect(() => {
160
160
  let _arr: number[] = [];
161
161
 
162
162
  arr.forEach((item) => {
@@ -166,7 +166,7 @@ describe('basic client > reactivity', () => {
166
166
  logs.push(_arr.join(', '));
167
167
  });
168
168
 
169
- #ripple.effect(() => {
169
+ effect(() => {
170
170
  if (arr.map((a) => @a).includes(1)) {
171
171
  logs.push('arr includes 1');
172
172
  }
@@ -211,7 +211,7 @@ describe('basic client > reactivity', () => {
211
211
 
212
212
  it('uses track get and set where both mutate value', () => {
213
213
  component App() {
214
- let count = #ripple.track(0, (v) => v + 1, (v) => v * 2);
214
+ let count = track(0, (v) => v + 1, (v) => v * 2);
215
215
 
216
216
  <div class="count">{@count}</div>
217
217
  <button
@@ -237,7 +237,7 @@ describe('basic client > reactivity', () => {
237
237
 
238
238
  it('uses track get and set where set only mutates value', () => {
239
239
  component App() {
240
- let count = #ripple.track(1, (v) => v, (v) => v * 2);
240
+ let count = track(1, (v) => v, (v) => v * 2);
241
241
 
242
242
  <div class="count">{@count}</div>
243
243
  <button
@@ -263,7 +263,7 @@ describe('basic client > reactivity', () => {
263
263
 
264
264
  it('uses track get and set where get only mutates value', () => {
265
265
  component App() {
266
- let count = #ripple.track(0, (v) => v + 1, (v) => v);
266
+ let count = track(0, (v) => v + 1, (v) => v);
267
267
 
268
268
  <div class="count">{@count}</div>
269
269
  <button
@@ -291,7 +291,7 @@ describe('basic client > reactivity', () => {
291
291
  let logs: number[] = [];
292
292
 
293
293
  component App() {
294
- let count = #ripple.track(0, (v) => v, (next, prev) => {
294
+ let count = track(0, (v) => v, (next, prev) => {
295
295
  logs.push(prev, next);
296
296
  return next;
297
297
  });
@@ -314,11 +314,11 @@ describe('basic client > reactivity', () => {
314
314
  expect(logs).toEqual([0, 1]);
315
315
  });
316
316
 
317
- it('doesn\'t error on mutating a tracked variable in #ripple.track() setter', () => {
317
+ it('doesn\'t error on mutating a tracked variable in track() setter', () => {
318
318
  component Basic() {
319
- let count = #ripple.track(0);
319
+ let count = track(0);
320
320
 
321
- const doubled = #ripple.track(0, undefined, (value) => {
321
+ const doubled = track(0, undefined, (value) => {
322
322
  @count += value;
323
323
  return value;
324
324
  });
@@ -335,9 +335,9 @@ describe('basic client > reactivity', () => {
335
335
  let state: { count?: number } = {};
336
336
 
337
337
  component Basic() {
338
- let count = #ripple.track(0);
338
+ let count = track(0);
339
339
 
340
- #ripple.effect(() => {
340
+ effect(() => {
341
341
  state.count = @count;
342
342
  });
343
343
  }
@@ -359,10 +359,10 @@ describe('basic client > reactivity', () => {
359
359
  } = {};
360
360
 
361
361
  component Basic() {
362
- let count = #ripple.track(5);
362
+ let count = track(5);
363
363
 
364
- #ripple.effect(() => {
365
- #ripple.untrack(() => {
364
+ effect(() => {
365
+ untrack(() => {
366
366
  state.initialValue = @count;
367
367
  state.preIncrement = ++@count;
368
368
  state.postIncrement = @count++;
@@ -387,10 +387,10 @@ describe('basic client > reactivity', () => {
387
387
  describe('track/trackSplit APIs', () => {
388
388
  it('errors on invalid value as null for track with trackSplit', () => {
389
389
  component App() {
390
- let message = #ripple.track('');
390
+ let message = track('');
391
391
 
392
392
  try {
393
- const [a, b, rest] = #ripple.trackSplit(null, ['a', 'b']);
393
+ const [a, b, rest] = trackSplit(null, ['a', 'b']);
394
394
  } catch (e) {
395
395
  @message = (e as Error).message;
396
396
  }
@@ -406,10 +406,10 @@ describe('basic client > reactivity', () => {
406
406
 
407
407
  it('errors on invalid value as array for track with trackSplit', () => {
408
408
  component App() {
409
- let message = #ripple.track('');
409
+ let message = track('');
410
410
 
411
411
  try {
412
- const [a, b, rest] = #ripple.trackSplit([1, 2, 3], ['a', 'b']);
412
+ const [a, b, rest] = trackSplit([1, 2, 3], ['a', 'b']);
413
413
  } catch (e) {
414
414
  @message = (e as Error).message;
415
415
  }
@@ -425,11 +425,11 @@ describe('basic client > reactivity', () => {
425
425
 
426
426
  it('errors on invalid value as tracked for track with trackSplit', () => {
427
427
  component App() {
428
- const t = #ripple.track({ a: 1, b: 2, c: 3 });
429
- let message = #ripple.track('');
428
+ const t = track({ a: 1, b: 2, c: 3 });
429
+ let message = track('');
430
430
 
431
431
  try {
432
- const [a, b, rest] = #ripple.trackSplit(t, ['a', 'b']);
432
+ const [a, b, rest] = trackSplit(t, ['a', 'b']);
433
433
  } catch (e) {
434
434
  @message = (e as Error).message;
435
435
  }
@@ -445,7 +445,7 @@ describe('basic client > reactivity', () => {
445
445
 
446
446
  it('returns undefined for non-existent props in track with trackSplit', () => {
447
447
  component App() {
448
- const [a, b, rest] = #ripple.trackSplit({ a: 1, c: 1 }, ['a', 'b']);
448
+ const [a, b, rest] = trackSplit({ a: 1, c: 1 }, ['a', 'b']);
449
449
 
450
450
  <pre>{@a}</pre>
451
451
  <pre>{String(@b)}</pre>
@@ -465,8 +465,8 @@ describe('basic client > reactivity', () => {
465
465
 
466
466
  it('returns the same tracked object if plain track is called with a tracked object', () => {
467
467
  component App() {
468
- const t = #ripple.track({ a: 1, b: 2, c: 3 });
469
- const doublet = #ripple.track(t);
468
+ const t = track({ a: 1, b: 2, c: 3 });
469
+ const doublet = track(t);
470
470
 
471
471
  <pre>{t === doublet}</pre>
472
472
  }
@@ -481,8 +481,8 @@ describe('basic client > reactivity', () => {
481
481
  let logs: string[] = [];
482
482
 
483
483
  component App() {
484
- let count = #ripple.track(0);
485
- let name = #ripple.track('Click Me');
484
+ let count = track(0);
485
+ let name = track('Click Me');
486
486
 
487
487
  function buttonRef(el: HTMLButtonElement) {
488
488
  logs.push('ref called');
@@ -508,7 +508,7 @@ describe('basic client > reactivity', () => {
508
508
  class: string;
509
509
  onClick: () => void;
510
510
  }>) {
511
- const [children, count, rest] = #ripple.trackSplit(props, ['children', 'count']);
511
+ const [children, count, rest] = trackSplit(props, ['children', 'count']);
512
512
 
513
513
  if (@count < 2) {
514
514
  <button {...@rest}>
@@ -1,5 +1,5 @@
1
1
  import { describe, it, expect } from 'vitest';
2
- import { flushSync } from 'ripple';
2
+ import { flushSync, track } from 'ripple';
3
3
 
4
4
  describe('basic client > rendering & text', () => {
5
5
  it('renders static text', () => {
@@ -27,7 +27,7 @@ describe('basic client > rendering & text', () => {
27
27
 
28
28
  it('renders dynamic text', () => {
29
29
  component Basic() {
30
- let text = #ripple.track('Hello World');
30
+ let text = track('Hello World');
31
31
 
32
32
  <button
33
33
  onClick={() => {
@@ -104,8 +104,8 @@ describe('basic client > rendering & text', () => {
104
104
 
105
105
  it('renders with mixed static and dynamic content', () => {
106
106
  component Basic() {
107
- let name = #ripple.track('World');
108
- let count = #ripple.track(0);
107
+ let name = track('World');
108
+ let count = track(0);
109
109
  const staticMessage = 'Welcome to Ripple!';
110
110
 
111
111
  <div class="mixed-content">
@@ -151,7 +151,7 @@ describe('basic client > rendering & text', () => {
151
151
 
152
152
  it('basic operations', () => {
153
153
  component App() {
154
- let count = #ripple.track(0);
154
+ let count = track(0);
155
155
  <div>{@count++}</div>
156
156
  <div>{++@count}</div>
157
157
  <div>{5}</div>
@@ -164,8 +164,8 @@ describe('basic client > rendering & text', () => {
164
164
 
165
165
  it('renders with conditional rendering using if statements', () => {
166
166
  component Basic() {
167
- let showContent = #ripple.track(false);
168
- let userRole = #ripple.track('guest');
167
+ let showContent = track(false);
168
+ let userRole = track('guest');
169
169
 
170
170
  <button
171
171
  onClick={() => {
@@ -1,4 +1,4 @@
1
- import { tick } from 'ripple';
1
+ import { effect, tick, track, untrack } from 'ripple';
2
2
 
3
3
  describe('basic client > utilities', () => {
4
4
  it('tick function', async () => {
@@ -6,9 +6,9 @@ describe('basic client > utilities', () => {
6
6
  const promise = new Promise<void>((res) => (resolve = res));
7
7
 
8
8
  component Basic() {
9
- let value = #ripple.track(0);
10
- #ripple.effect(() => {
11
- #ripple.untrack(() => {
9
+ let value = track(0);
10
+ effect(() => {
11
+ untrack(() => {
12
12
  @value++;
13
13
  tick().then(() => resolve());
14
14
  });
@@ -1,13 +1,13 @@
1
- import { flushSync, type Tracked } from 'ripple';
1
+ import { effect, flushSync, track, type Tracked } from 'ripple';
2
2
 
3
3
  describe('passing reactivity between boundaries tests', () => {
4
4
  it('can pass reactivity between functions with simple arrays and destructuring', () => {
5
5
  let log: string[] = [];
6
6
 
7
7
  function createDouble([count]: [Tracked<number>]) {
8
- const double = #ripple.track(() => @count * 2);
8
+ const double = track(() => @count * 2);
9
9
 
10
- #ripple.effect(() => {
10
+ effect(() => {
11
11
  log.push('Count:' + @count);
12
12
  });
13
13
 
@@ -15,7 +15,7 @@ describe('passing reactivity between boundaries tests', () => {
15
15
  }
16
16
 
17
17
  component App() {
18
- let count = #ripple.track(0);
18
+ let count = track(0);
19
19
 
20
20
  const [double] = createDouble([count]);
21
21
 
@@ -54,9 +54,9 @@ describe('passing reactivity between boundaries tests', () => {
54
54
  let log: string[] = [];
55
55
 
56
56
  function createDouble({ count }: { count: Tracked<number> }) {
57
- const double = #ripple.track(() => @count * 2);
57
+ const double = track(() => @count * 2);
58
58
 
59
- #ripple.effect(() => {
59
+ effect(() => {
60
60
  log.push('Count:' + @count);
61
61
  });
62
62
 
@@ -64,7 +64,7 @@ describe('passing reactivity between boundaries tests', () => {
64
64
  }
65
65
 
66
66
  component App() {
67
- let count = #ripple.track(0);
67
+ let count = track(0);
68
68
 
69
69
  const { double } = createDouble({ count });
70
70
 
@@ -13,6 +13,30 @@ const ErrorMap = (Map);
13
13
  const errorMap = new ErrorMap();"
14
14
  `;
15
15
 
16
+ exports[`compiler > typescript > removes class TypeScript syntax from JS output 1`] = `
17
+ "import * as _$_ from 'ripple/internal/client';
18
+
19
+ class PrintEvent {
20
+ text;
21
+
22
+ constructor(text) {
23
+ this.text = text;
24
+ }
25
+ }"
26
+ `;
27
+
28
+ exports[`compiler > typescript > removes class extends type arguments from JS output 1`] = `
29
+ "import * as _$_ from 'ripple/internal/client';
30
+
31
+ class StringMap extends Map {
32
+ constructor() {
33
+ var __block = _$_.scope();
34
+
35
+ super();
36
+ }
37
+ }"
38
+ `;
39
+
16
40
  exports[`compiler > typescript > removes type assertions from function parameters and leaves default values 1`] = `
17
41
  "import * as _$_ from 'ripple/internal/client';
18
42
 
@@ -1,4 +1,4 @@
1
- import { RippleArray } from 'ripple';
1
+ import { RippleArray, effect, track, untrack } from 'ripple';
2
2
  import { compile } from 'ripple/compiler';
3
3
 
4
4
  const EFFECT_BODY_REGEX = /_\$\_\.effect\(\(\) => \{([\s\S]*?)\n\t\}\);/;
@@ -9,9 +9,9 @@ describe('compiler > assignments', () => {
9
9
 
10
10
  component App() {
11
11
  let items: number[] = [];
12
- let tracked_items = #ripple.track<number[]>([]);
12
+ let tracked_items = track<number[]>([]);
13
13
  let items2 = new Array();
14
- let items3: RippleArray<number> = #ripple[];
14
+ let items3: RippleArray<number> = new RippleArray();
15
15
  let i = 0;
16
16
 
17
17
  logs.push(items[0]);
@@ -108,10 +108,11 @@ describe('compiler > assignments', () => {
108
108
  });
109
109
 
110
110
  it('compiles tracked values in effect with assignment expression', () => {
111
- const source = `component App() {
112
- let count = #ripple.track(0);
111
+ const source = `import { track, effect } from 'ripple';
112
+ component App() {
113
+ let count = track(0);
113
114
 
114
- #ripple.effect(() => {
115
+ effect(() => {
115
116
  state.count = @count;
116
117
  });
117
118
  }`;
@@ -121,11 +122,12 @@ describe('compiler > assignments', () => {
121
122
  });
122
123
 
123
124
  it('compiles tracked values in effect with update expressions', () => {
124
- const source = `component App() {
125
- let count = #ripple.track(5);
125
+ const source = `import { track, effect, untrack } from 'ripple';
126
+ component App() {
127
+ let count = track(5);
126
128
 
127
- #ripple.effect(() => {
128
- #ripple.untrack(() => {
129
+ effect(() => {
130
+ untrack(() => {
129
131
  state.preIncrement = ++@count;
130
132
  state.postIncrement = @count++;
131
133
  state.preDecrement = --@count;
@@ -1,8 +1,5 @@
1
1
  import { parse, compile, compile_to_volar_mappings } from 'ripple/compiler';
2
- import {
3
- obfuscate_identifier,
4
- RIPPLE_NAMESPACE_IDENTIFIER,
5
- } from 'ripple/compiler/internal/identifier/utils';
2
+ import { obfuscate_identifier } from 'ripple/compiler/internal/identifier/utils';
6
3
  import type * as AST from 'estree';
7
4
 
8
5
  function count_occurrences(string: string, subString: string): number {
@@ -22,25 +19,25 @@ describe('compiler > basics', () => {
22
19
  const source = `export component App() {
23
20
  <div id="myid" class="myclass">{"Hello World"}</div>
24
21
 
25
- <style>#ripple.style</style>
22
+ <style>#style</style>
26
23
  }`;
27
24
  const style1 = '.myid {color: green }';
28
25
  const style2 = '#myid {color: green }';
29
26
  const style3 = 'div {color: green }';
30
27
 
31
- let input = source.replace('#ripple.style', style1);
28
+ let input = source.replace('#style', style1);
32
29
  let ast = parse(input);
33
30
  expect(
34
31
  ((ast.body[0] as AST.ExportNamedDeclaration).declaration as unknown as AST.Component)?.css.source,
35
32
  ).toEqual(style1);
36
33
 
37
- input = source.replace('#ripple.style', style2);
34
+ input = source.replace('#style', style2);
38
35
  ast = parse(input);
39
36
  expect(
40
37
  ((ast.body[0] as AST.ExportNamedDeclaration).declaration as unknown as AST.Component)?.css.source,
41
38
  ).toEqual(style2);
42
39
 
43
- input = source.replace('#ripple.style', style3);
40
+ input = source.replace('#style', style3);
44
41
  ast = parse(input);
45
42
  expect(
46
43
  ((ast.body[0] as AST.ExportNamedDeclaration).declaration as unknown as AST.Component)?.css.source,
@@ -256,10 +253,10 @@ describe('compiler > basics', () => {
256
253
  // const source = `
257
254
  // import { RippleArray, RippleObject, RippleSet, RippleMap, createRefKey } from 'ripple';
258
255
  // 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]]);
256
+ // const items = new RippleArray(1, 2, 3);
257
+ // const obj = new RippleObject({ a: 1, b: 2, c: 3 });
258
+ // const set = RippleSet([1, 2, 3]);
259
+ // const map = RippleMap([['a', 1], ['b', 2], ['c', 3]]);
263
260
 
264
261
  // <div {ref () => {}} />
265
262
  // }
@@ -280,10 +277,10 @@ describe('compiler > basics', () => {
280
277
  // const source = `
281
278
  // import { RippleArray as TA, RippleObject as TO, RippleSet as TS, RippleMap as TM, createRefKey as crk } from 'ripple';
282
279
  // 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]]);
280
+ // const items = new RippleArray(1, 2, 3);
281
+ // const obj = new RippleObject({ a: 1, b: 2, c: 3 });
282
+ // const set = RippleSet([1, 2, 3]);
283
+ // const map = RippleMap([['a', 1], ['b', 2], ['c', 3]]);
287
284
 
288
285
  // <div {ref () => {}} />
289
286
  // }
@@ -306,10 +303,10 @@ describe('compiler > basics', () => {
306
303
  // it('adds hidden obfuscated imports for shorthand syntax', () => {
307
304
  // const source = `
308
305
  // 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]]);
306
+ // const items = new RippleArray(1, 2, 3);
307
+ // const obj = new RippleObject({ a: 1, b: 2, c: 3 });
308
+ // const set = RippleSet([1, 2, 3]);
309
+ // const map = RippleMap([['a', 1], ['b', 2], ['c', 3]]);
313
310
 
314
311
  // <div {ref () => {}} />
315
312
  // }
@@ -325,10 +322,9 @@ describe('compiler > basics', () => {
325
322
 
326
323
  it('prints longhand tracked property values in to_ts output while preserving [\'#v\']', () => {
327
324
  const source = `
328
- import { createRefKey } from 'ripple';
329
-
325
+ import { RippleArray, RippleMap, RippleObject, RippleSet, createRefKey, effect, track, untrack } from 'ripple';
330
326
  component App() {
331
- let value = #ripple.track('test');
327
+ let value = track('test');
332
328
  function inputRef(node) {}
333
329
 
334
330
  const props = {
@@ -344,20 +340,6 @@ component App() {
344
340
  expect(result).toMatch(/value:\s*value\??\.\['#v'\]/);
345
341
  });
346
342
 
347
- it('rewrites standalone #ripple to the hidden namespace identifier in to_ts output', () => {
348
- const source = `
349
- export component App() {
350
- #ripple
351
- }
352
- `;
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
343
  it('preserves generic type args in interface extends for Volar mappings', () => {
362
344
  const source = `
363
345
  interface PolymorphicProps<T extends keyof HTMLElementTagNameMap> {
@@ -380,8 +362,9 @@ export component App(props: Props) {
380
362
 
381
363
  it('handles if-else expression statements in Volar mappings', () => {
382
364
  const source = `
365
+ import { track } from 'ripple';
383
366
  export component App() {
384
- let level = #ripple.track(1);
367
+ let level = track(1);
385
368
 
386
369
  <button
387
370
  onClick={() => {
@@ -455,13 +438,14 @@ export component App() {
455
438
  expect(() => compile(code, 'test.ripple')).not.toThrow();
456
439
  });
457
440
 
458
- it('should inject __block for #ripple.track() calls inside class constructors', () => {
441
+ it('should inject __block for track() calls inside class constructors', () => {
459
442
  const source = `
443
+ import { track, RippleArray } from 'ripple';
460
444
 
461
445
  class Store {
462
446
  constructor() {
463
- this.count = #ripple.track(0);
464
- this.items = #ripple[1, 2, 3];
447
+ this.count = track(0);
448
+ this.items = new RippleArray(1, 2, 3);
465
449
  }
466
450
  }
467
451
 
@@ -478,13 +462,15 @@ export component App() {
478
462
  expect(code).toContain('_$_.scope()');
479
463
  });
480
464
 
481
- it('parses #ripple.effect and #ripple.untrack calls', () => {
465
+ it('parses effect and untrack calls', () => {
482
466
  const source = `
467
+ import { track, effect, untrack } from 'ripple';
468
+
483
469
  component App() {
484
- let count = #ripple.track(0);
470
+ let count = track(0);
485
471
 
486
- #ripple.effect(() => {
487
- const snapshot = #ripple.untrack(() => @count);
472
+ effect(() => {
473
+ const snapshot = untrack(() => @count);
488
474
  console.log(snapshot);
489
475
  });
490
476
  }
@@ -493,14 +479,15 @@ component App() {
493
479
  const ast = parse(source);
494
480
  const ast_json = JSON.stringify(ast);
495
481
 
496
- expect(ast_json).toContain('"source_name":"#ripple.effect"');
497
- expect(ast_json).toContain('"source_name":"#ripple.untrack"');
482
+ expect(ast_json).toContain('"name":"effect"');
483
+ expect(ast_json).toContain('"name":"untrack"');
498
484
  });
499
485
 
500
486
  it('collects duplicate declaration parser errors in loose mode', () => {
501
487
  const source = `
488
+ import { track } from 'ripple';
502
489
  export component App() {
503
- let test = #ripple.track(false);
490
+ let test = track(false);
504
491
  let test = 'hey';
505
492
  }
506
493
  `;
@@ -518,24 +505,35 @@ export component App() {
518
505
  ).toBe(true);
519
506
  });
520
507
 
521
- it('parses bare #ripple in loose mode for autocomplete recovery', () => {
522
- const source = `
523
- export component App() {
524
- #ripple
525
- }
526
- `;
508
+ it('throws for unclosed tsx compat tags instead of hanging', () => {
509
+ const source = `export component App() {
510
+ <tsx:react>1
511
+ }`;
512
+
513
+ expect(() => compile(source, 'test.ripple')).toThrow(
514
+ 'Unclosed tag \'<tsx:react>\'. Expected \'</tsx:react>\' before end of component.',
515
+ );
516
+ });
517
+
518
+ it('recovers unclosed tsx compat tags in loose mode', () => {
519
+ const source = `export component App() {
520
+ <tsx:react>1
521
+ }`;
527
522
 
528
523
  expect(() => compile_to_volar_mappings(source, 'test.ripple', { loose: true })).not.toThrow();
529
524
 
530
525
  const result = compile_to_volar_mappings(source, 'test.ripple', { loose: true });
531
526
  expect(result.errors).toEqual([]);
527
+ });
532
528
 
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
- );
529
+ it('preserves class extends generic type arguments in volar output', () => {
530
+ const source = `class StringMap extends Map<string, string> {}
531
+ export component App() {}`;
532
+
533
+ expect(() => compile_to_volar_mappings(source, 'test.ripple', { loose: true })).not.toThrow();
534
+
535
+ const result = compile_to_volar_mappings(source, 'test.ripple', { loose: true }).code;
538
536
 
539
- expect(mapping?.data.customData.suppressedDiagnostics).toContain(18016);
537
+ expect(result).toContain('class StringMap extends Map<string, string> {}');
540
538
  });
541
539
  });