diff --git a/packages/compiler-core/__tests__/transforms/vBind.spec.ts b/packages/compiler-core/__tests__/transforms/vBind.spec.ts index 322cf9d1bde..2e94dc1f7de 100644 --- a/packages/compiler-core/__tests__/transforms/vBind.spec.ts +++ b/packages/compiler-core/__tests__/transforms/vBind.spec.ts @@ -72,6 +72,60 @@ describe('compiler: transform v-bind', () => { }) }) + test('no expression', () => { + const node = parseWithVBind(`
`) + const props = (node.codegenNode as VNodeCall).props as ObjectExpression + expect(props.properties[0]).toMatchObject({ + 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: 1, + offset: 0 + }, + end: { + line: 1, + column: 1, + offset: 0 + } + } + } + }) + }) + + test('no expression (shorthand)', () => { + const node = parseWithVBind(`
`) + const props = (node.codegenNode as VNodeCall).props as ObjectExpression + expect(props.properties[0]).toMatchObject({ + key: { + content: `id`, + isStatic: true + }, + value: { + content: `id`, + isStatic: false + } + }) + }) + test('dynamic arg', () => { const node = parseWithVBind(`
`) const props = (node.codegenNode as VNodeCall).props as CallExpression @@ -98,9 +152,9 @@ describe('compiler: transform v-bind', () => { }) }) - test('should error if no expression', () => { + test('should error if empty expression', () => { const onError = vi.fn() - const node = parseWithVBind(`
`, { onError }) + const node = parseWithVBind(`
`, { onError }) const props = (node.codegenNode as VNodeCall).props as ObjectExpression expect(onError.mock.calls[0][0]).toMatchObject({ code: ErrorCodes.X_V_BIND_NO_EXPRESSION, @@ -111,7 +165,7 @@ describe('compiler: transform v-bind', () => { }, end: { line: 1, - column: 16 + column: 19 } } }) @@ -142,6 +196,21 @@ describe('compiler: transform v-bind', () => { }) }) + test('.camel modifier w/ no expression', () => { + const node = parseWithVBind(`
`) + const props = (node.codegenNode as VNodeCall).props as ObjectExpression + expect(props.properties[0]).toMatchObject({ + key: { + content: `fooBar`, + isStatic: true + }, + value: { + content: `fooBar`, + isStatic: false + } + }) + }) + test('.camel modifier w/ dynamic arg', () => { const node = parseWithVBind(`
`) const props = (node.codegenNode as VNodeCall).props as CallExpression @@ -219,6 +288,21 @@ describe('compiler: transform v-bind', () => { }) }) + test('.prop modifier w/ no expression', () => { + const node = parseWithVBind(`
`) + const props = (node.codegenNode as VNodeCall).props as ObjectExpression + expect(props.properties[0]).toMatchObject({ + key: { + content: `.fooBar`, + isStatic: true + }, + value: { + content: `fooBar`, + isStatic: false + } + }) + }) + test('.prop modifier w/ dynamic arg', () => { const node = parseWithVBind(`
`) const props = (node.codegenNode as VNodeCall).props as CallExpression @@ -296,6 +380,21 @@ describe('compiler: transform v-bind', () => { }) }) + test('.prop modifier (shortband) w/ no expression', () => { + const node = parseWithVBind(`
`) + const props = (node.codegenNode as VNodeCall).props as ObjectExpression + expect(props.properties[0]).toMatchObject({ + key: { + content: `.fooBar`, + isStatic: true + }, + value: { + content: `fooBar`, + isStatic: false + } + }) + }) + test('.attr modifier', () => { const node = parseWithVBind(`
`) const props = (node.codegenNode as VNodeCall).props as ObjectExpression @@ -310,4 +409,19 @@ describe('compiler: transform v-bind', () => { } }) }) + + test('.attr modifier w/ no expression', () => { + const node = parseWithVBind(`
`) + const props = (node.codegenNode as VNodeCall).props as ObjectExpression + expect(props.properties[0]).toMatchObject({ + key: { + content: `^foo-bar`, + isStatic: true + }, + value: { + content: `fooBar`, + isStatic: false + } + }) + }) }) diff --git a/packages/compiler-core/src/transforms/vBind.ts b/packages/compiler-core/src/transforms/vBind.ts index ffaf903b9e8..9f85d6b3d63 100644 --- a/packages/compiler-core/src/transforms/vBind.ts +++ b/packages/compiler-core/src/transforms/vBind.ts @@ -3,17 +3,19 @@ import { createObjectProperty, createSimpleExpression, ExpressionNode, + locStub, NodeTypes } from '../ast' import { createCompilerError, ErrorCodes } from '../errors' import { camelize } from '@vue/shared' import { CAMELIZE } from '../runtimeHelpers' +import { processExpression } from './transformExpression' // v-bind without arg is handled directly in ./transformElements.ts due to it affecting // codegen for the entire props object. This transform here is only for v-bind // *with* args. export const transformBind: DirectiveTransform = (dir, _node, context) => { - const { exp, modifiers, loc } = dir + const { modifiers, loc } = dir const arg = dir.arg! if (arg.type !== NodeTypes.SIMPLE_EXPRESSION) { @@ -46,6 +48,18 @@ export const transformBind: DirectiveTransform = (dir, _node, context) => { } } + // :arg is replaced by :arg="arg" + let { exp } = dir + if (!exp && arg.type === NodeTypes.SIMPLE_EXPRESSION) { + const propName = camelize(arg.loc.source) + const simpleExpression = createSimpleExpression(propName, false, { + ...locStub, + source: propName + }) + + exp = dir.exp = processExpression(simpleExpression, context) + } + if ( !exp || (exp.type === NodeTypes.SIMPLE_EXPRESSION && !exp.content.trim()) diff --git a/packages/compiler-sfc/__tests__/__snapshots__/compileTemplate.spec.ts.snap b/packages/compiler-sfc/__tests__/__snapshots__/compileTemplate.spec.ts.snap index 35b87c4ec71..f97eef6094e 100644 --- a/packages/compiler-sfc/__tests__/__snapshots__/compileTemplate.spec.ts.snap +++ b/packages/compiler-sfc/__tests__/__snapshots__/compileTemplate.spec.ts.snap @@ -79,7 +79,6 @@ exports[`source map 1`] = ` exports[`template errors 1`] = ` [ [SyntaxError: Error parsing JavaScript expression: Unexpected token (1:3)], - [SyntaxError: v-bind is missing expression.], [SyntaxError: v-model can only be used on ,