ripple 0.3.72 → 0.3.76
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 +116 -0
- package/package.json +3 -3
- package/src/jsx-runtime.d.ts +4 -10
- package/src/runtime/dynamic-client.js +33 -0
- package/src/runtime/dynamic-server.js +80 -0
- package/src/runtime/index-client.js +5 -13
- package/src/runtime/index-server.js +2 -0
- package/src/runtime/internal/client/blocks.js +6 -27
- package/src/runtime/internal/client/composite.js +11 -6
- package/src/runtime/internal/client/for.js +80 -5
- package/src/runtime/internal/client/index.js +0 -2
- package/src/runtime/internal/client/render.js +5 -2
- package/src/runtime/internal/client/types.d.ts +0 -10
- package/src/runtime/internal/server/index.js +8 -1
- package/tests/client/__snapshots__/computed-properties.test.tsrx.snap +8 -0
- package/tests/client/__snapshots__/for.test.tsrx.snap +22 -0
- package/tests/client/__snapshots__/html.test.tsrx.snap +4 -0
- package/tests/client/array/array.copy-within.test.tsrx +19 -19
- package/tests/client/array/array.derived.test.tsrx +97 -109
- package/tests/client/array/array.iteration.test.tsrx +28 -28
- package/tests/client/array/array.mutations.test.tsrx +68 -68
- package/tests/client/array/array.static.test.tsrx +82 -92
- package/tests/client/array/array.to-methods.test.tsrx +15 -15
- package/tests/client/async-suspend.test.tsrx +180 -179
- package/tests/client/basic/__snapshots__/basic.attributes.test.tsrx.snap +2 -0
- package/tests/client/basic/__snapshots__/basic.rendering.test.tsrx.snap +4 -0
- package/tests/client/basic/basic.attributes.test.tsrx +273 -317
- package/tests/client/basic/basic.collections.test.tsrx +55 -61
- package/tests/client/basic/basic.components.test.tsrx +198 -220
- package/tests/client/basic/basic.errors.test.tsrx +70 -76
- package/tests/client/basic/basic.events.test.tsrx +80 -85
- package/tests/client/basic/basic.get-set.test.tsrx +54 -64
- package/tests/client/basic/basic.hmr.test.tsrx +15 -19
- package/tests/client/basic/basic.reactivity.test.tsrx +121 -135
- package/tests/client/basic/basic.rendering.test.tsrx +273 -178
- package/tests/client/basic/basic.styling.test.tsrx +16 -14
- package/tests/client/basic/basic.utilities.test.tsrx +8 -10
- package/tests/client/boundaries.test.tsrx +18 -18
- package/tests/client/compiler/compiler.assignments.test.tsrx +77 -76
- package/tests/client/compiler/compiler.attributes.test.tsrx +18 -14
- package/tests/client/compiler/compiler.basic.test.tsrx +357 -288
- package/tests/client/compiler/compiler.regex.test.tsrx +40 -44
- package/tests/client/compiler/compiler.tracked-access.test.tsrx +57 -38
- package/tests/client/compiler/compiler.try-in-function.test.tsrx +16 -16
- package/tests/client/compiler/compiler.typescript.test.tsrx +4 -3
- package/tests/client/composite/composite.dynamic-components.test.tsrx +62 -47
- package/tests/client/composite/composite.generics.test.tsrx +165 -167
- package/tests/client/composite/composite.props.test.tsrx +66 -74
- package/tests/client/composite/composite.reactivity.test.tsrx +132 -166
- package/tests/client/composite/composite.render.test.tsrx +92 -101
- package/tests/client/computed-properties.test.tsrx +14 -18
- package/tests/client/context.test.tsrx +14 -18
- package/tests/client/css/global-additional-cases.test.tsrx +493 -439
- package/tests/client/css/global-advanced-selectors.test.tsrx +169 -153
- package/tests/client/css/global-at-rules.test.tsrx +71 -66
- package/tests/client/css/global-basic.test.tsrx +105 -98
- package/tests/client/css/global-classes-ids.test.tsrx +128 -114
- package/tests/client/css/global-combinators.test.tsrx +83 -78
- package/tests/client/css/global-complex-nesting.test.tsrx +134 -120
- package/tests/client/css/global-edge-cases.test.tsrx +138 -120
- package/tests/client/css/global-keyframes.test.tsrx +108 -96
- package/tests/client/css/global-nested.test.tsrx +88 -78
- package/tests/client/css/global-pseudo.test.tsrx +104 -98
- package/tests/client/css/global-scoping.test.tsrx +145 -125
- package/tests/client/css/style-identifier.test.tsrx +65 -72
- package/tests/client/date.test.tsrx +83 -83
- package/tests/client/dynamic-elements.test.tsrx +318 -299
- package/tests/client/events.test.tsrx +252 -266
- package/tests/client/for.test.tsrx +120 -127
- package/tests/client/head.test.tsrx +74 -48
- package/tests/client/html.test.tsrx +37 -49
- package/tests/client/input-value.test.tsrx +1125 -1354
- package/tests/client/lazy-array.test.tsrx +10 -16
- package/tests/client/lazy-destructuring.test.tsrx +169 -221
- package/tests/client/map.test.tsrx +39 -41
- package/tests/client/media-query.test.tsrx +15 -19
- package/tests/client/object.test.tsrx +46 -56
- package/tests/client/portal.test.tsrx +31 -37
- package/tests/client/ref.test.tsrx +173 -193
- package/tests/client/return.test.tsrx +62 -37
- package/tests/client/set.test.tsrx +33 -33
- package/tests/client/svg.test.tsrx +197 -216
- package/tests/client/switch.test.tsrx +201 -191
- package/tests/client/track-async-hydration.test.tsrx +14 -18
- package/tests/client/tracked-index-access.test.tsrx +18 -28
- package/tests/client/try.test.tsrx +494 -619
- package/tests/client/tsx.test.tsrx +286 -292
- package/tests/client/typescript-generics.test.tsrx +121 -129
- package/tests/client/url/url.derived.test.tsrx +21 -25
- package/tests/client/url/url.parsing.test.tsrx +35 -35
- package/tests/client/url/url.partial-removal.test.tsrx +32 -32
- package/tests/client/url/url.reactivity.test.tsrx +68 -72
- package/tests/client/url/url.serialization.test.tsrx +8 -8
- package/tests/client/url-search-params/url-search-params.derived.test.tsrx +21 -27
- package/tests/client/url-search-params/url-search-params.initialization.test.tsrx +16 -16
- package/tests/client/url-search-params/url-search-params.iteration.test.tsrx +37 -37
- package/tests/client/url-search-params/url-search-params.mutation.test.tsrx +56 -60
- package/tests/client/url-search-params/url-search-params.retrieval.test.tsrx +32 -34
- package/tests/client/url-search-params/url-search-params.serialization.test.tsrx +9 -9
- package/tests/client/url-search-params/url-search-params.tracked-url.test.tsrx +10 -10
- package/tests/hydration/compiled/client/basic.js +390 -319
- package/tests/hydration/compiled/client/composite.js +52 -44
- package/tests/hydration/compiled/client/for.js +734 -604
- package/tests/hydration/compiled/client/head.js +183 -103
- package/tests/hydration/compiled/client/html.js +93 -86
- package/tests/hydration/compiled/client/if-children.js +95 -71
- package/tests/hydration/compiled/client/if.js +113 -89
- package/tests/hydration/compiled/client/mixed-control-flow.js +225 -209
- package/tests/hydration/compiled/client/nested-control-flow.js +94 -98
- package/tests/hydration/compiled/client/reactivity.js +26 -24
- package/tests/hydration/compiled/client/return.js +8 -42
- package/tests/hydration/compiled/client/switch.js +208 -173
- package/tests/hydration/compiled/client/track-async-serialization.js +176 -128
- package/tests/hydration/compiled/client/try.js +29 -21
- package/tests/hydration/compiled/server/basic.js +210 -221
- package/tests/hydration/compiled/server/composite.js +13 -14
- package/tests/hydration/compiled/server/for.js +427 -444
- package/tests/hydration/compiled/server/head.js +199 -189
- package/tests/hydration/compiled/server/html.js +33 -41
- package/tests/hydration/compiled/server/if-children.js +114 -117
- package/tests/hydration/compiled/server/if.js +77 -83
- package/tests/hydration/compiled/server/mixed-control-flow.js +145 -150
- package/tests/hydration/compiled/server/nested-control-flow.js +10 -0
- package/tests/hydration/compiled/server/reactivity.js +24 -22
- package/tests/hydration/compiled/server/return.js +6 -18
- package/tests/hydration/compiled/server/switch.js +179 -176
- package/tests/hydration/compiled/server/track-async-serialization.js +88 -70
- package/tests/hydration/compiled/server/try.js +31 -35
- package/tests/hydration/components/basic.tsrx +216 -258
- package/tests/hydration/components/composite.tsrx +32 -42
- package/tests/hydration/components/events.tsrx +81 -101
- package/tests/hydration/components/for.tsrx +270 -336
- package/tests/hydration/components/head.tsrx +43 -39
- package/tests/hydration/components/hmr.tsrx +16 -22
- package/tests/hydration/components/html-in-template.tsrx +15 -21
- package/tests/hydration/components/html.tsrx +442 -526
- package/tests/hydration/components/if-children.tsrx +107 -125
- package/tests/hydration/components/if.tsrx +68 -90
- package/tests/hydration/components/mixed-control-flow.tsrx +65 -72
- package/tests/hydration/components/nested-control-flow.tsrx +202 -216
- package/tests/hydration/components/portal.tsrx +33 -41
- package/tests/hydration/components/reactivity.tsrx +26 -34
- package/tests/hydration/components/return.tsrx +4 -6
- package/tests/hydration/components/switch.tsrx +73 -78
- package/tests/hydration/components/track-async-serialization.tsrx +83 -93
- package/tests/hydration/components/try.tsrx +37 -51
- package/tests/hydration/switch.test.js +8 -8
- package/tests/server/await.test.tsrx +3 -3
- package/tests/server/basic.attributes.test.tsrx +117 -162
- package/tests/server/basic.components.test.tsrx +164 -194
- package/tests/server/basic.test.tsrx +299 -199
- package/tests/server/compiler.test.tsrx +142 -72
- package/tests/server/composite.props.test.tsrx +54 -58
- package/tests/server/composite.test.tsrx +165 -167
- package/tests/server/context.test.tsrx +13 -17
- package/tests/server/dynamic-elements.test.tsrx +147 -148
- package/tests/server/for.test.tsrx +115 -84
- package/tests/server/head.test.tsrx +54 -31
- package/tests/server/html-nesting-validation.test.tsrx +16 -8
- package/tests/server/if.test.tsrx +49 -59
- package/tests/server/lazy-destructuring.test.tsrx +288 -366
- package/tests/server/return.test.tsrx +58 -36
- package/tests/server/streaming-ssr.test.tsrx +4 -4
- package/tests/server/style-identifier.test.tsrx +61 -69
- package/tests/server/switch.test.tsrx +89 -97
- package/tests/server/track-async-serialization.test.tsrx +85 -103
- package/tests/server/try.test.tsrx +275 -360
- package/tests/utils/ref-types.test.js +72 -0
- package/tests/utils/vite-plugin-config.test.js +41 -74
- package/types/index.d.ts +29 -4
- package/src/runtime/internal/client/compat.js +0 -40
- package/tests/utils/compiler-compat-config.test.js +0 -38
|
@@ -3,13 +3,11 @@ import { compile } from '@tsrx/ripple';
|
|
|
3
3
|
|
|
4
4
|
describe('for statements', () => {
|
|
5
5
|
it('renders a simple static array', () => {
|
|
6
|
-
function App() {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
}
|
|
12
|
-
</>;
|
|
6
|
+
function App() @{
|
|
7
|
+
const items = ['Item 1', 'Item 2', 'Item 3'];
|
|
8
|
+
@for (const item of items) {
|
|
9
|
+
<div class={item}>{item}</div>
|
|
10
|
+
}
|
|
13
11
|
}
|
|
14
12
|
|
|
15
13
|
render(App);
|
|
@@ -17,15 +15,28 @@ describe('for statements', () => {
|
|
|
17
15
|
expect(container).toMatchSnapshot();
|
|
18
16
|
});
|
|
19
17
|
|
|
20
|
-
it('allows
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
18
|
+
it('allows plain JavaScript control flow in setup when rendered output follows', () => {
|
|
19
|
+
expect(
|
|
20
|
+
() => compile(
|
|
21
|
+
`function App() @{
|
|
22
|
+
const items = [1, 2, 3];
|
|
23
|
+
for (const item of items) {
|
|
24
|
+
<div class="selected">{item}</div>
|
|
25
|
+
}
|
|
26
|
+
<></>
|
|
27
|
+
}`,
|
|
28
|
+
'App.tsrx',
|
|
29
|
+
{ mode: 'client' },
|
|
30
|
+
),
|
|
31
|
+
).not.toThrow();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('renders filtered items before passing them to @for', () => {
|
|
35
|
+
function App() @{
|
|
36
|
+
const items = ['Item 1', '', 'Item 3'].filter(Boolean);
|
|
37
|
+
@for (const item of items) {
|
|
38
|
+
<div class="item">{item}</div>
|
|
39
|
+
}
|
|
29
40
|
}
|
|
30
41
|
|
|
31
42
|
render(App);
|
|
@@ -36,63 +47,47 @@ describe('for statements', () => {
|
|
|
36
47
|
]);
|
|
37
48
|
});
|
|
38
49
|
|
|
39
|
-
it('
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
skipped.push('skip');
|
|
48
|
-
continue;
|
|
49
|
-
}
|
|
50
|
-
<div class="item">{item}</div>
|
|
51
|
-
}
|
|
52
|
-
</>;
|
|
50
|
+
it('renders an empty fallback', () => {
|
|
51
|
+
function App() @{
|
|
52
|
+
const items = [];
|
|
53
|
+
@for (const item of items) {
|
|
54
|
+
<div class="item">{item}</div>
|
|
55
|
+
} @empty {
|
|
56
|
+
<div class="empty">{'No items'}</div>
|
|
57
|
+
}
|
|
53
58
|
}
|
|
54
59
|
|
|
55
60
|
render(App);
|
|
56
61
|
|
|
57
|
-
expect(
|
|
58
|
-
expect(
|
|
59
|
-
'Item 1',
|
|
60
|
-
'Item 3',
|
|
61
|
-
]);
|
|
62
|
+
expect(container.querySelector('.empty').textContent).toBe('No items');
|
|
63
|
+
expect(container.querySelector('.item')).toBeNull();
|
|
62
64
|
});
|
|
63
65
|
|
|
64
|
-
it('
|
|
65
|
-
|
|
66
|
-
|
|
66
|
+
it('throws for continue statements inside for...of loops', () => {
|
|
67
|
+
expect(
|
|
68
|
+
() => compile(
|
|
69
|
+
`function App() @{
|
|
67
70
|
const items = ['Item 1', '', 'Item 3'];
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
for (const item of items) {
|
|
71
|
-
if (!item) {
|
|
72
|
-
skipped.push('skip');
|
|
73
|
-
continue;
|
|
74
|
-
}
|
|
71
|
+
@for (const item of items) {
|
|
72
|
+
if (!item) continue;
|
|
75
73
|
<div class="item">{item}</div>
|
|
76
74
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
expect(code).toContain('skipped.push(\'skip\')');
|
|
83
|
-
expect(code).not.toContain('continue;');
|
|
84
|
-
expect(code).not.toMatch(/continue;\s*return/);
|
|
75
|
+
}`,
|
|
76
|
+
'App.tsrx',
|
|
77
|
+
{ mode: 'client' },
|
|
78
|
+
),
|
|
79
|
+
).toThrow('Continue statements are not allowed inside TSRX template for...of loops');
|
|
85
80
|
});
|
|
86
81
|
|
|
87
82
|
it('renders a simple dynamic array', () => {
|
|
88
|
-
function App() {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
for (const item of items) {
|
|
83
|
+
function App() @{
|
|
84
|
+
const items = new RippleArray('Item 1', 'Item 2', 'Item 3');
|
|
85
|
+
<>
|
|
86
|
+
@for (const item of items) {
|
|
92
87
|
<div class={item}>{item}</div>
|
|
93
88
|
}
|
|
94
89
|
<button onClick={() => items.push(`Item ${items.length + 1}`)}>{'Add Item'}</button>
|
|
95
|
-
|
|
90
|
+
</>
|
|
96
91
|
}
|
|
97
92
|
|
|
98
93
|
render(App);
|
|
@@ -107,20 +102,20 @@ describe('for statements', () => {
|
|
|
107
102
|
});
|
|
108
103
|
|
|
109
104
|
it('correctly handles intermediate statements in for block', () => {
|
|
110
|
-
function App() {
|
|
111
|
-
|
|
112
|
-
|
|
105
|
+
function App() @{
|
|
106
|
+
const items = new RippleArray(1, 2, 3);
|
|
107
|
+
<>
|
|
113
108
|
<div>
|
|
114
|
-
for (const item of items) {
|
|
109
|
+
@for (const item of items) {
|
|
110
|
+
const some_text = item;
|
|
115
111
|
<div>
|
|
116
112
|
<div>{item}</div>
|
|
117
|
-
const some_text = item;
|
|
118
113
|
<div>{some_text}</div>
|
|
119
114
|
</div>
|
|
120
115
|
}
|
|
121
116
|
</div>
|
|
122
117
|
<button onClick={() => items.push(items.length + 1)}>{'Add Item'}</button>
|
|
123
|
-
|
|
118
|
+
</>
|
|
124
119
|
}
|
|
125
120
|
|
|
126
121
|
render(App);
|
|
@@ -136,19 +131,21 @@ describe('for statements', () => {
|
|
|
136
131
|
});
|
|
137
132
|
|
|
138
133
|
it('correctly handles the index in a for...of loop', () => {
|
|
139
|
-
function App() {
|
|
140
|
-
|
|
141
|
-
|
|
134
|
+
function App() @{
|
|
135
|
+
const items = new RippleArray('a', 'b', 'c');
|
|
136
|
+
<>
|
|
142
137
|
<div>
|
|
143
|
-
for (let item of items; index i) {
|
|
144
|
-
<div>
|
|
138
|
+
@for (let item of items; index i) {
|
|
139
|
+
<div>
|
|
140
|
+
{i + ' : ' + item}
|
|
141
|
+
</div>
|
|
145
142
|
}
|
|
146
143
|
</div>
|
|
147
|
-
<button
|
|
148
|
-
{
|
|
149
|
-
</button>
|
|
144
|
+
<button
|
|
145
|
+
onClick={() => items.push(String.fromCharCode(97 + items.length))}
|
|
146
|
+
>{'Add Item'}</button>
|
|
150
147
|
<button onClick={() => items.reverse()}>{'Reverse'}</button>
|
|
151
|
-
|
|
148
|
+
</>
|
|
152
149
|
}
|
|
153
150
|
|
|
154
151
|
render(App);
|
|
@@ -169,24 +166,24 @@ describe('for statements', () => {
|
|
|
169
166
|
});
|
|
170
167
|
|
|
171
168
|
it('correctly handles keyed for...of loops', () => {
|
|
172
|
-
function App() {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
for (let item of items; index i; key item.id) {
|
|
180
|
-
<div>
|
|
169
|
+
function App() @{
|
|
170
|
+
let &[items] = track([
|
|
171
|
+
{ id: 1, text: 'Item 1' },
|
|
172
|
+
{ id: 2, text: 'Item 2' },
|
|
173
|
+
{ id: 3, text: 'Item 3' },
|
|
174
|
+
]);
|
|
175
|
+
<>
|
|
176
|
+
@for (let item of items; index i; key item.id) {
|
|
177
|
+
<div>
|
|
178
|
+
{i + ':' + item.text}
|
|
179
|
+
</div>
|
|
181
180
|
}
|
|
182
181
|
<button
|
|
183
182
|
onClick={() => {
|
|
184
183
|
items = items.toReversed();
|
|
185
184
|
}}
|
|
186
|
-
>
|
|
187
|
-
|
|
188
|
-
</button>
|
|
189
|
-
</>;
|
|
185
|
+
>{'Reverse'}</button>
|
|
186
|
+
</>
|
|
190
187
|
}
|
|
191
188
|
|
|
192
189
|
render(App);
|
|
@@ -202,24 +199,22 @@ describe('for statements', () => {
|
|
|
202
199
|
});
|
|
203
200
|
|
|
204
201
|
it('keyed for over derived updates sibling text nodes', () => {
|
|
205
|
-
function App() {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
202
|
+
function App() @{
|
|
203
|
+
let &[count] = track(0);
|
|
204
|
+
let &[items] = track(
|
|
205
|
+
() => Array.from({ length: count }).map((_, id) => ({ id, label: `Item ${id}` })),
|
|
206
|
+
);
|
|
207
|
+
<>
|
|
211
208
|
<button
|
|
212
209
|
onClick={() => {
|
|
213
210
|
count++;
|
|
214
211
|
}}
|
|
215
|
-
>
|
|
216
|
-
|
|
217
|
-
</button>
|
|
218
|
-
for (const item of items; key item.id) {
|
|
212
|
+
>{'Add'}</button>
|
|
213
|
+
@for (const item of items; key item.id) {
|
|
219
214
|
<div class="item">{item.label}</div>
|
|
220
215
|
}
|
|
221
216
|
<p class="count">{count}</p>
|
|
222
|
-
|
|
217
|
+
</>
|
|
223
218
|
}
|
|
224
219
|
|
|
225
220
|
render(App);
|
|
@@ -243,22 +238,22 @@ describe('for statements', () => {
|
|
|
243
238
|
});
|
|
244
239
|
|
|
245
240
|
it('keyed for with 32+ items: full reversal updates values via Map path', () => {
|
|
246
|
-
function App() {
|
|
247
|
-
|
|
248
|
-
|
|
241
|
+
function App() @{
|
|
242
|
+
let &[items] = track(Array.from({ length: 40 }, (_, i) => ({ id: i, text: `Item ${i}` })));
|
|
243
|
+
<>
|
|
249
244
|
<div>
|
|
250
|
-
for (let item of items; index idx; key item.id) {
|
|
251
|
-
<span class="item">
|
|
245
|
+
@for (let item of items; index idx; key item.id) {
|
|
246
|
+
<span class="item">
|
|
247
|
+
{idx + ':' + item.text}
|
|
248
|
+
</span>
|
|
252
249
|
}
|
|
253
250
|
</div>
|
|
254
251
|
<button
|
|
255
252
|
onClick={() => {
|
|
256
253
|
items = items.toReversed();
|
|
257
254
|
}}
|
|
258
|
-
>
|
|
259
|
-
|
|
260
|
-
</button>
|
|
261
|
-
</>;
|
|
255
|
+
>{'Reverse'}</button>
|
|
256
|
+
</>
|
|
262
257
|
}
|
|
263
258
|
|
|
264
259
|
render(App);
|
|
@@ -276,15 +271,17 @@ describe('for statements', () => {
|
|
|
276
271
|
});
|
|
277
272
|
|
|
278
273
|
it('handles updating with new objects with same key', () => {
|
|
279
|
-
function App() {
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
for (let item of items; index i; key item.id) {
|
|
287
|
-
<div>
|
|
274
|
+
function App() @{
|
|
275
|
+
let &[items] = track([
|
|
276
|
+
{ id: 1, text: 'Item 1' },
|
|
277
|
+
{ id: 2, text: 'Item 2' },
|
|
278
|
+
{ id: 3, text: 'Item 3' },
|
|
279
|
+
]);
|
|
280
|
+
<>
|
|
281
|
+
@for (let item of items; index i; key item.id) {
|
|
282
|
+
<div>
|
|
283
|
+
{i + ':' + item.text}
|
|
284
|
+
</div>
|
|
288
285
|
}
|
|
289
286
|
<button
|
|
290
287
|
onClick={() => {
|
|
@@ -298,10 +295,8 @@ describe('for statements', () => {
|
|
|
298
295
|
{ ...items[2], text: 'Item 3!' },
|
|
299
296
|
];
|
|
300
297
|
}}
|
|
301
|
-
>
|
|
302
|
-
|
|
303
|
-
</button>
|
|
304
|
-
</>;
|
|
298
|
+
>{'Reverse'}</button>
|
|
299
|
+
</>
|
|
305
300
|
}
|
|
306
301
|
|
|
307
302
|
render(App);
|
|
@@ -317,11 +312,11 @@ describe('for statements', () => {
|
|
|
317
312
|
it('ref-based for with 32+ items: remove from start with shared refs via Map path', () => {
|
|
318
313
|
const objects = Array.from({ length: 50 }, (_, i) => ({ id: i, text: `Obj ${i}` }));
|
|
319
314
|
|
|
320
|
-
function App() {
|
|
321
|
-
|
|
322
|
-
|
|
315
|
+
function App() @{
|
|
316
|
+
let &[items] = track(objects.slice());
|
|
317
|
+
<>
|
|
323
318
|
<div>
|
|
324
|
-
for (const item of items) {
|
|
319
|
+
@for (const item of items) {
|
|
325
320
|
<span class="item">{item.text}</span>
|
|
326
321
|
}
|
|
327
322
|
</div>
|
|
@@ -329,10 +324,8 @@ describe('for statements', () => {
|
|
|
329
324
|
onClick={() => {
|
|
330
325
|
items = objects.slice(15).reverse();
|
|
331
326
|
}}
|
|
332
|
-
>
|
|
333
|
-
|
|
334
|
-
</button>
|
|
335
|
-
</>;
|
|
327
|
+
>{'Trim and reverse'}</button>
|
|
328
|
+
</>
|
|
336
329
|
}
|
|
337
330
|
|
|
338
331
|
render(App);
|
|
@@ -14,13 +14,13 @@ describe('head elements', () => {
|
|
|
14
14
|
});
|
|
15
15
|
|
|
16
16
|
it('renders static title element', () => {
|
|
17
|
-
function App() {
|
|
18
|
-
|
|
17
|
+
function App() @{
|
|
18
|
+
<>
|
|
19
19
|
<head>
|
|
20
20
|
<title>{'Static Test Title'}</title>
|
|
21
21
|
</head>
|
|
22
22
|
<div>{'Content'}</div>
|
|
23
|
-
|
|
23
|
+
</>
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
render(App);
|
|
@@ -30,9 +30,9 @@ describe('head elements', () => {
|
|
|
30
30
|
});
|
|
31
31
|
|
|
32
32
|
it('renders reactive title element', () => {
|
|
33
|
-
function App() {
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
function App() @{
|
|
34
|
+
let &[title] = track('Initial Title');
|
|
35
|
+
<>
|
|
36
36
|
<head>
|
|
37
37
|
<title>{title}</title>
|
|
38
38
|
</head>
|
|
@@ -41,12 +41,10 @@ describe('head elements', () => {
|
|
|
41
41
|
onClick={() => {
|
|
42
42
|
title = 'Updated Title';
|
|
43
43
|
}}
|
|
44
|
-
>
|
|
45
|
-
{'Update Title'}
|
|
46
|
-
</button>
|
|
44
|
+
>{'Update Title'}</button>
|
|
47
45
|
<span>{title}</span>
|
|
48
46
|
</div>
|
|
49
|
-
|
|
47
|
+
</>
|
|
50
48
|
}
|
|
51
49
|
|
|
52
50
|
render(App);
|
|
@@ -63,9 +61,9 @@ describe('head elements', () => {
|
|
|
63
61
|
});
|
|
64
62
|
|
|
65
63
|
it('renders title with template literal', () => {
|
|
66
|
-
function App() {
|
|
67
|
-
|
|
68
|
-
|
|
64
|
+
function App() @{
|
|
65
|
+
let &[name] = track('World');
|
|
66
|
+
<>
|
|
69
67
|
<head>
|
|
70
68
|
<title>{`Hello ${name}!`}</title>
|
|
71
69
|
</head>
|
|
@@ -74,11 +72,9 @@ describe('head elements', () => {
|
|
|
74
72
|
onClick={() => {
|
|
75
73
|
name = 'Ripple';
|
|
76
74
|
}}
|
|
77
|
-
>
|
|
78
|
-
{'Change Name'}
|
|
79
|
-
</button>
|
|
75
|
+
>{'Change Name'}</button>
|
|
80
76
|
</div>
|
|
81
|
-
|
|
77
|
+
</>
|
|
82
78
|
}
|
|
83
79
|
|
|
84
80
|
render(App);
|
|
@@ -93,24 +89,24 @@ describe('head elements', () => {
|
|
|
93
89
|
});
|
|
94
90
|
|
|
95
91
|
it('renders title with computed value', () => {
|
|
96
|
-
function App() {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
92
|
+
function App() @{
|
|
93
|
+
let &[count] = track(0);
|
|
94
|
+
let prefix = 'Count: ';
|
|
95
|
+
<>
|
|
100
96
|
<head>
|
|
101
|
-
<title>
|
|
97
|
+
<title>
|
|
98
|
+
{prefix + count}
|
|
99
|
+
</title>
|
|
102
100
|
</head>
|
|
103
101
|
<div>
|
|
104
102
|
<button
|
|
105
103
|
onClick={() => {
|
|
106
104
|
count++;
|
|
107
105
|
}}
|
|
108
|
-
>
|
|
109
|
-
{'Increment'}
|
|
110
|
-
</button>
|
|
106
|
+
>{'Increment'}</button>
|
|
111
107
|
<span>{count}</span>
|
|
112
108
|
</div>
|
|
113
|
-
|
|
109
|
+
</>
|
|
114
110
|
}
|
|
115
111
|
|
|
116
112
|
render(App);
|
|
@@ -126,9 +122,9 @@ describe('head elements', () => {
|
|
|
126
122
|
});
|
|
127
123
|
|
|
128
124
|
it('handles multiple title updates', () => {
|
|
129
|
-
function App() {
|
|
130
|
-
|
|
131
|
-
|
|
125
|
+
function App() @{
|
|
126
|
+
let &[step] = track(1);
|
|
127
|
+
<>
|
|
132
128
|
<head>
|
|
133
129
|
<title>{`Step ${step} of 3`}</title>
|
|
134
130
|
</head>
|
|
@@ -137,11 +133,9 @@ describe('head elements', () => {
|
|
|
137
133
|
onClick={() => {
|
|
138
134
|
step = step % 3 + 1;
|
|
139
135
|
}}
|
|
140
|
-
>
|
|
141
|
-
{'Next Step'}
|
|
142
|
-
</button>
|
|
136
|
+
>{'Next Step'}</button>
|
|
143
137
|
</div>
|
|
144
|
-
|
|
138
|
+
</>
|
|
145
139
|
}
|
|
146
140
|
|
|
147
141
|
render(App);
|
|
@@ -164,13 +158,13 @@ describe('head elements', () => {
|
|
|
164
158
|
});
|
|
165
159
|
|
|
166
160
|
it('renders empty title', () => {
|
|
167
|
-
function App() {
|
|
168
|
-
|
|
161
|
+
function App() @{
|
|
162
|
+
<>
|
|
169
163
|
<head>
|
|
170
164
|
<title>{''}</title>
|
|
171
165
|
</head>
|
|
172
166
|
<div>{'Empty title test'}</div>
|
|
173
|
-
|
|
167
|
+
</>
|
|
174
168
|
}
|
|
175
169
|
|
|
176
170
|
render(App);
|
|
@@ -178,31 +172,63 @@ describe('head elements', () => {
|
|
|
178
172
|
expect(document.title).toBe('');
|
|
179
173
|
});
|
|
180
174
|
|
|
175
|
+
it('renders external scripts with src attributes from a loop', () => {
|
|
176
|
+
const added: HTMLScriptElement[] = [];
|
|
177
|
+
const scripts = [{ src: '/a.js' }, { src: '/b.js' }];
|
|
178
|
+
|
|
179
|
+
function App() @{
|
|
180
|
+
<head>
|
|
181
|
+
@for (const script of scripts) {
|
|
182
|
+
<script src={script.src} />
|
|
183
|
+
}
|
|
184
|
+
</head>
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
try {
|
|
188
|
+
render(App);
|
|
189
|
+
|
|
190
|
+
const head_scripts = Array.from(
|
|
191
|
+
document.head.querySelectorAll('script[src]'),
|
|
192
|
+
) as HTMLScriptElement[];
|
|
193
|
+
|
|
194
|
+
for (const node of head_scripts) {
|
|
195
|
+
if (node.getAttribute('src') === '/a.js' || node.getAttribute('src') === '/b.js') {
|
|
196
|
+
added.push(node);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const srcs = added.map((node) => node.getAttribute('src')).sort();
|
|
201
|
+
expect(srcs).toEqual(['/a.js', '/b.js']);
|
|
202
|
+
} finally {
|
|
203
|
+
for (const node of added) {
|
|
204
|
+
node.remove();
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
|
|
181
209
|
it('renders title with conditional content', () => {
|
|
182
|
-
function App() {
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
210
|
+
function App() @{
|
|
211
|
+
let &[showPrefix] = track(true);
|
|
212
|
+
let &[title] = track('Main Page');
|
|
213
|
+
<>
|
|
186
214
|
<head>
|
|
187
|
-
<title>
|
|
215
|
+
<title>
|
|
216
|
+
{showPrefix ? 'App - ' + title : title}
|
|
217
|
+
</title>
|
|
188
218
|
</head>
|
|
189
219
|
<div>
|
|
190
220
|
<button
|
|
191
221
|
onClick={() => {
|
|
192
222
|
showPrefix = !showPrefix;
|
|
193
223
|
}}
|
|
194
|
-
>
|
|
195
|
-
{'Toggle Prefix'}
|
|
196
|
-
</button>
|
|
224
|
+
>{'Toggle Prefix'}</button>
|
|
197
225
|
<button
|
|
198
226
|
onClick={() => {
|
|
199
227
|
title = title === 'Main Page' ? 'Settings' : 'Main Page';
|
|
200
228
|
}}
|
|
201
|
-
>
|
|
202
|
-
{'Change Page'}
|
|
203
|
-
</button>
|
|
229
|
+
>{'Change Page'}</button>
|
|
204
230
|
</div>
|
|
205
|
-
|
|
231
|
+
</>
|
|
206
232
|
}
|
|
207
233
|
|
|
208
234
|
render(App);
|