Skip to content

Commit 441e7a7

Browse files
committed
dx(compiler-core): warn for :v-
1 parent fe77e2b commit 441e7a7

File tree

4 files changed

+158
-9
lines changed

4 files changed

+158
-9
lines changed

packages/compiler-core/__tests__/__snapshots__/parse.spec.ts.snap

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5832,6 +5832,115 @@ exports[`compiler: parse Errors UNEXPECTED_SOLIDUS_IN_TAG <template><div a/b></d
58325832
}
58335833
`;
58345834
5835+
exports[`compiler: parse Errors X_COLON_BEFORE_DIRECTIVE <div :v-foo="obj" /> 1`] = `
5836+
{
5837+
"cached": 0,
5838+
"children": [
5839+
{
5840+
"children": [],
5841+
"codegenNode": undefined,
5842+
"isSelfClosing": true,
5843+
"loc": {
5844+
"end": {
5845+
"column": 21,
5846+
"line": 1,
5847+
"offset": 20,
5848+
},
5849+
"source": "<div :v-foo="obj" />",
5850+
"start": {
5851+
"column": 1,
5852+
"line": 1,
5853+
"offset": 0,
5854+
},
5855+
},
5856+
"ns": 0,
5857+
"props": [
5858+
{
5859+
"arg": {
5860+
"constType": 3,
5861+
"content": "v-foo",
5862+
"isStatic": true,
5863+
"loc": {
5864+
"end": {
5865+
"column": 12,
5866+
"line": 1,
5867+
"offset": 11,
5868+
},
5869+
"source": "v-foo",
5870+
"start": {
5871+
"column": 7,
5872+
"line": 1,
5873+
"offset": 6,
5874+
},
5875+
},
5876+
"type": 4,
5877+
},
5878+
"exp": {
5879+
"constType": 0,
5880+
"content": "obj",
5881+
"isStatic": false,
5882+
"loc": {
5883+
"end": {
5884+
"column": 17,
5885+
"line": 1,
5886+
"offset": 16,
5887+
},
5888+
"source": "obj",
5889+
"start": {
5890+
"column": 14,
5891+
"line": 1,
5892+
"offset": 13,
5893+
},
5894+
},
5895+
"type": 4,
5896+
},
5897+
"loc": {
5898+
"end": {
5899+
"column": 18,
5900+
"line": 1,
5901+
"offset": 17,
5902+
},
5903+
"source": ":v-foo="obj"",
5904+
"start": {
5905+
"column": 6,
5906+
"line": 1,
5907+
"offset": 5,
5908+
},
5909+
},
5910+
"modifiers": [],
5911+
"name": "bind",
5912+
"type": 7,
5913+
},
5914+
],
5915+
"tag": "div",
5916+
"tagType": 0,
5917+
"type": 1,
5918+
},
5919+
],
5920+
"codegenNode": undefined,
5921+
"components": [],
5922+
"directives": [],
5923+
"helpers": Set {},
5924+
"hoists": [],
5925+
"imports": [],
5926+
"loc": {
5927+
"end": {
5928+
"column": 21,
5929+
"line": 1,
5930+
"offset": 20,
5931+
},
5932+
"source": "<div :v-foo="obj" />",
5933+
"start": {
5934+
"column": 1,
5935+
"line": 1,
5936+
"offset": 0,
5937+
},
5938+
},
5939+
"temps": 0,
5940+
"type": 0,
5941+
}
5942+
`;
5943+
58355944
exports[`compiler: parse Errors X_INVALID_END_TAG <svg><![CDATA[</div>]]></svg> 1`] = `
58365945
{
58375946
"cached": 0,

packages/compiler-core/__tests__/parse.spec.ts

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1990,7 +1990,7 @@ foo
19901990
})
19911991
expect(ast.children[2].type).toBe(NodeTypes.INTERPOLATION)
19921992
})
1993-
1993+
19941994
it('should NOT remove whitespaces w/o newline between elements', () => {
19951995
const ast = parse(`<div/> <div/> <div/>`)
19961996
expect(ast.children.length).toBe(5)
@@ -2131,7 +2131,8 @@ foo
21312131
const patterns: {
21322132
[key: string]: Array<{
21332133
code: string
2134-
errors: Array<{ type: ErrorCodes; loc: Position }>
2134+
errors?: Array<{ type: ErrorCodes; loc: Position }>
2135+
warnings?: Array<{ type: ErrorCodes; loc: Position }>
21352136
options?: Partial<ParserOptions>
21362137
}>
21372138
} = {
@@ -3022,19 +3023,33 @@ foo
30223023
}
30233024
]
30243025
}
3026+
],
3027+
X_COLON_BEFORE_DIRECTIVE: [
3028+
{
3029+
code: `<div :v-foo="obj" />`,
3030+
warnings: [
3031+
{
3032+
type: ErrorCodes.X_COLON_BEFORE_DIRECTIVE,
3033+
loc: { offset: 5, line: 1, column: 6 }
3034+
}
3035+
]
3036+
}
30253037
]
30263038
}
30273039

30283040
for (const key of Object.keys(patterns) as (keyof typeof patterns)[]) {
30293041
describe(key, () => {
3030-
for (const { code, errors, options } of patterns[key]) {
3042+
for (const { code, errors = [], warnings = [], options } of patterns[
3043+
key
3044+
]) {
30313045
test(
30323046
code.replace(
30333047
/[\r\n]/g,
30343048
c => `\\x0${c.codePointAt(0)!.toString(16)};`
30353049
),
30363050
() => {
3037-
const spy = jest.fn()
3051+
const errorSpy = jest.fn()
3052+
const warnSpy = jest.fn()
30383053
const ast = baseParse(code, {
30393054
getNamespace: (tag, parent) => {
30403055
const ns = parent ? parent.ns : Namespaces.HTML
@@ -3055,15 +3070,22 @@ foo
30553070
return TextModes.DATA
30563071
},
30573072
...options,
3058-
onError: spy
3073+
onError: errorSpy,
3074+
onWarn: warnSpy
30593075
})
30603076

30613077
expect(
3062-
spy.mock.calls.map(([err]) => ({
3078+
errorSpy.mock.calls.map(([err]) => ({
30633079
type: err.code,
30643080
loc: err.loc.start
30653081
}))
30663082
).toMatchObject(errors)
3083+
expect(
3084+
warnSpy.mock.calls.map(([err]) => ({
3085+
type: err.code,
3086+
loc: err.loc.start
3087+
}))
3088+
).toMatchObject(warnings)
30673089
expect(ast).toMatchSnapshot()
30683090
}
30693091
)

packages/compiler-core/src/errors.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ export const enum ErrorCodes {
9090
X_V_MODEL_ON_PROPS,
9191
X_INVALID_EXPRESSION,
9292
X_KEEP_ALIVE_INVALID_CHILDREN,
93+
X_COLON_BEFORE_DIRECTIVE,
9394

9495
// generic errors
9596
X_PREFIX_ID_NOT_SUPPORTED,
@@ -172,6 +173,7 @@ export const errorMessages: Record<ErrorCodes, string> = {
172173
[ErrorCodes.X_V_MODEL_ON_PROPS]: `v-model cannot be used on a prop, because local prop bindings are not writable.\nUse a v-bind binding combined with a v-on listener that emits update:x event instead.`,
173174
[ErrorCodes.X_INVALID_EXPRESSION]: `Error parsing JavaScript expression: `,
174175
[ErrorCodes.X_KEEP_ALIVE_INVALID_CHILDREN]: `<KeepAlive> expects exactly one child component.`,
176+
[ErrorCodes.X_COLON_BEFORE_DIRECTIVE]: `Colon before directive: `,
175177

176178
// generic errors
177179
[ErrorCodes.X_PREFIX_ID_NOT_SUPPORTED]: `"prefixIdentifiers" option is not supported in this build of compiler.`,

packages/compiler-core/src/parse.ts

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -273,10 +273,10 @@ function parseChildren(
273273
(shouldCondense &&
274274
((prev.type === NodeTypes.COMMENT &&
275275
next.type === NodeTypes.COMMENT) ||
276-
(prev.type === NodeTypes.COMMENT &&
277-
next.type === NodeTypes.ELEMENT) ||
276+
(prev.type === NodeTypes.COMMENT &&
277+
next.type === NodeTypes.ELEMENT) ||
278278
(prev.type === NodeTypes.ELEMENT &&
279-
next.type === NodeTypes.COMMENT) ||
279+
next.type === NodeTypes.COMMENT) ||
280280
(prev.type === NodeTypes.ELEMENT &&
281281
next.type === NodeTypes.ELEMENT &&
282282
/[\r\n]/.test(node.content))))
@@ -813,6 +813,22 @@ function parseAttribute(
813813
: startsWith(name, '@')
814814
? 'on'
815815
: 'slot')
816+
817+
if (__DEV__) {
818+
if (name.startsWith(':v-')) {
819+
context.options.onWarn(
820+
createCompilerError(
821+
ErrorCodes.X_COLON_BEFORE_DIRECTIVE,
822+
loc,
823+
undefined,
824+
`the attribute name ${name} is probably a mistake. Did you mean ${name.slice(
825+
1
826+
)} instead?`
827+
)
828+
)
829+
}
830+
}
831+
816832
let arg: ExpressionNode | undefined
817833

818834
if (match[2]) {

0 commit comments

Comments
 (0)