Skip to content

Commit 83679e9

Browse files
ngtr6788dummdidumm
andauthored
fix: array rest destructuring in markup (#8555)
Fixes #8554 --------- Co-authored-by: Simon H <[email protected]>
1 parent 17bf6db commit 83679e9

File tree

15 files changed

+281
-31
lines changed

15 files changed

+281
-31
lines changed

src/compiler/compile/compiler_warnings.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ export default {
220220
},
221221
invalid_rest_eachblock_binding: (rest_element_name: string) => ({
222222
code: 'invalid-rest-eachblock-binding',
223-
message: `...${rest_element_name} operator will create a new object and binding propagation with original object will not work`
223+
message: `The rest operator (...) will create a new object and binding '${rest_element_name}' with the original object will not work`
224224
}),
225225
avoid_mouse_events_on_document: {
226226
code: 'avoid-mouse-events-on-document',

src/compiler/compile/nodes/shared/Context.ts

Lines changed: 27 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { x } from 'code-red';
2-
import { Node, Identifier, Expression, PrivateIdentifier } from 'estree';
2+
import { Node, Identifier, Expression, PrivateIdentifier, Pattern } from 'estree';
33
import { walk } from 'estree-walker';
44
import is_reference, { NodeWithPropertyDefinition } from 'is-reference';
55
import { clone } from '../../../utils/clone';
@@ -30,15 +30,17 @@ export function unpack_destructuring({
3030
default_modifier = (node) => node,
3131
scope,
3232
component,
33-
context_rest_properties
33+
context_rest_properties,
34+
in_rest_element = false
3435
}: {
3536
contexts: Context[];
36-
node: Node;
37+
node: Pattern;
3738
modifier?: DestructuredVariable['modifier'];
3839
default_modifier?: DestructuredVariable['default_modifier'];
3940
scope: TemplateScope;
4041
component: Component;
4142
context_rest_properties: Map<string, Node>;
43+
in_rest_element?: boolean;
4244
}) {
4345
if (!node) return;
4446

@@ -49,28 +51,26 @@ export function unpack_destructuring({
4951
modifier,
5052
default_modifier
5153
});
52-
} else if (node.type === 'RestElement') {
53-
contexts.push({
54-
type: 'DestructuredVariable',
55-
key: node.argument as Identifier,
56-
modifier,
57-
default_modifier
58-
});
59-
context_rest_properties.set((node.argument as Identifier).name, node);
54+
55+
if (in_rest_element) {
56+
context_rest_properties.set(node.name, node);
57+
}
6058
} else if (node.type === 'ArrayPattern') {
61-
node.elements.forEach((element, i) => {
62-
if (element && element.type === 'RestElement') {
59+
node.elements.forEach((element: Pattern | null, i: number) => {
60+
if (!element) {
61+
return;
62+
} else if (element.type === 'RestElement') {
6363
unpack_destructuring({
6464
contexts,
65-
node: element,
65+
node: element.argument,
6666
modifier: (node) => x`${modifier(node)}.slice(${i})` as Node,
6767
default_modifier,
6868
scope,
6969
component,
70-
context_rest_properties
70+
context_rest_properties,
71+
in_rest_element: true
7172
});
72-
context_rest_properties.set((element.argument as Identifier).name, element);
73-
} else if (element && element.type === 'AssignmentPattern') {
73+
} else if (element.type === 'AssignmentPattern') {
7474
const n = contexts.length;
7575
mark_referenced(element.right, scope, component);
7676

@@ -87,7 +87,8 @@ export function unpack_destructuring({
8787
)}` as Node,
8888
scope,
8989
component,
90-
context_rest_properties
90+
context_rest_properties,
91+
in_rest_element
9192
});
9293
} else {
9394
unpack_destructuring({
@@ -97,7 +98,8 @@ export function unpack_destructuring({
9798
default_modifier,
9899
scope,
99100
component,
100-
context_rest_properties
101+
context_rest_properties,
102+
in_rest_element
101103
});
102104
}
103105
});
@@ -116,9 +118,9 @@ export function unpack_destructuring({
116118
default_modifier,
117119
scope,
118120
component,
119-
context_rest_properties
121+
context_rest_properties,
122+
in_rest_element: true
120123
});
121-
context_rest_properties.set((property.argument as Identifier).name, property);
122124
} else if (property.type === 'Property') {
123125
const key = property.key;
124126
const value = property.value;
@@ -168,7 +170,8 @@ export function unpack_destructuring({
168170
)}` as Node,
169171
scope,
170172
component,
171-
context_rest_properties
173+
context_rest_properties,
174+
in_rest_element
172175
});
173176
} else {
174177
// e.g. { property } or { property: newName }
@@ -179,7 +182,8 @@ export function unpack_destructuring({
179182
default_modifier,
180183
scope,
181184
component,
182-
context_rest_properties
185+
context_rest_properties,
186+
in_rest_element
183187
});
184188
}
185189
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
export default {
2+
props: {
3+
thePromise: new Promise(_ => {})
4+
},
5+
6+
html: `
7+
loading...
8+
`,
9+
10+
async test({ assert, component, target }) {
11+
await (component.thePromise = Promise.resolve([1, 2, 3, 4, 5, 6, 7, 8]));
12+
13+
assert.htmlEqual(
14+
target.innerHTML,
15+
`
16+
<p>a: 1</p>
17+
<p>b: 2</p>
18+
<p>c: 5</p>
19+
<p>remaining length: 3</p>
20+
`
21+
);
22+
23+
await (component.thePromise = Promise.resolve([9, 10, 11, 12, 13, 14, 15]));
24+
25+
assert.htmlEqual(
26+
target.innerHTML,
27+
`
28+
<p>a: 9</p>
29+
<p>b: 10</p>
30+
<p>c: 13</p>
31+
<p>remaining length: 2</p>
32+
`
33+
);
34+
35+
try {
36+
await (component.thePromise = Promise.reject([16, 17, 18, 19, 20, 21, 22]));
37+
} catch (e) {
38+
// do nothing
39+
}
40+
41+
assert.htmlEqual(
42+
target.innerHTML,
43+
`
44+
<p>c: 16</p>
45+
<p>d: 17</p>
46+
<p>e: 18</p>
47+
<p>f: 19</p>
48+
<p>g: 22</p>
49+
`
50+
);
51+
52+
try {
53+
await (component.thePromise = Promise.reject([23, 24, 25, 26, 27, 28, 29, 30, 31]));
54+
} catch (e) {
55+
// do nothing
56+
}
57+
58+
assert.htmlEqual(
59+
target.innerHTML,
60+
`
61+
<p>c: 23</p>
62+
<p>d: 24</p>
63+
<p>e: 25</p>
64+
<p>f: 26</p>
65+
<p>g: 29</p>
66+
`
67+
);
68+
}
69+
};
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<script>
2+
export let thePromise;
3+
</script>
4+
5+
{#await thePromise}
6+
loading...
7+
{:then [ a, b, ...[,, c, ...{ length } ]]}
8+
<p>a: {a}</p>
9+
<p>b: {b}</p>
10+
<p>c: {c}</p>
11+
<p>remaining length: {length}</p>
12+
{:catch [c, ...[d, e, f, ...[,,g]]]}
13+
<p>c: {c}</p>
14+
<p>d: {d}</p>
15+
<p>e: {e}</p>
16+
<p>f: {f}</p>
17+
<p>g: {g}</p>
18+
{/await}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
export default {
2+
html: '<div>12 120 70, 30+4=34</div>',
3+
async test({ component, target, assert }) {
4+
component.promise1 = Promise.resolve({width: 5, height: 6});
5+
component.promise2 = Promise.reject({width: 6, height: 7});
6+
7+
await Promise.resolve();
8+
assert.htmlEqual(target.innerHTML, `
9+
<div>30 300 110, 50+6=56</div>
10+
<div>42 420 130, 60+7=67</div>
11+
`);
12+
13+
component.constant = 20;
14+
assert.htmlEqual(target.innerHTML, `
15+
<div>30 600 220, 100+6=106</div>
16+
<div>42 840 260, 120+7=127</div>
17+
`);
18+
}
19+
};
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<script>
2+
export let promise1 = {width: 3, height: 4};
3+
export let promise2 = {width: 5, height: 7};
4+
export let constant = 10;
5+
6+
function calculate(width, height, constant) {
7+
return { area: width * height, volume: width * height * constant };
8+
}
9+
</script>
10+
11+
{#await promise1 then { width, height }}
12+
{@const {area, volume} = calculate(width, height, constant)}
13+
{@const perimeter = (width + height) * constant}
14+
{@const [_width, ...[_height, ...[sum]]] = [width * constant, height, width * constant + height]}
15+
<div>{area} {volume} {perimeter}, {_width}+{_height}={sum}</div>
16+
{/await}
17+
18+
{#await promise2 catch { width, height }}
19+
{@const {area, volume} = calculate(width, height, constant)}
20+
{@const perimeter = (width + height) * constant}
21+
{@const [_width, ...[_height, ...[sum]]] = [width * constant, height, width * constant + height]}
22+
<div>{area} {volume} {perimeter}, {_width}+{_height}={sum}</div>
23+
{/await}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
export default {
2+
html: `
3+
<div>12 120 70, 30+4=34</div>
4+
<div>35 350 120, 50+7=57</div>
5+
<div>48 480 140, 60+8=68</div>
6+
`,
7+
async test({ component, target, assert }) {
8+
component.constant = 20;
9+
10+
assert.htmlEqual(target.innerHTML, `
11+
<div>12 240 140, 60+4=64</div>
12+
<div>35 700 240, 100+7=107</div>
13+
<div>48 960 280, 120+8=128</div>
14+
`);
15+
16+
component.boxes = [
17+
{width: 3, height: 4},
18+
{width: 4, height: 5},
19+
{width: 5, height: 6},
20+
{width: 6, height: 7}
21+
];
22+
23+
assert.htmlEqual(target.innerHTML, `
24+
<div>12 240 140, 60+4=64</div>
25+
<div>20 400 180, 80+5=85</div>
26+
<div>30 600 220, 100+6=106</div>
27+
<div>42 840 260, 120+7=127</div>
28+
`);
29+
}
30+
};
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<script>
2+
export let boxes = [
3+
{width: 3, height: 4},
4+
{width: 5, height: 7},
5+
{width: 6, height: 8},
6+
];
7+
export let constant = 10;
8+
9+
function calculate(width, height, constant) {
10+
return { area: width * height, volume: width * height * constant };
11+
}
12+
</script>
13+
14+
{#each boxes as { width, height }}
15+
{@const {area, volume} = calculate(width, height, constant)}
16+
{@const perimeter = (width + height) * constant}
17+
{@const [_width, ...[_height, ...[sum]]] = [width * constant, height, width * constant + height]}
18+
<div>{area} {volume} {perimeter}, {_width}+{_height}={sum}</div>
19+
{/each}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
export default {
2+
props: {
3+
array: [
4+
[1, 2, 3, 4, 5],
5+
[6, 7, 8],
6+
[9, 10, 11, 12],
7+
[13, 14, 15, 16, 17, 18, 19, 20, 21, 22]
8+
]
9+
},
10+
11+
html: `
12+
<p>First: 1, Second: 2, Third: 3, Elements remaining: 2</p>
13+
<p>First: 6, Second: 7, Third: 8, Elements remaining: 0</p>
14+
<p>First: 9, Second: 10, Third: 11, Elements remaining: 1</p>
15+
<p>First: 13, Second: 14, Third: 15, Elements remaining: 7</p>
16+
`,
17+
18+
test({ assert, component, target }) {
19+
component.array = [[23, 24, 25, 26, 27, 28, 29]];
20+
assert.htmlEqual( target.innerHTML, `
21+
<p>First: 23, Second: 24, Third: 25, Elements remaining: 4</p>
22+
`);
23+
}
24+
};
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<script>
2+
export let array;
3+
</script>
4+
5+
{#each array as [first, second, ...[third, ...{ length }]]}
6+
<p>
7+
First: {first}, Second: {second}, Third: {third}, Elements remaining: {length}
8+
</p>
9+
{/each}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
[
22
{
33
"code": "invalid-rest-eachblock-binding",
4-
"message": "...rest operator will create a new object and binding propagation with original object will not work",
5-
"start": { "line": 8, "column": 24 },
4+
"message": "The rest operator (...) will create a new object and binding 'rest' with the original object will not work",
5+
"start": { "line": 8, "column": 27 },
66
"end": { "line": 8, "column": 31 }
77
}
88
]
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
[
22
{
33
"code": "invalid-rest-eachblock-binding",
4-
"message": "...rest operator will create a new object and binding propagation with original object will not work",
5-
"start": { "line": 5, "column": 32 },
6-
"end": { "line": 5, "column": 39 }
4+
"message": "The rest operator (...) will create a new object and binding 'rest' with the original object will not work",
5+
"start": { "line": 5, "column": 35 },
6+
"end": { "line": 5, "column": 39 }
77
}
88
]
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<script>
2+
const a = [[1, 2, 3, 4, 5]];
3+
</script>
4+
5+
{#each a as [first, second, ...[third, ...{ length }]]}
6+
<p>{first}, {second}, {length}</p>
7+
<input bind:value={third} />
8+
<input bind:value={length} />
9+
{/each}

0 commit comments

Comments
 (0)