Skip to content

Commit

Permalink
feat(compiler-core): support v-bind shorthand for key and value with …
Browse files Browse the repository at this point in the history
…the same name (#9451)
  • Loading branch information
zhiyuanzmj authored Nov 2, 2023
1 parent 48b47a1 commit 26399aa
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 6 deletions.
120 changes: 117 additions & 3 deletions packages/compiler-core/__tests__/transforms/vBind.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,60 @@ describe('compiler: transform v-bind', () => {
})
})

test('no expression', () => {
const node = parseWithVBind(`<div v-bind:id />`)
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(`<div :id />`)
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(`<div v-bind:[id]="id"/>`)
const props = (node.codegenNode as VNodeCall).props as CallExpression
Expand All @@ -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(`<div v-bind:arg />`, { onError })
const node = parseWithVBind(`<div v-bind:arg="" />`, { onError })
const props = (node.codegenNode as VNodeCall).props as ObjectExpression
expect(onError.mock.calls[0][0]).toMatchObject({
code: ErrorCodes.X_V_BIND_NO_EXPRESSION,
Expand All @@ -111,7 +165,7 @@ describe('compiler: transform v-bind', () => {
},
end: {
line: 1,
column: 16
column: 19
}
}
})
Expand Down Expand Up @@ -142,6 +196,21 @@ describe('compiler: transform v-bind', () => {
})
})

test('.camel modifier w/ no expression', () => {
const node = parseWithVBind(`<div v-bind:foo-bar.camel />`)
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(`<div v-bind:[foo].camel="id"/>`)
const props = (node.codegenNode as VNodeCall).props as CallExpression
Expand Down Expand Up @@ -219,6 +288,21 @@ describe('compiler: transform v-bind', () => {
})
})

test('.prop modifier w/ no expression', () => {
const node = parseWithVBind(`<div v-bind:fooBar.prop />`)
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(`<div v-bind:[fooBar].prop="id"/>`)
const props = (node.codegenNode as VNodeCall).props as CallExpression
Expand Down Expand Up @@ -296,6 +380,21 @@ describe('compiler: transform v-bind', () => {
})
})

test('.prop modifier (shortband) w/ no expression', () => {
const node = parseWithVBind(`<div .fooBar />`)
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(`<div v-bind:foo-bar.attr="id"/>`)
const props = (node.codegenNode as VNodeCall).props as ObjectExpression
Expand All @@ -310,4 +409,19 @@ describe('compiler: transform v-bind', () => {
}
})
})

test('.attr modifier w/ no expression', () => {
const node = parseWithVBind(`<div v-bind:foo-bar.attr />`)
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
}
})
})
})
16 changes: 15 additions & 1 deletion packages/compiler-core/src/transforms/vBind.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 <input>, <textarea> and <select> elements.],
]
`;
2 changes: 1 addition & 1 deletion packages/compiler-sfc/__tests__/compileTemplate.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ test('source map', () => {
test('template errors', () => {
const result = compile({
filename: 'example.vue',
source: `<div :foo
source: `<div
:bar="a[" v-model="baz"/>`
})
expect(result.errors).toMatchSnapshot()
Expand Down

0 comments on commit 26399aa

Please sign in to comment.