Skip to content

Commit 49b40d6

Browse files
authored
Update: upgrade vue-eslint-parser (fixes #36, fixes #56, fixes #96) (#116)
* Chore: upgrade dependencies * Fix: several bugs and improve syntax errors (fixes #36, fixes #56, fixes #96) * add more tests.
1 parent 3361366 commit 49b40d6

20 files changed

+2249
-498
lines changed

docs/rules/no-parsing-error.md

+62-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
# Disallow parsing errors in `<template>` (no-parsing-error)
22

3-
This rule reports syntax errors in directives/mustaches of `<template>`.
3+
This rule reports syntax errors in `<template>`. For example:
4+
5+
- Syntax errors of scripts in directives.
6+
- Syntax errors of scripts in mustaches.
7+
- Syntax errors of HTML.
8+
- Invalid end tags.
9+
- Attributes in end tags.
10+
- ...
11+
- See also: https://html.spec.whatwg.org/multipage/parsing.html#parse-errors
412

513
## :book: Rule Details
614

@@ -25,4 +33,56 @@ Then reports syntax errors if exist.
2533

2634
## :wrench: Options
2735

28-
Nothing.
36+
```json
37+
{
38+
"vue/no-parsing-error": ["error", {
39+
"abrupt-closing-of-empty-comment": false,
40+
"absence-of-digits-in-numeric-character-reference": false,
41+
"cdata-in-html-content": false,
42+
"character-reference-outside-unicode-range": false,
43+
"control-character-in-input-stream": false,
44+
"control-character-reference": false,
45+
"eof-before-tag-name": false,
46+
"eof-in-cdata": false,
47+
"eof-in-comment": false,
48+
"eof-in-tag": false,
49+
"incorrectly-closed-comment": false,
50+
"incorrectly-opened-comment": false,
51+
"invalid-first-character-of-tag-name": false,
52+
"missing-attribute-value": false,
53+
"missing-end-tag-name": false,
54+
"missing-semicolon-after-character-reference": false,
55+
"missing-whitespace-between-attributes": false,
56+
"nested-comment": false,
57+
"noncharacter-character-reference": false,
58+
"noncharacter-in-input-stream": false,
59+
"null-character-reference": false,
60+
"surrogate-character-reference": false,
61+
"surrogate-in-input-stream": false,
62+
"unexpected-character-in-attribute-name": false,
63+
"unexpected-character-in-unquoted-attribute-value": false,
64+
"unexpected-equals-sign-before-attribute-name": false,
65+
"unexpected-null-character": false,
66+
"unexpected-question-mark-instead-of-tag-name": false,
67+
"unexpected-solidus-in-tag": false,
68+
"unknown-named-character-reference": false,
69+
"end-tag-with-attributes": false,
70+
"duplicate-attribute": false,
71+
"end-tag-with-trailing-solidus": false,
72+
"non-void-html-element-start-tag-with-trailing-solidus": false,
73+
"x-invalid-end-tag": false,
74+
"x-invalid-namespace": false
75+
}]
76+
}
77+
```
78+
79+
You can enable HTML syntax errors by opt-in.
80+
81+
For example, if `"x-invalid-end-tag": true` is given then this rule will catch the end tags of elements which have not opened.
82+
The error codes are defined in [WHATWG spec](https://html.spec.whatwg.org/multipage/parsing.html#parse-errors), but this rule does not support all of those (E.g., it does not catch errors about DOCTYPE).
83+
Also, The codes which have `x-` prefix are original in this rule because errors in tree construction phase have not codified yet.
84+
85+
- `x-invalid-end-tag` enables the errors about the end tags of elements which have not opened.
86+
- `x-invalid-namespace` enables the errors about invalid `xmlns` attributes. See also [step 10. of "create an element for a token"](https://html.spec.whatwg.org/multipage/parsing.html#create-an-element-for-the-token).
87+
88+
> TODO(mysticatea): I will revisit errors in tree construction phase after those are codified.

lib/rules/html-end-tags.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ const utils = require('../utils')
2424
function create (context) {
2525
utils.registerTemplateBodyVisitor(context, {
2626
VElement (node) {
27-
const name = node.startTag.id.name
27+
const name = node.name
2828
const isVoid = utils.isVoidElementName(name)
2929
const hasEndTag = node.endTag != null
3030

lib/rules/html-no-self-closing.js

+19-10
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,23 @@ const utils = require('../utils')
2323
*/
2424
function create (context) {
2525
utils.registerTemplateBodyVisitor(context, {
26-
'VStartTag[selfClosing=true]' (node) {
27-
if (!utils.isSvgElementName(node.id.name)) {
28-
const pos = node.range[1] - 2
29-
context.report({
30-
node,
31-
loc: node.loc,
32-
message: 'Self-closing should not be used.',
33-
fix: (fixer) => fixer.removeRange([pos, pos + 1])
34-
})
26+
'VElement' (node) {
27+
if (utils.isSvgElementName(node.name)) {
28+
return
3529
}
30+
31+
const sourceCode = context.parserServices.getTemplateBodyTokenStore(context)
32+
const lastToken = sourceCode.getLastToken(node.startTag)
33+
if (lastToken.type !== 'HTMLSelfClosingTagClose') {
34+
return
35+
}
36+
37+
context.report({
38+
node: lastToken,
39+
loc: lastToken.loc,
40+
message: 'Self-closing should not be used.',
41+
fix: (fixer) => fixer.removeRange([lastToken.range[0], lastToken.range[0] + 1])
42+
})
3643
}
3744
})
3845

@@ -49,8 +56,10 @@ module.exports = {
4956
docs: {
5057
description: 'disallow self-closing elements.',
5158
category: 'Best Practices',
52-
recommended: false
59+
recommended: false,
60+
replacedBy: []
5361
},
62+
deprecated: true,
5463
fixable: 'code',
5564
schema: []
5665
}

lib/rules/no-confusing-v-for-v-if.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,9 @@ function isUsingIterationVar (vIf) {
3939
function create (context) {
4040
utils.registerTemplateBodyVisitor(context, {
4141
"VAttribute[directive=true][key.name='if']" (node) {
42-
if (utils.hasDirective(node.parent, 'for') && !isUsingIterationVar(node)) {
42+
const element = node.parent.parent
43+
44+
if (utils.hasDirective(element, 'for') && !isUsingIterationVar(node)) {
4345
context.report({
4446
node,
4547
loc: node.loc,

lib/rules/no-invalid-template-root.js

+11-11
Original file line numberDiff line numberDiff line change
@@ -26,24 +26,24 @@ function create (context) {
2626

2727
return {
2828
Program (program) {
29-
const node = program.templateBody
30-
if (node == null) {
29+
const element = program.templateBody
30+
if (element == null) {
3131
return
3232
}
3333

34-
const hasSrc = utils.hasAttribute(node.startTag, 'src')
34+
const hasSrc = utils.hasAttribute(element, 'src')
3535
const rootElements = []
3636
let extraText = null
3737
let extraElement = null
3838
let vIf = false
39-
for (const child of node.children) {
39+
for (const child of element.children) {
4040
if (child.type === 'VElement') {
4141
if (rootElements.length === 0 && !hasSrc) {
4242
rootElements.push(child)
43-
vIf = utils.hasDirective(child.startTag, 'if')
44-
} else if (vIf && utils.hasDirective(child.startTag, 'else-if')) {
43+
vIf = utils.hasDirective(child, 'if')
44+
} else if (vIf && utils.hasDirective(child, 'else-if')) {
4545
rootElements.push(child)
46-
} else if (vIf && utils.hasDirective(child.startTag, 'else')) {
46+
} else if (vIf && utils.hasDirective(child, 'else')) {
4747
rootElements.push(child)
4848
vIf = false
4949
} else {
@@ -74,14 +74,14 @@ function create (context) {
7474
})
7575
} else if (rootElements.length === 0 && !hasSrc) {
7676
context.report({
77-
node,
78-
loc: node.loc,
77+
node: element,
78+
loc: element.loc,
7979
message: 'The template root requires exactly one element.'
8080
})
8181
} else {
8282
for (const element of rootElements) {
8383
const tag = element.startTag
84-
const name = tag.id.name
84+
const name = element.name
8585

8686
if (name === 'template' || name === 'slot') {
8787
context.report({
@@ -91,7 +91,7 @@ function create (context) {
9191
data: { name }
9292
})
9393
}
94-
if (utils.hasDirective(tag, 'for')) {
94+
if (utils.hasDirective(element, 'for')) {
9595
context.report({
9696
node: tag,
9797
loc: tag.loc,

lib/rules/no-invalid-v-else-if.js

+5-3
Original file line numberDiff line numberDiff line change
@@ -24,21 +24,23 @@ const utils = require('../utils')
2424
function create (context) {
2525
utils.registerTemplateBodyVisitor(context, {
2626
"VAttribute[directive=true][key.name='else-if']" (node) {
27-
if (!utils.prevElementHasIf(node.parent.parent)) {
27+
const element = node.parent.parent
28+
29+
if (!utils.prevElementHasIf(element)) {
2830
context.report({
2931
node,
3032
loc: node.loc,
3133
message: "'v-else-if' directives require being preceded by the element which has a 'v-if' or 'v-else-if' directive."
3234
})
3335
}
34-
if (utils.hasDirective(node.parent, 'if')) {
36+
if (utils.hasDirective(element, 'if')) {
3537
context.report({
3638
node,
3739
loc: node.loc,
3840
message: "'v-else-if' and 'v-if' directives can't exist on the same element."
3941
})
4042
}
41-
if (utils.hasDirective(node.parent, 'else')) {
43+
if (utils.hasDirective(element, 'else')) {
4244
context.report({
4345
node,
4446
loc: node.loc,

lib/rules/no-invalid-v-else.js

+5-3
Original file line numberDiff line numberDiff line change
@@ -24,21 +24,23 @@ const utils = require('../utils')
2424
function create (context) {
2525
utils.registerTemplateBodyVisitor(context, {
2626
"VAttribute[directive=true][key.name='else']" (node) {
27-
if (!utils.prevElementHasIf(node.parent.parent)) {
27+
const element = node.parent.parent
28+
29+
if (!utils.prevElementHasIf(element)) {
2830
context.report({
2931
node,
3032
loc: node.loc,
3133
message: "'v-else' directives require being preceded by the element which has a 'v-if' or 'v-else' directive."
3234
})
3335
}
34-
if (utils.hasDirective(node.parent, 'if')) {
36+
if (utils.hasDirective(element, 'if')) {
3537
context.report({
3638
node,
3739
loc: node.loc,
3840
message: "'v-else' and 'v-if' directives can't exist on the same element. You may want 'v-else-if' directives."
3941
})
4042
}
41-
if (utils.hasDirective(node.parent, 'else-if')) {
43+
if (utils.hasDirective(element, 'else-if')) {
4244
context.report({
4345
node,
4446
loc: node.loc,

lib/rules/no-invalid-v-for.js

+13-12
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,21 @@ function isUsingIterationVar (vFor, vBindKey) {
4343
* @param {ASTNode} element The element node to check.
4444
*/
4545
function checkKey (context, vFor, element) {
46-
const startTag = element.startTag
47-
const vBindKey = utils.getDirective(startTag, 'bind', 'key')
46+
if (element.name === 'template') {
47+
for (const child of element.children) {
48+
if (child.type === 'VElement') {
49+
checkKey(context, vFor, child)
50+
}
51+
}
52+
return
53+
}
4854

49-
if (utils.isCustomComponent(startTag) && vBindKey == null) {
55+
const vBindKey = utils.getDirective(element, 'bind', 'key')
56+
57+
if (utils.isCustomComponent(element) && vBindKey == null) {
5058
context.report({
51-
node: startTag,
52-
loc: startTag.loc,
59+
node: element.startTag,
60+
loc: element.startTag.loc,
5361
message: "Custom elements in iteration require 'v-bind:key' directives."
5462
})
5563
}
@@ -76,13 +84,6 @@ function create (context) {
7684
const element = node.parent.parent
7785

7886
checkKey(context, node, element)
79-
if (element.startTag.id.name === 'template') {
80-
for (const child of element.children) {
81-
if (child.type === 'VElement') {
82-
checkKey(context, node, child)
83-
}
84-
}
85-
}
8687

8788
if (node.key.argument) {
8889
context.report({

lib/rules/no-invalid-v-if.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,16 @@ const utils = require('../utils')
2424
function create (context) {
2525
utils.registerTemplateBodyVisitor(context, {
2626
"VAttribute[directive=true][key.name='if']" (node) {
27-
if (utils.hasDirective(node.parent, 'else')) {
27+
const element = node.parent.parent
28+
29+
if (utils.hasDirective(element, 'else')) {
2830
context.report({
2931
node,
3032
loc: node.loc,
3133
message: "'v-if' and 'v-else' directives can't exist on the same element. You may want 'v-else-if' directives."
3234
})
3335
}
34-
if (utils.hasDirective(node.parent, 'else-if')) {
36+
if (utils.hasDirective(element, 'else-if')) {
3537
context.report({
3638
node,
3739
loc: node.loc,

lib/rules/no-invalid-v-model.js

+15-11
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,11 @@ const VALID_MODIFIERS = new Set(['lazy', 'number', 'trim'])
1919

2020
/**
2121
* Check whether the given node is valid or not.
22-
* @param {ASTNode} node The start tag node to check.
22+
* @param {ASTNode} node The element node to check.
2323
* @returns {boolean} `true` if the node is valid.
2424
*/
2525
function isValidElement (node) {
26-
const name = node.id.name
26+
const name = node.name
2727
return (
2828
name === 'input' ||
2929
name === 'select' ||
@@ -82,39 +82,44 @@ function getVariable (name, leafNode) {
8282
function create (context) {
8383
utils.registerTemplateBodyVisitor(context, {
8484
"VAttribute[directive=true][key.name='model']" (node) {
85-
if (!isValidElement(node.parent)) {
85+
const element = node.parent.parent
86+
const name = element.name
87+
88+
if (!isValidElement(element)) {
8689
context.report({
8790
node,
8891
loc: node.loc,
8992
message: "'v-model' directives aren't supported on <{{name}}> elements.",
90-
data: node.parent.id
93+
data: { name }
9194
})
9295
}
93-
if (node.parent.id.name === 'input') {
94-
if (utils.hasDirective(node.parent, 'bind', 'type')) {
96+
if (name === 'input') {
97+
if (utils.hasDirective(element, 'bind', 'type')) {
9598
context.report({
9699
node,
97100
loc: node.loc,
98101
message: "'v-model' directives don't support dynamic input types.",
99-
data: node.parent.id
102+
data: { name }
100103
})
101104
}
102-
if (utils.hasAttribute(node.parent, 'type', 'file')) {
105+
if (utils.hasAttribute(element, 'type', 'file')) {
103106
context.report({
104107
node,
105108
loc: node.loc,
106109
message: "'v-model' directives don't support 'file' input type.",
107-
data: node.parent.id
110+
data: { name }
108111
})
109112
}
110113
}
114+
111115
if (node.key.argument) {
112116
context.report({
113117
node,
114118
loc: node.loc,
115119
message: "'v-model' directives require no argument."
116120
})
117121
}
122+
118123
for (const modifier of node.key.modifiers) {
119124
if (!VALID_MODIFIERS.has(modifier)) {
120125
context.report({
@@ -147,8 +152,7 @@ function create (context) {
147152
continue
148153
}
149154

150-
const elementNode = node.parent.parent
151-
const variable = getVariable(id.name, elementNode)
155+
const variable = getVariable(id.name, element)
152156
if (variable != null) {
153157
context.report({
154158
node,

0 commit comments

Comments
 (0)