) => {
if (element) {
if (element.type === 'Identifier') {
array[index] = get_new_name(element);
@@ -1110,7 +1110,11 @@ export default class Component {
}
case 'RestElement':
- param.argument = get_new_name(param.argument);
+ if (param.argument.type === 'Identifier') {
+ param.argument = get_new_name(param.argument);
+ } else {
+ rename_identifiers(param.argument);
+ }
break;
case 'AssignmentPattern':
diff --git a/test/runtime/samples/array-rest-is-array-or-object/_config.js b/test/runtime/samples/array-rest-is-array-or-object/_config.js
new file mode 100644
index 0000000000..c971e109ce
--- /dev/null
+++ b/test/runtime/samples/array-rest-is-array-or-object/_config.js
@@ -0,0 +1,12 @@
+export default {
+ html: `
+ 1
+ 2
+ 3
+ 5
+ 10
+ 20
+ 30
+ 6
+ `
+};
diff --git a/test/runtime/samples/array-rest-is-array-or-object/main.svelte b/test/runtime/samples/array-rest-is-array-or-object/main.svelte
new file mode 100644
index 0000000000..fb3a5b7d85
--- /dev/null
+++ b/test/runtime/samples/array-rest-is-array-or-object/main.svelte
@@ -0,0 +1,15 @@
+
+
+{first}
+{second}
+{third}
+{fifth}
+
+{one}
+{two}
+{three}
+{length}
+
diff --git a/test/runtime/samples/destructured-props-4/A.svelte b/test/runtime/samples/destructured-props-4/A.svelte
new file mode 100644
index 0000000000..ab5e6b7689
--- /dev/null
+++ b/test/runtime/samples/destructured-props-4/A.svelte
@@ -0,0 +1,25 @@
+
+
+
+a: {a},
+b: {typeof b},
+c: {c},
+d_one: {d_one},
+d_three: {$d_three},
+length: {length},
+f: {f},
+g: {g},
+e: {typeof e},
+e_one: {e_one},
+A: {A},
+C: {C}
+
+{JSON.stringify(THING)}
diff --git a/test/runtime/samples/destructured-props-4/_config.js b/test/runtime/samples/destructured-props-4/_config.js
new file mode 100644
index 0000000000..2c48ac5b3a
--- /dev/null
+++ b/test/runtime/samples/destructured-props-4/_config.js
@@ -0,0 +1,9 @@
+export default {
+ html: `
+ a: 1, b: undefined, c: 2, d_one: 3, d_three: 5, length: 2, f: undefined, g: 9, e: undefined, e_one: 6, A: 1, C: 2
+ {"a":1,"b":{"c":2,"d":[3,4,{},6,7]},"e":[6],"h":8}
+
+ a: a, b: undefined, c: 2, d_one: d_one, d_three: 5, length: 7, f: f, g: g, e: undefined, e_one: 6, A: 1, C: 2
+ {"a":1,"b":{"c":2,"d":[3,4,{},6,7]},"e":[6],"h":8}
+ `
+};
diff --git a/test/runtime/samples/destructured-props-4/main.svelte b/test/runtime/samples/destructured-props-4/main.svelte
new file mode 100644
index 0000000000..cc1a31f542
--- /dev/null
+++ b/test/runtime/samples/destructured-props-4/main.svelte
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/test/runtime/samples/destructured-props-5/A.svelte b/test/runtime/samples/destructured-props-5/A.svelte
new file mode 100644
index 0000000000..898ce5aa3d
--- /dev/null
+++ b/test/runtime/samples/destructured-props-5/A.svelte
@@ -0,0 +1,23 @@
+
+
+
+ x: {x}, list_two_a: {list_two_a}, list_two_b: {list_two_b}, y: {y}, l: {l}, m: {m},
+ n: {n}, o: {o}, p: {p}, q: {$q}, r: {$r}, s: {s}
+
+{JSON.stringify(LIST)}
diff --git a/test/runtime/samples/destructured-props-5/_config.js b/test/runtime/samples/destructured-props-5/_config.js
new file mode 100644
index 0000000000..6c8ca89216
--- /dev/null
+++ b/test/runtime/samples/destructured-props-5/_config.js
@@ -0,0 +1,19 @@
+export default {
+ html: `
+ x: 1, list_two_a: 4, list_two_b: 5, y: 3, l: 1, m: 2, n: 4, o: 5, p: 5, q: 6, r: 7, s: 1
+ [1,2,3,{"a":4},[5,{},{},8]]
+ x: 1, list_two_a: 4, list_two_b: 5, y: 3, l: l, m: m, n: n, o: o, p: p, q: q, r: r, s: s
+ [1,2,3,{"a":4},[5,{},{},8]]
+ `,
+
+ async test({ component, assert, target }) {
+ await component.update();
+
+ assert.htmlEqual(target.innerHTML, `
+ x: 1, list_two_a: 4, list_two_b: 5, y: 3, l: 1, m: 2, n: 4, o: 5, p: 5, q: 6, r: 7, s: 1
+ [1,2,3,{"a":4},[5,{},{},8]]
+ x: 1, list_two_a: 4, list_two_b: 5, y: 3, l: LL, m: MM, n: NN, o: OO, p: PP, q: QQ, r: RR, s: SS
+ [1,2,3,{"a":4},[5,{},{},8]]
+ `);
+ }
+};
diff --git a/test/runtime/samples/destructured-props-5/main.svelte b/test/runtime/samples/destructured-props-5/main.svelte
new file mode 100644
index 0000000000..a8b9e8a704
--- /dev/null
+++ b/test/runtime/samples/destructured-props-5/main.svelte
@@ -0,0 +1,36 @@
+
+
+
+
+
From 83679e97001bf5d0d74f85b62cb69d083ae01d34 Mon Sep 17 00:00:00 2001
From: Nguyen Tran <88808276+ngtr6788@users.noreply.github.com>
Date: Thu, 4 May 2023 07:59:33 -0400
Subject: [PATCH 7/7] fix: array rest destructuring in markup (#8555)
Fixes #8554
---------
Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>
---
src/compiler/compile/compiler_warnings.ts | 2 +-
src/compiler/compile/nodes/shared/Context.ts | 50 +++++++-------
.../_config.js | 69 +++++++++++++++++++
.../main.svelte | 18 +++++
.../_config.js | 19 +++++
.../main.svelte | 23 +++++++
.../_config.js | 30 ++++++++
.../main.svelte | 19 +++++
.../_config.js | 24 +++++++
.../main.svelte | 9 +++
.../rest-eachblock-binding-2/warnings.json | 4 +-
.../rest-eachblock-binding-3/warnings.json | 6 +-
.../input.svelte | 9 +++
.../warnings.json | 26 +++++++
.../rest-eachblock-binding/warnings.json | 4 +-
15 files changed, 281 insertions(+), 31 deletions(-)
create mode 100644 test/runtime/samples/await-then-destruct-array-nested-rest/_config.js
create mode 100644 test/runtime/samples/await-then-destruct-array-nested-rest/main.svelte
create mode 100644 test/runtime/samples/const-tag-await-then-destructuring-nested-rest/_config.js
create mode 100644 test/runtime/samples/const-tag-await-then-destructuring-nested-rest/main.svelte
create mode 100644 test/runtime/samples/const-tag-each-destructure-nested-rest/_config.js
create mode 100644 test/runtime/samples/const-tag-each-destructure-nested-rest/main.svelte
create mode 100644 test/runtime/samples/each-block-destructured-array-nested-rest/_config.js
create mode 100644 test/runtime/samples/each-block-destructured-array-nested-rest/main.svelte
create mode 100644 test/validator/samples/rest-eachblock-binding-nested-rest/input.svelte
create mode 100644 test/validator/samples/rest-eachblock-binding-nested-rest/warnings.json
diff --git a/src/compiler/compile/compiler_warnings.ts b/src/compiler/compile/compiler_warnings.ts
index a851bc24c2..d778ca1dbc 100644
--- a/src/compiler/compile/compiler_warnings.ts
+++ b/src/compiler/compile/compiler_warnings.ts
@@ -220,7 +220,7 @@ export default {
},
invalid_rest_eachblock_binding: (rest_element_name: string) => ({
code: 'invalid-rest-eachblock-binding',
- message: `...${rest_element_name} operator will create a new object and binding propagation with original object will not work`
+ message: `The rest operator (...) will create a new object and binding '${rest_element_name}' with the original object will not work`
}),
avoid_mouse_events_on_document: {
code: 'avoid-mouse-events-on-document',
diff --git a/src/compiler/compile/nodes/shared/Context.ts b/src/compiler/compile/nodes/shared/Context.ts
index 76ac895681..43d4d1f626 100644
--- a/src/compiler/compile/nodes/shared/Context.ts
+++ b/src/compiler/compile/nodes/shared/Context.ts
@@ -1,5 +1,5 @@
import { x } from 'code-red';
-import { Node, Identifier, Expression, PrivateIdentifier } from 'estree';
+import { Node, Identifier, Expression, PrivateIdentifier, Pattern } from 'estree';
import { walk } from 'estree-walker';
import is_reference, { NodeWithPropertyDefinition } from 'is-reference';
import { clone } from '../../../utils/clone';
@@ -30,15 +30,17 @@ export function unpack_destructuring({
default_modifier = (node) => node,
scope,
component,
- context_rest_properties
+ context_rest_properties,
+ in_rest_element = false
}: {
contexts: Context[];
- node: Node;
+ node: Pattern;
modifier?: DestructuredVariable['modifier'];
default_modifier?: DestructuredVariable['default_modifier'];
scope: TemplateScope;
component: Component;
context_rest_properties: Map;
+ in_rest_element?: boolean;
}) {
if (!node) return;
@@ -49,28 +51,26 @@ export function unpack_destructuring({
modifier,
default_modifier
});
- } else if (node.type === 'RestElement') {
- contexts.push({
- type: 'DestructuredVariable',
- key: node.argument as Identifier,
- modifier,
- default_modifier
- });
- context_rest_properties.set((node.argument as Identifier).name, node);
+
+ if (in_rest_element) {
+ context_rest_properties.set(node.name, node);
+ }
} else if (node.type === 'ArrayPattern') {
- node.elements.forEach((element, i) => {
- if (element && element.type === 'RestElement') {
+ node.elements.forEach((element: Pattern | null, i: number) => {
+ if (!element) {
+ return;
+ } else if (element.type === 'RestElement') {
unpack_destructuring({
contexts,
- node: element,
+ node: element.argument,
modifier: (node) => x`${modifier(node)}.slice(${i})` as Node,
default_modifier,
scope,
component,
- context_rest_properties
+ context_rest_properties,
+ in_rest_element: true
});
- context_rest_properties.set((element.argument as Identifier).name, element);
- } else if (element && element.type === 'AssignmentPattern') {
+ } else if (element.type === 'AssignmentPattern') {
const n = contexts.length;
mark_referenced(element.right, scope, component);
@@ -87,7 +87,8 @@ export function unpack_destructuring({
)}` as Node,
scope,
component,
- context_rest_properties
+ context_rest_properties,
+ in_rest_element
});
} else {
unpack_destructuring({
@@ -97,7 +98,8 @@ export function unpack_destructuring({
default_modifier,
scope,
component,
- context_rest_properties
+ context_rest_properties,
+ in_rest_element
});
}
});
@@ -116,9 +118,9 @@ export function unpack_destructuring({
default_modifier,
scope,
component,
- context_rest_properties
+ context_rest_properties,
+ in_rest_element: true
});
- context_rest_properties.set((property.argument as Identifier).name, property);
} else if (property.type === 'Property') {
const key = property.key;
const value = property.value;
@@ -168,7 +170,8 @@ export function unpack_destructuring({
)}` as Node,
scope,
component,
- context_rest_properties
+ context_rest_properties,
+ in_rest_element
});
} else {
// e.g. { property } or { property: newName }
@@ -179,7 +182,8 @@ export function unpack_destructuring({
default_modifier,
scope,
component,
- context_rest_properties
+ context_rest_properties,
+ in_rest_element
});
}
}
diff --git a/test/runtime/samples/await-then-destruct-array-nested-rest/_config.js b/test/runtime/samples/await-then-destruct-array-nested-rest/_config.js
new file mode 100644
index 0000000000..9a287e35e2
--- /dev/null
+++ b/test/runtime/samples/await-then-destruct-array-nested-rest/_config.js
@@ -0,0 +1,69 @@
+export default {
+ props: {
+ thePromise: new Promise(_ => {})
+ },
+
+ html: `
+ loading...
+ `,
+
+ async test({ assert, component, target }) {
+ await (component.thePromise = Promise.resolve([1, 2, 3, 4, 5, 6, 7, 8]));
+
+ assert.htmlEqual(
+ target.innerHTML,
+ `
+ a: 1
+ b: 2
+ c: 5
+ remaining length: 3
+ `
+ );
+
+ await (component.thePromise = Promise.resolve([9, 10, 11, 12, 13, 14, 15]));
+
+ assert.htmlEqual(
+ target.innerHTML,
+ `
+ a: 9
+ b: 10
+ c: 13
+ remaining length: 2
+ `
+ );
+
+ try {
+ await (component.thePromise = Promise.reject([16, 17, 18, 19, 20, 21, 22]));
+ } catch (e) {
+ // do nothing
+ }
+
+ assert.htmlEqual(
+ target.innerHTML,
+ `
+ c: 16
+ d: 17
+ e: 18
+ f: 19
+ g: 22
+ `
+ );
+
+ try {
+ await (component.thePromise = Promise.reject([23, 24, 25, 26, 27, 28, 29, 30, 31]));
+ } catch (e) {
+ // do nothing
+ }
+
+ assert.htmlEqual(
+ target.innerHTML,
+ `
+ c: 23
+ d: 24
+ e: 25
+ f: 26
+ g: 29
+ `
+ );
+ }
+};
diff --git a/test/runtime/samples/await-then-destruct-array-nested-rest/main.svelte b/test/runtime/samples/await-then-destruct-array-nested-rest/main.svelte
new file mode 100644
index 0000000000..4bb8ad0077
--- /dev/null
+++ b/test/runtime/samples/await-then-destruct-array-nested-rest/main.svelte
@@ -0,0 +1,18 @@
+
+
+{#await thePromise}
+ loading...
+{:then [ a, b, ...[,, c, ...{ length } ]]}
+ a: {a}
+ b: {b}
+ c: {c}
+ remaining length: {length}
+{:catch [c, ...[d, e, f, ...[,,g]]]}
+ c: {c}
+ d: {d}
+ e: {e}
+ f: {f}
+ g: {g}
+{/await}
diff --git a/test/runtime/samples/const-tag-await-then-destructuring-nested-rest/_config.js b/test/runtime/samples/const-tag-await-then-destructuring-nested-rest/_config.js
new file mode 100644
index 0000000000..cb40e7c456
--- /dev/null
+++ b/test/runtime/samples/const-tag-await-then-destructuring-nested-rest/_config.js
@@ -0,0 +1,19 @@
+export default {
+ html: '12 120 70, 30+4=34
',
+ async test({ component, target, assert }) {
+ component.promise1 = Promise.resolve({width: 5, height: 6});
+ component.promise2 = Promise.reject({width: 6, height: 7});
+
+ await Promise.resolve();
+ assert.htmlEqual(target.innerHTML, `
+ 30 300 110, 50+6=56
+ 42 420 130, 60+7=67
+ `);
+
+ component.constant = 20;
+ assert.htmlEqual(target.innerHTML, `
+ 30 600 220, 100+6=106
+ 42 840 260, 120+7=127
+ `);
+ }
+};
diff --git a/test/runtime/samples/const-tag-await-then-destructuring-nested-rest/main.svelte b/test/runtime/samples/const-tag-await-then-destructuring-nested-rest/main.svelte
new file mode 100644
index 0000000000..7af5c989e4
--- /dev/null
+++ b/test/runtime/samples/const-tag-await-then-destructuring-nested-rest/main.svelte
@@ -0,0 +1,23 @@
+
+
+{#await promise1 then { width, height }}
+ {@const {area, volume} = calculate(width, height, constant)}
+ {@const perimeter = (width + height) * constant}
+ {@const [_width, ...[_height, ...[sum]]] = [width * constant, height, width * constant + height]}
+ {area} {volume} {perimeter}, {_width}+{_height}={sum}
+{/await}
+
+{#await promise2 catch { width, height }}
+ {@const {area, volume} = calculate(width, height, constant)}
+ {@const perimeter = (width + height) * constant}
+ {@const [_width, ...[_height, ...[sum]]] = [width * constant, height, width * constant + height]}
+ {area} {volume} {perimeter}, {_width}+{_height}={sum}
+{/await}
diff --git a/test/runtime/samples/const-tag-each-destructure-nested-rest/_config.js b/test/runtime/samples/const-tag-each-destructure-nested-rest/_config.js
new file mode 100644
index 0000000000..00f8b31540
--- /dev/null
+++ b/test/runtime/samples/const-tag-each-destructure-nested-rest/_config.js
@@ -0,0 +1,30 @@
+export default {
+ html: `
+ 12 120 70, 30+4=34
+ 35 350 120, 50+7=57
+ 48 480 140, 60+8=68
+ `,
+ async test({ component, target, assert }) {
+ component.constant = 20;
+
+ assert.htmlEqual(target.innerHTML, `
+ 12 240 140, 60+4=64
+ 35 700 240, 100+7=107
+ 48 960 280, 120+8=128
+ `);
+
+ component.boxes = [
+ {width: 3, height: 4},
+ {width: 4, height: 5},
+ {width: 5, height: 6},
+ {width: 6, height: 7}
+ ];
+
+ assert.htmlEqual(target.innerHTML, `
+ 12 240 140, 60+4=64
+ 20 400 180, 80+5=85
+ 30 600 220, 100+6=106
+ 42 840 260, 120+7=127
+ `);
+ }
+};
diff --git a/test/runtime/samples/const-tag-each-destructure-nested-rest/main.svelte b/test/runtime/samples/const-tag-each-destructure-nested-rest/main.svelte
new file mode 100644
index 0000000000..4361314b19
--- /dev/null
+++ b/test/runtime/samples/const-tag-each-destructure-nested-rest/main.svelte
@@ -0,0 +1,19 @@
+
+
+{#each boxes as { width, height }}
+ {@const {area, volume} = calculate(width, height, constant)}
+ {@const perimeter = (width + height) * constant}
+ {@const [_width, ...[_height, ...[sum]]] = [width * constant, height, width * constant + height]}
+ {area} {volume} {perimeter}, {_width}+{_height}={sum}
+{/each}
diff --git a/test/runtime/samples/each-block-destructured-array-nested-rest/_config.js b/test/runtime/samples/each-block-destructured-array-nested-rest/_config.js
new file mode 100644
index 0000000000..5922925956
--- /dev/null
+++ b/test/runtime/samples/each-block-destructured-array-nested-rest/_config.js
@@ -0,0 +1,24 @@
+export default {
+ props: {
+ array: [
+ [1, 2, 3, 4, 5],
+ [6, 7, 8],
+ [9, 10, 11, 12],
+ [13, 14, 15, 16, 17, 18, 19, 20, 21, 22]
+ ]
+ },
+
+ html: `
+ First: 1, Second: 2, Third: 3, Elements remaining: 2
+ First: 6, Second: 7, Third: 8, Elements remaining: 0
+ First: 9, Second: 10, Third: 11, Elements remaining: 1
+ First: 13, Second: 14, Third: 15, Elements remaining: 7
+ `,
+
+ test({ assert, component, target }) {
+ component.array = [[23, 24, 25, 26, 27, 28, 29]];
+ assert.htmlEqual( target.innerHTML, `
+ First: 23, Second: 24, Third: 25, Elements remaining: 4
+ `);
+ }
+};
diff --git a/test/runtime/samples/each-block-destructured-array-nested-rest/main.svelte b/test/runtime/samples/each-block-destructured-array-nested-rest/main.svelte
new file mode 100644
index 0000000000..fc73462464
--- /dev/null
+++ b/test/runtime/samples/each-block-destructured-array-nested-rest/main.svelte
@@ -0,0 +1,9 @@
+
+
+{#each array as [first, second, ...[third, ...{ length }]]}
+
+ First: {first}, Second: {second}, Third: {third}, Elements remaining: {length}
+
+{/each}
diff --git a/test/validator/samples/rest-eachblock-binding-2/warnings.json b/test/validator/samples/rest-eachblock-binding-2/warnings.json
index a471dd6a86..174982f21b 100644
--- a/test/validator/samples/rest-eachblock-binding-2/warnings.json
+++ b/test/validator/samples/rest-eachblock-binding-2/warnings.json
@@ -1,8 +1,8 @@
[
{
"code": "invalid-rest-eachblock-binding",
- "message": "...rest operator will create a new object and binding propagation with original object will not work",
- "start": { "line": 8, "column": 24 },
+ "message": "The rest operator (...) will create a new object and binding 'rest' with the original object will not work",
+ "start": { "line": 8, "column": 27 },
"end": { "line": 8, "column": 31 }
}
]
diff --git a/test/validator/samples/rest-eachblock-binding-3/warnings.json b/test/validator/samples/rest-eachblock-binding-3/warnings.json
index eda7e1fc5d..3311d2afe4 100644
--- a/test/validator/samples/rest-eachblock-binding-3/warnings.json
+++ b/test/validator/samples/rest-eachblock-binding-3/warnings.json
@@ -1,8 +1,8 @@
[
{
"code": "invalid-rest-eachblock-binding",
- "message": "...rest operator will create a new object and binding propagation with original object will not work",
- "start": { "line": 5, "column": 32 },
- "end": { "line": 5, "column": 39 }
+ "message": "The rest operator (...) will create a new object and binding 'rest' with the original object will not work",
+ "start": { "line": 5, "column": 35 },
+ "end": { "line": 5, "column": 39 }
}
]
diff --git a/test/validator/samples/rest-eachblock-binding-nested-rest/input.svelte b/test/validator/samples/rest-eachblock-binding-nested-rest/input.svelte
new file mode 100644
index 0000000000..31f32975f6
--- /dev/null
+++ b/test/validator/samples/rest-eachblock-binding-nested-rest/input.svelte
@@ -0,0 +1,9 @@
+
+
+{#each a as [first, second, ...[third, ...{ length }]]}
+ {first}, {second}, {length}
+
+
+{/each}
diff --git a/test/validator/samples/rest-eachblock-binding-nested-rest/warnings.json b/test/validator/samples/rest-eachblock-binding-nested-rest/warnings.json
new file mode 100644
index 0000000000..d935275edf
--- /dev/null
+++ b/test/validator/samples/rest-eachblock-binding-nested-rest/warnings.json
@@ -0,0 +1,26 @@
+[
+ {
+ "code": "invalid-rest-eachblock-binding",
+ "end": {
+ "column": 37,
+ "line": 5
+ },
+ "message": "The rest operator (...) will create a new object and binding 'third' with the original object will not work",
+ "start": {
+ "column": 32,
+ "line": 5
+ }
+ },
+ {
+ "code": "invalid-rest-eachblock-binding",
+ "end": {
+ "column": 50,
+ "line": 5
+ },
+ "message": "The rest operator (...) will create a new object and binding 'length' with the original object will not work",
+ "start": {
+ "column": 44,
+ "line": 5
+ }
+ }
+]
diff --git a/test/validator/samples/rest-eachblock-binding/warnings.json b/test/validator/samples/rest-eachblock-binding/warnings.json
index 35fb2d0b6e..992e8880d9 100644
--- a/test/validator/samples/rest-eachblock-binding/warnings.json
+++ b/test/validator/samples/rest-eachblock-binding/warnings.json
@@ -1,8 +1,8 @@
[
{
"code": "invalid-rest-eachblock-binding",
- "message": "...rest operator will create a new object and binding propagation with original object will not work",
- "start": { "line": 5, "column": 25 },
+ "message": "The rest operator (...) will create a new object and binding 'rest' with the original object will not work",
+ "start": { "line": 5, "column": 28 },
"end": { "line": 5, "column": 32 }
}
]