From 40552f8a4e660297593f66b629a951b18a218329 Mon Sep 17 00:00:00 2001 From: "M. Habib Rosyad" Date: Mon, 3 Aug 2020 22:25:33 +0700 Subject: [PATCH 1/7] fix each block destructuring can't have default values refered to destructured params --- src/compiler/compile/nodes/shared/Context.ts | 56 ++++++++++++++++++- .../_config.js | 8 +-- .../main.svelte | 4 +- 3 files changed, 58 insertions(+), 10 deletions(-) diff --git a/src/compiler/compile/nodes/shared/Context.ts b/src/compiler/compile/nodes/shared/Context.ts index bcc0521ffa5f..16abef27b259 100644 --- a/src/compiler/compile/nodes/shared/Context.ts +++ b/src/compiler/compile/nodes/shared/Context.ts @@ -25,7 +25,15 @@ export function unpack_destructuring(contexts: Context[], node: Node, modifier: if (element && element.type === 'RestElement') { unpack_destructuring(contexts, element, node => x`${modifier(node)}.slice(${i})` as Node); } else if (element && element.type === 'AssignmentPattern') { - unpack_destructuring(contexts, element.left, node => x`${modifier(node)}[${i}] !== undefined ? ${modifier(node)}[${i}] : ${element.right}` as Node); + const n = contexts.length; + + unpack_destructuring(contexts, element.left, node => { + const alternate = JSON.parse(JSON.stringify(element.right)); + + update_reference(contexts.slice(0, n), node, alternate); + + return x`${modifier(node)}[${i}] !== undefined ? ${modifier(node)}[${i}] : ${alternate.replacement || alternate}` as Node + }); } else { unpack_destructuring(contexts, element, node => x`${modifier(node)}[${i}]` as Node); } @@ -44,9 +52,17 @@ export function unpack_destructuring(contexts: Context[], node: Node, modifier: const key = property.key as Identifier; const value = property.value; - used_properties.push(x`"${(key as Identifier).name}"`); + used_properties.push(x`"${key.name}"`); if (value.type === 'AssignmentPattern') { - unpack_destructuring(contexts, value.left, node => x`${modifier(node)}.${key.name} !== undefined ? ${modifier(node)}.${key.name} : ${value.right}` as Node); + const n = contexts.length; + + unpack_destructuring(contexts, value.left, node => { + const alternate = JSON.parse(JSON.stringify(value.right)); + + update_reference(contexts.slice(0, n), node, alternate); + + return x`${modifier(node)}.${key.name} !== undefined ? ${modifier(node)}.${key.name} : ${alternate.replacement || alternate}` as Node + }); } else { unpack_destructuring(contexts, value, node => x`${modifier(node)}.${key.name}` as Node); } @@ -54,3 +70,37 @@ export function unpack_destructuring(contexts: Context[], node: Node, modifier: }); } } + +function update_reference(contexts: Context[], node: Node, parent: any, property?: any) { + const current_node = (property !== undefined && parent[property] || parent); + + if (!current_node || !contexts.length) return; + + if (current_node.type === 'Identifier') { + contexts.forEach((context) => { + const { key, modifier } = context; + + if (current_node.name === key.name) { + const replacement = modifier(node); + + if (property === undefined) { + current_node.replacement = replacement; + } else { + parent[property] = replacement; + } + } + }); + } else if (current_node.type === 'BinaryExpression') { + update_reference(contexts, node, current_node, 'left'); + update_reference(contexts, node, current_node, 'right'); + } else if (current_node.type === 'CallExpression') { + for (let i = 0; i < current_node.arguments.length; i += 1) { + update_reference(contexts, node, current_node.arguments, i); + } + + update_reference(contexts, node, current_node, 'callee'); + } else if (current_node.type === 'MemberExpression') { + update_reference(contexts, node, current_node, 'object'); + update_reference(contexts, node, current_node, 'property'); + } +} diff --git a/test/runtime/samples/each-block-destructured-default/_config.js b/test/runtime/samples/each-block-destructured-default/_config.js index 0e99fd589f3a..7300b1b64251 100644 --- a/test/runtime/samples/each-block-destructured-default/_config.js +++ b/test/runtime/samples/each-block-destructured-default/_config.js @@ -7,16 +7,14 @@ export default { }, html: ` -

raccoon - P. lotor - 25kg

-

eagle - unknown - 5.4kg

+

raccoon - P. lotor - 25kg (55 lb)

+

eagle - unknown - 5.4kg (12 lb)

`, - - test({ assert, component, target }) { component.animalEntries = [{ animal: 'cow', class: 'mammal', species: '‎B. taurus' }]; assert.htmlEqual(target.innerHTML, ` -

cow - ‎B. taurus - 50kg

+

cow - ‎B. taurus - 50kg (110 lb)

`); } }; diff --git a/test/runtime/samples/each-block-destructured-default/main.svelte b/test/runtime/samples/each-block-destructured-default/main.svelte index a91b45299e5c..f8140625db91 100644 --- a/test/runtime/samples/each-block-destructured-default/main.svelte +++ b/test/runtime/samples/each-block-destructured-default/main.svelte @@ -2,6 +2,6 @@ export let animalEntries; -{#each animalEntries as { animal, species = 'unknown', kilogram: weight = 50 , ...props } } -

{animal} - {species} - {weight}kg

+{#each animalEntries as { animal, species = 'unknown', kilogram: weight = 50, pound = (weight * 2.2).toFixed(0), ...props } } +

{animal} - {species} - {weight}kg ({pound} lb)

{/each} From 79f80a7e918d5bcfbcd8d26fdb3e60d532a13e43 Mon Sep 17 00:00:00 2001 From: "M. Habib Rosyad" Date: Mon, 3 Aug 2020 22:46:02 +0700 Subject: [PATCH 2/7] fix missing semicolon --- src/compiler/compile/nodes/shared/Context.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/compile/nodes/shared/Context.ts b/src/compiler/compile/nodes/shared/Context.ts index 16abef27b259..271ccd4045e3 100644 --- a/src/compiler/compile/nodes/shared/Context.ts +++ b/src/compiler/compile/nodes/shared/Context.ts @@ -32,7 +32,7 @@ export function unpack_destructuring(contexts: Context[], node: Node, modifier: update_reference(contexts.slice(0, n), node, alternate); - return x`${modifier(node)}[${i}] !== undefined ? ${modifier(node)}[${i}] : ${alternate.replacement || alternate}` as Node + return x`${modifier(node)}[${i}] !== undefined ? ${modifier(node)}[${i}] : ${alternate.replacement || alternate}` as Node; }); } else { unpack_destructuring(contexts, element, node => x`${modifier(node)}[${i}]` as Node); @@ -61,7 +61,7 @@ export function unpack_destructuring(contexts: Context[], node: Node, modifier: update_reference(contexts.slice(0, n), node, alternate); - return x`${modifier(node)}.${key.name} !== undefined ? ${modifier(node)}.${key.name} : ${alternate.replacement || alternate}` as Node + return x`${modifier(node)}.${key.name} !== undefined ? ${modifier(node)}.${key.name} : ${alternate.replacement || alternate}` as Node; }); } else { unpack_destructuring(contexts, value, node => x`${modifier(node)}.${key.name}` as Node); From 54c4734b076e06abce12387e82e76135b8a97ee1 Mon Sep 17 00:00:00 2001 From: "M. Habib Rosyad" Date: Mon, 10 Aug 2020 15:41:31 +0700 Subject: [PATCH 3/7] use estree-walker --- src/compiler/compile/nodes/shared/Context.ts | 68 +++++++++----------- 1 file changed, 29 insertions(+), 39 deletions(-) diff --git a/src/compiler/compile/nodes/shared/Context.ts b/src/compiler/compile/nodes/shared/Context.ts index 271ccd4045e3..92bcb48cd774 100644 --- a/src/compiler/compile/nodes/shared/Context.ts +++ b/src/compiler/compile/nodes/shared/Context.ts @@ -1,5 +1,7 @@ import { x } from 'code-red'; import { Node, Identifier } from 'estree'; +import { walk } from 'estree-walker'; +import is_reference from 'is-reference'; export interface Context { key: Identifier; @@ -27,13 +29,7 @@ export function unpack_destructuring(contexts: Context[], node: Node, modifier: } else if (element && element.type === 'AssignmentPattern') { const n = contexts.length; - unpack_destructuring(contexts, element.left, node => { - const alternate = JSON.parse(JSON.stringify(element.right)); - - update_reference(contexts.slice(0, n), node, alternate); - - return x`${modifier(node)}[${i}] !== undefined ? ${modifier(node)}[${i}] : ${alternate.replacement || alternate}` as Node; - }); + unpack_destructuring(contexts, element.left, node => x`${modifier(node)}[${i}] !== undefined ? ${modifier(node)}[${i}] : ${update_reference(contexts, n, element.right, node)}` as Node); } else { unpack_destructuring(contexts, element, node => x`${modifier(node)}[${i}]` as Node); } @@ -56,13 +52,7 @@ export function unpack_destructuring(contexts: Context[], node: Node, modifier: if (value.type === 'AssignmentPattern') { const n = contexts.length; - unpack_destructuring(contexts, value.left, node => { - const alternate = JSON.parse(JSON.stringify(value.right)); - - update_reference(contexts.slice(0, n), node, alternate); - - return x`${modifier(node)}.${key.name} !== undefined ? ${modifier(node)}.${key.name} : ${alternate.replacement || alternate}` as Node; - }); + unpack_destructuring(contexts, value.left, node => x`${modifier(node)}.${key.name} !== undefined ? ${modifier(node)}.${key.name} : ${update_reference(contexts, n, value.right, node)}` as Node); } else { unpack_destructuring(contexts, value, node => x`${modifier(node)}.${key.name}` as Node); } @@ -71,36 +61,36 @@ export function unpack_destructuring(contexts: Context[], node: Node, modifier: } } -function update_reference(contexts: Context[], node: Node, parent: any, property?: any) { - const current_node = (property !== undefined && parent[property] || parent); +function update_reference(contexts: Context[], n: number, info, replacement: Node): Node { + if (!n) return info; - if (!current_node || !contexts.length) return; + let copy = JSON.parse(JSON.stringify(info)) as Node; + const replace = (node: Identifier, callback: (node: Node, context: any) => void, context?: any) => { + for (let i = 0; i < n; i++) { + const { key, modifier } = contexts[i]; - if (current_node.type === 'Identifier') { - contexts.forEach((context) => { - const { key, modifier } = context; + if (node.name === key.name) { + callback(modifier(replacement), context); + break; + } + } + }; + + if (copy.type === 'Identifier') { + replace(copy, (node: Node, _context: any) => copy = node); + } else { + walk(copy, { + enter(node, parent: Node) { + if (!['Identifier', 'BinaryExpression', 'CallExpression', 'MemberExpression'].includes(node.type)) { + return this.skip(); + } - if (current_node.name === key.name) { - const replacement = modifier(node); - - if (property === undefined) { - current_node.replacement = replacement; - } else { - parent[property] = replacement; + if (is_reference(node, parent)) { + replace(node as Identifier, (node: Node, context: any) => context && context.replace(node), this); } } }); - } else if (current_node.type === 'BinaryExpression') { - update_reference(contexts, node, current_node, 'left'); - update_reference(contexts, node, current_node, 'right'); - } else if (current_node.type === 'CallExpression') { - for (let i = 0; i < current_node.arguments.length; i += 1) { - update_reference(contexts, node, current_node.arguments, i); - } - - update_reference(contexts, node, current_node, 'callee'); - } else if (current_node.type === 'MemberExpression') { - update_reference(contexts, node, current_node, 'object'); - update_reference(contexts, node, current_node, 'property'); } + + return copy; } From d6defbea1ca49eac477a0683a805e4ca563b607d Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Sat, 13 Feb 2021 18:21:11 +0800 Subject: [PATCH 4/7] allow using context variables in default in object pattern --- src/compiler/compile/nodes/AwaitBlock.ts | 4 +- src/compiler/compile/nodes/EachBlock.ts | 2 +- src/compiler/compile/nodes/shared/Context.ts | 67 +++++++++---------- src/compiler/compile/render_dom/Block.ts | 1 - .../compile/render_dom/wrappers/AwaitBlock.ts | 2 +- .../compile/render_dom/wrappers/EachBlock.ts | 5 +- .../_config.js | 3 + .../main.svelte | 7 ++ .../_config.js | 23 +++++++ .../main.svelte | 7 ++ .../_config.js | 16 +++-- .../main.svelte | 5 +- 12 files changed, 93 insertions(+), 49 deletions(-) create mode 100644 test/runtime/samples/each-block-destructured-default-before-initialised/_config.js create mode 100644 test/runtime/samples/each-block-destructured-default-before-initialised/main.svelte create mode 100644 test/runtime/samples/each-block-destructured-default-binding/_config.js create mode 100644 test/runtime/samples/each-block-destructured-default-binding/main.svelte diff --git a/src/compiler/compile/nodes/AwaitBlock.ts b/src/compiler/compile/nodes/AwaitBlock.ts index e5b07f90beea..ef74d26848fe 100644 --- a/src/compiler/compile/nodes/AwaitBlock.ts +++ b/src/compiler/compile/nodes/AwaitBlock.ts @@ -33,12 +33,12 @@ export default class AwaitBlock extends Node { if (this.then_node) { this.then_contexts = []; - unpack_destructuring(this.then_contexts, info.value, node => node); + unpack_destructuring(this.then_contexts, info.value); } if (this.catch_node) { this.catch_contexts = []; - unpack_destructuring(this.catch_contexts, info.error, node => node); + unpack_destructuring(this.catch_contexts, info.error); } this.pending = new PendingBlock(component, this, scope, info.pending); diff --git a/src/compiler/compile/nodes/EachBlock.ts b/src/compiler/compile/nodes/EachBlock.ts index 8968e14ab99d..a0de7148442b 100644 --- a/src/compiler/compile/nodes/EachBlock.ts +++ b/src/compiler/compile/nodes/EachBlock.ts @@ -38,7 +38,7 @@ export default class EachBlock extends AbstractBlock { this.scope = scope.child(); this.contexts = []; - unpack_destructuring(this.contexts, info.context, node => node); + unpack_destructuring(this.contexts, info.context); this.contexts.forEach(context => { this.scope.add(context.key.name, this.expression.dependencies, this); diff --git a/src/compiler/compile/nodes/shared/Context.ts b/src/compiler/compile/nodes/shared/Context.ts index 92bcb48cd774..c6ad2c289370 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 } from 'estree'; +import { Node, Identifier, Expression } from 'estree'; import { walk } from 'estree-walker'; import is_reference from 'is-reference'; @@ -7,31 +7,34 @@ export interface Context { key: Identifier; name?: string; modifier: (node: Node) => Node; + default_modifier: (node: Node, to_ctx: (name: string) => Node) => Node; } -export function unpack_destructuring(contexts: Context[], node: Node, modifier: (node: Node) => Node) { +export function unpack_destructuring(contexts: Context[], node: Node, modifier: Context['modifier'] = node => node, default_modifier: Context['default_modifier'] = node => node) { if (!node) return; if (node.type === 'Identifier') { contexts.push({ key: node as Identifier, - modifier + modifier, + default_modifier }); } else if (node.type === 'RestElement') { contexts.push({ key: node.argument as Identifier, - modifier + modifier, + default_modifier }); } else if (node.type === 'ArrayPattern') { node.elements.forEach((element, i) => { if (element && element.type === 'RestElement') { - unpack_destructuring(contexts, element, node => x`${modifier(node)}.slice(${i})` as Node); + unpack_destructuring(contexts, element, node => x`${modifier(node)}.slice(${i})` as Node, default_modifier); } else if (element && element.type === 'AssignmentPattern') { const n = contexts.length; - unpack_destructuring(contexts, element.left, node => x`${modifier(node)}[${i}] !== undefined ? ${modifier(node)}[${i}] : ${update_reference(contexts, n, element.right, node)}` as Node); + unpack_destructuring(contexts, element.left, node => x`${modifier(node)}[${i}]`, (node, to_ctx) => x`${node} !== undefined ? ${node} : ${update_reference(contexts, n, element.right, to_ctx)}` as Node); } else { - unpack_destructuring(contexts, element, node => x`${modifier(node)}[${i}]` as Node); + unpack_destructuring(contexts, element, node => x`${modifier(node)}[${i}]` as Node, default_modifier); } }); } else if (node.type === 'ObjectPattern') { @@ -42,7 +45,8 @@ export function unpack_destructuring(contexts: Context[], node: Node, modifier: unpack_destructuring( contexts, property.argument, - node => x`@object_without_properties(${modifier(node)}, [${used_properties}])` as Node + node => x`@object_without_properties(${modifier(node)}, [${used_properties}])` as Node, + default_modifier ); } else { const key = property.key as Identifier; @@ -52,45 +56,40 @@ export function unpack_destructuring(contexts: Context[], node: Node, modifier: if (value.type === 'AssignmentPattern') { const n = contexts.length; - unpack_destructuring(contexts, value.left, node => x`${modifier(node)}.${key.name} !== undefined ? ${modifier(node)}.${key.name} : ${update_reference(contexts, n, value.right, node)}` as Node); + unpack_destructuring(contexts, value.left, node => x`${modifier(node)}.${key.name}`, (node, to_ctx) => x`${node} !== undefined ? ${node} : ${update_reference(contexts, n, value.right, to_ctx)}` as Node); } else { - unpack_destructuring(contexts, value, node => x`${modifier(node)}.${key.name}` as Node); + unpack_destructuring(contexts, value, node => x`${modifier(node)}.${key.name}` as Node, default_modifier); } } }); } } -function update_reference(contexts: Context[], n: number, info, replacement: Node): Node { - if (!n) return info; - - let copy = JSON.parse(JSON.stringify(info)) as Node; - const replace = (node: Identifier, callback: (node: Node, context: any) => void, context?: any) => { - for (let i = 0; i < n; i++) { - const { key, modifier } = contexts[i]; - +function update_reference(contexts: Context[], n: number, expression: Expression, to_ctx: (name: string) => Node): Node { + const find_from_context = (node: Identifier) => { + for (let i = n; i < contexts.length; i++) { + const { key } = contexts[i]; if (node.name === key.name) { - callback(modifier(replacement), context); - break; + throw new Error(`Cannot access '${node.name}' before initialization`); } } + return to_ctx(node.name); }; - if (copy.type === 'Identifier') { - replace(copy, (node: Node, _context: any) => copy = node); - } else { - walk(copy, { - enter(node, parent: Node) { - if (!['Identifier', 'BinaryExpression', 'CallExpression', 'MemberExpression'].includes(node.type)) { - return this.skip(); - } + if (expression.type === 'Identifier') { + return find_from_context(expression); + } - if (is_reference(node, parent)) { - replace(node as Identifier, (node: Node, context: any) => context && context.replace(node), this); - } + // NOTE: avoid unnecessary deep clone? + expression = JSON.parse(JSON.stringify(expression)) as Expression; + walk(expression, { + enter(node, parent: Node) { + if (is_reference(node, parent)) { + this.replace(find_from_context(node as Identifier)); + this.skip(); } - }); - } + } + }); - return copy; + return expression; } diff --git a/src/compiler/compile/render_dom/Block.ts b/src/compiler/compile/render_dom/Block.ts index c511bb0b9509..63195b610ffe 100644 --- a/src/compiler/compile/render_dom/Block.ts +++ b/src/compiler/compile/render_dom/Block.ts @@ -9,7 +9,6 @@ export interface Bindings { property: Identifier; snippet: Node; store: string; - tail: Node; modifier: (node: Node) => Node; } diff --git a/src/compiler/compile/render_dom/wrappers/AwaitBlock.ts b/src/compiler/compile/render_dom/wrappers/AwaitBlock.ts index 2038943d8bbc..cc186d1c02fa 100644 --- a/src/compiler/compile/render_dom/wrappers/AwaitBlock.ts +++ b/src/compiler/compile/render_dom/wrappers/AwaitBlock.ts @@ -87,7 +87,7 @@ class AwaitBlockBranch extends Wrapper { } render_destructure() { - const props = this.value_contexts.map(prop => b`#ctx[${this.block.renderer.context_lookup.get(prop.key.name).index}] = ${prop.modifier(x`#ctx[${this.value_index}]`)};`); + const props = this.value_contexts.map(prop => b`#ctx[${this.block.renderer.context_lookup.get(prop.key.name).index}] = ${prop.default_modifier(prop.modifier(x`#ctx[${this.value_index}]`), name => this.renderer.reference(name))};`); const get_context = this.block.renderer.component.get_unique_name(`get_${this.status}_context`); this.block.renderer.blocks.push(b` function ${get_context}(#ctx) { diff --git a/src/compiler/compile/render_dom/wrappers/EachBlock.ts b/src/compiler/compile/render_dom/wrappers/EachBlock.ts index 75db59161b3c..134976ca6543 100644 --- a/src/compiler/compile/render_dom/wrappers/EachBlock.ts +++ b/src/compiler/compile/render_dom/wrappers/EachBlock.ts @@ -149,8 +149,7 @@ export default class EachBlockWrapper extends Wrapper { property: this.index_name, modifier: prop.modifier, snippet: prop.modifier(x`${this.vars.each_block_value}[${this.index_name}]` as Node), - store, - tail: prop.modifier(x`[${this.index_name}]` as Node) + store }); }); @@ -347,7 +346,7 @@ export default class EachBlockWrapper extends Wrapper { this.else.fragment.render(this.else.block, null, x`#nodes` as Identifier); } - this.context_props = this.node.contexts.map(prop => b`child_ctx[${renderer.context_lookup.get(prop.key.name).index}] = ${prop.modifier(x`list[i]`)};`); + this.context_props = this.node.contexts.map(prop => b`child_ctx[${renderer.context_lookup.get(prop.key.name).index}] = ${prop.default_modifier(prop.modifier(x`list[i]`), name => renderer.context_lookup.has(name) ? x`child_ctx[${renderer.context_lookup.get(name).index}]`: { type: 'Identifier', name })};`); if (this.node.has_binding) this.context_props.push(b`child_ctx[${renderer.context_lookup.get(this.vars.each_block_value.name).index}] = list;`); if (this.node.has_binding || this.node.has_index_binding || this.node.index) this.context_props.push(b`child_ctx[${renderer.context_lookup.get(this.index_name.name).index}] = i;`); diff --git a/test/runtime/samples/each-block-destructured-default-before-initialised/_config.js b/test/runtime/samples/each-block-destructured-default-before-initialised/_config.js new file mode 100644 index 000000000000..aa86b02c738c --- /dev/null +++ b/test/runtime/samples/each-block-destructured-default-before-initialised/_config.js @@ -0,0 +1,3 @@ +export default { + error: "Cannot access 'c' before initialization" +}; diff --git a/test/runtime/samples/each-block-destructured-default-before-initialised/main.svelte b/test/runtime/samples/each-block-destructured-default-before-initialised/main.svelte new file mode 100644 index 000000000000..76eb09c0de56 --- /dev/null +++ b/test/runtime/samples/each-block-destructured-default-before-initialised/main.svelte @@ -0,0 +1,7 @@ + + +{#each array as { a, b = c, c }} + {a}{b}{c} +{/each} diff --git a/test/runtime/samples/each-block-destructured-default-binding/_config.js b/test/runtime/samples/each-block-destructured-default-binding/_config.js new file mode 100644 index 000000000000..3697e374412d --- /dev/null +++ b/test/runtime/samples/each-block-destructured-default-binding/_config.js @@ -0,0 +1,23 @@ +export default { + html: ` + + + `, + ssrHtml: ` + + + `, + + test({ assert, component, target, window }) { + const [input1, input2] = target.querySelectorAll('input'); + assert.equal(input1.value, ''); + assert.equal(input2.value, 'hello'); + + const inputEvent = new window.InputEvent('input'); + + input2.value = 'world'; + input2.dispatchEvent(inputEvent); + assert.equal(input2.value, 'world'); + assert.equal(component.array[1].value, 'world'); + } +}; diff --git a/test/runtime/samples/each-block-destructured-default-binding/main.svelte b/test/runtime/samples/each-block-destructured-default-binding/main.svelte new file mode 100644 index 000000000000..e93a01038b06 --- /dev/null +++ b/test/runtime/samples/each-block-destructured-default-binding/main.svelte @@ -0,0 +1,7 @@ + + +{#each array as { value = "hello" }} + +{/each} diff --git a/test/runtime/samples/each-block-destructured-default/_config.js b/test/runtime/samples/each-block-destructured-default/_config.js index 7300b1b64251..0115c9d1ce92 100644 --- a/test/runtime/samples/each-block-destructured-default/_config.js +++ b/test/runtime/samples/each-block-destructured-default/_config.js @@ -1,20 +1,26 @@ export default { props: { animalEntries: [ - { animal: 'raccoon', class: 'mammal', species: 'P. lotor', kilogram: 25 }, - { animal: 'eagle', class: 'bird', kilogram: 5.4 } + { animal: 'raccoon', class: 'mammal', species: 'P. lotor', kilogram: 25, bmi: 0.04 }, + { animal: 'eagle', class: 'bird', kilogram: 5.4 }, + { animal: 'tiger', class: 'mammal', kilogram: 10, pound: 30 }, + { animal: 'lion', class: 'mammal', kilogram: 10, height: 50 }, + { animal: 'leopard', class: 'mammal', kilogram: 30, height: 50, bmi: 10 } ] }, html: ` -

raccoon - P. lotor - 25kg (55 lb)

-

eagle - unknown - 5.4kg (12 lb)

+

raccoon - P. lotor - 25kg (55 lb) - 30cm - 0.04

+

eagle - unknown - 5.4kg (12 lb) - 30cm - 0.006

+

tiger - unknown - 10kg (30 lb) - 30cm - 0.011111111111111112

+

lion - unknown - 10kg (22 lb) - 50cm - 0.004

+

leopard - unknown - 30kg (66 lb) - 50cm - 10

`, test({ assert, component, target }) { component.animalEntries = [{ animal: 'cow', class: 'mammal', species: '‎B. taurus' }]; assert.htmlEqual(target.innerHTML, ` -

cow - ‎B. taurus - 50kg (110 lb)

+

cow - ‎B. taurus - 50kg (110 lb) - 30cm - 0.05555555555555555

`); } }; diff --git a/test/runtime/samples/each-block-destructured-default/main.svelte b/test/runtime/samples/each-block-destructured-default/main.svelte index f8140625db91..44e720e17451 100644 --- a/test/runtime/samples/each-block-destructured-default/main.svelte +++ b/test/runtime/samples/each-block-destructured-default/main.svelte @@ -1,7 +1,8 @@ -{#each animalEntries as { animal, species = 'unknown', kilogram: weight = 50, pound = (weight * 2.2).toFixed(0), ...props } } -

{animal} - {species} - {weight}kg ({pound} lb)

+{#each animalEntries as { animal, species = 'unknown', kilogram: weight = 50, pound = (weight * 2.2).toFixed(0), height = defaultHeight, bmi = weight / (height * height), ...props } } +

{animal} - {species} - {weight}kg ({pound} lb) - {height}cm - {bmi}

{/each} From 58f870d1bf98be50ba1ef9f268470ea6240b4f1c Mon Sep 17 00:00:00 2001 From: Conduitry Date: Thu, 25 Feb 2021 07:13:02 -0500 Subject: [PATCH 5/7] fix build: add default_modifier in manually created Context --- src/compiler/compile/nodes/shared/Expression.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/compiler/compile/nodes/shared/Expression.ts b/src/compiler/compile/nodes/shared/Expression.ts index 6b051157f53d..caece14f0086 100644 --- a/src/compiler/compile/nodes/shared/Expression.ts +++ b/src/compiler/compile/nodes/shared/Expression.ts @@ -325,7 +325,8 @@ export default class Expression { // child_ctx[x] = function () { ... } (template_scope.get_owner(deps[0]) as EachBlock).contexts.push({ key: func_id, - modifier: () => func_expression + modifier: () => func_expression, + default_modifier: node => node }); this.replace(block.renderer.reference(func_id)); } From 2cf8129b5795948c4f846276e0f5f1610e4f8566 Mon Sep 17 00:00:00 2001 From: Conduitry Date: Thu, 25 Feb 2021 07:14:00 -0500 Subject: [PATCH 6/7] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bd57c193de21..849a6e20fd07 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Unreleased * In custom elements, call `onMount` functions when connecting and clean up when disconnecting ([#1152](https://github.com/sveltejs/svelte/issues/1152), [#2227](https://github.com/sveltejs/svelte/issues/2227), [#4522](https://github.com/sveltejs/svelte/pull/4522)) +* Allow destructured defaults to refer to other variables ([#5066](https://github.com/sveltejs/svelte/issues/5066)) * Do not emit `contextual-store` warnings for function parameters or declared variables ([#6008](https://github.com/sveltejs/svelte/pull/6008)) ## 3.32.3 From 0ab38a9e464dae21b9a803c3626b4f1a09cfc095 Mon Sep 17 00:00:00 2001 From: Conduitry Date: Thu, 25 Feb 2021 07:28:20 -0500 Subject: [PATCH 7/7] account for differing error messages on different Node versions --- .../_config.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/runtime/samples/each-block-destructured-default-before-initialised/_config.js b/test/runtime/samples/each-block-destructured-default-before-initialised/_config.js index aa86b02c738c..c42261df4041 100644 --- a/test/runtime/samples/each-block-destructured-default-before-initialised/_config.js +++ b/test/runtime/samples/each-block-destructured-default-before-initialised/_config.js @@ -1,3 +1,5 @@ export default { - error: "Cannot access 'c' before initialization" + error(assert, err) { + assert.ok(err.message === "Cannot access 'c' before initialization" || err.message === 'c is not defined'); + } };