ripple 0.2.152 → 0.2.154
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/README.md +3 -3
- package/package.json +5 -5
- package/src/compiler/phases/1-parse/index.js +1 -1
- package/src/compiler/phases/3-transform/client/index.js +37 -16
- package/src/compiler/phases/3-transform/server/index.js +43 -25
- package/src/runtime/internal/client/events.js +5 -1
- package/src/runtime/internal/client/index.js +2 -1
- package/src/runtime/internal/client/render.js +18 -15
- package/src/runtime/internal/client/runtime.js +75 -10
- package/src/runtime/internal/server/index.js +51 -11
- package/src/server/index.js +1 -1
- package/tests/client/array/array.derived.test.ripple +61 -33
- package/tests/client/array/array.iteration.test.ripple +3 -1
- package/tests/client/array/array.mutations.test.ripple +19 -15
- package/tests/client/array/array.static.test.ripple +115 -104
- package/tests/client/array/array.to-methods.test.ripple +3 -3
- package/tests/client/basic/basic.attributes.test.ripple +110 -57
- package/tests/client/basic/basic.collections.test.ripple +41 -22
- package/tests/client/basic/basic.errors.test.ripple +12 -6
- package/tests/client/basic/basic.events.test.ripple +51 -33
- package/tests/client/basic/basic.reactivity.test.ripple +120 -56
- package/tests/client/basic/basic.rendering.test.ripple +49 -19
- package/tests/client/basic/basic.styling.test.ripple +2 -2
- package/tests/client/basic/basic.utilities.test.ripple +1 -1
- package/tests/client/boundaries.test.ripple +70 -58
- package/tests/client/compiler/compiler.assignments.test.ripple +32 -4
- package/tests/client/compiler/compiler.attributes.test.ripple +46 -46
- package/tests/client/compiler/compiler.basic.test.ripple +18 -15
- package/tests/client/compiler/compiler.tracked-access.test.ripple +53 -42
- package/tests/client/compiler/compiler.typescript.test.ripple +1 -2
- package/tests/client/composite/composite.dynamic-components.test.ripple +6 -6
- package/tests/client/composite/composite.generics.test.ripple +39 -36
- package/tests/client/composite/composite.props.test.ripple +4 -3
- package/tests/client/composite/composite.reactivity.test.ripple +112 -27
- package/tests/client/composite/composite.render.test.ripple +9 -8
- package/tests/client/computed-properties.test.ripple +24 -24
- package/tests/client/context.test.ripple +11 -9
- package/tests/client/date.test.ripple +3 -1
- package/tests/client/dynamic-elements.test.ripple +103 -78
- package/tests/client/for.test.ripple +27 -17
- package/tests/client/head.test.ripple +42 -6
- package/tests/client/html.test.ripple +42 -32
- package/tests/client/input-value.test.ripple +4 -4
- package/tests/client/map.test.ripple +140 -141
- package/tests/client/media-query.test.ripple +31 -31
- package/tests/client/object.test.ripple +148 -112
- package/tests/client/portal.test.ripple +29 -15
- package/tests/client/ref.test.ripple +9 -3
- package/tests/client/set.test.ripple +111 -111
- package/tests/client/tracked-expression.test.ripple +16 -17
- package/tests/client/url/url.derived.test.ripple +19 -9
- package/tests/client/url/url.parsing.test.ripple +24 -8
- package/tests/client/url/url.partial-removal.test.ripple +12 -4
- package/tests/client/url/url.reactivity.test.ripple +63 -25
- package/tests/client/url/url.serialization.test.ripple +18 -6
- package/tests/client/url-search-params/url-search-params.derived.test.ripple +10 -6
- package/tests/client/url-search-params/url-search-params.iteration.test.ripple +3 -1
- package/tests/client/url-search-params/url-search-params.mutation.test.ripple +26 -14
- package/tests/client/url-search-params/url-search-params.tracked-url.test.ripple +3 -1
- package/tests/server/await.test.ripple +23 -22
- package/tests/server/basic.test.ripple +1 -1
- package/tests/server/compiler.test.ripple +3 -7
- package/tests/server/composite.test.ripple +38 -36
- package/tests/server/for.test.ripple +9 -5
- package/tests/server/if.test.ripple +1 -1
- package/tests/server/streaming-ssr.test.ripple +67 -0
- package/types/server.d.ts +5 -4
|
@@ -110,7 +110,9 @@ describe('TrackedURL > reactivity', () => {
|
|
|
110
110
|
button.click();
|
|
111
111
|
flushSync();
|
|
112
112
|
|
|
113
|
-
expect(container.querySelectorAll('pre')[0].textContent).toBe(
|
|
113
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe(
|
|
114
|
+
'https://newdomain.com:9090/path',
|
|
115
|
+
);
|
|
114
116
|
expect(container.querySelectorAll('pre')[1].textContent).toBe('newdomain.com:9090');
|
|
115
117
|
expect(container.querySelectorAll('pre')[2].textContent).toBe('newdomain.com');
|
|
116
118
|
expect(container.querySelectorAll('pre')[3].textContent).toBe('9090');
|
|
@@ -155,14 +157,18 @@ describe('TrackedURL > reactivity', () => {
|
|
|
155
157
|
const button = container.querySelector('button');
|
|
156
158
|
|
|
157
159
|
// Initial state
|
|
158
|
-
expect(container.querySelectorAll('pre')[0].textContent).toBe(
|
|
160
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe(
|
|
161
|
+
'https://example.com/path?foo=bar',
|
|
162
|
+
);
|
|
159
163
|
expect(container.querySelectorAll('pre')[1].textContent).toBe('?foo=bar');
|
|
160
164
|
|
|
161
165
|
// Change search
|
|
162
166
|
button.click();
|
|
163
167
|
flushSync();
|
|
164
168
|
|
|
165
|
-
expect(container.querySelectorAll('pre')[0].textContent).toBe(
|
|
169
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe(
|
|
170
|
+
'https://example.com/path?baz=qux',
|
|
171
|
+
);
|
|
166
172
|
expect(container.querySelectorAll('pre')[1].textContent).toBe('?baz=qux');
|
|
167
173
|
});
|
|
168
174
|
|
|
@@ -180,14 +186,18 @@ describe('TrackedURL > reactivity', () => {
|
|
|
180
186
|
const button = container.querySelector('button');
|
|
181
187
|
|
|
182
188
|
// Initial state
|
|
183
|
-
expect(container.querySelectorAll('pre')[0].textContent).toBe(
|
|
189
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe(
|
|
190
|
+
'https://example.com/path#section1',
|
|
191
|
+
);
|
|
184
192
|
expect(container.querySelectorAll('pre')[1].textContent).toBe('#section1');
|
|
185
193
|
|
|
186
194
|
// Change hash
|
|
187
195
|
button.click();
|
|
188
196
|
flushSync();
|
|
189
197
|
|
|
190
|
-
expect(container.querySelectorAll('pre')[0].textContent).toBe(
|
|
198
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe(
|
|
199
|
+
'https://example.com/path#section2',
|
|
200
|
+
);
|
|
191
201
|
expect(container.querySelectorAll('pre')[1].textContent).toBe('#section2');
|
|
192
202
|
});
|
|
193
203
|
|
|
@@ -205,14 +215,18 @@ describe('TrackedURL > reactivity', () => {
|
|
|
205
215
|
const button = container.querySelector('button');
|
|
206
216
|
|
|
207
217
|
// Initial state
|
|
208
|
-
expect(container.querySelectorAll('pre')[0].textContent).toBe(
|
|
218
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe(
|
|
219
|
+
'https://user:pass@example.com/path',
|
|
220
|
+
);
|
|
209
221
|
expect(container.querySelectorAll('pre')[1].textContent).toBe('user');
|
|
210
222
|
|
|
211
223
|
// Change username
|
|
212
224
|
button.click();
|
|
213
225
|
flushSync();
|
|
214
226
|
|
|
215
|
-
expect(container.querySelectorAll('pre')[0].textContent).toBe(
|
|
227
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe(
|
|
228
|
+
'https://newuser:pass@example.com/path',
|
|
229
|
+
);
|
|
216
230
|
expect(container.querySelectorAll('pre')[1].textContent).toBe('newuser');
|
|
217
231
|
});
|
|
218
232
|
|
|
@@ -230,14 +244,18 @@ describe('TrackedURL > reactivity', () => {
|
|
|
230
244
|
const button = container.querySelector('button');
|
|
231
245
|
|
|
232
246
|
// Initial state
|
|
233
|
-
expect(container.querySelectorAll('pre')[0].textContent).toBe(
|
|
247
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe(
|
|
248
|
+
'https://user:pass@example.com/path',
|
|
249
|
+
);
|
|
234
250
|
expect(container.querySelectorAll('pre')[1].textContent).toBe('pass');
|
|
235
251
|
|
|
236
252
|
// Change password
|
|
237
253
|
button.click();
|
|
238
254
|
flushSync();
|
|
239
255
|
|
|
240
|
-
expect(container.querySelectorAll('pre')[0].textContent).toBe(
|
|
256
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe(
|
|
257
|
+
'https://user:newpass@example.com/path',
|
|
258
|
+
);
|
|
241
259
|
expect(container.querySelectorAll('pre')[1].textContent).toBe('newpass');
|
|
242
260
|
});
|
|
243
261
|
|
|
@@ -245,7 +263,9 @@ describe('TrackedURL > reactivity', () => {
|
|
|
245
263
|
component URLTest() {
|
|
246
264
|
const url = new TrackedURL('https://example.com/path?foo=bar#section');
|
|
247
265
|
|
|
248
|
-
<button onClick={() => url.href = 'https://newdomain.com:9090/newpath?baz=qux#newsection'}>
|
|
266
|
+
<button onClick={() => url.href = 'https://newdomain.com:9090/newpath?baz=qux#newsection'}>
|
|
267
|
+
{'Change Href'}
|
|
268
|
+
</button>
|
|
249
269
|
<pre>{url.href}</pre>
|
|
250
270
|
<pre>{url.protocol}</pre>
|
|
251
271
|
<pre>{url.hostname}</pre>
|
|
@@ -260,7 +280,9 @@ describe('TrackedURL > reactivity', () => {
|
|
|
260
280
|
const button = container.querySelector('button');
|
|
261
281
|
|
|
262
282
|
// Initial state
|
|
263
|
-
expect(container.querySelectorAll('pre')[0].textContent).toBe(
|
|
283
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe(
|
|
284
|
+
'https://example.com/path?foo=bar#section',
|
|
285
|
+
);
|
|
264
286
|
expect(container.querySelectorAll('pre')[1].textContent).toBe('https:');
|
|
265
287
|
expect(container.querySelectorAll('pre')[2].textContent).toBe('example.com');
|
|
266
288
|
expect(container.querySelectorAll('pre')[3].textContent).toBe('');
|
|
@@ -272,7 +294,9 @@ describe('TrackedURL > reactivity', () => {
|
|
|
272
294
|
button.click();
|
|
273
295
|
flushSync();
|
|
274
296
|
|
|
275
|
-
expect(container.querySelectorAll('pre')[0].textContent).toBe(
|
|
297
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe(
|
|
298
|
+
'https://newdomain.com:9090/newpath?baz=qux#newsection',
|
|
299
|
+
);
|
|
276
300
|
expect(container.querySelectorAll('pre')[1].textContent).toBe('https:');
|
|
277
301
|
expect(container.querySelectorAll('pre')[2].textContent).toBe('newdomain.com');
|
|
278
302
|
expect(container.querySelectorAll('pre')[3].textContent).toBe('9090');
|
|
@@ -331,7 +355,9 @@ describe('TrackedURL > reactivity', () => {
|
|
|
331
355
|
const addButton = container.querySelectorAll('button')[1];
|
|
332
356
|
|
|
333
357
|
// Initial state
|
|
334
|
-
expect(container.querySelectorAll('pre')[0].textContent).toBe(
|
|
358
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe(
|
|
359
|
+
'https://example.com/path?foo=bar',
|
|
360
|
+
);
|
|
335
361
|
expect(container.querySelectorAll('pre')[1].textContent).toBe('?foo=bar');
|
|
336
362
|
expect(container.querySelectorAll('pre')[2].textContent).toBe('bar');
|
|
337
363
|
|
|
@@ -339,7 +365,9 @@ describe('TrackedURL > reactivity', () => {
|
|
|
339
365
|
updateButton.click();
|
|
340
366
|
flushSync();
|
|
341
367
|
|
|
342
|
-
expect(container.querySelectorAll('pre')[0].textContent).toBe(
|
|
368
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe(
|
|
369
|
+
'https://example.com/path?foo=updated',
|
|
370
|
+
);
|
|
343
371
|
expect(container.querySelectorAll('pre')[1].textContent).toBe('?foo=updated');
|
|
344
372
|
expect(container.querySelectorAll('pre')[2].textContent).toBe('updated');
|
|
345
373
|
|
|
@@ -347,7 +375,9 @@ describe('TrackedURL > reactivity', () => {
|
|
|
347
375
|
addButton.click();
|
|
348
376
|
flushSync();
|
|
349
377
|
|
|
350
|
-
expect(container.querySelectorAll('pre')[0].textContent).toBe(
|
|
378
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe(
|
|
379
|
+
'https://example.com/path?foo=updated&baz=qux',
|
|
380
|
+
);
|
|
351
381
|
expect(container.querySelectorAll('pre')[1].textContent).toBe('?foo=updated&baz=qux');
|
|
352
382
|
});
|
|
353
383
|
|
|
@@ -390,14 +420,18 @@ describe('TrackedURL > reactivity', () => {
|
|
|
390
420
|
component URLTest() {
|
|
391
421
|
const url = new TrackedURL('https://example.com/path');
|
|
392
422
|
|
|
393
|
-
<button
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
423
|
+
<button
|
|
424
|
+
onClick={() => {
|
|
425
|
+
url.protocol = 'http:';
|
|
426
|
+
url.hostname = 'newdomain.com';
|
|
427
|
+
url.port = '8080';
|
|
428
|
+
url.pathname = '/api';
|
|
429
|
+
url.search = '?key=value';
|
|
430
|
+
url.hash = '#section';
|
|
431
|
+
}}
|
|
432
|
+
>
|
|
433
|
+
{'Change All'}
|
|
434
|
+
</button>
|
|
401
435
|
<pre>{url.href}</pre>
|
|
402
436
|
}
|
|
403
437
|
|
|
@@ -412,7 +446,9 @@ describe('TrackedURL > reactivity', () => {
|
|
|
412
446
|
button.click();
|
|
413
447
|
flushSync();
|
|
414
448
|
|
|
415
|
-
expect(container.querySelector('pre').textContent).toBe(
|
|
449
|
+
expect(container.querySelector('pre').textContent).toBe(
|
|
450
|
+
'http://newdomain.com:8080/api?key=value#section',
|
|
451
|
+
);
|
|
416
452
|
});
|
|
417
453
|
|
|
418
454
|
it('handles href change updates all properties and searchParams', () => {
|
|
@@ -420,7 +456,9 @@ describe('TrackedURL > reactivity', () => {
|
|
|
420
456
|
const url = new TrackedURL('https://old.com/old?foo=bar#old');
|
|
421
457
|
const params = url.searchParams;
|
|
422
458
|
|
|
423
|
-
<button onClick={() => url.href = 'https://new.com:9090/new?baz=qux#new'}>
|
|
459
|
+
<button onClick={() => url.href = 'https://new.com:9090/new?baz=qux#new'}>
|
|
460
|
+
{'Change Href'}
|
|
461
|
+
</button>
|
|
424
462
|
<pre>{params.get('foo')}</pre>
|
|
425
463
|
<pre>{params.get('baz')}</pre>
|
|
426
464
|
<pre>{params.size}</pre>
|
|
@@ -14,13 +14,17 @@ describe('TrackedURL > serialization', () => {
|
|
|
14
14
|
const button = container.querySelector('button');
|
|
15
15
|
|
|
16
16
|
// Initial state
|
|
17
|
-
expect(container.querySelector('pre').textContent).toBe(
|
|
17
|
+
expect(container.querySelector('pre').textContent).toBe(
|
|
18
|
+
'https://example.com/path?foo=bar#section',
|
|
19
|
+
);
|
|
18
20
|
|
|
19
21
|
// Change pathname
|
|
20
22
|
button.click();
|
|
21
23
|
flushSync();
|
|
22
24
|
|
|
23
|
-
expect(container.querySelector('pre').textContent).toBe(
|
|
25
|
+
expect(container.querySelector('pre').textContent).toBe(
|
|
26
|
+
'https://example.com/newpath?foo=bar#section',
|
|
27
|
+
);
|
|
24
28
|
});
|
|
25
29
|
|
|
26
30
|
it('handles toJSON method', () => {
|
|
@@ -37,14 +41,22 @@ describe('TrackedURL > serialization', () => {
|
|
|
37
41
|
const button = container.querySelector('button');
|
|
38
42
|
|
|
39
43
|
// Initial state
|
|
40
|
-
expect(container.querySelectorAll('pre')[0].textContent).toBe(
|
|
41
|
-
|
|
44
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe(
|
|
45
|
+
'https://example.com/path?foo=bar',
|
|
46
|
+
);
|
|
47
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe(
|
|
48
|
+
'{"url":"https://example.com/path?foo=bar"}',
|
|
49
|
+
);
|
|
42
50
|
|
|
43
51
|
// Change pathname
|
|
44
52
|
button.click();
|
|
45
53
|
flushSync();
|
|
46
54
|
|
|
47
|
-
expect(container.querySelectorAll('pre')[0].textContent).toBe(
|
|
48
|
-
|
|
55
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe(
|
|
56
|
+
'https://example.com/api?foo=bar',
|
|
57
|
+
);
|
|
58
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe(
|
|
59
|
+
'{"url":"https://example.com/api?foo=bar"}',
|
|
60
|
+
);
|
|
49
61
|
});
|
|
50
62
|
});
|
|
@@ -46,15 +46,19 @@ describe('TrackedURLSearchParams > derived', () => {
|
|
|
46
46
|
component ParentTest() {
|
|
47
47
|
const params = new TrackedURLSearchParams('count=0');
|
|
48
48
|
|
|
49
|
-
<ChildA
|
|
50
|
-
<ChildB
|
|
49
|
+
<ChildA {params} />
|
|
50
|
+
<ChildB {params} />
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
component ChildA({ params }: { params: TrackedURLSearchParams }) {
|
|
54
|
-
<button
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
54
|
+
<button
|
|
55
|
+
onClick={() => {
|
|
56
|
+
const current = parseInt(params.get('count') || '0', 10);
|
|
57
|
+
params.set('count', String(current + 1));
|
|
58
|
+
}}
|
|
59
|
+
>
|
|
60
|
+
{'increment'}
|
|
61
|
+
</button>
|
|
58
62
|
}
|
|
59
63
|
|
|
60
64
|
component ChildB({ params }: { params: TrackedURLSearchParams }) {
|
|
@@ -67,7 +67,9 @@ describe('TrackedURLSearchParams > iteration', () => {
|
|
|
67
67
|
button.click();
|
|
68
68
|
flushSync();
|
|
69
69
|
|
|
70
|
-
expect(container.querySelector('pre').textContent).toBe(
|
|
70
|
+
expect(container.querySelector('pre').textContent).toBe(
|
|
71
|
+
'[["foo","bar"],["baz","qux"],["new","value"]]',
|
|
72
|
+
);
|
|
71
73
|
});
|
|
72
74
|
|
|
73
75
|
it('handles Symbol.iterator with reactivity', () => {
|
|
@@ -243,10 +243,14 @@ describe('TrackedURLSearchParams > mutation', () => {
|
|
|
243
243
|
const url = new TrackedURL('https://example.com?foo=bar&baz=qux');
|
|
244
244
|
const params = url.searchParams;
|
|
245
245
|
|
|
246
|
-
<button
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
246
|
+
<button
|
|
247
|
+
onClick={() => {
|
|
248
|
+
params.delete('foo');
|
|
249
|
+
params.delete('baz');
|
|
250
|
+
}}
|
|
251
|
+
>
|
|
252
|
+
{'clear all'}
|
|
253
|
+
</button>
|
|
250
254
|
<pre>{url.href}</pre>
|
|
251
255
|
<pre>{params.size}</pre>
|
|
252
256
|
}
|
|
@@ -256,7 +260,9 @@ describe('TrackedURLSearchParams > mutation', () => {
|
|
|
256
260
|
const button = container.querySelector('button');
|
|
257
261
|
|
|
258
262
|
// Initial state
|
|
259
|
-
expect(container.querySelectorAll('pre')[0].textContent).toBe(
|
|
263
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe(
|
|
264
|
+
'https://example.com/?foo=bar&baz=qux',
|
|
265
|
+
);
|
|
260
266
|
expect(container.querySelectorAll('pre')[1].textContent).toBe('2');
|
|
261
267
|
|
|
262
268
|
// Test clear all
|
|
@@ -271,14 +277,18 @@ describe('TrackedURLSearchParams > mutation', () => {
|
|
|
271
277
|
component URLTest() {
|
|
272
278
|
const params = new TrackedURLSearchParams();
|
|
273
279
|
|
|
274
|
-
<button
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
280
|
+
<button
|
|
281
|
+
onClick={() => {
|
|
282
|
+
params.append('a', '1');
|
|
283
|
+
params.append('b', '2');
|
|
284
|
+
params.set('a', '10');
|
|
285
|
+
params.delete('b');
|
|
286
|
+
params.append('c', '3');
|
|
287
|
+
params.sort();
|
|
288
|
+
}}
|
|
289
|
+
>
|
|
290
|
+
{'complex operations'}
|
|
291
|
+
</button>
|
|
282
292
|
<pre>{params.toString()}</pre>
|
|
283
293
|
<pre>{params.size}</pre>
|
|
284
294
|
}
|
|
@@ -337,7 +347,9 @@ describe('TrackedURLSearchParams > mutation', () => {
|
|
|
337
347
|
rippleButton.click();
|
|
338
348
|
flushSync();
|
|
339
349
|
|
|
340
|
-
expect(container.querySelectorAll('pre')[0].textContent).toBe(
|
|
350
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe(
|
|
351
|
+
'["javascript","typescript","ripple"]',
|
|
352
|
+
);
|
|
341
353
|
expect(container.querySelectorAll('pre')[1].textContent).toBe('3');
|
|
342
354
|
});
|
|
343
355
|
});
|
|
@@ -23,7 +23,9 @@ describe('TrackedURLSearchParams > TrackedURL integration', () => {
|
|
|
23
23
|
button.click();
|
|
24
24
|
flushSync();
|
|
25
25
|
|
|
26
|
-
expect(container.querySelectorAll('pre')[0].textContent).toBe(
|
|
26
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe(
|
|
27
|
+
'https://example.com/?foo=bar&baz=qux',
|
|
28
|
+
);
|
|
27
29
|
expect(container.querySelectorAll('pre')[1].textContent).toBe('foo=bar&baz=qux');
|
|
28
30
|
});
|
|
29
31
|
|
|
@@ -9,7 +9,7 @@ describe('await in control flow', () => {
|
|
|
9
9
|
let data = track('loading');
|
|
10
10
|
|
|
11
11
|
if (condition) {
|
|
12
|
-
await new Promise(resolve => setTimeout(() => {
|
|
12
|
+
await new Promise((resolve) => setTimeout(() => {
|
|
13
13
|
@data = 'loaded';
|
|
14
14
|
resolve();
|
|
15
15
|
}, 10));
|
|
@@ -28,7 +28,7 @@ describe('await in control flow', () => {
|
|
|
28
28
|
let result = '';
|
|
29
29
|
|
|
30
30
|
for (const item of items) {
|
|
31
|
-
await new Promise(resolve => setTimeout(resolve, 5));
|
|
31
|
+
await new Promise((resolve) => setTimeout(resolve, 5));
|
|
32
32
|
result += item;
|
|
33
33
|
}
|
|
34
34
|
|
|
@@ -39,23 +39,24 @@ describe('await in control flow', () => {
|
|
|
39
39
|
expect(body).toBe('<div>123</div>');
|
|
40
40
|
});
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
42
|
+
it('should handle await inside switch statement', async () => {
|
|
43
|
+
component App() {
|
|
44
|
+
let value = 'b';
|
|
45
|
+
|
|
46
|
+
switch (value) {
|
|
47
|
+
case 'a':
|
|
48
|
+
<div>{'Case A'}</div>
|
|
49
|
+
break;
|
|
50
|
+
case 'b':
|
|
51
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
52
|
+
<div>{'Case B'}</div>
|
|
53
|
+
break;
|
|
54
|
+
default:
|
|
55
|
+
<div>{'Default Case'}</div>
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const { body } = await render(App);
|
|
60
|
+
expect(body).toBe('<div>Case B</div>');
|
|
61
|
+
});
|
|
62
|
+
});
|
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { compile } from 'ripple/compiler'
|
|
2
|
+
import { compile } from 'ripple/compiler';
|
|
3
3
|
|
|
4
4
|
describe('compiler success tests', () => {
|
|
5
|
-
|
|
6
|
-
|
|
7
5
|
it('compiles TSInstantiationExpression', () => {
|
|
8
|
-
const source =
|
|
9
|
-
`function makeBox<T>(value: T) {
|
|
6
|
+
const source = `function makeBox<T,>(value: T) {
|
|
10
7
|
return { value };
|
|
11
8
|
}
|
|
12
9
|
const makeStringBox = makeBox<string>;
|
|
@@ -20,8 +17,7 @@ const errorMap = new ErrorMap();`;
|
|
|
20
17
|
});
|
|
21
18
|
|
|
22
19
|
it('compiles imported component with conditional async in SSR', () => {
|
|
23
|
-
const source =
|
|
24
|
-
`import { ChildComponent } from './Child.ripple';
|
|
20
|
+
const source = `import { ChildComponent } from './Child.ripple';
|
|
25
21
|
|
|
26
22
|
export component App() {
|
|
27
23
|
<div>
|
|
@@ -9,32 +9,36 @@ describe('generics', () => {
|
|
|
9
9
|
|
|
10
10
|
// 7. Generic following optional chaining
|
|
11
11
|
const maybe = {
|
|
12
|
-
factory<T>() {
|
|
12
|
+
factory: function <T>() {
|
|
13
13
|
return {
|
|
14
|
-
make<U>() {
|
|
14
|
+
make: function <U>() {
|
|
15
15
|
return 1;
|
|
16
|
-
}
|
|
16
|
+
},
|
|
17
17
|
};
|
|
18
|
-
}
|
|
18
|
+
},
|
|
19
19
|
};
|
|
20
20
|
const g = maybe?.factory<number>()?.make<boolean>();
|
|
21
21
|
|
|
22
22
|
// 8. Comparison operator (ensure '<' here NOT misparsed as generics)
|
|
23
23
|
let x = 10, y = 20;
|
|
24
|
-
const h =
|
|
24
|
+
const h =
|
|
25
|
+
x < y ? 'lt' : 'ge';
|
|
25
26
|
|
|
26
27
|
// 9. Chained comparisons with intervening generics
|
|
27
28
|
class Box<T> {
|
|
28
29
|
value: T;
|
|
29
|
-
|
|
30
|
+
|
|
31
|
+
constructor(value: T) {
|
|
30
32
|
this.value = value;
|
|
31
33
|
}
|
|
34
|
+
|
|
32
35
|
open<U>() {
|
|
33
36
|
return new Box<U>();
|
|
34
37
|
}
|
|
35
38
|
}
|
|
36
39
|
const limit = 100;
|
|
37
|
-
const i =
|
|
40
|
+
const i =
|
|
41
|
+
new Box<number>().value < limit ? 'ok' : 'no';
|
|
38
42
|
|
|
39
43
|
// 10. JSX / Element should still work
|
|
40
44
|
<div class="still-works">
|
|
@@ -59,13 +63,13 @@ describe('generics', () => {
|
|
|
59
63
|
// 13. Multiple generic segments in chain
|
|
60
64
|
function foo<T>() {
|
|
61
65
|
return {
|
|
62
|
-
bar<U>() {
|
|
66
|
+
bar: function <U>() {
|
|
63
67
|
return {
|
|
64
|
-
baz<V>() {
|
|
68
|
+
baz: function <V>() {
|
|
65
69
|
return true;
|
|
66
|
-
}
|
|
70
|
+
},
|
|
67
71
|
};
|
|
68
|
-
}
|
|
72
|
+
},
|
|
69
73
|
};
|
|
70
74
|
}
|
|
71
75
|
const l = foo<number>().bar<string>().baz<boolean>();
|
|
@@ -77,9 +81,11 @@ describe('generics', () => {
|
|
|
77
81
|
// 15. Generic in angle after "new" + trailing call
|
|
78
82
|
class Wrapper<T> {
|
|
79
83
|
value: T;
|
|
84
|
+
|
|
80
85
|
constructor() {
|
|
81
86
|
this.value = null as unknown as T;
|
|
82
87
|
}
|
|
88
|
+
|
|
83
89
|
unwrap<U>() {
|
|
84
90
|
return null as unknown as U;
|
|
85
91
|
}
|
|
@@ -90,11 +96,11 @@ describe('generics', () => {
|
|
|
90
96
|
function getUnknown(): unknown {
|
|
91
97
|
return new Map<string, number>([['a', 1]]);
|
|
92
98
|
}
|
|
93
|
-
getUnknown.factory = function<T>() {
|
|
99
|
+
getUnknown.factory = function <T>() {
|
|
94
100
|
return {
|
|
95
|
-
make<U>() {
|
|
101
|
+
make: function <U>() {
|
|
96
102
|
return 2;
|
|
97
|
-
}
|
|
103
|
+
},
|
|
98
104
|
};
|
|
99
105
|
};
|
|
100
106
|
const raw = getUnknown();
|
|
@@ -103,14 +109,17 @@ describe('generics', () => {
|
|
|
103
109
|
// 17. Generic with comma + trailing less-than comparison on next token
|
|
104
110
|
class Pair<T1, T2> {
|
|
105
111
|
first: T1;
|
|
112
|
+
|
|
106
113
|
second: T2;
|
|
114
|
+
|
|
107
115
|
constructor() {
|
|
108
116
|
this.first = null as unknown as T1;
|
|
109
117
|
this.second = null as unknown as T2;
|
|
110
118
|
}
|
|
111
119
|
}
|
|
112
120
|
const p = new Pair<number, string>();
|
|
113
|
-
const q =
|
|
121
|
+
const q =
|
|
122
|
+
1 < 2 ? p : null;
|
|
114
123
|
|
|
115
124
|
// 18. Nested generics with line breaks resembling JSX indentation
|
|
116
125
|
interface Node<T> {
|
|
@@ -121,16 +130,15 @@ describe('generics', () => {
|
|
|
121
130
|
}
|
|
122
131
|
class Graph<N, E> {
|
|
123
132
|
nodes: N[];
|
|
133
|
+
|
|
124
134
|
edges: E[];
|
|
135
|
+
|
|
125
136
|
constructor() {
|
|
126
137
|
this.nodes = [];
|
|
127
138
|
this.edges = [];
|
|
128
139
|
}
|
|
129
140
|
}
|
|
130
|
-
const r = new Graph<
|
|
131
|
-
Node<string>,
|
|
132
|
-
Edge<number>
|
|
133
|
-
>();
|
|
141
|
+
const r = new Graph<Node<string>, Edge<number>>();
|
|
134
142
|
|
|
135
143
|
// 19. Ternary containing generics in both branches
|
|
136
144
|
let flag = true;
|
|
@@ -150,18 +158,17 @@ describe('generics', () => {
|
|
|
150
158
|
const v = make<number>()(10);
|
|
151
159
|
|
|
152
160
|
// 23. Generic followed by tagged template (ensure not confused with JSX)
|
|
153
|
-
function tagFn<T>(strings: TemplateStringsArray, ...values
|
|
161
|
+
function tagFn<T>(strings: TemplateStringsArray, ...values) {
|
|
154
162
|
return values[0];
|
|
155
163
|
}
|
|
156
|
-
const tagResult = tagFn
|
|
164
|
+
const tagResult = tagFn`value`;
|
|
157
165
|
|
|
158
166
|
// 24. Sequence mixing: (a < b) + generic call in same statement
|
|
159
167
|
function compute<T>(x: T, y: T): T {
|
|
160
168
|
return y;
|
|
161
169
|
}
|
|
162
170
|
|
|
163
|
-
const w =
|
|
164
|
-
|
|
171
|
+
const w = x < y && compute<number>(x, y);
|
|
165
172
|
|
|
166
173
|
// Additional component focusing on edge crankers
|
|
167
174
|
|
|
@@ -172,37 +179,32 @@ describe('generics', () => {
|
|
|
172
179
|
class Builder<Kind> {
|
|
173
180
|
finalize<Result>() {
|
|
174
181
|
return {
|
|
175
|
-
result: null as unknown as Result
|
|
182
|
+
result: null as unknown as Result,
|
|
176
183
|
};
|
|
177
184
|
}
|
|
178
185
|
}
|
|
179
186
|
const builder = new Builder<Number>();
|
|
180
|
-
const result = (
|
|
187
|
+
const result = (function () {
|
|
188
|
+
return builder;
|
|
189
|
+
}() as Builder<Number>).finalize<boolean>();
|
|
181
190
|
|
|
182
191
|
// 30. Angle bracket start of conditional expression line
|
|
183
192
|
function adjust<T>(value: T): T {
|
|
184
193
|
return value;
|
|
185
194
|
}
|
|
186
195
|
const val =
|
|
187
|
-
new Wrapper<number>()
|
|
188
|
-
.value < 100
|
|
189
|
-
? adjust<number>(10)
|
|
190
|
-
: adjust<number>(20);
|
|
191
|
-
|
|
196
|
+
new Wrapper<number>().value < 100 ? adjust<number>(10) : adjust<number>(20);
|
|
192
197
|
|
|
193
198
|
// 32. Generic with comments inside angle list
|
|
194
199
|
class Mapper<Key, Value> {
|
|
195
200
|
map: Map<Key, Value>;
|
|
201
|
+
|
|
196
202
|
constructor() {
|
|
197
203
|
this.map = new Map<Key, Value>();
|
|
198
204
|
}
|
|
199
205
|
}
|
|
200
|
-
const gg = new Mapper
|
|
201
|
-
|
|
202
|
-
string,
|
|
203
|
-
/* value type */
|
|
204
|
-
number
|
|
205
|
-
>();
|
|
206
|
+
const gg = new Mapper<// key type
|
|
207
|
+
string /* value type */, number>();
|
|
206
208
|
|
|
207
209
|
// 33. Map of generic instance as key
|
|
208
210
|
const mm = new Map<TrackedArray<number>, TrackedArray<string>>();
|