Skip to content

Commit a6c329f

Browse files
authored
fix: object destructring picks up literal properties (#8357)
Part of #6609
1 parent 26c38e7 commit a6c329f

File tree

15 files changed

+314
-5
lines changed

15 files changed

+314
-5
lines changed

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

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -108,20 +108,36 @@ export function unpack_destructuring({
108108
context_rest_properties
109109
});
110110
context_rest_properties.set((property.argument as Identifier).name, property);
111-
} else {
112-
const key = property.key as Identifier;
111+
} else if (property.type === 'Property') {
112+
const key = property.key;
113113
const value = property.value;
114114

115-
used_properties.push(x`"${key.name}"`);
115+
let property_name: any;
116+
let new_modifier: (node: Node) => Node;
117+
118+
if (property.computed) {
119+
// TODO: If the property is computed, ie, { [computed_key]: prop }, the computed_key can be any type of expression.
120+
} else if (key.type === 'Identifier') {
121+
// e.g. { someProperty: ... }
122+
property_name = key.name;
123+
new_modifier = (node) => x`${modifier(node)}.${property_name}`;
124+
} else if (key.type === 'Literal') {
125+
// e.g. { "property-in-quotes": ... } or { 14: ... }
126+
property_name = key.value;
127+
new_modifier = (node) => x`${modifier(node)}["${property_name}"]`;
128+
}
129+
130+
used_properties.push(x`"${property_name}"`);
116131
if (value.type === 'AssignmentPattern') {
132+
// e.g. { property = default } or { property: newName = default }
117133
const n = contexts.length;
118134

119135
mark_referenced(value.right, scope, component);
120136

121137
unpack_destructuring({
122138
contexts,
123139
node: value.left,
124-
modifier: (node) => x`${modifier(node)}.${key.name}`,
140+
modifier: new_modifier,
125141
default_modifier: (node, to_ctx) =>
126142
x`${node} !== undefined ? ${node} : ${update_reference(
127143
contexts,
@@ -134,10 +150,11 @@ export function unpack_destructuring({
134150
context_rest_properties
135151
});
136152
} else {
153+
// e.g. { property } or { property: newName }
137154
unpack_destructuring({
138155
contexts,
139156
node: value,
140-
modifier: (node) => x`${modifier(node)}.${key.name}` as Node,
157+
modifier: new_modifier,
141158
default_modifier,
142159
scope,
143160
component,
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
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([10, 11, 12, 13, 14, 15]));
12+
13+
assert.htmlEqual(
14+
target.innerHTML,
15+
`
16+
<p>[1] 11</p>
17+
<p>[3] 13</p>
18+
<p>[4] 14</p>
19+
`
20+
);
21+
22+
await (component.thePromise = Promise.resolve({ 1: 21, 3: 23, 4: 24 }));
23+
24+
assert.htmlEqual(
25+
target.innerHTML,
26+
`
27+
<p>[1] 21</p>
28+
<p>[3] 23</p>
29+
<p>[4] 24</p>
30+
`
31+
);
32+
33+
try {
34+
await (component.thePromise = Promise.reject([30, 31, 32, 33, 34, 35]));
35+
} catch (e) {
36+
// do nothing
37+
}
38+
39+
assert.htmlEqual(
40+
target.innerHTML,
41+
`
42+
<p>[0] 30</p>
43+
<p>[2] 32</p>
44+
<p>[5] 35</p>
45+
`
46+
);
47+
48+
try {
49+
await (component.thePromise = Promise.reject({ 0: 40, 2: 42, 5: 45 }));
50+
} catch (e) {
51+
// do nothing
52+
}
53+
54+
assert.htmlEqual(
55+
target.innerHTML,
56+
`
57+
<p>[0] 40</p>
58+
<p>[2] 42</p>
59+
<p>[5] 45</p>
60+
`
61+
);
62+
}
63+
};
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<script>
2+
export let thePromise;
3+
</script>
4+
5+
{#await thePromise}
6+
loading...
7+
{:then { 1: a, 3: b, 4: c }}
8+
<p>[1] {a}</p>
9+
<p>[3] {b}</p>
10+
<p>[4] {c}</p>
11+
{:catch { 0: d, 2: e, 5: f }}
12+
<p>[0] {d}</p>
13+
<p>[2] {e}</p>
14+
<p>[5] {f}</p>
15+
{/await}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
export default {
2+
async test({ assert, target }) {
3+
await Promise.resolve();
4+
5+
assert.htmlEqual(
6+
target.innerHTML,
7+
`
8+
<p>prop-1: 1</p>
9+
<p>prop4: 4</p>
10+
<p>rest: {"prop2":2,"prop-3":3}</p>
11+
<p>prop-7: 7</p>
12+
<p>prop6: 6</p>
13+
<p>rest: {"prop-5":5,"prop8":8}</p>
14+
`
15+
);
16+
}
17+
};
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<script>
2+
const object = Promise.resolve({ 'prop-1': 1, 'prop2': 2, 'prop-3': 3, 'prop4': 4 });
3+
const objectReject = Promise.reject({ 'prop-5': 5, 'prop6': 6, 'prop-7': 7, 'prop8': 8 });
4+
</script>
5+
6+
{#await object then { 'prop-1': prop1, 'prop4': fourthProp, ...rest }}
7+
<p>prop-1: {prop1}</p>
8+
<p>prop4: {fourthProp}</p>
9+
<p>rest: {JSON.stringify(rest)}</p>
10+
{/await}
11+
12+
{#await objectReject then value}
13+
resolved
14+
{:catch { 'prop-7': prop7, 'prop6': sixthProp, ...rest }}
15+
<p>prop-7: {prop7}</p>
16+
<p>prop6: {sixthProp}</p>
17+
<p>rest: {JSON.stringify(rest)}</p>
18+
{/await}
19+
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 { 'the-area': width * height, 'the-volume': width * height * constant };
8+
}
9+
</script>
10+
11+
{#await promise1 then { width, height }}
12+
{@const {'the-area': area, 'the-volume': volume} = calculate(width, height, constant)}
13+
{@const perimeter = (width + height) * constant}
14+
{@const { 0: _width, 1: _height, 2: 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 {'the-area': area, 'the-volume': volume} = calculate(width, height, constant)}
20+
{@const perimeter = (width + height) * constant}
21+
{@const { 0: _width, 1: _height, 2: 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 { 'the-area': width * height, 'the-volume': width * height * constant };
11+
}
12+
</script>
13+
14+
{#each boxes as { width, height }}
15+
{@const { 'the-area': area, 'the-volume': volume } = calculate(width, height, constant)}
16+
{@const perimeter = (width + height) * constant}
17+
{@const { 2: sum, 0: _width, 1: _height } = [width * constant, height, width * constant + height]}
18+
<div>{area} {volume} {perimeter}, {_width}+{_height}={sum}</div>
19+
{/each}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
export default {
2+
props: {
3+
array: [
4+
[1, 2, 3, 4, 5],
5+
[6, 7, 8],
6+
[9, 10, 11, 12]
7+
]
8+
},
9+
10+
html: `
11+
<p>First: 1, Third: 3, Length: 5</p>
12+
<p>First: 6, Third: 8, Length: 3</p>
13+
<p>First: 9, Third: 11, Length: 4</p>
14+
`,
15+
16+
test({ assert, component, target }) {
17+
component.array = [[12, 13]];
18+
assert.htmlEqual( target.innerHTML, `
19+
<p>First: 12, Third: undefined, Length: 2</p>
20+
`);
21+
}
22+
};
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<script>
2+
export let array;
3+
</script>
4+
5+
{#each array as { 0: first, '2': third, "length": length }}
6+
<p>First: {first}, Third: {third}, Length: {length}</p>
7+
{/each}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
export default {
2+
props: {
3+
objectsArray: [
4+
{ 'foo-bar': 'FooBar', 0: 'zero', prop: 'prop' },
5+
{ 'foo-bar': 'foobar', 0: 'null', prop: 'a prop' },
6+
{ 'foo-bar': 'FOO BAR', 0: 'nada', prop: 'the prop' }
7+
]
8+
},
9+
10+
html: `
11+
<p>FooBar: prop zero</p>
12+
<p>foobar: a prop null</p>
13+
<p>FOO BAR: the prop nada</p>
14+
`,
15+
16+
test({ assert, component, target }) {
17+
component.objectsArray = [{ 'foo-bar': 'Fool Ball', 0: 'nil', prop: 'one prop' }];
18+
assert.htmlEqual( target.innerHTML, `
19+
<p>Fool Ball: one prop nil</p>
20+
`);
21+
}
22+
};
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<script>
2+
export let objectsArray;
3+
</script>
4+
5+
{#each objectsArray as { 0: prop0, "foo-bar": propFooBar, prop: varProp } }
6+
<p>{propFooBar}: {varProp} {prop0}</p>
7+
{/each}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
export default {
2+
props: {
3+
objectsArray: [
4+
{ quote: 'q1', 'wrong-quote': 'wq1', 16: '16', class: 'class' },
5+
{ quote: 'q2', 'wrong-quote': 'wq2', 16: 'sixteen', class: 'glass' },
6+
{ quote: 'q3', 'wrong-quote': 'wq3', 16: 'seize', class: 'mass' }
7+
]
8+
},
9+
10+
html: `
11+
<p class="class">Quote: q1, Wrong Quote: wq1, 16: 16</p>
12+
<p class="glass">Quote: q2, Wrong Quote: wq2, 16: sixteen</p>
13+
<p class="mass">Quote: q3, Wrong Quote: wq3, 16: seize</p>
14+
`,
15+
16+
test({ assert, component, target }) {
17+
component.objectsArray = [{ quote: 'new-quote', 'wrong-quote': 'wq4', 16: 'ten+six', role: 'role' }];
18+
assert.htmlEqual(target.innerHTML, `
19+
<p role="role">Quote: new-quote, Wrong Quote: wq4, 16: ten+six</p>
20+
`);
21+
}
22+
};
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<script>
2+
export let objectsArray;
3+
</script>
4+
5+
{#each objectsArray as { "quote": quotedProp, "wrong-quote": wrongQuote, 16: sixteen, ...props } }
6+
<p {...props}>Quote: {quotedProp}, Wrong Quote: {wrongQuote}, 16: {sixteen}</p>
7+
{/each}

0 commit comments

Comments
 (0)