mirror of https://github.com/vuejs/core.git
feat(compiler-core): support v-bind shorthand for key and value with the same name (#9451)
This commit is contained in:
parent
48b47a1ab6
commit
26399aa6fa
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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(`<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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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.],
|
||||
]
|
||||
`;
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Reference in New Issue