Skip to content
This repository was archived by the owner on Jul 19, 2025. It is now read-only.

feat(compiler-vapor): slot outlet #182

Merged
merged 16 commits into from
May 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`compiler: transform <slot> outlets > default slot outlet 1`] = `
"import { createSlot as _createSlot } from 'vue/vapor';

export function render(_ctx) {
const n0 = _createSlot("default", null)
return n0
}"
`;

exports[`compiler: transform <slot> outlets > default slot outlet with fallback 1`] = `
"import { createSlot as _createSlot, template as _template } from 'vue/vapor';
const t0 = _template("<div></div>")

export function render(_ctx) {
const n0 = _createSlot("default", null, () => {
const n2 = t0()
return n2
})
return n0
}"
`;

exports[`compiler: transform <slot> outlets > default slot outlet with props 1`] = `
"import { createSlot as _createSlot } from 'vue/vapor';

export function render(_ctx) {
const n0 = _createSlot("default", [
{
foo: () => ("bar"),
baz: () => (_ctx.qux),
fooBar: () => (_ctx.foo-_ctx.bar)
}
])
return n0
}"
`;

exports[`compiler: transform <slot> outlets > dynamically named slot outlet 1`] = `
"import { createSlot as _createSlot } from 'vue/vapor';

export function render(_ctx) {
const n0 = _createSlot(() => (_ctx.foo + _ctx.bar), null)
return n0
}"
`;

exports[`compiler: transform <slot> outlets > dynamically named slot outlet with v-bind shorthand 1`] = `
"import { createSlot as _createSlot } from 'vue/vapor';

export function render(_ctx) {
const n0 = _createSlot(() => (_ctx.name), null)
return n0
}"
`;

exports[`compiler: transform <slot> outlets > error on unexpected custom directive on <slot> 1`] = `
"import { createSlot as _createSlot } from 'vue/vapor';

export function render(_ctx) {
const n0 = _createSlot("default", null)
return n0
}"
`;

exports[`compiler: transform <slot> outlets > error on unexpected custom directive with v-show on <slot> 1`] = `
"import { createSlot as _createSlot } from 'vue/vapor';

export function render(_ctx) {
const n0 = _createSlot("default", null)
return n0
}"
`;

exports[`compiler: transform <slot> outlets > named slot outlet with fallback 1`] = `
"import { createSlot as _createSlot, template as _template } from 'vue/vapor';
const t0 = _template("<div></div>")

export function render(_ctx) {
const n0 = _createSlot("foo", null, () => {
const n2 = t0()
return n2
})
return n0
}"
`;

exports[`compiler: transform <slot> outlets > statically named slot outlet 1`] = `
"import { createSlot as _createSlot } from 'vue/vapor';

export function render(_ctx) {
const n0 = _createSlot("foo", null)
return n0
}"
`;

exports[`compiler: transform <slot> outlets > statically named slot outlet with props 1`] = `
"import { createSlot as _createSlot } from 'vue/vapor';

export function render(_ctx) {
const n0 = _createSlot("foo", [
{
foo: () => ("bar"),
baz: () => (_ctx.qux)
}
])
return n0
}"
`;

exports[`compiler: transform <slot> outlets > statically named slot outlet with v-bind="obj" 1`] = `
"import { createSlot as _createSlot } from 'vue/vapor';

export function render(_ctx) {
const n0 = _createSlot("foo", [
{ foo: () => ("bar") },
() => (_ctx.obj),
{ baz: () => (_ctx.qux) }
])
return n0
}"
`;

exports[`compiler: transform <slot> outlets > statically named slot outlet with v-on 1`] = `
"import { createSlot as _createSlot, toHandlers as _toHandlers } from 'vue/vapor';

export function render(_ctx) {
const n0 = _createSlot("default", [
{ onClick: () => _ctx.foo },
() => (_toHandlers(_ctx.bar)),
{ baz: () => (_ctx.qux) }
])
return n0
}"
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,258 @@
import { ErrorCodes, NodeTypes } from '@vue/compiler-core'
import {
IRNodeTypes,
transformChildren,
transformElement,
transformSlotOutlet,
transformText,
transformVBind,
transformVOn,
transformVShow,
} from '../../src'
import { makeCompile } from './_utils'

const compileWithSlotsOutlet = makeCompile({
nodeTransforms: [
transformText,
transformSlotOutlet,
transformElement,
transformChildren,
],
directiveTransforms: {
bind: transformVBind,
on: transformVOn,
show: transformVShow,
},
})

describe('compiler: transform <slot> outlets', () => {
test('default slot outlet', () => {
const { ir, code, vaporHelpers } = compileWithSlotsOutlet(`<slot />`)
expect(code).toMatchSnapshot()
expect(vaporHelpers).toContain('createSlot')
expect(ir.block.effect).toEqual([])
expect(ir.block.operation).toMatchObject([
{
type: IRNodeTypes.SLOT_OUTLET_NODE,
id: 0,
name: {
type: NodeTypes.SIMPLE_EXPRESSION,
content: 'default',
isStatic: true,
},
props: [],
fallback: undefined,
},
])
})

test('statically named slot outlet', () => {
const { ir, code } = compileWithSlotsOutlet(`<slot name="foo" />`)
expect(code).toMatchSnapshot()
expect(ir.block.operation).toMatchObject([
{
type: IRNodeTypes.SLOT_OUTLET_NODE,
id: 0,
name: {
type: NodeTypes.SIMPLE_EXPRESSION,
content: 'foo',
isStatic: true,
},
},
])
})

test('dynamically named slot outlet', () => {
const { ir, code } = compileWithSlotsOutlet(`<slot :name="foo + bar" />`)
expect(code).toMatchSnapshot()
expect(ir.block.operation).toMatchObject([
{
type: IRNodeTypes.SLOT_OUTLET_NODE,
id: 0,
name: {
type: NodeTypes.SIMPLE_EXPRESSION,
content: 'foo + bar',
isStatic: false,
},
},
])
})

test('dynamically named slot outlet with v-bind shorthand', () => {
const { ir, code } = compileWithSlotsOutlet(`<slot :name />`)
expect(code).toMatchSnapshot()
expect(ir.block.operation).toMatchObject([
{
type: IRNodeTypes.SLOT_OUTLET_NODE,
id: 0,
name: {
type: NodeTypes.SIMPLE_EXPRESSION,
content: 'name',
isStatic: false,
},
},
])
})

test('default slot outlet with props', () => {
const { ir, code } = compileWithSlotsOutlet(
`<slot foo="bar" :baz="qux" :foo-bar="foo-bar" />`,
)
expect(code).toMatchSnapshot()
expect(ir.block.operation).toMatchObject([
{
type: IRNodeTypes.SLOT_OUTLET_NODE,
name: { content: 'default' },
props: [
[
{ key: { content: 'foo' }, values: [{ content: 'bar' }] },
{ key: { content: 'baz' }, values: [{ content: 'qux' }] },
{ key: { content: 'fooBar' }, values: [{ content: 'foo-bar' }] },
],
],
},
])
})

test('statically named slot outlet with props', () => {
const { ir, code } = compileWithSlotsOutlet(
`<slot name="foo" foo="bar" :baz="qux" />`,
)
expect(code).toMatchSnapshot()
expect(ir.block.operation).toMatchObject([
{
type: IRNodeTypes.SLOT_OUTLET_NODE,
name: { content: 'foo' },
props: [
[
{ key: { content: 'foo' }, values: [{ content: 'bar' }] },
{ key: { content: 'baz' }, values: [{ content: 'qux' }] },
],
],
},
])
})

test('statically named slot outlet with v-bind="obj"', () => {
const { ir, code } = compileWithSlotsOutlet(
`<slot name="foo" foo="bar" v-bind="obj" :baz="qux" />`,
)
expect(code).toMatchSnapshot()
expect(ir.block.operation).toMatchObject([
{
type: IRNodeTypes.SLOT_OUTLET_NODE,
name: { content: 'foo' },
props: [
[{ key: { content: 'foo' }, values: [{ content: 'bar' }] }],
{ value: { content: 'obj', isStatic: false } },
[{ key: { content: 'baz' }, values: [{ content: 'qux' }] }],
],
},
])
})

test('statically named slot outlet with v-on', () => {
const { ir, code } = compileWithSlotsOutlet(
`<slot @click="foo" v-on="bar" :baz="qux" />`,
)
expect(code).toMatchSnapshot()
expect(ir.block.operation).toMatchObject([
{
type: IRNodeTypes.SLOT_OUTLET_NODE,
props: [
[{ key: { content: 'click' }, values: [{ content: 'foo' }] }],
{ value: { content: 'bar' }, handler: true },
[{ key: { content: 'baz' }, values: [{ content: 'qux' }] }],
],
},
])
})

test('default slot outlet with fallback', () => {
const { ir, code } = compileWithSlotsOutlet(`<slot><div/></slot>`)
expect(code).toMatchSnapshot()
expect(ir.template[0]).toMatchObject('<div></div>')
expect(ir.block.operation).toMatchObject([
{
type: IRNodeTypes.SLOT_OUTLET_NODE,
id: 0,
name: { content: 'default' },
fallback: {
type: IRNodeTypes.BLOCK,
dynamic: {
children: [{ template: 0, id: 2 }],
},
returns: [2],
},
},
])
})

test('named slot outlet with fallback', () => {
const { ir, code } = compileWithSlotsOutlet(
`<slot name="foo"><div/></slot>`,
)
expect(code).toMatchSnapshot()
expect(ir.template[0]).toMatchObject('<div></div>')
expect(ir.block.operation).toMatchObject([
{
type: IRNodeTypes.SLOT_OUTLET_NODE,
id: 0,
name: { content: 'foo' },
fallback: {
type: IRNodeTypes.BLOCK,
dynamic: {
children: [{ template: 0, id: 2 }],
},
returns: [2],
},
},
])
})

test('error on unexpected custom directive on <slot>', () => {
const onError = vi.fn()
const source = `<slot v-foo />`
const index = source.indexOf('v-foo')
const { code } = compileWithSlotsOutlet(source, { onError })
expect(code).toMatchSnapshot()
expect(onError.mock.calls[0][0]).toMatchObject({
code: ErrorCodes.X_V_SLOT_UNEXPECTED_DIRECTIVE_ON_SLOT_OUTLET,
loc: {
start: {
offset: index,
line: 1,
column: index + 1,
},
end: {
offset: index + 5,
line: 1,
column: index + 6,
},
},
})
})

test('error on unexpected custom directive with v-show on <slot>', () => {
const onError = vi.fn()
const source = `<slot v-show="ok" />`
const index = source.indexOf('v-show="ok"')
const { code } = compileWithSlotsOutlet(source, { onError })
expect(code).toMatchSnapshot()
expect(onError.mock.calls[0][0]).toMatchObject({
code: ErrorCodes.X_V_SLOT_UNEXPECTED_DIRECTIVE_ON_SLOT_OUTLET,
loc: {
start: {
offset: index,
line: 1,
column: index + 1,
},
end: {
offset: index + 11,
line: 1,
column: index + 12,
},
},
})
})
})
Loading