diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vFor.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vFor.spec.ts.snap
index 3d1368b74..2edfdd249 100644
--- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vFor.spec.ts.snap
+++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vFor.spec.ts.snap
@@ -5,7 +5,7 @@ exports[`compiler: v-for > array de-structured value 1`] = `
const t0 = _template("
")
export function render(_ctx) {
- const n0 = _createFor(() => (_ctx.list), _withDestructure((_state, [[id, ...other], index] = _state) => [id, other, index], (_ctx0) => {
+ const n0 = _createFor(() => (_ctx.list), _withDestructure(([[id, ...other], index]) => [id, other, index], (_ctx0) => {
const n2 = t0()
_renderEffect(() => _setText(n2, _ctx0[0] + _ctx0[1] + _ctx0[2]))
return n2
@@ -53,9 +53,9 @@ const t1 = _template("")
export function render(_ctx) {
const n0 = _createFor(() => (_ctx.list), (_ctx0) => {
const n5 = t1()
- const n2 = _createFor(() => (_ctx0[0]), (_ctx2) => {
+ const n2 = _createFor(() => (_ctx0[0]), (_ctx1) => {
const n4 = t0()
- _renderEffect(() => _setText(n4, _ctx2[0]+_ctx0[0]))
+ _renderEffect(() => _setText(n4, _ctx1[0]+_ctx0[0]))
return n4
})
_insert(n2, n5)
@@ -70,7 +70,7 @@ exports[`compiler: v-for > object de-structured value 1`] = `
const t0 = _template("")
export function render(_ctx) {
- const n0 = _createFor(() => (_ctx.list), _withDestructure((_state, [{ id, ...other }, index] = _state) => [id, other, index], (_ctx0) => {
+ const n0 = _createFor(() => (_ctx.list), _withDestructure(([{ id, ...other }, index]) => [id, other, index], (_ctx0) => {
const n2 = t0()
_renderEffect(() => _setText(n2, _ctx0[0] + _ctx0[1] + _ctx0[2]))
return n2
@@ -84,7 +84,7 @@ exports[`compiler: v-for > v-for aliases w/ complex expressions 1`] = `
const t0 = _template("")
export function render(_ctx) {
- const n0 = _createFor(() => (_ctx.list), _withDestructure((_state, [{ foo = bar, baz: [qux = quux] }] = _state) => [foo, qux], (_ctx0) => {
+ const n0 = _createFor(() => (_ctx.list), _withDestructure(([{ foo = bar, baz: [qux = quux] }]) => [foo, qux], (_ctx0) => {
const n2 = t0()
_renderEffect(() => _setText(n2, _ctx0[0] + _ctx.bar + _ctx.baz + _ctx0[1] + _ctx.quux))
return n2
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 b5075babe..05b8f1cc2 100644
--- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap
+++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap
@@ -18,17 +18,17 @@ export function render(_ctx) {
`;
exports[`compiler: transform slot > dynamic slots name w/ v-for 1`] = `
-"import { resolveComponent as _resolveComponent, createComponent as _createComponent, createForSlots as _createForSlots, template as _template } from 'vue/vapor';
+"import { resolveComponent as _resolveComponent, createComponent as _createComponent, withDestructure as _withDestructure, createForSlots as _createForSlots, template as _template } from 'vue/vapor';
const t0 = _template("foo")
export function render(_ctx) {
const _component_Comp = _resolveComponent("Comp")
const n2 = _createComponent(_component_Comp, null, null, () => [_createForSlots(_ctx.list, (item) => ({
name: item,
- fn: () => {
+ fn: _withDestructure(({ bar }) => [bar], (_ctx0) => {
const n0 = t0()
return n0
- }
+ })
}))], true)
return n2
}"
@@ -52,7 +52,7 @@ export function render(_ctx) {
`;
exports[`compiler: transform slot > dynamic slots name w/ v-if / v-else[-if] 1`] = `
-"import { resolveComponent as _resolveComponent, createComponent as _createComponent, template as _template } from 'vue/vapor';
+"import { resolveComponent as _resolveComponent, createComponent as _createComponent, withDestructure as _withDestructure, template as _template } from 'vue/vapor';
const t0 = _template("condition slot")
const t1 = _template("another condition")
const t2 = _template("else condition")
@@ -71,10 +71,10 @@ export function render(_ctx) {
: _ctx.anotherCondition
? {
name: "condition",
- fn: () => {
+ fn: _withDestructure(({ foo, bar }) => [foo, bar], (_ctx0) => {
const n2 = t1()
return n2
- },
+ }),
key: "1"
}
: {
@@ -126,20 +126,64 @@ export function render(_ctx) {
}"
`;
-exports[`compiler: transform slot > nested slots 1`] = `
-"import { resolveComponent as _resolveComponent, createComponent as _createComponent, template as _template } from 'vue/vapor';
-const t0 = _template("")
+exports[`compiler: transform slot > nested slots scoping 1`] = `
+"import { resolveComponent as _resolveComponent, createComponent as _createComponent, createTextNode as _createTextNode, withDestructure as _withDestructure, template as _template } from 'vue/vapor';
+const t0 = _template(" ")
export function render(_ctx) {
- const _component_Bar = _resolveComponent("Bar")
- const _component_Foo = _resolveComponent("Foo")
- const n3 = _createComponent(_component_Foo, null, { one: () => {
- const n1 = _createComponent(_component_Bar, null, { default: () => {
- const n0 = t0()
+ const _component_Inner = _resolveComponent("Inner")
+ const _component_Comp = _resolveComponent("Comp")
+ const n5 = _createComponent(_component_Comp, null, { default: _withDestructure(({ foo }) => [foo], (_ctx0) => {
+ const n2 = t0()
+ const n1 = _createComponent(_component_Inner, null, { default: _withDestructure(({ bar }) => [bar], (_ctx1) => {
+ const n0 = _createTextNode(() => [_ctx0[0] + _ctx1[0] + _ctx.baz])
return n0
- } })
- return n1
- } }, null, true)
- return n3
+ }) })
+ const n3 = _createTextNode(() => [_ctx0[0] + _ctx.bar + _ctx.baz])
+ return [n1, n2, n3]
+ }) }, null, true)
+ return n5
+}"
+`;
+
+exports[`compiler: transform slot > on component dynamically named slot 1`] = `
+"import { resolveComponent as _resolveComponent, createComponent as _createComponent, createTextNode as _createTextNode, withDestructure as _withDestructure } from 'vue/vapor';
+
+export function render(_ctx) {
+ const _component_Comp = _resolveComponent("Comp")
+ const n1 = _createComponent(_component_Comp, null, { }, () => [{
+ name: _ctx.named,
+ fn: _withDestructure(({ foo }) => [foo], (_ctx0) => {
+ const n0 = _createTextNode(() => [_ctx0[0] + _ctx.bar])
+ return n0
+ })
+ }], true)
+ return n1
+}"
+`;
+
+exports[`compiler: transform slot > on component named slot 1`] = `
+"import { resolveComponent as _resolveComponent, createComponent as _createComponent, createTextNode as _createTextNode, withDestructure as _withDestructure } from 'vue/vapor';
+
+export function render(_ctx) {
+ const _component_Comp = _resolveComponent("Comp")
+ const n1 = _createComponent(_component_Comp, null, { named: _withDestructure(({ foo }) => [foo], (_ctx0) => {
+ const n0 = _createTextNode(() => [_ctx0[0] + _ctx.bar])
+ return n0
+ }) }, null, true)
+ return n1
+}"
+`;
+
+exports[`compiler: transform slot > on-component default slot 1`] = `
+"import { resolveComponent as _resolveComponent, createComponent as _createComponent, createTextNode as _createTextNode, withDestructure as _withDestructure } from 'vue/vapor';
+
+export function render(_ctx) {
+ const _component_Comp = _resolveComponent("Comp")
+ const n1 = _createComponent(_component_Comp, null, { default: _withDestructure(({ foo }) => [foo], (_ctx0) => {
+ const n0 = _createTextNode(() => [_ctx0[0] + _ctx.bar])
+ return n0
+ }) }, null, true)
+ return n1
}"
`;
diff --git a/packages/compiler-vapor/__tests__/transforms/vFor.spec.ts b/packages/compiler-vapor/__tests__/transforms/vFor.spec.ts
index f3fa72a47..9aca84243 100644
--- a/packages/compiler-vapor/__tests__/transforms/vFor.spec.ts
+++ b/packages/compiler-vapor/__tests__/transforms/vFor.spec.ts
@@ -93,8 +93,8 @@ describe('compiler: v-for', () => {
)
expect(code).matchSnapshot()
expect(code).contains(`_createFor(() => (_ctx.list), (_ctx0) => {`)
- expect(code).contains(`_createFor(() => (_ctx0[0]), (_ctx2) => {`)
- expect(code).contains(`_ctx2[0]+_ctx0[0]`)
+ expect(code).contains(`_createFor(() => (_ctx0[0]), (_ctx1) => {`)
+ expect(code).contains(`_ctx1[0]+_ctx0[0]`)
expect(ir.template).toEqual(['', ''])
expect(ir.block.operation).toMatchObject([
{
@@ -129,9 +129,7 @@ describe('compiler: v-for', () => {
`{{ id + other + index }}
`,
)
expect(code).matchSnapshot()
- expect(code).contains(
- `(_state, [{ id, ...other }, index] = _state) => [id, other, index]`,
- )
+ expect(code).contains(`([{ id, ...other }, index]) => [id, other, index]`)
expect(code).contains(`_ctx0[0] + _ctx0[1] + _ctx0[2]`)
expect(ir.block.operation[0]).toMatchObject({
type: IRNodeTypes.FOR,
@@ -164,9 +162,7 @@ describe('compiler: v-for', () => {
`{{ id + other + index }}
`,
)
expect(code).matchSnapshot()
- expect(code).contains(
- `(_state, [[id, ...other], index] = _state) => [id, other, index]`,
- )
+ expect(code).contains(`([[id, ...other], index]) => [id, other, index]`)
expect(code).contains(`_ctx0[0] + _ctx0[1] + _ctx0[2]`)
expect(ir.block.operation[0]).toMatchObject({
type: IRNodeTypes.FOR,
@@ -201,9 +197,7 @@ describe('compiler: v-for', () => {
`,
)
expect(code).matchSnapshot()
- expect(code).contains(
- `(_state, [{ foo = bar, baz: [qux = quux] }] = _state) => [foo, qux]`,
- )
+ expect(code).contains(`([{ foo = bar, baz: [qux = quux] }]) => [foo, qux]`)
expect(code).contains(
`_ctx0[0] + _ctx.bar + _ctx.baz + _ctx0[1] + _ctx.quux`,
)
diff --git a/packages/compiler-vapor/__tests__/transforms/vSlot.spec.ts b/packages/compiler-vapor/__tests__/transforms/vSlot.spec.ts
index ce09fb646..d6a021d31 100644
--- a/packages/compiler-vapor/__tests__/transforms/vSlot.spec.ts
+++ b/packages/compiler-vapor/__tests__/transforms/vSlot.spec.ts
@@ -58,6 +58,98 @@ describe('compiler: transform slot', () => {
})
})
+ test('on-component default slot', () => {
+ const { ir, code, vaporHelpers } = compileWithSlots(
+ `{{ foo + bar }}`,
+ )
+ expect(code).toMatchSnapshot()
+
+ expect(vaporHelpers).contains('withDestructure')
+ expect(code).contains(`({ foo }) => [foo]`)
+ expect(code).contains(`_ctx0[0] + _ctx.bar`)
+
+ expect(ir.block.operation).toMatchObject([
+ {
+ type: IRNodeTypes.CREATE_COMPONENT_NODE,
+ tag: 'Comp',
+ props: [[]],
+ slots: {
+ default: {
+ type: IRNodeTypes.BLOCK,
+ props: {
+ type: NodeTypes.SIMPLE_EXPRESSION,
+ content: '{ foo }',
+ ast: {
+ type: 'ArrowFunctionExpression',
+ params: [{ type: 'ObjectPattern' }],
+ },
+ },
+ },
+ },
+ },
+ ])
+ })
+
+ test('on component named slot', () => {
+ const { ir, code } = compileWithSlots(
+ `{{ foo + bar }}`,
+ )
+ expect(code).toMatchSnapshot()
+
+ expect(code).contains(`({ foo }) => [foo]`)
+ expect(code).contains(`_ctx0[0] + _ctx.bar`)
+
+ expect(ir.block.operation).toMatchObject([
+ {
+ type: IRNodeTypes.CREATE_COMPONENT_NODE,
+ tag: 'Comp',
+ slots: {
+ named: {
+ type: IRNodeTypes.BLOCK,
+ props: {
+ type: NodeTypes.SIMPLE_EXPRESSION,
+ content: '{ foo }',
+ },
+ },
+ },
+ },
+ ])
+ })
+
+ test('on component dynamically named slot', () => {
+ const { ir, code, vaporHelpers } = compileWithSlots(
+ `{{ foo + bar }}`,
+ )
+ expect(code).toMatchSnapshot()
+
+ expect(vaporHelpers).contains('withDestructure')
+ expect(code).contains(`({ foo }) => [foo]`)
+ expect(code).contains(`_ctx0[0] + _ctx.bar`)
+
+ expect(ir.block.operation).toMatchObject([
+ {
+ type: IRNodeTypes.CREATE_COMPONENT_NODE,
+ tag: 'Comp',
+ dynamicSlots: [
+ {
+ name: {
+ type: NodeTypes.SIMPLE_EXPRESSION,
+ content: 'named',
+ isStatic: false,
+ },
+ fn: {
+ type: IRNodeTypes.BLOCK,
+ props: {
+ type: NodeTypes.SIMPLE_EXPRESSION,
+ content: '{ foo }',
+ },
+ },
+ },
+ ],
+ },
+ ])
+ })
+
test('named slots w/ implicit default slot', () => {
const { ir, code } = compileWithSlots(
`
@@ -91,13 +183,56 @@ describe('compiler: transform slot', () => {
])
})
- test('nested slots', () => {
- const { code } = compileWithSlots(
- `
-
- `,
+ test('nested slots scoping', () => {
+ const { ir, code, vaporHelpers } = compileWithSlots(
+ `
+
+
+ {{ foo + bar + baz }}
+
+ {{ foo + bar + baz }}
+
+ `,
)
expect(code).toMatchSnapshot()
+
+ expect(vaporHelpers).contains('withDestructure')
+ expect(code).contains(`({ foo }) => [foo]`)
+ expect(code).contains(`({ bar }) => [bar]`)
+ expect(code).contains(`_ctx0[0] + _ctx1[0] + _ctx.baz`)
+ expect(code).contains(`_ctx0[0] + _ctx.bar + _ctx.baz`)
+
+ expect(ir.block.operation).toMatchObject([
+ {
+ type: IRNodeTypes.CREATE_COMPONENT_NODE,
+ tag: 'Comp',
+ props: [[]],
+ slots: {
+ default: {
+ type: IRNodeTypes.BLOCK,
+ props: {
+ type: NodeTypes.SIMPLE_EXPRESSION,
+ content: '{ foo }',
+ },
+ },
+ },
+ },
+ ])
+ expect(
+ (ir.block.operation[0] as any).slots.default.operation[0],
+ ).toMatchObject({
+ type: IRNodeTypes.CREATE_COMPONENT_NODE,
+ tag: 'Inner',
+ slots: {
+ default: {
+ type: IRNodeTypes.BLOCK,
+ props: {
+ type: NodeTypes.SIMPLE_EXPRESSION,
+ content: '{ bar }',
+ },
+ },
+ },
+ })
})
test('dynamic slots name', () => {
@@ -128,12 +263,16 @@ describe('compiler: transform slot', () => {
})
test('dynamic slots name w/ v-for', () => {
- const { ir, code } = compileWithSlots(
+ const { ir, code, vaporHelpers } = compileWithSlots(
`
- foo
+ foo
`,
)
expect(code).toMatchSnapshot()
+
+ expect(vaporHelpers).contains('withDestructure')
+ expect(code).contains(`({ bar }) => [bar]`)
+
expect(ir.block.operation[0].type).toBe(IRNodeTypes.CREATE_COMPONENT_NODE)
expect(ir.block.operation).toMatchObject([
{
@@ -196,14 +335,18 @@ describe('compiler: transform slot', () => {
})
test('dynamic slots name w/ v-if / v-else[-if]', () => {
- const { ir, code } = compileWithSlots(
+ const { ir, code, vaporHelpers } = compileWithSlots(
`
condition slot
- another condition
+ another condition
else condition
`,
)
expect(code).toMatchSnapshot()
+
+ expect(vaporHelpers).contains('withDestructure')
+ expect(code).contains(`({ foo, bar }) => [foo, bar]`)
+
expect(ir.block.operation[0].type).toBe(IRNodeTypes.CREATE_COMPONENT_NODE)
expect(ir.block.operation).toMatchObject([
{
@@ -277,5 +420,49 @@ describe('compiler: transform slot', () => {
},
})
})
+
+ test('error on invalid mixed slot usage', () => {
+ const onError = vi.fn()
+ const source = ``
+ compileWithSlots(source, { onError })
+ const index = source.lastIndexOf('v-slot="foo"')
+ expect(onError.mock.calls[0][0]).toMatchObject({
+ code: ErrorCodes.X_V_SLOT_MIXED_SLOT_USAGE,
+ loc: {
+ start: {
+ offset: index,
+ line: 1,
+ column: index + 1,
+ },
+ end: {
+ offset: index + 12,
+ line: 1,
+ column: index + 13,
+ },
+ },
+ })
+ })
+
+ test('error on v-slot usage on plain elements', () => {
+ const onError = vi.fn()
+ const source = ``
+ compileWithSlots(source, { onError })
+ const index = source.indexOf('v-slot')
+ expect(onError.mock.calls[0][0]).toMatchObject({
+ code: ErrorCodes.X_V_SLOT_MISPLACED,
+ loc: {
+ start: {
+ offset: index,
+ line: 1,
+ column: index + 1,
+ },
+ end: {
+ offset: index + 6,
+ line: 1,
+ column: index + 7,
+ },
+ },
+ })
+ })
})
})
diff --git a/packages/compiler-vapor/src/generate.ts b/packages/compiler-vapor/src/generate.ts
index f6f4b463f..bb36a3e2b 100644
--- a/packages/compiler-vapor/src/generate.ts
+++ b/packages/compiler-vapor/src/generate.ts
@@ -59,6 +59,11 @@ export class CodegenContext {
return () => (this.block = parent)
}
+ scopeLevel: number = 0
+ enterScope() {
+ return [this.scopeLevel++, () => this.scopeLevel--] as const
+ }
+
constructor(
public ir: RootIRNode,
options: CodegenOptions,
diff --git a/packages/compiler-vapor/src/generators/component.ts b/packages/compiler-vapor/src/generators/component.ts
index 1a3f9a6f6..b491b48f8 100644
--- a/packages/compiler-vapor/src/generators/component.ts
+++ b/packages/compiler-vapor/src/generators/component.ts
@@ -5,6 +5,7 @@ import {
type ComponentConditionalDynamicSlot,
type ComponentDynamicSlot,
type ComponentLoopDynamicSlot,
+ type ComponentSlotBlockIRNode,
type ComponentSlots,
type CreateComponentIRNode,
DynamicSlotType,
@@ -27,7 +28,11 @@ import {
} from './utils'
import { genExpression } from './expression'
import { genPropKey } from './prop'
-import { createSimpleExpression, toValidAssetId } from '@vue/compiler-dom'
+import {
+ createSimpleExpression,
+ toValidAssetId,
+ walkIdentifiers,
+} from '@vue/compiler-core'
import { genEventHandler } from './event'
import { genDirectiveModifiers, genDirectivesForElement } from './directive'
import { genModelHandler } from './modelValue'
@@ -151,7 +156,11 @@ function genSlots(slots: ComponentSlots, context: CodegenContext) {
const names = Object.keys(slots)
return genMulti(
names.length > 1 ? DELIMITERS_OBJECT_NEWLINE : DELIMITERS_OBJECT,
- ...names.map(name => [name, ': ', ...genBlock(slots[name], context)]),
+ ...names.map(name => [
+ name,
+ ': ',
+ ...genSlotBlockWithProps(slots[name], context),
+ ]),
)
}
@@ -188,7 +197,7 @@ function genBasicDynamicSlot(
return genMulti(
DELIMITERS_OBJECT_NEWLINE,
['name: ', ...genExpression(name, context)],
- ['fn: ', ...genBlock(fn, context)],
+ ['fn: ', ...genSlotBlockWithProps(fn, context)],
...(key !== undefined ? [`key: "${key}"`] : []),
)
}
@@ -210,7 +219,10 @@ function genLoopSlot(
const slotExpr = genMulti(
DELIMITERS_OBJECT_NEWLINE,
['name: ', ...context.withId(() => genExpression(name, context), idMap)],
- ['fn: ', ...context.withId(() => genBlock(fn, context), idMap)],
+ [
+ 'fn: ',
+ ...context.withId(() => genSlotBlockWithProps(fn, context), idMap),
+ ],
)
return [
...genCall(
@@ -248,3 +260,58 @@ function genConditionalSlot(
INDENT_END,
]
}
+
+function genSlotBlockWithProps(
+ oper: ComponentSlotBlockIRNode,
+ context: CodegenContext,
+) {
+ let isDestructureAssignment = false
+ let rawProps: string | undefined
+ let propsName: string | undefined
+ let exitScope: (() => void) | undefined
+ let depth: number | undefined
+ const { props } = oper
+ const idsOfProps = new Set()
+
+ if (props) {
+ rawProps = props.content
+ if ((isDestructureAssignment = !!props.ast)) {
+ ;[depth, exitScope] = context.enterScope()
+ propsName = `_ctx${depth}`
+ walkIdentifiers(
+ props.ast,
+ (id, _, __, ___, isLocal) => {
+ if (isLocal) idsOfProps.add(id.name)
+ },
+ true,
+ )
+ } else {
+ idsOfProps.add((propsName = rawProps))
+ }
+ }
+
+ const idMap: Record = {}
+
+ Array.from(idsOfProps).forEach(
+ (id, idIndex) =>
+ (idMap[id] = isDestructureAssignment ? `${propsName}[${idIndex}]` : null),
+ )
+ let blockFn = context.withId(
+ () => genBlock(oper, context, [propsName]),
+ idMap,
+ )
+ exitScope && exitScope()
+
+ if (isDestructureAssignment) {
+ const idMap: Record = {}
+ idsOfProps.forEach(id => (idMap[id] = null))
+
+ blockFn = genCall(
+ context.vaporHelper('withDestructure'),
+ ['(', rawProps, ') => ', ...genMulti(DELIMITERS_ARRAY, ...idsOfProps)],
+ blockFn,
+ )
+ }
+
+ return blockFn
+}
diff --git a/packages/compiler-vapor/src/generators/for.ts b/packages/compiler-vapor/src/generators/for.ts
index c8d74342c..20627c9e5 100644
--- a/packages/compiler-vapor/src/generators/for.ts
+++ b/packages/compiler-vapor/src/generators/for.ts
@@ -41,7 +41,8 @@ export function genFor(
}
}
- const propsName = `_ctx${id}`
+ const [depth, exitScope] = context.enterScope()
+ const propsName = `_ctx${depth}`
const idMap: Record = {}
Array.from(idsOfValue).forEach(
(id, idIndex) => (idMap[id] = `${propsName}[${idIndex}]`),
@@ -53,6 +54,7 @@ export function genFor(
() => genBlock(render, context, [propsName]),
idMap,
)
+ exitScope()
let getKeyFn: CodeFragment[] | false = false
if (keyProp) {
@@ -81,14 +83,14 @@ export function genFor(
if (rawKey) idMap[rawKey] = null
if (rawIndex) idMap[rawIndex] = null
const destructureAssignmentFn: CodeFragment[] = [
- '(_state, ',
+ '(',
...genMulti(
DELIMITERS_ARRAY,
rawValue ? rawValue : rawKey || rawIndex ? '_' : undefined,
rawKey ? rawKey : rawIndex ? '__' : undefined,
rawIndex,
),
- ' = _state) => ',
+ ') => ',
...genMulti(DELIMITERS_ARRAY, ...idsOfValue, rawKey, rawIndex),
]
@@ -101,7 +103,7 @@ export function genFor(
return [
NEWLINE,
- `const n${oper.id} = `,
+ `const n${id} = `,
...genCall(
vaporHelper('createFor'),
sourceExpr,
diff --git a/packages/compiler-vapor/src/ir.ts b/packages/compiler-vapor/src/ir.ts
index 83e03126f..0cbe6e772 100644
--- a/packages/compiler-vapor/src/ir.ts
+++ b/packages/compiler-vapor/src/ir.ts
@@ -208,7 +208,7 @@ export interface WithDirectiveIRNode extends BaseIRNode {
}
export interface ComponentSlotBlockIRNode extends BlockIRNode {
- // TODO slot props
+ props?: SimpleExpressionNode
}
export type ComponentSlots = Record
diff --git a/packages/compiler-vapor/src/transforms/vSlot.ts b/packages/compiler-vapor/src/transforms/vSlot.ts
index 7aeb2abc9..7af6d4b37 100644
--- a/packages/compiler-vapor/src/transforms/vSlot.ts
+++ b/packages/compiler-vapor/src/transforms/vSlot.ts
@@ -11,9 +11,9 @@ import {
import type { NodeTransform, TransformContext } from '../transform'
import { newBlock } from './utils'
import {
- type BlockIRNode,
type ComponentBasicDynamicSlot,
type ComponentConditionalDynamicSlot,
+ type ComponentSlotBlockIRNode,
DynamicFlag,
DynamicSlotType,
type IRFor,
@@ -25,19 +25,22 @@ import { findDir, resolveExpression } from '../utils'
export const transformVSlot: NodeTransform = (node, context) => {
if (node.type !== NodeTypes.ELEMENT) return
- let dir: VaporDirectiveNode | undefined
+ const dir = findDir(node, 'slot', true)
const { tagType, children } = node
const { parent } = context
- const isDefaultSlot = tagType === ElementTypes.COMPONENT && children.length
+ const isComponent = tagType === ElementTypes.COMPONENT
const isSlotTemplate =
isTemplateNode(node) &&
parent &&
parent.node.type === NodeTypes.ELEMENT &&
parent.node.tagType === ElementTypes.COMPONENT
- if (isDefaultSlot) {
- const defaultChildren = children.filter(
+ if (isComponent && children.length) {
+ const arg = dir && dir.arg
+ const slotName = arg ? arg.content : 'default'
+
+ const nonSlotTemplateChildren = children.filter(
n =>
isNonWhitespaceContent(node) &&
!(n.type === NodeTypes.ELEMENT && n.props.some(isVSlot)),
@@ -45,6 +48,7 @@ export const transformVSlot: NodeTransform = (node, context) => {
const [block, onExit] = createSlotBlock(
node,
+ dir,
context as TransformContext,
)
@@ -54,25 +58,44 @@ export const transformVSlot: NodeTransform = (node, context) => {
return () => {
onExit()
- if (defaultChildren.length) {
+ let hasOtherSlots = !!Object.keys(slots).length
+
+ if (dir && (hasOtherSlots || dynamicSlots.length)) {
+ // already has on-component slot - this is incorrect usage.
+ context.options.onError(
+ createCompilerError(ErrorCodes.X_V_SLOT_MIXED_SLOT_USAGE, dir.loc),
+ )
+ // discarding other slots, referenced how compiler-core implements
+ Object.keys(slots).forEach(slotName => delete slots[slotName])
+ dynamicSlots.length = 0
+ hasOtherSlots = false
+ }
+
+ if (nonSlotTemplateChildren.length) {
if (slots.default) {
context.options.onError(
createCompilerError(
ErrorCodes.X_V_SLOT_EXTRANEOUS_DEFAULT_SLOT_CHILDREN,
- defaultChildren[0].loc,
+ nonSlotTemplateChildren[0].loc,
),
)
+ } else if (!arg || arg.isStatic) {
+ slots[slotName] = block
} else {
- slots.default = block
+ dynamicSlots.push({
+ slotType: DynamicSlotType.BASIC,
+ name: arg,
+ fn: block,
+ })
}
context.slots = slots
- } else if (Object.keys(slots).length) {
+ } else if (hasOtherSlots) {
context.slots = slots
}
if (dynamicSlots.length) context.dynamicSlots = dynamicSlots
}
- } else if (isSlotTemplate && (dir = findDir(node, 'slot', true))) {
+ } else if (isSlotTemplate && dir) {
let { arg } = dir
context.dynamic.flags |= DynamicFlag.NON_TEMPLATE
@@ -85,6 +108,7 @@ export const transformVSlot: NodeTransform = (node, context) => {
const [block, onExit] = createSlotBlock(
node,
+ dir,
context as TransformContext,
)
@@ -173,16 +197,22 @@ export const transformVSlot: NodeTransform = (node, context) => {
}
return () => onExit()
+ } else if (!isComponent && dir) {
+ context.options.onError(
+ createCompilerError(ErrorCodes.X_V_SLOT_MISPLACED, dir.loc),
+ )
}
}
function createSlotBlock(
slotNode: ElementNode,
+ dir: VaporDirectiveNode | undefined,
context: TransformContext,
-): [BlockIRNode, () => void] {
- const branch: BlockIRNode = newBlock(slotNode)
- const exitBlock = context.enterBlock(branch)
- return [branch, exitBlock]
+): [ComponentSlotBlockIRNode, () => void] {
+ const block: ComponentSlotBlockIRNode = newBlock(slotNode)
+ block.props = dir && dir.exp
+ const exitBlock = context.enterBlock(block)
+ return [block, exitBlock]
}
function isNonWhitespaceContent(node: TemplateChildNode): boolean {