diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vBind.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vBind.spec.ts.snap index 379d8c21d..335280313 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vBind.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vBind.spec.ts.snap @@ -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) { diff --git a/packages/compiler-vapor/__tests__/transforms/vBind.spec.ts b/packages/compiler-vapor/__tests__/transforms/vBind.spec.ts index 6a990c786..fbaf2b089 100644 --- a/packages/compiler-vapor/__tests__/transforms/vBind.spec.ts +++ b/packages/compiler-vapor/__tests__/transforms/vBind.spec.ts @@ -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(`
`) + + expect(node.dynamic.children[0]).toMatchObject({ + id: 1, + referenced: true, + }) + expect(node.template[0]).toMatchObject({ + type: IRNodeTypes.TEMPLATE_FACTORY, + template: '', + }) + 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(``) + + 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(``) + + 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(``) + 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(``, { 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: '', + }) + }) + + test.fails('.camel modifier', () => { + const node = parseWithVBind(``) + 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(``) + 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(``) + 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(``, { bindingMetadata: { diff --git a/packages/compiler-vapor/src/generate.ts b/packages/compiler-vapor/src/generate.ts index bdb4f8820..d92def7fe 100644 --- a/packages/compiler-vapor/src/generate.ts +++ b/packages/compiler-vapor/src/generate.ts @@ -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 diff --git a/packages/compiler-vapor/src/index.ts b/packages/compiler-vapor/src/index.ts index 1e7b4bd9f..3b1c3d429 100644 --- a/packages/compiler-vapor/src/index.ts +++ b/packages/compiler-vapor/src/index.ts @@ -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' diff --git a/packages/compiler-vapor/src/ir.ts b/packages/compiler-vapor/src/ir.ts index a8c46ce16..172f32fd7 100644 --- a/packages/compiler-vapor/src/ir.ts +++ b/packages/compiler-vapor/src/ir.ts @@ -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 diff --git a/packages/compiler-vapor/src/transforms/vBind.ts b/packages/compiler-vapor/src/transforms/vBind.ts index 1502a2983..6bd4cc939 100644 --- a/packages/compiler-vapor/src/transforms/vBind.ts +++ b/packages/compiler-vapor/src/transforms/vBind.ts @@ -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, }, ], diff --git a/packages/compiler-vapor/src/transforms/vOn.ts b/packages/compiler-vapor/src/transforms/vOn.ts index 0183138ef..f1764ffb0 100644 --- a/packages/compiler-vapor/src/transforms/vOn.ts +++ b/packages/compiler-vapor/src/transforms/vOn.ts @@ -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,