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.
- package/CHANGELOG.md +85 -0
- package/package.json +2 -2
- package/src/compiler/identifier-utils.js +0 -2
- package/src/compiler/phases/1-parse/index.js +101 -195
- package/src/compiler/phases/2-analyze/index.js +82 -174
- package/src/compiler/phases/2-analyze/prune.js +2 -2
- package/src/compiler/phases/3-transform/client/index.js +174 -264
- package/src/compiler/phases/3-transform/segments.js +0 -22
- package/src/compiler/phases/3-transform/server/index.js +185 -42
- package/src/compiler/types/index.d.ts +14 -33
- package/src/compiler/utils.js +32 -20
- package/src/runtime/index-client.js +0 -17
- package/src/runtime/internal/client/bindings.js +118 -7
- package/src/runtime/internal/client/render.js +5 -1
- package/src/runtime/internal/client/runtime.js +1 -1
- package/src/runtime/internal/client/types.d.ts +4 -0
- package/tests/client/array/array.copy-within.test.ripple +7 -7
- package/tests/client/array/array.derived.test.ripple +24 -24
- package/tests/client/array/array.iteration.test.ripple +7 -7
- package/tests/client/array/array.mutations.test.ripple +17 -17
- package/tests/client/array/array.to-methods.test.ripple +4 -4
- package/tests/client/async-suspend.test.ripple +3 -3
- package/tests/client/basic/basic.attributes.test.ripple +31 -31
- package/tests/client/basic/basic.collections.test.ripple +6 -6
- package/tests/client/basic/basic.components.test.ripple +8 -8
- package/tests/client/basic/basic.errors.test.ripple +31 -34
- package/tests/client/basic/basic.events.test.ripple +11 -11
- package/tests/client/basic/basic.get-set.test.ripple +18 -18
- package/tests/client/basic/basic.reactivity.test.ripple +36 -36
- package/tests/client/basic/basic.rendering.test.ripple +7 -7
- package/tests/client/basic/basic.utilities.test.ripple +4 -4
- package/tests/client/boundaries.test.ripple +7 -7
- package/tests/client/compiler/__snapshots__/compiler.typescript.test.ripple.snap +24 -0
- package/tests/client/compiler/compiler.assignments.test.ripple +12 -10
- package/tests/client/compiler/compiler.basic.test.ripple +58 -60
- package/tests/client/compiler/compiler.tracked-access.test.ripple +14 -8
- package/tests/client/compiler/compiler.typescript.test.ripple +31 -0
- package/tests/client/composite/composite.dynamic-components.test.ripple +6 -6
- package/tests/client/composite/composite.props.test.ripple +9 -9
- package/tests/client/composite/composite.reactivity.test.ripple +23 -23
- package/tests/client/composite/composite.render.test.ripple +52 -4
- package/tests/client/computed-properties.test.ripple +3 -3
- package/tests/client/context.test.ripple +3 -3
- package/tests/client/css/global-additional-cases.test.ripple +5 -2
- package/tests/client/css/style-identifier.test.ripple +40 -49
- package/tests/client/date.test.ripple +39 -39
- package/tests/client/dynamic-elements.test.ripple +37 -37
- package/tests/client/events.test.ripple +25 -25
- package/tests/client/for.test.ripple +8 -8
- package/tests/client/head.test.ripple +7 -7
- package/tests/client/html.test.ripple +2 -2
- package/tests/client/input-value.test.ripple +376 -177
- package/tests/client/lazy-destructuring.test.ripple +185 -0
- package/tests/client/map.test.ripple +20 -20
- package/tests/client/media-query.test.ripple +4 -4
- package/tests/client/object.test.ripple +5 -5
- package/tests/client/portal.test.ripple +4 -4
- package/tests/client/ref.test.ripple +3 -3
- package/tests/client/return.test.ripple +17 -17
- package/tests/client/set.test.ripple +10 -10
- package/tests/client/svg.test.ripple +6 -5
- package/tests/client/switch.test.ripple +10 -10
- package/tests/client/tracked-expression.test.ripple +3 -1
- package/tests/client/try.test.ripple +4 -4
- package/tests/client/url/url.derived.test.ripple +6 -7
- package/tests/client/url/url.parsing.test.ripple +9 -9
- package/tests/client/url/url.partial-removal.test.ripple +9 -9
- package/tests/client/url/url.reactivity.test.ripple +16 -16
- package/tests/client/url/url.serialization.test.ripple +3 -3
- package/tests/client/url-search-params/url-search-params.derived.test.ripple +7 -8
- package/tests/client/url-search-params/url-search-params.initialization.test.ripple +6 -4
- package/tests/client/url-search-params/url-search-params.iteration.test.ripple +12 -12
- package/tests/client/url-search-params/url-search-params.mutation.test.ripple +18 -18
- package/tests/client/url-search-params/url-search-params.retrieval.test.ripple +16 -16
- package/tests/client/url-search-params/url-search-params.serialization.test.ripple +4 -4
- package/tests/client/url-search-params/url-search-params.tracked-url.test.ripple +3 -3
- package/tests/hydration/build-components.js +4 -10
- package/tests/hydration/compiled/client/basic.js +4 -4
- package/tests/hydration/compiled/client/events.js +2 -0
- package/tests/hydration/compiled/client/for.js +2 -0
- package/tests/hydration/compiled/client/head.js +13 -11
- package/tests/hydration/compiled/client/hmr.js +4 -2
- package/tests/hydration/compiled/client/html.js +82 -95
- package/tests/hydration/compiled/client/if-children.js +8 -9
- package/tests/hydration/compiled/client/if.js +2 -0
- package/tests/hydration/compiled/client/mixed-control-flow.js +4 -2
- package/tests/hydration/compiled/client/portal.js +1 -1
- package/tests/hydration/compiled/client/reactivity.js +2 -0
- package/tests/hydration/compiled/client/return.js +2 -0
- package/tests/hydration/compiled/client/switch.js +2 -0
- package/tests/hydration/compiled/server/composite.js +2 -2
- package/tests/hydration/compiled/server/events.js +2 -0
- package/tests/hydration/compiled/server/for.js +2 -0
- package/tests/hydration/compiled/server/head.js +13 -11
- package/tests/hydration/compiled/server/hmr.js +2 -0
- package/tests/hydration/compiled/server/html.js +2 -0
- package/tests/hydration/compiled/server/if-children.js +2 -0
- package/tests/hydration/compiled/server/if.js +2 -0
- package/tests/hydration/compiled/server/mixed-control-flow.js +2 -0
- package/tests/hydration/compiled/server/portal.js +1 -1
- package/tests/hydration/compiled/server/reactivity.js +2 -0
- package/tests/hydration/compiled/server/return.js +2 -0
- package/tests/hydration/compiled/server/switch.js +2 -0
- package/tests/hydration/components/composite.ripple +1 -1
- package/tests/hydration/components/events.ripple +10 -8
- package/tests/hydration/components/for.ripple +22 -20
- package/tests/hydration/components/head.ripple +8 -6
- package/tests/hydration/components/hmr.ripple +3 -1
- package/tests/hydration/components/html.ripple +3 -1
- package/tests/hydration/components/if-children.ripple +9 -7
- package/tests/hydration/components/if.ripple +7 -5
- package/tests/hydration/components/mixed-control-flow.ripple +5 -3
- package/tests/hydration/components/portal.ripple +2 -2
- package/tests/hydration/components/reactivity.ripple +11 -9
- package/tests/hydration/components/return.ripple +13 -11
- package/tests/hydration/components/switch.ripple +6 -4
- package/tests/server/__snapshots__/compiler.test.ripple.snap +22 -0
- package/tests/server/await.test.ripple +2 -2
- package/tests/server/basic.attributes.test.ripple +21 -19
- package/tests/server/basic.components.test.ripple +5 -4
- package/tests/server/basic.test.ripple +21 -20
- package/tests/server/compiler.test.ripple +36 -5
- package/tests/server/composite.props.test.ripple +7 -6
- package/tests/server/context.test.ripple +3 -1
- package/tests/server/dynamic-elements.test.ripple +24 -24
- package/tests/server/head.test.ripple +7 -5
- package/tests/server/style-identifier.test.ripple +95 -16
- 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:
|
|
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:
|
|
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:
|
|
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:
|
|
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 =
|
|
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 =
|
|
139
|
-
let second =
|
|
138
|
+
let first = track(0);
|
|
139
|
+
let second = track(0);
|
|
140
140
|
const arr = [first, second];
|
|
141
141
|
|
|
142
|
-
const total =
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
317
|
+
it('doesn\'t error on mutating a tracked variable in track() setter', () => {
|
|
318
318
|
component Basic() {
|
|
319
|
-
let count =
|
|
319
|
+
let count = track(0);
|
|
320
320
|
|
|
321
|
-
const doubled =
|
|
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 =
|
|
338
|
+
let count = track(0);
|
|
339
339
|
|
|
340
|
-
|
|
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 =
|
|
362
|
+
let count = track(5);
|
|
363
363
|
|
|
364
|
-
|
|
365
|
-
|
|
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 =
|
|
390
|
+
let message = track('');
|
|
391
391
|
|
|
392
392
|
try {
|
|
393
|
-
const [a, b, rest] =
|
|
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 =
|
|
409
|
+
let message = track('');
|
|
410
410
|
|
|
411
411
|
try {
|
|
412
|
-
const [a, b, rest] =
|
|
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 =
|
|
429
|
-
let message =
|
|
428
|
+
const t = track({ a: 1, b: 2, c: 3 });
|
|
429
|
+
let message = track('');
|
|
430
430
|
|
|
431
431
|
try {
|
|
432
|
-
const [a, b, rest] =
|
|
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] =
|
|
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 =
|
|
469
|
-
const doublet =
|
|
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 =
|
|
485
|
-
let name =
|
|
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] =
|
|
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 =
|
|
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 =
|
|
108
|
-
let count =
|
|
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 =
|
|
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 =
|
|
168
|
-
let userRole =
|
|
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 =
|
|
10
|
-
|
|
11
|
-
|
|
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 =
|
|
8
|
+
const double = track(() => @count * 2);
|
|
9
9
|
|
|
10
|
-
|
|
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 =
|
|
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 =
|
|
57
|
+
const double = track(() => @count * 2);
|
|
58
58
|
|
|
59
|
-
|
|
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 =
|
|
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 =
|
|
12
|
+
let tracked_items = track<number[]>([]);
|
|
13
13
|
let items2 = new Array();
|
|
14
|
-
let items3: RippleArray<number> =
|
|
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 = `
|
|
112
|
-
|
|
111
|
+
const source = `import { track, effect } from 'ripple';
|
|
112
|
+
component App() {
|
|
113
|
+
let count = track(0);
|
|
113
114
|
|
|
114
|
-
|
|
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 = `
|
|
125
|
-
|
|
125
|
+
const source = `import { track, effect, untrack } from 'ripple';
|
|
126
|
+
component App() {
|
|
127
|
+
let count = track(5);
|
|
126
128
|
|
|
127
|
-
|
|
128
|
-
|
|
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>#
|
|
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('#
|
|
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('#
|
|
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('#
|
|
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 =
|
|
260
|
-
// const obj =
|
|
261
|
-
// const set =
|
|
262
|
-
// const map =
|
|
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 =
|
|
284
|
-
// const obj =
|
|
285
|
-
// const set =
|
|
286
|
-
// const map =
|
|
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 =
|
|
310
|
-
// const obj =
|
|
311
|
-
// const set =
|
|
312
|
-
// const map =
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
464
|
-
this.items =
|
|
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
|
|
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 =
|
|
470
|
+
let count = track(0);
|
|
485
471
|
|
|
486
|
-
|
|
487
|
-
const snapshot =
|
|
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('"
|
|
497
|
-
expect(ast_json).toContain('"
|
|
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 =
|
|
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('
|
|
522
|
-
const source = `
|
|
523
|
-
|
|
524
|
-
|
|
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
|
-
|
|
534
|
-
const
|
|
535
|
-
|
|
536
|
-
|
|
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(
|
|
537
|
+
expect(result).toContain('class StringMap extends Map<string, string> {}');
|
|
540
538
|
});
|
|
541
539
|
});
|