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(
+ `{{ msg1 }}`,
+ )
+
+ expect(code).toMatchSnapshot()
+ expect(code).contains(`"default": _withVaporCtx((_slotProps0) =>`)
+ expect(code).contains(`_slotProps0.msg`)
+ })
+
+ test('slot prop nested destructuring', () => {
+ const { code } = compileWithSlots(
+ `{{ baz }}`,
+ )
+
+ expect(code).toMatchSnapshot()
+ expect(code).contains(`"default": _withVaporCtx((_slotProps0) =>`)
+ expect(code).contains(`_slotProps0.foo.bar`)
+ })
+
+ test('slot prop computed key destructuring', () => {
+ const { code } = compileWithSlots(
+ `{{ val }}`,
+ )
+
+ expect(code).toMatchSnapshot()
+ expect(code).contains(`"default": _withVaporCtx((_slotProps0) =>`)
+ expect(code).contains(`_slotProps0[_ctx.key]`)
+ })
+
+ test('slot prop rest destructuring', () => {
+ const { code } = compileWithSlots(
+ `{{ rest.bar }}`,
+ )
+
+ expect(code).toMatchSnapshot()
+ expect(code).contains(`"default": _withVaporCtx((_slotProps0) =>`)
+ expect(code).contains(`_getRestElement(_slotProps0`)
+ })
+
+ test('slot prop array rest destructuring', () => {
+ const { code } = compileWithSlots(
+ `{{ rest[0] }}`,
+ )
+
+ expect(code).toMatchSnapshot()
+ expect(code).contains(`"default": _withVaporCtx((_slotProps0) =>`)
+ expect(code).contains(`_slotProps0.arr.slice(1)`)
+ })
+
+ test('slot prop default value', () => {
+ const { code } = compileWithSlots(
+ `{{ foo }}`,
+ )
+
+ 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(
+ `{{ bar + qux }}`,
+ )
+
+ 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(
+ `{{ foo + rest.other }}`,
+ )
+
+ 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,