diff --git a/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap b/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap index a5d422aed2a..cb7b5de8683 100644 --- a/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap @@ -324,7 +324,7 @@ export function render(_ctx) { const n1 = _createComponentWithFallback(_component_Comp, null, { "foo": _withVaporCtx((_slotProps0) => { const n0 = t0() - _renderEffect(() => _setText(n0, _toDisplayString(_slotProps0["a"] + _slotProps0["b"]))) + _renderEffect(() => _setText(n0, _toDisplayString(_slotProps0.a + _slotProps0.b))) return n0 }) }, true) diff --git a/packages/compiler-vapor/__tests__/__snapshots__/scopeId.spec.ts.snap b/packages/compiler-vapor/__tests__/__snapshots__/scopeId.spec.ts.snap index 3d0322ce519..c49073973a1 100644 --- a/packages/compiler-vapor/__tests__/__snapshots__/scopeId.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/__snapshots__/scopeId.spec.ts.snap @@ -56,7 +56,7 @@ export function render(_ctx) { const n4 = _createComponentWithFallback(_component_Child, null, { "foo": _withVaporCtx((_slotProps0) => { const n0 = t0() - _renderEffect(() => _setText(n0, _toDisplayString(_slotProps0["msg"]))) + _renderEffect(() => _setText(n0, _toDisplayString(_slotProps0.msg))) return n0 }), "bar": _withVaporCtx(() => { diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap index e3251a687d9..95c90354c6a 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap @@ -33,7 +33,7 @@ export function render(_ctx) { name: item, fn: _withVaporCtx((_slotProps0) => { const n0 = t0() - _renderEffect(() => _setText(n0, _toDisplayString(_slotProps0["bar"]))) + _renderEffect(() => _setText(n0, _toDisplayString(_slotProps0.bar))) return n0 }) }))) @@ -275,12 +275,12 @@ export function render(_ctx) { const n1 = _createComponentWithFallback(_component_Inner, null, { "default": _withVaporCtx((_slotProps1) => { const n0 = t0() - _renderEffect(() => _setText(n0, _toDisplayString(_slotProps0["foo"] + _slotProps1["bar"] + _ctx.baz))) + _renderEffect(() => _setText(n0, _toDisplayString(_slotProps0.foo + _slotProps1.bar + _ctx.baz))) return n0 }) }) const n3 = t0() - _renderEffect(() => _setText(n3, " " + _toDisplayString(_slotProps0["foo"] + _ctx.bar + _ctx.baz))) + _renderEffect(() => _setText(n3, " " + _toDisplayString(_slotProps0.foo + _ctx.bar + _ctx.baz))) return [n1, n3] }) }, true) @@ -300,7 +300,7 @@ export function render(_ctx) { name: _ctx.named, fn: _withVaporCtx((_slotProps0) => { const n0 = t0() - _renderEffect(() => _setText(n0, _toDisplayString(_slotProps0["foo"] + _ctx.bar))) + _renderEffect(() => _setText(n0, _toDisplayString(_slotProps0.foo + _ctx.bar))) return n0 }) }) @@ -319,7 +319,7 @@ export function render(_ctx) { const n1 = _createComponentWithFallback(_component_Comp, null, { "named": _withVaporCtx((_slotProps0) => { const n0 = t0() - _renderEffect(() => _setText(n0, _toDisplayString(_slotProps0["foo"] + _ctx.bar))) + _renderEffect(() => _setText(n0, _toDisplayString(_slotProps0.foo + _ctx.bar))) return n0 }) }, true) @@ -336,7 +336,7 @@ export function render(_ctx) { const n1 = _createComponentWithFallback(_component_Comp, null, { "default": _withVaporCtx((_slotProps0) => { const n0 = t0() - _renderEffect(() => _setText(n0, _toDisplayString(_slotProps0["foo"] + _ctx.bar))) + _renderEffect(() => _setText(n0, _toDisplayString(_slotProps0.foo + _ctx.bar))) return n0 }) }, true) @@ -380,6 +380,142 @@ export function render(_ctx) { }" `; +exports[`compiler: transform slot > slot prop alias uses original key 1`] = ` +"import { resolveComponent as _resolveComponent, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, withVaporCtx as _withVaporCtx, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; +const t0 = _template(" ") + +export function render(_ctx) { + const _component_Comp = _resolveComponent("Comp") + const n2 = _createComponentWithFallback(_component_Comp, null, { + "default": _withVaporCtx((_slotProps0) => { + const n0 = t0() + _renderEffect(() => _setText(n0, _toDisplayString(_slotProps0.msg))) + return n0 + }) + }, true) + return n2 +}" +`; + +exports[`compiler: transform slot > slot prop array rest destructuring 1`] = ` +"import { resolveComponent as _resolveComponent, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, withVaporCtx as _withVaporCtx, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; +const t0 = _template(" ") + +export function render(_ctx) { + const _component_Comp = _resolveComponent("Comp") + const n2 = _createComponentWithFallback(_component_Comp, null, { + "default": _withVaporCtx((_slotProps0) => { + const n0 = t0() + _renderEffect(() => _setText(n0, _toDisplayString(_slotProps0.arr.slice(1)[0]))) + return n0 + }) + }, true) + return n2 +}" +`; + +exports[`compiler: transform slot > slot prop computed key destructuring 1`] = ` +"import { resolveComponent as _resolveComponent, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, withVaporCtx as _withVaporCtx, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; +const t0 = _template(" ") + +export function render(_ctx) { + const _component_Comp = _resolveComponent("Comp") + const n2 = _createComponentWithFallback(_component_Comp, null, { + "default": _withVaporCtx((_slotProps0) => { + const n0 = t0() + _renderEffect(() => _setText(n0, _toDisplayString(_slotProps0[_ctx.key]))) + return n0 + }) + }, true) + return n2 +}" +`; + +exports[`compiler: transform slot > slot prop default value 1`] = ` +"import { resolveComponent as _resolveComponent, getDefaultValue as _getDefaultValue, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, withVaporCtx as _withVaporCtx, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; +const t0 = _template(" ") + +export function render(_ctx) { + const _component_Comp = _resolveComponent("Comp") + const n2 = _createComponentWithFallback(_component_Comp, null, { + "default": _withVaporCtx((_slotProps0) => { + const n0 = t0() + _renderEffect(() => _setText(n0, _toDisplayString(_getDefaultValue(_slotProps0.foo, 1)))) + return n0 + }) + }, true) + return n2 +}" +`; + +exports[`compiler: transform slot > slot prop nested default value 1`] = ` +"import { resolveComponent as _resolveComponent, getDefaultValue as _getDefaultValue, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, withVaporCtx as _withVaporCtx, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; +const t0 = _template(" ") + +export function render(_ctx) { + const _component_Comp = _resolveComponent("Comp") + const n2 = _createComponentWithFallback(_component_Comp, null, { + "default": _withVaporCtx((_slotProps0) => { + const n0 = t0() + _renderEffect(() => _setText(n0, _toDisplayString(_getDefaultValue(_slotProps0.foo[0], 1) + _getDefaultValue(_slotProps0.baz.qux, 2)))) + return n0 + }) + }, true) + return n2 +}" +`; + +exports[`compiler: transform slot > slot prop nested destructuring 1`] = ` +"import { resolveComponent as _resolveComponent, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, withVaporCtx as _withVaporCtx, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; +const t0 = _template(" ") + +export function render(_ctx) { + const _component_Comp = _resolveComponent("Comp") + const n2 = _createComponentWithFallback(_component_Comp, null, { + "default": _withVaporCtx((_slotProps0) => { + const n0 = t0() + _renderEffect(() => _setText(n0, _toDisplayString(_slotProps0.foo.bar))) + return n0 + }) + }, true) + return n2 +}" +`; + +exports[`compiler: transform slot > slot prop rest destructuring 1`] = ` +"import { resolveComponent as _resolveComponent, getRestElement as _getRestElement, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, withVaporCtx as _withVaporCtx, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; +const t0 = _template(" ") + +export function render(_ctx) { + const _component_Comp = _resolveComponent("Comp") + const n2 = _createComponentWithFallback(_component_Comp, null, { + "default": _withVaporCtx((_slotProps0) => { + const n0 = t0() + _renderEffect(() => _setText(n0, _toDisplayString(_getRestElement(_slotProps0, ["foo"]).bar))) + return n0 + }) + }, true) + return n2 +}" +`; + +exports[`compiler: transform slot > slot prop rest with computed keys preserved 1`] = ` +"import { resolveComponent as _resolveComponent, getRestElement as _getRestElement, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, withVaporCtx as _withVaporCtx, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; +const t0 = _template(" ") + +export function render(_ctx) { + const _component_Comp = _resolveComponent("Comp") + const n2 = _createComponentWithFallback(_component_Comp, null, { + "default": _withVaporCtx((_slotProps0) => { + const n0 = t0() + _renderEffect(() => _setText(n0, _toDisplayString(_slotProps0.foo + _getRestElement(_slotProps0, ["foo", _ctx.key]).other))) + return n0 + }) + }, true) + return n2 +}" +`; + exports[`compiler: transform slot > with whitespace: 'preserve' > implicit default slot 1`] = ` "import { resolveComponent as _resolveComponent, withVaporCtx as _withVaporCtx, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue'; const t0 = _template(" Header ") diff --git a/packages/compiler-vapor/__tests__/transforms/vSlot.spec.ts b/packages/compiler-vapor/__tests__/transforms/vSlot.spec.ts index 093f1d577be..b70ee26970b 100644 --- a/packages/compiler-vapor/__tests__/transforms/vSlot.spec.ts +++ b/packages/compiler-vapor/__tests__/transforms/vSlot.spec.ts @@ -68,7 +68,7 @@ describe('compiler: transform slot', () => { expect(code).toMatchSnapshot() expect(code).contains(`"default": _withVaporCtx((_slotProps0) =>`) - expect(code).contains(`_slotProps0["foo"] + _ctx.bar`) + expect(code).contains(`_slotProps0.foo + _ctx.bar`) expect(ir.block.dynamic.children[0].operation).toMatchObject({ type: IRNodeTypes.CREATE_COMPONENT_NODE, @@ -102,7 +102,7 @@ describe('compiler: transform slot', () => { expect(code).toMatchSnapshot() expect(code).contains(`"named": _withVaporCtx((_slotProps0) =>`) - expect(code).contains(`_slotProps0["foo"] + _ctx.bar`) + expect(code).contains(`_slotProps0.foo + _ctx.bar`) expect(ir.block.dynamic.children[0].operation).toMatchObject({ type: IRNodeTypes.CREATE_COMPONENT_NODE, @@ -131,7 +131,7 @@ describe('compiler: transform slot', () => { expect(code).toMatchSnapshot() expect(code).contains(`fn: _withVaporCtx((_slotProps0) =>`) - expect(code).contains(`_slotProps0["foo"] + _ctx.bar`) + expect(code).contains(`_slotProps0.foo + _ctx.bar`) expect(ir.block.dynamic.children[0].operation).toMatchObject({ type: IRNodeTypes.CREATE_COMPONENT_NODE, @@ -165,6 +165,87 @@ describe('compiler: transform slot', () => { expect(code).toMatchSnapshot() }) + test('slot prop alias uses original key', () => { + const { code } = compileWithSlots( + ``, + ) + + expect(code).toMatchSnapshot() + expect(code).contains(`"default": _withVaporCtx((_slotProps0) =>`) + expect(code).contains(`_slotProps0.msg`) + }) + + test('slot prop nested destructuring', () => { + const { code } = compileWithSlots( + ``, + ) + + expect(code).toMatchSnapshot() + expect(code).contains(`"default": _withVaporCtx((_slotProps0) =>`) + expect(code).contains(`_slotProps0.foo.bar`) + }) + + test('slot prop computed key destructuring', () => { + const { code } = compileWithSlots( + ``, + ) + + expect(code).toMatchSnapshot() + expect(code).contains(`"default": _withVaporCtx((_slotProps0) =>`) + expect(code).contains(`_slotProps0[_ctx.key]`) + }) + + test('slot prop rest destructuring', () => { + const { code } = compileWithSlots( + ``, + ) + + expect(code).toMatchSnapshot() + expect(code).contains(`"default": _withVaporCtx((_slotProps0) =>`) + expect(code).contains(`_getRestElement(_slotProps0`) + }) + + test('slot prop array rest destructuring', () => { + const { code } = compileWithSlots( + ``, + ) + + expect(code).toMatchSnapshot() + expect(code).contains(`"default": _withVaporCtx((_slotProps0) =>`) + expect(code).contains(`_slotProps0.arr.slice(1)`) + }) + + test('slot prop default value', () => { + const { code } = compileWithSlots( + ``, + ) + + expect(code).toMatchSnapshot() + expect(code).contains(`"default": _withVaporCtx((_slotProps0) =>`) + expect(code).contains(`_getDefaultValue(_slotProps0.foo, 1)`) + }) + + test('slot prop nested default value', () => { + const { code } = compileWithSlots( + ``, + ) + + expect(code).toMatchSnapshot() + expect(code).contains(`"default": _withVaporCtx((_slotProps0) =>`) + expect(code).contains(`_getDefaultValue(_slotProps0.foo[0], 1)`) + expect(code).contains(`_getDefaultValue(_slotProps0.baz.qux, 2)`) + }) + + test('slot prop rest with computed keys preserved', () => { + const { code } = compileWithSlots( + ``, + ) + + expect(code).toMatchSnapshot() + expect(code).contains(`"default": _withVaporCtx((_slotProps0) =>`) + expect(code).contains(`_getRestElement(_slotProps0, ["foo", _ctx.key])`) + }) + test('named slots w/ implicit default slot', () => { const { ir, code } = compileWithSlots( ` @@ -216,8 +297,8 @@ describe('compiler: transform slot', () => { expect(code).contains(`"default": _withVaporCtx((_slotProps0) =>`) expect(code).contains(`"default": _withVaporCtx((_slotProps1) =>`) - expect(code).contains(`_slotProps0["foo"] + _slotProps1["bar"] + _ctx.baz`) - expect(code).contains(`_slotProps0["foo"] + _ctx.bar + _ctx.baz`) + expect(code).contains(`_slotProps0.foo + _slotProps1.bar + _ctx.baz`) + expect(code).contains(`_slotProps0.foo + _ctx.bar + _ctx.baz`) const outerOp = ir.block.dynamic.children[0].operation expect(outerOp).toMatchObject({ @@ -293,7 +374,7 @@ describe('compiler: transform slot', () => { expect(code).toMatchSnapshot() expect(code).contains(`fn: _withVaporCtx((_slotProps0) =>`) - expect(code).contains(`_setText(n0, _toDisplayString(_slotProps0["bar"]))`) + expect(code).contains(`_setText(n0, _toDisplayString(_slotProps0.bar))`) expect(ir.block.dynamic.children[0].operation).toMatchObject({ type: IRNodeTypes.CREATE_COMPONENT_NODE, diff --git a/packages/compiler-vapor/src/generators/component.ts b/packages/compiler-vapor/src/generators/component.ts index b1fe3612c11..c4e9061345d 100644 --- a/packages/compiler-vapor/src/generators/component.ts +++ b/packages/compiler-vapor/src/generators/component.ts @@ -34,11 +34,16 @@ import { createSimpleExpression, isMemberExpression, toValidAssetId, - walkIdentifiers, } from '@vue/compiler-dom' import { genEventHandler } from './event' import { genDirectiveModifiers, genDirectivesForElement } from './directive' import { genBlock } from './block' +import { + type DestructureMap, + type DestructureMapValue, + buildDestructureIdMap, + parseValueDestructure, +} from './for' import { genModelHandler } from './vModel' import { isBuiltInComponent, isKeepAliveTag } from '../utils' @@ -405,40 +410,37 @@ function genConditionalSlot( } function genSlotBlockWithProps(oper: SlotBlockIRNode, context: CodegenContext) { - let isDestructureAssignment = false - let rawProps: string | undefined let propsName: string | undefined let exitScope: (() => void) | undefined let depth: number | undefined const { props, key, node } = oper - const idsOfProps = new Set() + const idToPathMap: DestructureMap = props + ? parseValueDestructure(props, context) + : new Map() + if (props) { - rawProps = props.content - if ((isDestructureAssignment = !!props.ast)) { + if (props.ast) { ;[depth, exitScope] = context.enterScope() propsName = `_slotProps${depth}` - walkIdentifiers( - props.ast, - (id, _, __, ___, isLocal) => { - if (isLocal) idsOfProps.add(id.name) - }, - true, - ) } else { - idsOfProps.add((propsName = rawProps)) + propsName = props.content } } - const idMap: Record = {} + const idMap = idToPathMap.size + ? buildDestructureIdMap( + idToPathMap, + propsName || '', + context.options.expressionPlugins, + ) + : {} + + if (propsName) { + idMap[propsName] = null + } - idsOfProps.forEach( - id => - (idMap[id] = isDestructureAssignment - ? `${propsName}[${JSON.stringify(id)}]` - : null), - ) let blockFn = context.withId( - () => genBlock(oper, context, [propsName]), + () => genBlock(oper, context, propsName ? [propsName] : []), idMap, ) exitScope && exitScope() diff --git a/packages/compiler-vapor/src/generators/for.ts b/packages/compiler-vapor/src/generators/for.ts index ddb5d3b43a8..2ab9d5f655a 100644 --- a/packages/compiler-vapor/src/generators/for.ts +++ b/packages/compiler-vapor/src/generators/for.ts @@ -40,40 +40,22 @@ export function genFor( onlyChild, } = oper - let rawValue: string | null = null + const rawValue = value && value.content const rawKey = key && key.content const rawIndex = index && index.content const sourceExpr = ['() => (', ...genExpression(source, context), ')'] - const idToPathMap = parseValueDestructure() + const idToPathMap = parseValueDestructure(value, context) const [depth, exitScope] = context.enterScope() - const idMap: Record = {} - const itemVar = `_for_item${depth}` + const idMap = buildDestructureIdMap( + idToPathMap, + `${itemVar}.value`, + context.options.expressionPlugins, + ) idMap[itemVar] = null - idToPathMap.forEach((pathInfo, id) => { - let path = `${itemVar}.value${pathInfo ? pathInfo.path : ''}` - if (pathInfo) { - if (pathInfo.helper) { - idMap[pathInfo.helper] = null - path = `${pathInfo.helper}(${path}, ${pathInfo.helperArgs})` - } - if (pathInfo.dynamic) { - const node = (idMap[id] = createSimpleExpression(path)) - const plugins = context.options.expressionPlugins - node.ast = parseExpression(`(${path})`, { - plugins: plugins ? [...plugins, 'typescript'] : ['typescript'], - }) - } else { - idMap[id] = path - } - } else { - idMap[id] = path - } - }) - const args = [itemVar] if (rawKey) { const keyVar = `_for_key${depth}` @@ -180,108 +162,6 @@ export function genFor( ), ] - // construct a id -> accessor path map. - // e.g. `{ x: { y: [z] }}` -> `Map{ 'z' => '.x.y[0]' }` - function parseValueDestructure() { - const map = new Map< - string, - { - path: string - dynamic: boolean - helper?: string - helperArgs?: string - } | null - >() - if (value) { - rawValue = value && value.content - if (value.ast) { - walkIdentifiers( - value.ast, - (id, _, parentStack, ___, isLocal) => { - if (isLocal) { - let path = '' - let isDynamic = false - let helper - let helperArgs - for (let i = 0; i < parentStack.length; i++) { - const parent = parentStack[i] - const child = parentStack[i + 1] || id - - if ( - parent.type === 'ObjectProperty' && - parent.value === child - ) { - if (parent.key.type === 'StringLiteral') { - path += `[${JSON.stringify(parent.key.value)}]` - } else if (parent.computed) { - isDynamic = true - path += `[${value.content.slice( - parent.key.start! - 1, - parent.key.end! - 1, - )}]` - } else { - // non-computed, can only be identifier - path += `.${(parent.key as Identifier).name}` - } - } else if (parent.type === 'ArrayPattern') { - const index = parent.elements.indexOf(child as any) - if (child.type === 'RestElement') { - path += `.slice(${index})` - } else { - path += `[${index}]` - } - } else if ( - parent.type === 'ObjectPattern' && - child.type === 'RestElement' - ) { - helper = context.helper('getRestElement') - helperArgs = - '[' + - parent.properties - .filter(p => p.type === 'ObjectProperty') - .map(p => { - if (p.key.type === 'StringLiteral') { - return JSON.stringify(p.key.value) - } else if (p.computed) { - isDynamic = true - return value.content.slice( - p.key.start! - 1, - p.key.end! - 1, - ) - } else { - return JSON.stringify((p.key as Identifier).name) - } - }) - .join(', ') + - ']' - } - - // default value - if ( - child.type === 'AssignmentPattern' && - (parent.type === 'ObjectProperty' || - parent.type === 'ArrayPattern') - ) { - isDynamic = true - helper = context.helper('getDefaultValue') - helperArgs = value.content.slice( - child.right.start! - 1, - child.right.end! - 1, - ) - } - } - map.set(id.name, { path, dynamic: isDynamic, helper, helperArgs }) - } - }, - true, - ) - } else { - map.set(rawValue, null) - } - } - return map - } - function genCallback(expr: SimpleExpressionNode | undefined) { if (!expr) return false const res = context.withId( @@ -310,6 +190,139 @@ export function genFor( } } +export type DestructureMapValue = { + path: string + dynamic: boolean + helper?: string + helperArgs?: string +} + +export type DestructureMap = Map + +// construct a id -> accessor path map. +// e.g. `{ x: { y: [z] }}` -> `Map{ 'z' => '.x.y[0]' }` +export function parseValueDestructure( + value: SimpleExpressionNode | undefined, + context: CodegenContext, +): DestructureMap { + const map: DestructureMap = new Map() + if (value) { + const rawValue = value.content + if (value.ast) { + walkIdentifiers( + value.ast, + (id, _, parentStack, ___, isLocal) => { + if (isLocal) { + let path = '' + let isDynamic = false + let helper + let helperArgs + for (let i = 0; i < parentStack.length; i++) { + const parent = parentStack[i] + const child = parentStack[i + 1] || id + + if (parent.type === 'ObjectProperty' && parent.value === child) { + if (parent.key.type === 'StringLiteral') { + path += `[${JSON.stringify(parent.key.value)}]` + } else if (parent.computed) { + isDynamic = true + path += `[${rawValue.slice( + parent.key.start! - 1, + parent.key.end! - 1, + )}]` + } else { + // non-computed, can only be identifier + path += `.${(parent.key as Identifier).name}` + } + } else if (parent.type === 'ArrayPattern') { + const index = parent.elements.indexOf(child as any) + if (child.type === 'RestElement') { + path += `.slice(${index})` + } else { + path += `[${index}]` + } + } else if ( + parent.type === 'ObjectPattern' && + child.type === 'RestElement' + ) { + helper = context.helper('getRestElement') + helperArgs = + '[' + + parent.properties + .filter(p => p.type === 'ObjectProperty') + .map(p => { + if (p.key.type === 'StringLiteral') { + return JSON.stringify(p.key.value) + } else if (p.computed) { + isDynamic = true + return rawValue.slice(p.key.start! - 1, p.key.end! - 1) + } else { + return JSON.stringify((p.key as Identifier).name) + } + }) + .join(', ') + + ']' + } + + // default value + if ( + child.type === 'AssignmentPattern' && + (parent.type === 'ObjectProperty' || + parent.type === 'ArrayPattern') + ) { + isDynamic = true + helper = context.helper('getDefaultValue') + helperArgs = rawValue.slice( + child.right.start! - 1, + child.right.end! - 1, + ) + } + } + map.set(id.name, { path, dynamic: isDynamic, helper, helperArgs }) + } + }, + true, + ) + } else if (rawValue) { + map.set(rawValue, null) + } + } + return map +} + +export function buildDestructureIdMap( + idToPathMap: DestructureMap, + baseAccessor: string, + plugins: CodegenContext['options']['expressionPlugins'], +): Record { + const idMap: Record = {} + idToPathMap.forEach((pathInfo, id) => { + let path = baseAccessor + if (pathInfo) { + path = `${baseAccessor}${pathInfo.path}` + + if (pathInfo.helper) { + idMap[pathInfo.helper] = null + path = pathInfo.helperArgs + ? `${pathInfo.helper}(${path}, ${pathInfo.helperArgs})` + : `${pathInfo.helper}(${path})` + } + + if (pathInfo.dynamic) { + const node = (idMap[id] = createSimpleExpression(path)) + node.ast = parseExpression(`(${path})`, { + plugins: plugins ? [...plugins, 'typescript'] : ['typescript'], + }) + } else { + idMap[id] = path + } + } else { + idMap[id] = path + } + }) + return idMap +} + function matchPatterns( render: BlockIRNode, keyProp: SimpleExpressionNode | undefined,