test: add transform test

This commit is contained in:
三咲智子 Kevin Deng 2023-12-09 04:06:46 +08:00
parent e3b21b25b1
commit 5f769745fa
No known key found for this signature in database
GPG Key ID: 69992F2250DFD93E
7 changed files with 234 additions and 15 deletions

View File

@ -1,6 +1,6 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`v-bind > .camel modifier 1`] = `
exports[`compiler: codegen v-bind > .camel modifier 1`] = `
"import { template as _template, children as _children, effect as _effect, setAttr as _setAttr } from 'vue/vapor';
export function render(_ctx) {
@ -14,7 +14,7 @@ export function render(_ctx) {
}"
`;
exports[`v-bind > dynamic arg 1`] = `
exports[`compiler: codegen v-bind > dynamic arg 1`] = `
"import { template as _template, children as _children, effect as _effect, setAttr as _setAttr } from 'vue/vapor';
export function render(_ctx) {
@ -28,7 +28,7 @@ export function render(_ctx) {
}"
`;
exports[`v-bind > no expression (shorthand) 1`] = `
exports[`compiler: codegen v-bind > no expression (shorthand) 1`] = `
"import { template as _template, children as _children, effect as _effect, setAttr as _setAttr } from 'vue/vapor';
export function render(_ctx) {
@ -42,7 +42,7 @@ export function render(_ctx) {
}"
`;
exports[`v-bind > no expression 1`] = `
exports[`compiler: codegen v-bind > no expression 1`] = `
"import { template as _template, children as _children, effect as _effect, setAttr as _setAttr } from 'vue/vapor';
export function render(_ctx) {
@ -56,7 +56,7 @@ export function render(_ctx) {
}"
`;
exports[`v-bind > should error if no expression 1`] = `
exports[`compiler: codegen v-bind > should error if no expression 1`] = `
"import { template as _template } from 'vue/vapor';
export function render(_ctx) {
@ -66,7 +66,7 @@ export function render(_ctx) {
}"
`;
exports[`v-bind > simple expression 1`] = `
exports[`compiler: codegen v-bind > simple expression 1`] = `
"import { template as _template, children as _children, effect as _effect, setAttr as _setAttr } from 'vue/vapor';
export function render(_ctx) {

View File

@ -1,5 +1,34 @@
import { type RootNode, BindingTypes, ErrorCodes } from '@vue/compiler-dom'
import { type CompilerOptions, compile as _compile } from '../../src'
import {
type RootNode,
ErrorCodes,
NodeTypes,
BindingTypes,
} from '@vue/compiler-dom'
import {
type RootIRNode,
type CompilerOptions,
parse,
transform,
transformVBind,
transformElement,
IRNodeTypes,
compile as _compile,
} from '../../src'
function parseWithVBind(
template: string,
options: CompilerOptions = {},
): RootIRNode {
const ast = parse(template)
const ir = transform(ast, {
nodeTransforms: [transformElement],
directiveTransforms: {
bind: transformVBind,
},
...options,
})
return ir
}
function compile(template: string | RootNode, options: CompilerOptions = {}) {
let { code } = _compile(template, {
@ -10,7 +39,189 @@ function compile(template: string | RootNode, options: CompilerOptions = {}) {
return code
}
describe('v-bind', () => {
describe('compiler: transform v-bind', () => {
test('basic', () => {
const node = parseWithVBind(`<div v-bind:id="id"/>`)
expect(node.dynamic.children[0]).toMatchObject({
id: 1,
referenced: true,
})
expect(node.template[0]).toMatchObject({
type: IRNodeTypes.TEMPLATE_FACTORY,
template: '<div></div>',
})
expect(node.effect).lengthOf(1)
expect(node.effect[0].expressions).lengthOf(1)
expect(node.effect[0].operations).lengthOf(1)
expect(node.effect[0]).toMatchObject({
expressions: [
{
type: NodeTypes.SIMPLE_EXPRESSION,
content: 'id',
isStatic: false,
},
],
operations: [
{
type: IRNodeTypes.SET_PROP,
element: 1,
key: {
type: NodeTypes.SIMPLE_EXPRESSION,
content: 'id',
isStatic: true,
loc: {
start: { line: 1, column: 13, offset: 12 },
end: { line: 1, column: 15, offset: 14 },
source: 'id',
},
},
value: {
type: NodeTypes.SIMPLE_EXPRESSION,
content: 'id',
isStatic: false,
loc: {
source: 'id',
start: { line: 1, column: 17, offset: 16 },
end: { line: 1, column: 19, offset: 18 },
},
},
},
],
})
})
test('no expression', () => {
const node = parseWithVBind(`<div v-bind:id />`)
expect(node.effect[0].operations[0]).toMatchObject({
type: IRNodeTypes.SET_PROP,
key: {
content: `id`,
isStatic: true,
loc: {
start: { line: 1, column: 13, offset: 12 },
end: { line: 1, column: 15, offset: 14 },
},
},
value: {
content: `id`,
isStatic: false,
loc: {
start: { line: 1, column: 13, offset: 12 },
end: { line: 1, column: 15, offset: 14 },
},
},
})
})
test('no expression (shorthand)', () => {
const node = parseWithVBind(`<div :id />`)
expect(node.effect[0].operations[0]).toMatchObject({
type: IRNodeTypes.SET_PROP,
key: {
content: `id`,
isStatic: true,
},
value: {
content: `id`,
isStatic: false,
},
})
})
test('dynamic arg', () => {
const node = parseWithVBind(`<div v-bind:[id]="id"/>`)
expect(node.effect[0].operations[0]).toMatchObject({
type: IRNodeTypes.SET_PROP,
element: 1,
key: {
type: NodeTypes.SIMPLE_EXPRESSION,
content: 'id',
isStatic: false,
},
value: {
type: NodeTypes.SIMPLE_EXPRESSION,
content: 'id',
isStatic: false,
},
})
})
test('should error if empty expression', () => {
const onError = vi.fn()
const node = parseWithVBind(`<div v-bind:arg="" />`, { onError })
expect(onError.mock.calls[0][0]).toMatchObject({
code: ErrorCodes.X_V_BIND_NO_EXPRESSION,
loc: {
start: { line: 1, column: 6 },
end: { line: 1, column: 19 },
},
})
expect(node.template[0]).toMatchObject({
type: IRNodeTypes.TEMPLATE_FACTORY,
template: '<div arg=""></div>',
})
})
test.fails('.camel modifier', () => {
const node = parseWithVBind(`<div v-bind:foo-bar.camel="id"/>`)
expect(node.effect[0].operations[0]).toMatchObject({
key: {
content: `fooBar`,
isStatic: true,
},
value: {
content: `id`,
isStatic: false,
},
})
})
test.fails('.camel modifier w/ no expression', () => {
const node = parseWithVBind(`<div v-bind:foo-bar.camel />`)
expect(node.effect[0].operations[0]).toMatchObject({
key: {
content: `fooBar`,
isStatic: true,
},
value: {
content: `fooBar`,
isStatic: false,
},
})
})
test.fails('.camel modifier w/ dynamic arg', () => {
const node = parseWithVBind(`<div v-bind:[foo].camel="id"/>`)
expect(node.effect[0].operations[0]).toMatchObject({
key: {
content: `foo`,
isStatic: false,
somethingShouldBeTrue: true,
},
value: {
content: `id`,
isStatic: false,
},
})
})
test.todo('.camel modifier w/ dynamic arg + prefixIdentifiers')
test.todo('.prop modifier')
test.todo('.prop modifier w/ no expression')
test.todo('.prop modifier w/ dynamic arg')
test.todo('.prop modifier w/ dynamic arg + prefixIdentifiers')
test.todo('.prop modifier (shorthand)')
test.todo('.prop modifier (shortband) w/ no expression')
test.todo('.attr modifier')
test.todo('.attr modifier w/ no expression')
})
// TODO: combine with above
describe('compiler: codegen v-bind', () => {
test('simple expression', () => {
const code = compile(`<div :id="id"></div>`, {
bindingMetadata: {

View File

@ -372,7 +372,7 @@ function genOperation(oper: OperationNode, context: CodegenContext) {
function genSetProp(oper: SetPropIRNode, context: CodegenContext) {
const { push, pushWithNewline, vaporHelper } = context
pushWithNewline(`${vaporHelper('setAttr')}(n${oper.element}, `)
genExpression(oper.name, context)
genExpression(oper.key, context)
push(`, undefined, `)
genExpression(oper.value, context)
push(')')
@ -437,7 +437,7 @@ function genSetEvent(oper: SetEventIRNode, context: CodegenContext) {
pushWithNewline(`${vaporHelper('on')}(n${oper.element}, `)
// second arg: event name
genExpression(oper.name, context)
genExpression(oper.key, context)
push(', ')
const { keys, nonKeys, options } = oper.modifiers

View File

@ -4,3 +4,11 @@ export { generate } from './generate'
export { compile, type CompilerOptions } from './compile'
export * from './ir'
export * from './errors'
export { transformElement } from './transforms/transformElement'
export { transformInterpolation } from './transforms/transformInterpolation'
export { transformVBind } from './transforms/vBind'
export { transformVHtml } from './transforms/vHtml'
export { transformVOn } from './transforms/vOn'
export { transformOnce } from './transforms/vOnce'
export { transformVShow } from './transforms/vShow'
export { transformVText } from './transforms/vText'

View File

@ -58,7 +58,7 @@ export interface FragmentFactoryIRNode extends BaseIRNode {
export interface SetPropIRNode extends BaseIRNode {
type: IRNodeTypes.SET_PROP
element: number
name: IRExpression
key: IRExpression
value: IRExpression
}
@ -71,7 +71,7 @@ export interface SetTextIRNode extends BaseIRNode {
export interface SetEventIRNode extends BaseIRNode {
type: IRNodeTypes.SET_EVENT
element: number
name: IRExpression
key: IRExpression
value: IRExpression
modifiers: {
// modifiers for addEventListener() options, e.g. .passive & .capture

View File

@ -36,7 +36,7 @@ export const transformVBind: DirectiveTransform = (dir, node, context) => {
type: IRNodeTypes.SET_PROP,
loc: dir.loc,
element: context.reference(),
name: arg,
key: arg,
value: exp,
},
],

View File

@ -45,7 +45,7 @@ export const transformVOn: DirectiveTransform = (dir, node, context) => {
type: IRNodeTypes.SET_EVENT,
loc,
element: context.reference(),
name: createSimpleExpression(name, true, arg.loc),
key: createSimpleExpression(name, true, arg.loc),
value: exp,
modifiers: {
keys: keyModifiers,