Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(compiler-vapor): slot outlet #182

Merged
merged 16 commits into from
May 2, 2024
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
Loading