Skip to content

Commit 02d0bd5

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

File tree

3 files changed

+49
-9
lines changed

3 files changed

+49
-9
lines changed

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)