From 3a726a9c39630f698bc132ac4e99f2e3212a6075 Mon Sep 17 00:00:00 2001 From: "S. Elliott Johnson" Date: Mon, 25 Dec 2023 22:35:03 -0500 Subject: [PATCH] feat: I think non-destructured rest should work now? --- .../3-transform/client/visitors/template.js | 69 ++++++++++--------- 1 file changed, 37 insertions(+), 32 deletions(-) diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js index 505ef37544..bdf8a23f69 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js @@ -2460,9 +2460,7 @@ export const template_visitors = { node.context.elements.forEach((argument, i) => { if (!argument) return; - // If the argument is itself an identifier (`foo`) or is a simple rest identifier (`...foo`) - // we don't have to do anything fancy -- just push it onto the args array and make sure its - // binding expression is a call of itself. + const arg_alias = `$$arg${i}`; /** @type {import('estree').Identifier | undefined} */ let identifier; /** @type {import('estree').Identifier | import('estree').RestElement | string} */ @@ -2470,9 +2468,6 @@ export const template_visitors = { if (argument.type === 'Identifier') { identifier = argument; arg = argument; - } else if (argument.type === 'RestElement' && argument.argument.type === 'Identifier') { - identifier = argument.argument; - arg = argument; } else if (argument.type === 'RestElement') { arg = b.rest(b.id(`$$arg${i}`)); } else { @@ -2485,36 +2480,46 @@ export const template_visitors = { context.state.scope.get(identifier.name) ); binding.expression = b.call(identifier); - } else { - // If, on the other hand, it isn't an identifier, it's a destructuring expression, which could be - // a rest destructure (eg. `...[foo, bar, { baz }, ...rest]`, which, absurdly, is all valid syntax). - // In this case, we need to follow the destructuring expression to figure out what variables are being extracted. - const paths = extract_paths(argument.type === 'RestElement' ? argument.argument : argument); - - for (const path of paths) { - const name = /** @type {import('estree').Identifier} */ (path.node).name; - const binding = /** @type {import('#compiler').Binding} */ ( - context.state.scope.get(name) - ); - declarations.push( - b.let( - path.node, - b.thunk( - /** @type {import('estree').Expression} */ ( - context.visit(path.expression?.(b.call(`$$arg${i}`))) - ) + return; + } + + if (argument.type === 'RestElement' && argument.argument.type === 'Identifier') { + // this is a "simple" rest argument (not destructured), so we just need to thunkify it + const binding = /** @type {import('#compiler').Binding} */ ( + context.state.scope.get(argument.argument.name) + ); + binding.expression = b.call(argument.argument.name); + // TODO: where does `context.visit` go here? does it matter? i don't understand what it's for :thinkies: + declarations.push(b.let(argument.argument.name, b.thunk(b.id(`$$arg${i}`)))); + return; + } + + // If, on the other hand, it's a destructuring expression, which could be + // a rest destructure (eg. `...[foo, bar, { baz }, ...rest]`, which, absurdly, is all valid syntax), + // we need to follow the destructuring expression to figure out what variables are being extracted. + const paths = extract_paths(argument.type === 'RestElement' ? argument.argument : argument); + + for (const path of paths) { + const name = /** @type {import('estree').Identifier} */ (path.node).name; + const binding = /** @type {import('#compiler').Binding} */ (context.state.scope.get(name)); + declarations.push( + b.let( + path.node, + b.thunk( + /** @type {import('estree').Expression} */ ( + context.visit(path.expression?.(b.call(`$$arg${i}`))) ) ) - ); - - // we need to eagerly evaluate the expression in order to hit any - // 'Cannot access x before initialization' errors - if (context.state.options.dev) { - declarations.push(b.stmt(b.call(name))); - } + ) + ); - binding.expression = b.call(name); + // we need to eagerly evaluate the expression in order to hit any + // 'Cannot access x before initialization' errors + if (context.state.options.dev) { + declarations.push(b.stmt(b.call(name))); } + + binding.expression = b.call(name); } });