ripple 0.2.216 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +52 -0
- package/package.json +16 -7
- package/src/compiler/errors.js +1 -1
- package/src/compiler/identifier-utils.js +2 -0
- package/src/compiler/index.d.ts +2 -6
- package/src/compiler/phases/1-parse/index.js +171 -233
- package/src/compiler/phases/2-analyze/index.js +192 -16
- package/src/compiler/phases/2-analyze/prune.js +2 -2
- package/src/compiler/phases/3-transform/client/index.js +308 -91
- package/src/compiler/phases/3-transform/segments.js +43 -15
- package/src/compiler/phases/3-transform/server/index.js +71 -21
- package/src/compiler/scope.js +31 -12
- package/src/compiler/source-map-utils.js +4 -6
- package/src/compiler/types/acorn.d.ts +11 -0
- package/src/compiler/types/estree-jsx.d.ts +11 -0
- package/src/compiler/types/estree.d.ts +11 -0
- package/src/compiler/types/import.d.ts +32 -18
- package/src/compiler/types/index.d.ts +75 -23
- package/src/compiler/types/parse.d.ts +7 -10
- package/src/compiler/utils.js +48 -0
- package/src/runtime/array.js +53 -22
- package/src/runtime/date.js +15 -5
- package/src/runtime/index-client.js +41 -7
- package/src/runtime/index-server.js +7 -7
- package/src/runtime/internal/client/bindings.js +2 -2
- package/src/runtime/internal/client/blocks.js +40 -1
- package/src/runtime/internal/client/context.js +8 -0
- package/src/runtime/internal/client/for.js +3 -3
- package/src/runtime/internal/client/index.js +27 -5
- package/src/runtime/internal/client/render.js +20 -8
- package/src/runtime/internal/client/runtime.js +9 -7
- package/src/runtime/internal/client/try.js +15 -22
- package/src/runtime/internal/client/utils.js +1 -1
- package/src/runtime/internal/server/context.js +8 -0
- package/src/runtime/internal/server/index.js +99 -6
- package/src/runtime/map.js +7 -7
- package/src/runtime/media-query.js +10 -1
- package/src/runtime/object.js +6 -6
- package/src/runtime/proxy.js +6 -6
- package/src/runtime/set.js +11 -11
- package/src/runtime/url-search-params.js +13 -2
- package/src/runtime/url.js +15 -5
- package/src/utils/builders.js +13 -3
- package/tests/client/array/array.copy-within.test.ripple +11 -11
- package/tests/client/array/array.derived.test.ripple +42 -42
- package/tests/client/array/array.iteration.test.ripple +12 -12
- package/tests/client/array/array.mutations.test.ripple +25 -25
- package/tests/client/array/array.static.test.ripple +103 -106
- package/tests/client/array/array.to-methods.test.ripple +8 -8
- package/tests/client/async-suspend.test.ripple +94 -0
- package/tests/client/basic/basic.attributes.test.ripple +31 -31
- package/tests/client/basic/basic.collections.test.ripple +7 -7
- package/tests/client/basic/basic.components.test.ripple +48 -10
- package/tests/client/basic/basic.errors.test.ripple +46 -31
- 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 +47 -42
- 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.assignments.test.ripple.snap +2 -2
- package/tests/client/compiler/compiler.assignments.test.ripple +21 -21
- package/tests/client/compiler/compiler.basic.test.ripple +223 -82
- package/tests/client/compiler/compiler.tracked-access.test.ripple +8 -9
- package/tests/client/composite/composite.dynamic-components.test.ripple +8 -8
- package/tests/client/composite/composite.generics.test.ripple +4 -4
- package/tests/client/composite/composite.props.test.ripple +9 -9
- package/tests/client/composite/composite.reactivity.test.ripple +32 -26
- package/tests/client/composite/composite.render.test.ripple +13 -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 +4 -4
- package/tests/client/css/style-identifier.test.ripple +49 -41
- package/tests/client/date.test.ripple +40 -40
- package/tests/client/dynamic-elements.test.ripple +165 -30
- package/tests/client/events.test.ripple +25 -25
- package/tests/client/for.test.ripple +76 -8
- package/tests/client/function-overload.test.ripple +0 -1
- package/tests/client/head.test.ripple +7 -7
- package/tests/client/html.test.ripple +2 -2
- package/tests/client/input-value.test.ripple +174 -176
- package/tests/client/map.test.ripple +21 -21
- package/tests/client/media-query.test.ripple +4 -4
- package/tests/client/object.test.ripple +12 -12
- package/tests/client/portal.test.ripple +4 -4
- package/tests/client/ref.test.ripple +5 -5
- package/tests/client/return.test.ripple +17 -17
- package/tests/client/set.test.ripple +16 -16
- package/tests/client/svg.test.ripple +6 -7
- package/tests/client/switch.test.ripple +10 -10
- package/tests/client/tracked-expression.test.ripple +1 -3
- package/tests/client/try.test.ripple +33 -4
- package/tests/client/url/url.derived.test.ripple +10 -9
- package/tests/client/url/url.parsing.test.ripple +10 -10
- package/tests/client/url/url.partial-removal.test.ripple +10 -10
- package/tests/client/url/url.reactivity.test.ripple +17 -17
- package/tests/client/url/url.serialization.test.ripple +4 -4
- package/tests/client/url-search-params/url-search-params.derived.test.ripple +11 -10
- package/tests/client/url-search-params/url-search-params.initialization.test.ripple +5 -7
- package/tests/client/url-search-params/url-search-params.iteration.test.ripple +13 -13
- package/tests/client/url-search-params/url-search-params.mutation.test.ripple +19 -19
- package/tests/client/url-search-params/url-search-params.retrieval.test.ripple +17 -17
- package/tests/client/url-search-params/url-search-params.serialization.test.ripple +5 -5
- package/tests/client/url-search-params/url-search-params.tracked-url.test.ripple +5 -5
- package/tests/hydration/compiled/client/events.js +8 -11
- package/tests/hydration/compiled/client/for.js +20 -23
- package/tests/hydration/compiled/client/head.js +17 -19
- package/tests/hydration/compiled/client/hmr.js +1 -3
- package/tests/hydration/compiled/client/html.js +1 -15
- package/tests/hydration/compiled/client/if-children.js +7 -9
- package/tests/hydration/compiled/client/if.js +5 -7
- package/tests/hydration/compiled/client/mixed-control-flow.js +3 -5
- package/tests/hydration/compiled/client/portal.js +1 -1
- package/tests/hydration/compiled/client/reactivity.js +9 -11
- package/tests/hydration/compiled/client/return.js +11 -13
- package/tests/hydration/compiled/client/switch.js +4 -6
- package/tests/hydration/compiled/server/basic.js +0 -1
- package/tests/hydration/compiled/server/composite.js +0 -3
- package/tests/hydration/compiled/server/events.js +8 -12
- package/tests/hydration/compiled/server/for.js +20 -23
- package/tests/hydration/compiled/server/head.js +17 -19
- package/tests/hydration/compiled/server/hmr.js +1 -4
- package/tests/hydration/compiled/server/html.js +1 -35
- package/tests/hydration/compiled/server/if-children.js +7 -11
- package/tests/hydration/compiled/server/if.js +5 -7
- package/tests/hydration/compiled/server/mixed-control-flow.js +3 -5
- package/tests/hydration/compiled/server/portal.js +1 -9
- package/tests/hydration/compiled/server/reactivity.js +9 -11
- package/tests/hydration/compiled/server/return.js +11 -13
- package/tests/hydration/compiled/server/switch.js +4 -6
- package/tests/hydration/components/events.ripple +8 -9
- package/tests/hydration/components/for.ripple +20 -21
- package/tests/hydration/components/head.ripple +6 -8
- package/tests/hydration/components/hmr.ripple +1 -2
- package/tests/hydration/components/html.ripple +1 -3
- package/tests/hydration/components/if-children.ripple +7 -8
- package/tests/hydration/components/if.ripple +5 -6
- package/tests/hydration/components/mixed-control-flow.ripple +4 -6
- package/tests/hydration/components/portal.ripple +1 -1
- package/tests/hydration/components/reactivity.ripple +9 -10
- package/tests/hydration/components/return.ripple +11 -12
- package/tests/hydration/components/switch.ripple +6 -8
- package/tests/server/await.test.ripple +2 -2
- package/tests/server/basic.attributes.test.ripple +19 -21
- package/tests/server/basic.components.test.ripple +13 -7
- package/tests/server/basic.test.ripple +20 -21
- package/tests/server/compiler.test.ripple +5 -5
- package/tests/server/composite.props.test.ripple +6 -7
- package/tests/server/composite.test.ripple +4 -4
- package/tests/server/context.test.ripple +1 -3
- package/tests/server/dynamic-elements.test.ripple +24 -24
- package/tests/server/head.test.ripple +5 -7
- package/tests/server/style-identifier.test.ripple +16 -17
- package/types/index.d.ts +266 -62
- package/types/server.d.ts +6 -6
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { flushSync,
|
|
1
|
+
import { flushSync, RippleArray } from 'ripple';
|
|
2
2
|
import { MAX_ARRAY_LENGTH } from '../../../src/runtime/internal/client/constants.js';
|
|
3
3
|
|
|
4
|
-
describe('
|
|
4
|
+
describe('RippleArray > static', () => {
|
|
5
5
|
it('handles static methods - from and of', () => {
|
|
6
6
|
component ArrayTest() {
|
|
7
|
-
let itemsFrom =
|
|
8
|
-
let itemsOf =
|
|
7
|
+
let itemsFrom = RippleArray.from([1, 2, 3], (x: number) => x * 2);
|
|
8
|
+
let itemsOf = RippleArray.of(4, 5, 6);
|
|
9
9
|
|
|
10
10
|
<button onClick={() => itemsFrom.push(8)}>{'add to from'}</button>
|
|
11
11
|
<button onClick={() => itemsOf.push(7)}>{'add to of'}</button>
|
|
@@ -35,49 +35,87 @@ describe('TrackedArray > static', () => {
|
|
|
35
35
|
expect(container.querySelectorAll('pre')[1].textContent).toBe('[4,5,6,7]');
|
|
36
36
|
});
|
|
37
37
|
|
|
38
|
-
('fromAsync' in Array.prototype ? describe : describe.skip)(
|
|
39
|
-
'
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
} pending {
|
|
46
|
-
<div>{'Loading placeholder...'}</div>
|
|
47
|
-
}
|
|
38
|
+
('fromAsync' in Array.prototype ? describe : describe.skip)('RippleArray fromAsync', async () => {
|
|
39
|
+
it('handles static fromAsync method with reactivity', async () => {
|
|
40
|
+
component Parent() {
|
|
41
|
+
try {
|
|
42
|
+
<ArrayTest />
|
|
43
|
+
} pending {
|
|
44
|
+
<div>{'Loading placeholder...'}</div>
|
|
48
45
|
}
|
|
46
|
+
}
|
|
49
47
|
|
|
50
|
-
|
|
51
|
-
|
|
48
|
+
component ArrayTest() {
|
|
49
|
+
let items = await RippleArray.fromAsync([1, 2, 3]);
|
|
50
|
+
|
|
51
|
+
<button
|
|
52
|
+
onClick={() => {
|
|
53
|
+
if (items) items.push(4);
|
|
54
|
+
}}
|
|
55
|
+
>
|
|
56
|
+
{'add item'}
|
|
57
|
+
</button>
|
|
58
|
+
|
|
59
|
+
<pre>{JSON.stringify(items)}</pre>
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
render(Parent);
|
|
52
63
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
if (items) items.push(4);
|
|
56
|
-
}}
|
|
57
|
-
>
|
|
58
|
-
{'add item'}
|
|
59
|
-
</button>
|
|
64
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
65
|
+
flushSync();
|
|
60
66
|
|
|
61
|
-
|
|
67
|
+
const addButton = container.querySelector('button');
|
|
68
|
+
|
|
69
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('[1,2,3]');
|
|
70
|
+
|
|
71
|
+
// Test adding an item to the async-created array
|
|
72
|
+
addButton.click();
|
|
73
|
+
flushSync();
|
|
74
|
+
|
|
75
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('[1,2,3,4]');
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('handles static fromAsync method with mapping function', async () => {
|
|
79
|
+
component Parent() {
|
|
80
|
+
try {
|
|
81
|
+
<ArrayTest />
|
|
82
|
+
} pending {
|
|
83
|
+
<div>{'Loading placeholder...'}</div>
|
|
62
84
|
}
|
|
85
|
+
}
|
|
63
86
|
|
|
64
|
-
|
|
87
|
+
component ArrayTest() {
|
|
88
|
+
let items = await RippleArray.fromAsync([1, 2, 3], (x: number) => x * 2);
|
|
89
|
+
|
|
90
|
+
<button
|
|
91
|
+
onClick={() => {
|
|
92
|
+
if (items) items.push(8);
|
|
93
|
+
}}
|
|
94
|
+
>
|
|
95
|
+
{'add item'}
|
|
96
|
+
</button>
|
|
97
|
+
<pre>{items ? JSON.stringify(items) : 'Loading...'}</pre>
|
|
98
|
+
}
|
|
65
99
|
|
|
66
|
-
|
|
67
|
-
flushSync();
|
|
100
|
+
render(Parent);
|
|
68
101
|
|
|
69
|
-
|
|
102
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
103
|
+
flushSync();
|
|
70
104
|
|
|
71
|
-
|
|
105
|
+
const addButton = container.querySelector('button');
|
|
72
106
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
107
|
+
expect(container.querySelector('pre').textContent).toBe('[2,4,6]');
|
|
108
|
+
|
|
109
|
+
addButton.click();
|
|
110
|
+
flushSync();
|
|
76
111
|
|
|
77
|
-
|
|
78
|
-
|
|
112
|
+
expect(container.querySelector('pre').textContent).toBe('[2,4,6,8]');
|
|
113
|
+
});
|
|
79
114
|
|
|
80
|
-
|
|
115
|
+
// TODO: Fix this test case, needs some async love around try statements being using in a not template way
|
|
116
|
+
('fromAsync' in Array.prototype ? it : it.skip)(
|
|
117
|
+
'handles error in fromAsync method',
|
|
118
|
+
async () => {
|
|
81
119
|
component Parent() {
|
|
82
120
|
try {
|
|
83
121
|
<ArrayTest />
|
|
@@ -87,84 +125,43 @@ describe('TrackedArray > static', () => {
|
|
|
87
125
|
}
|
|
88
126
|
|
|
89
127
|
component ArrayTest() {
|
|
90
|
-
let items
|
|
91
|
-
|
|
92
|
-
<button
|
|
93
|
-
onClick={() => {
|
|
94
|
-
if (items) items.push(8);
|
|
95
|
-
}}
|
|
96
|
-
>
|
|
97
|
-
{'add item'}
|
|
98
|
-
</button>
|
|
99
|
-
<pre>{items ? JSON.stringify(items) : 'Loading...'}</pre>
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
render(Parent);
|
|
128
|
+
let items: RippleArray<string> | null = null;
|
|
129
|
+
let error: string | null = null;
|
|
103
130
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
const addButton = container.querySelector('button');
|
|
108
|
-
|
|
109
|
-
expect(container.querySelector('pre').textContent).toBe('[2,4,6]');
|
|
110
|
-
|
|
111
|
-
addButton.click();
|
|
112
|
-
flushSync();
|
|
113
|
-
|
|
114
|
-
expect(container.querySelector('pre').textContent).toBe('[2,4,6,8]');
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
// TODO: Fix this test case, needs some async love around try statements being using in a not template way
|
|
118
|
-
('fromAsync' in Array.prototype ? it : it.skip)(
|
|
119
|
-
'handles error in fromAsync method',
|
|
120
|
-
async () => {
|
|
121
|
-
component Parent() {
|
|
122
|
-
try {
|
|
123
|
-
<ArrayTest />
|
|
124
|
-
} pending {
|
|
125
|
-
<div>{'Loading placeholder...'}</div>
|
|
126
|
-
}
|
|
131
|
+
async function* throwingIterable() {
|
|
132
|
+
throw new Error('Async error');
|
|
127
133
|
}
|
|
128
134
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
async function* throwingIterable() {
|
|
134
|
-
throw new Error('Async error');
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
try {
|
|
138
|
-
items = await TrackedArray.fromAsync(throwingIterable());
|
|
139
|
-
for (const item of items) {
|
|
140
|
-
<li>{item}</li>
|
|
141
|
-
}
|
|
142
|
-
} pending {
|
|
143
|
-
<div>{'Loading...'}</div>
|
|
144
|
-
} catch (e) {
|
|
145
|
-
error = (e as Error).message;
|
|
135
|
+
try {
|
|
136
|
+
items = await RippleArray.fromAsync(throwingIterable());
|
|
137
|
+
for (const item of items) {
|
|
138
|
+
<li>{item}</li>
|
|
146
139
|
}
|
|
147
|
-
|
|
148
|
-
<
|
|
149
|
-
|
|
140
|
+
} pending {
|
|
141
|
+
<div>{'Loading...'}</div>
|
|
142
|
+
} catch (e) {
|
|
143
|
+
error = (e as Error).message;
|
|
150
144
|
}
|
|
151
145
|
|
|
152
|
-
|
|
146
|
+
<pre>{error ? 'Error: ' + error : 'No error'}</pre>
|
|
147
|
+
<pre>{items ? JSON.stringify(items) : 'No items'}</pre>
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
render(Parent);
|
|
153
151
|
|
|
154
|
-
|
|
155
|
-
|
|
152
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
153
|
+
flushSync();
|
|
156
154
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
);
|
|
155
|
+
expect(container.querySelectorAll('pre')[0].textContent).toBe('Error: Async error');
|
|
156
|
+
expect(container.querySelectorAll('pre')[1].textContent).toBe('No items');
|
|
157
|
+
},
|
|
158
|
+
);
|
|
159
|
+
});
|
|
163
160
|
|
|
164
|
-
describe('Creates
|
|
161
|
+
describe('Creates RippleArray with a single element', () => {
|
|
165
162
|
it('specifies int', () => {
|
|
166
163
|
component ArrayTest() {
|
|
167
|
-
let items = new
|
|
164
|
+
let items = new RippleArray(3);
|
|
168
165
|
<pre>{JSON.stringify(items)}</pre>
|
|
169
166
|
<pre>{items.length}</pre>
|
|
170
167
|
}
|
|
@@ -180,7 +177,7 @@ describe('TrackedArray > static', () => {
|
|
|
180
177
|
let error = null;
|
|
181
178
|
|
|
182
179
|
try {
|
|
183
|
-
new
|
|
180
|
+
new RippleArray(MAX_ARRAY_LENGTH + 1);
|
|
184
181
|
} catch (e) {
|
|
185
182
|
error = (e as Error).message;
|
|
186
183
|
}
|
|
@@ -195,7 +192,7 @@ describe('TrackedArray > static', () => {
|
|
|
195
192
|
|
|
196
193
|
it('specifies int using static from method', () => {
|
|
197
194
|
component ArrayTest() {
|
|
198
|
-
let items =
|
|
195
|
+
let items = RippleArray.from([4]);
|
|
199
196
|
<pre>{JSON.stringify(items)}</pre>
|
|
200
197
|
<pre>{items.length}</pre>
|
|
201
198
|
}
|
|
@@ -208,7 +205,7 @@ describe('TrackedArray > static', () => {
|
|
|
208
205
|
|
|
209
206
|
it('specifies int using static of method', () => {
|
|
210
207
|
component ArrayTest() {
|
|
211
|
-
let items =
|
|
208
|
+
let items = RippleArray.of(5);
|
|
212
209
|
<pre>{JSON.stringify(items)}</pre>
|
|
213
210
|
<pre>{items.length}</pre>
|
|
214
211
|
}
|
|
@@ -231,7 +228,7 @@ describe('TrackedArray > static', () => {
|
|
|
231
228
|
}
|
|
232
229
|
|
|
233
230
|
component ArrayTest() {
|
|
234
|
-
const items = await
|
|
231
|
+
const items = await RippleArray.fromAsync([6]);
|
|
235
232
|
|
|
236
233
|
<pre>{items ? JSON.stringify(items) : 'Loading...'}</pre>
|
|
237
234
|
<pre>{items ? items.length : ''}</pre>
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { flushSync, RippleArray } from 'ripple';
|
|
2
2
|
|
|
3
|
-
describe('
|
|
3
|
+
describe('RippleArray > to* methods', () => {
|
|
4
4
|
it('handles toReversed method with reactivity', (context) => {
|
|
5
5
|
if (!('toReversed' in Array.prototype)) {
|
|
6
6
|
context.skip();
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
component ArrayTest() {
|
|
10
|
-
let items = new
|
|
11
|
-
let reversed = track(() => items.toReversed());
|
|
10
|
+
let items = new RippleArray(1, 2, 3, 4);
|
|
11
|
+
let reversed = #ripple.track(() => items.toReversed());
|
|
12
12
|
|
|
13
13
|
<button onClick={() => items.push(5)}>{'add item'}</button>
|
|
14
14
|
<pre>{JSON.stringify(items)}</pre>
|
|
@@ -37,8 +37,8 @@ describe('TrackedArray > to* methods', () => {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
component ArrayTest() {
|
|
40
|
-
let items = new
|
|
41
|
-
let sorted = track(() => items.toSorted());
|
|
40
|
+
let items = new RippleArray(3, 1, 4, 2);
|
|
41
|
+
let sorted = #ripple.track(() => items.toSorted());
|
|
42
42
|
|
|
43
43
|
<button onClick={() => items.push(0)}>{'add item'}</button>
|
|
44
44
|
<pre>{JSON.stringify(items)}</pre>
|
|
@@ -67,8 +67,8 @@ describe('TrackedArray > to* methods', () => {
|
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
component ArrayTest() {
|
|
70
|
-
let items = new
|
|
71
|
-
let spliced = track(() => items.toSpliced(1, 2, 'a', 'b'));
|
|
70
|
+
let items = new RippleArray<string | number>(1, 2, 3, 4, 5);
|
|
71
|
+
let spliced = #ripple.track(() => items.toSpliced(1, 2, 'a', 'b'));
|
|
72
72
|
|
|
73
73
|
<button onClick={() => (items[2] = 30)}>{'change item'}</button>
|
|
74
74
|
<pre>{JSON.stringify(items)}</pre>
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { flushSync } from 'ripple';
|
|
2
|
+
|
|
3
|
+
describe('async suspense', () => {
|
|
4
|
+
it('hides child content during re-suspension when tracked dependency changes', async () => {
|
|
5
|
+
let resolve_fn: (() => void) | null = null;
|
|
6
|
+
|
|
7
|
+
component Child({ count }: { count: any }) {
|
|
8
|
+
await #ripple.track(() => {
|
|
9
|
+
@count;
|
|
10
|
+
return new Promise<void>((resolve) => {
|
|
11
|
+
resolve_fn = resolve;
|
|
12
|
+
});
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
<div class="child-content">{'child content'}</div>
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
component App() {
|
|
19
|
+
let count = #ripple.track(0);
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
<Child {count} />
|
|
23
|
+
} pending {
|
|
24
|
+
<div class="pending">{'pending...'}</div>
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
<button onClick={() => @count++}>{'Increment'}</button>
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
render(App);
|
|
31
|
+
|
|
32
|
+
// Initial state: should show pending
|
|
33
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
34
|
+
flushSync();
|
|
35
|
+
expect(container.innerHTML).toContain('pending...');
|
|
36
|
+
expect(container.innerHTML).not.toContain('child content');
|
|
37
|
+
|
|
38
|
+
// Resolve the first promise
|
|
39
|
+
(resolve_fn as () => void)?.();
|
|
40
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
41
|
+
flushSync();
|
|
42
|
+
|
|
43
|
+
// After resolution: should show child content, not pending
|
|
44
|
+
expect(container.innerHTML).toContain('child content');
|
|
45
|
+
expect(container.innerHTML).not.toContain('pending...');
|
|
46
|
+
|
|
47
|
+
// Now trigger re-suspension by changing count
|
|
48
|
+
const button = container.querySelector('button');
|
|
49
|
+
button?.click();
|
|
50
|
+
flushSync();
|
|
51
|
+
|
|
52
|
+
// Wait for microtask to process
|
|
53
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
54
|
+
flushSync();
|
|
55
|
+
|
|
56
|
+
// BUG: During re-suspension, we should only see pending, not child content
|
|
57
|
+
// The child content should be hidden when the new promise is pending
|
|
58
|
+
expect(container.innerHTML).toContain('pending...');
|
|
59
|
+
expect(container.innerHTML).not.toContain('child content');
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('shows pending UI immediately when child suspends', async () => {
|
|
63
|
+
component Child() {
|
|
64
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
65
|
+
<div class="child">{'loaded'}</div>
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
component App() {
|
|
69
|
+
try {
|
|
70
|
+
<Child />
|
|
71
|
+
} pending {
|
|
72
|
+
<div class="pending">{'loading...'}</div>
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
render(App);
|
|
77
|
+
|
|
78
|
+
// Wait for microtask
|
|
79
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
80
|
+
flushSync();
|
|
81
|
+
|
|
82
|
+
// Should show pending, not child content
|
|
83
|
+
expect(container.innerHTML).toContain('loading...');
|
|
84
|
+
expect(container.innerHTML).not.toContain('loaded');
|
|
85
|
+
|
|
86
|
+
// Wait for child to resolve
|
|
87
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
88
|
+
flushSync();
|
|
89
|
+
|
|
90
|
+
// Should show child content, not pending
|
|
91
|
+
expect(container.innerHTML).toContain('loaded');
|
|
92
|
+
expect(container.innerHTML).not.toContain('loading...');
|
|
93
|
+
});
|
|
94
|
+
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import {
|
|
2
|
+
import { flushSync, type RippleArray } from 'ripple';
|
|
3
3
|
|
|
4
4
|
describe('basic client > attribute rendering', () => {
|
|
5
5
|
it('render static attributes', () => {
|
|
@@ -14,7 +14,7 @@ describe('basic client > attribute rendering', () => {
|
|
|
14
14
|
|
|
15
15
|
it('render dynamic class attribute', () => {
|
|
16
16
|
component Basic() {
|
|
17
|
-
let active = track(false);
|
|
17
|
+
let active = #ripple.track(false);
|
|
18
18
|
|
|
19
19
|
<button
|
|
20
20
|
onClick={() => {
|
|
@@ -98,7 +98,7 @@ describe('basic client > attribute rendering', () => {
|
|
|
98
98
|
|
|
99
99
|
it('render dynamic class object', () => {
|
|
100
100
|
component Basic() {
|
|
101
|
-
let active = track(false);
|
|
101
|
+
let active = #ripple.track(false);
|
|
102
102
|
|
|
103
103
|
<button
|
|
104
104
|
onClick={() => {
|
|
@@ -141,7 +141,7 @@ describe('basic client > attribute rendering', () => {
|
|
|
141
141
|
|
|
142
142
|
it('applies scoped ripple class to multiple elements with dynamic class expressions', () => {
|
|
143
143
|
component Basic() {
|
|
144
|
-
let selected = track(1);
|
|
144
|
+
let selected = #ripple.track(1);
|
|
145
145
|
|
|
146
146
|
<div class={@selected === 0 ? 'selected' : ''}>{`div 1`}</div>
|
|
147
147
|
<div class={@selected === 0 ? 'selected' : ''}>{`div 2`}</div>
|
|
@@ -170,7 +170,7 @@ describe('basic client > attribute rendering', () => {
|
|
|
170
170
|
|
|
171
171
|
it('render dynamic id attribute', () => {
|
|
172
172
|
component Basic() {
|
|
173
|
-
let count = track(0);
|
|
173
|
+
let count = #ripple.track(0);
|
|
174
174
|
|
|
175
175
|
<button
|
|
176
176
|
onClick={() => {
|
|
@@ -202,7 +202,7 @@ describe('basic client > attribute rendering', () => {
|
|
|
202
202
|
|
|
203
203
|
it('render dynamic style attribute', () => {
|
|
204
204
|
component Basic() {
|
|
205
|
-
let color = track('red');
|
|
205
|
+
let color = #ripple.track('red');
|
|
206
206
|
|
|
207
207
|
<button
|
|
208
208
|
onClick={() => {
|
|
@@ -231,7 +231,7 @@ describe('basic client > attribute rendering', () => {
|
|
|
231
231
|
|
|
232
232
|
it('render style attribute as dynamic object', () => {
|
|
233
233
|
component Basic() {
|
|
234
|
-
let color = track('red');
|
|
234
|
+
let color = #ripple.track('red');
|
|
235
235
|
|
|
236
236
|
<button
|
|
237
237
|
onClick={() => {
|
|
@@ -260,7 +260,7 @@ describe('basic client > attribute rendering', () => {
|
|
|
260
260
|
|
|
261
261
|
it('render tracked variable as style attribute', () => {
|
|
262
262
|
component Basic() {
|
|
263
|
-
let style = track({ color: 'red', fontWeight: 'bold' });
|
|
263
|
+
let style = #ripple.track({ color: 'red', fontWeight: 'bold' });
|
|
264
264
|
|
|
265
265
|
function toggleColor() {
|
|
266
266
|
@style = { ...@style, color: @style.color === 'red' ? 'blue' : 'red' };
|
|
@@ -287,7 +287,7 @@ describe('basic client > attribute rendering', () => {
|
|
|
287
287
|
|
|
288
288
|
it('render tracked object as style attribute', () => {
|
|
289
289
|
component Basic() {
|
|
290
|
-
let style = #{ color: 'red', fontWeight: 'bold' };
|
|
290
|
+
let style = #ripple{ color: 'red', fontWeight: 'bold' };
|
|
291
291
|
|
|
292
292
|
function toggleColor() {
|
|
293
293
|
style.color = style.color === 'red' ? 'blue' : 'red';
|
|
@@ -360,8 +360,8 @@ describe('basic client > attribute rendering', () => {
|
|
|
360
360
|
|
|
361
361
|
it('render dynamic boolean attributes', () => {
|
|
362
362
|
component Basic() {
|
|
363
|
-
let disabled = track(false);
|
|
364
|
-
let checked = track(false);
|
|
363
|
+
let disabled = #ripple.track(false);
|
|
364
|
+
let checked = #ripple.track(false);
|
|
365
365
|
|
|
366
366
|
<button
|
|
367
367
|
onClick={() => {
|
|
@@ -391,8 +391,8 @@ describe('basic client > attribute rendering', () => {
|
|
|
391
391
|
|
|
392
392
|
it('render multiple dynamic attributes', () => {
|
|
393
393
|
component Basic() {
|
|
394
|
-
let theme = track('light');
|
|
395
|
-
let size = track('medium');
|
|
394
|
+
let theme = #ripple.track('light');
|
|
395
|
+
let size = #ripple.track('medium');
|
|
396
396
|
|
|
397
397
|
<button
|
|
398
398
|
onClick={() => {
|
|
@@ -426,8 +426,8 @@ describe('basic client > attribute rendering', () => {
|
|
|
426
426
|
|
|
427
427
|
it('render conditional attributes', () => {
|
|
428
428
|
component Basic() {
|
|
429
|
-
let showTitle = track(false);
|
|
430
|
-
let showAria = track(false);
|
|
429
|
+
let showTitle = #ripple.track(false);
|
|
430
|
+
let showAria = #ripple.track(false);
|
|
431
431
|
|
|
432
432
|
<button
|
|
433
433
|
onClick={() => {
|
|
@@ -468,7 +468,7 @@ describe('basic client > attribute rendering', () => {
|
|
|
468
468
|
|
|
469
469
|
it('render spread attributes', () => {
|
|
470
470
|
component Basic() {
|
|
471
|
-
let attrs = track<TestAttributes>(
|
|
471
|
+
let attrs = #ripple.track<TestAttributes>(
|
|
472
472
|
{
|
|
473
473
|
class: 'initial',
|
|
474
474
|
id: 'test-1',
|
|
@@ -508,12 +508,12 @@ describe('basic client > attribute rendering', () => {
|
|
|
508
508
|
|
|
509
509
|
it('renders with reactive attributes with nested reactive attributes', () => {
|
|
510
510
|
component App() {
|
|
511
|
-
let value = track('parent-class');
|
|
511
|
+
let value = #ripple.track('parent-class');
|
|
512
512
|
|
|
513
513
|
<p class={@value}>{'Colored parent value'}</p>
|
|
514
514
|
|
|
515
515
|
<div>
|
|
516
|
-
let nested = track('nested-class');
|
|
516
|
+
let nested = #ripple.track('nested-class');
|
|
517
517
|
|
|
518
518
|
<p class={@nested}>{'Colored nested value'}</p>
|
|
519
519
|
</div>
|
|
@@ -556,8 +556,8 @@ describe('basic client > attribute rendering', () => {
|
|
|
556
556
|
|
|
557
557
|
it('handles reactive event handler changes', () => {
|
|
558
558
|
component Basic() {
|
|
559
|
-
let count = track(0);
|
|
560
|
-
let mode = track<'increment' | 'decrement'>('increment');
|
|
559
|
+
let count = #ripple.track(0);
|
|
560
|
+
let mode = #ripple.track<'increment' | 'decrement'>('increment');
|
|
561
561
|
|
|
562
562
|
const incrementHandler = () => {
|
|
563
563
|
@count++;
|
|
@@ -613,7 +613,7 @@ describe('basic client > attribute rendering', () => {
|
|
|
613
613
|
|
|
614
614
|
it('handles events with capture option', () => {
|
|
615
615
|
component Basic() {
|
|
616
|
-
let captureOrder:
|
|
616
|
+
let captureOrder: RippleArray<string> = #ripple[];
|
|
617
617
|
|
|
618
618
|
const handleCaptureClick = {
|
|
619
619
|
handleEvent() {
|
|
@@ -647,7 +647,7 @@ describe('basic client > attribute rendering', () => {
|
|
|
647
647
|
|
|
648
648
|
it('handles events with Capture suffix in the name', () => {
|
|
649
649
|
component Basic() {
|
|
650
|
-
let captureOrder:
|
|
650
|
+
let captureOrder: RippleArray<string> = #ripple[];
|
|
651
651
|
|
|
652
652
|
const handleCaptureClick = () => {
|
|
653
653
|
captureOrder.push('capture');
|
|
@@ -678,7 +678,7 @@ describe('basic client > attribute rendering', () => {
|
|
|
678
678
|
|
|
679
679
|
it('handles custom events with customName option', () => {
|
|
680
680
|
component Basic() {
|
|
681
|
-
let customEventCount = track(0);
|
|
681
|
+
let customEventCount = #ripple.track(0);
|
|
682
682
|
|
|
683
683
|
const handleCustom = {
|
|
684
684
|
handleEvent(event: CustomEvent) {
|
|
@@ -713,8 +713,8 @@ describe('basic client > attribute rendering', () => {
|
|
|
713
713
|
|
|
714
714
|
it('handles events with delegated: false option to bypass delegation', () => {
|
|
715
715
|
component Basic() {
|
|
716
|
-
let delegatedCount = track(0);
|
|
717
|
-
let nonDelegatedCount = track(0);
|
|
716
|
+
let delegatedCount = #ripple.track(0);
|
|
717
|
+
let nonDelegatedCount = #ripple.track(0);
|
|
718
718
|
|
|
719
719
|
const delegatedHandler = () => {
|
|
720
720
|
@delegatedCount++;
|
|
@@ -827,8 +827,8 @@ describe('basic client > attribute rendering', () => {
|
|
|
827
827
|
|
|
828
828
|
it('handles events defined as function directly vs as object', () => {
|
|
829
829
|
component Basic() {
|
|
830
|
-
let functionCount = track(0);
|
|
831
|
-
let objectCount = track(0);
|
|
830
|
+
let functionCount = #ripple.track(0);
|
|
831
|
+
let objectCount = #ripple.track(0);
|
|
832
832
|
|
|
833
833
|
const functionHandler = () => {
|
|
834
834
|
@functionCount++;
|
|
@@ -880,8 +880,8 @@ describe('basic client > attribute rendering', () => {
|
|
|
880
880
|
|
|
881
881
|
it('handles passive event option', () => {
|
|
882
882
|
component Basic() {
|
|
883
|
-
let passiveDefaultPrevented = track<boolean | null>(null);
|
|
884
|
-
let nonPassiveDefaultPrevented = track<boolean | null>(null);
|
|
883
|
+
let passiveDefaultPrevented = #ripple.track<boolean | null>(null);
|
|
884
|
+
let nonPassiveDefaultPrevented = #ripple.track<boolean | null>(null);
|
|
885
885
|
|
|
886
886
|
const passiveHandler = {
|
|
887
887
|
handleEvent(event: Event) {
|
|
@@ -945,8 +945,8 @@ describe('basic client > attribute rendering', () => {
|
|
|
945
945
|
|
|
946
946
|
it('handles once option to fire event only once', () => {
|
|
947
947
|
component Basic() {
|
|
948
|
-
let onceCount = track(0);
|
|
949
|
-
let regularCount = track(0);
|
|
948
|
+
let onceCount = #ripple.track(0);
|
|
949
|
+
let regularCount = #ripple.track(0);
|
|
950
950
|
|
|
951
951
|
const onceHandler = {
|
|
952
952
|
handleEvent() {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { flushSync } from 'ripple';
|
|
2
2
|
import { TRACKED_ARRAY } from '../../../src/runtime/internal/client/constants.js';
|
|
3
3
|
|
|
4
4
|
describe('basic client > collections', () => {
|
|
5
5
|
it('renders with simple reactive objects', () => {
|
|
6
6
|
component Basic() {
|
|
7
|
-
let user = track(
|
|
7
|
+
let user = #ripple.track(
|
|
8
8
|
{
|
|
9
9
|
name: 'John',
|
|
10
10
|
age: 25,
|
|
@@ -40,10 +40,10 @@ describe('basic client > collections', () => {
|
|
|
40
40
|
|
|
41
41
|
it('renders with nested reactive objects', () => {
|
|
42
42
|
component Basic() {
|
|
43
|
-
let user = track(
|
|
43
|
+
let user = #ripple.track(
|
|
44
44
|
{
|
|
45
|
-
name: track('John'),
|
|
46
|
-
age: track(25),
|
|
45
|
+
name: #ripple.track('John'),
|
|
46
|
+
age: #ripple.track(25),
|
|
47
47
|
},
|
|
48
48
|
);
|
|
49
49
|
|
|
@@ -75,9 +75,9 @@ describe('basic client > collections', () => {
|
|
|
75
75
|
expect(ageDiv.textContent).toBe('30');
|
|
76
76
|
});
|
|
77
77
|
|
|
78
|
-
it('works as a reactive
|
|
78
|
+
it('works as a reactive RippleArray when constructed using # syntactic sugar', () => {
|
|
79
79
|
component App() {
|
|
80
|
-
const array = #[1, 2, 3];
|
|
80
|
+
const array = #ripple[1, 2, 3];
|
|
81
81
|
|
|
82
82
|
<pre>{String(array[3])}</pre>
|
|
83
83
|
<pre>{array[0]}</pre>
|