Skip to content

Update: upgrade vue-eslint-parser (fixes #36, fixes #56, fixes #96) #116

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Aug 1, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 62 additions & 2 deletions docs/rules/no-parsing-error.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
# Disallow parsing errors in `<template>` (no-parsing-error)

This rule reports syntax errors in directives/mustaches of `<template>`.
This rule reports syntax errors in `<template>`. For example:

- Syntax errors of scripts in directives.
- Syntax errors of scripts in mustaches.
- Syntax errors of HTML.
- Invalid end tags.
- Attributes in end tags.
- ...
- See also: https://html.spec.whatwg.org/multipage/parsing.html#parse-errors

## :book: Rule Details

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

## :wrench: Options

Nothing.
```json
{
"vue/no-parsing-error": ["error", {
"abrupt-closing-of-empty-comment": false,
"absence-of-digits-in-numeric-character-reference": false,
"cdata-in-html-content": false,
"character-reference-outside-unicode-range": false,
"control-character-in-input-stream": false,
"control-character-reference": false,
"eof-before-tag-name": false,
"eof-in-cdata": false,
"eof-in-comment": false,
"eof-in-tag": false,
"incorrectly-closed-comment": false,
"incorrectly-opened-comment": false,
"invalid-first-character-of-tag-name": false,
"missing-attribute-value": false,
"missing-end-tag-name": false,
"missing-semicolon-after-character-reference": false,
"missing-whitespace-between-attributes": false,
"nested-comment": false,
"noncharacter-character-reference": false,
"noncharacter-in-input-stream": false,
"null-character-reference": false,
"surrogate-character-reference": false,
"surrogate-in-input-stream": false,
"unexpected-character-in-attribute-name": false,
"unexpected-character-in-unquoted-attribute-value": false,
"unexpected-equals-sign-before-attribute-name": false,
"unexpected-null-character": false,
"unexpected-question-mark-instead-of-tag-name": false,
"unexpected-solidus-in-tag": false,
"unknown-named-character-reference": false,
"end-tag-with-attributes": false,
"duplicate-attribute": false,
"end-tag-with-trailing-solidus": false,
"non-void-html-element-start-tag-with-trailing-solidus": false,
"x-invalid-end-tag": false,
"x-invalid-namespace": false
}]
}
```

You can enable HTML syntax errors by opt-in.

For example, if `"x-invalid-end-tag": true` is given then this rule will catch the end tags of elements which have not opened.
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).
Also, The codes which have `x-` prefix are original in this rule because errors in tree construction phase have not codified yet.

- `x-invalid-end-tag` enables the errors about the end tags of elements which have not opened.
- `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).

> TODO(mysticatea): I will revisit errors in tree construction phase after those are codified.
2 changes: 1 addition & 1 deletion lib/rules/html-end-tags.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const utils = require('../utils')
function create (context) {
utils.registerTemplateBodyVisitor(context, {
VElement (node) {
const name = node.startTag.id.name
const name = node.name
const isVoid = utils.isVoidElementName(name)
const hasEndTag = node.endTag != null

Expand Down
29 changes: 19 additions & 10 deletions lib/rules/html-no-self-closing.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,23 @@ const utils = require('../utils')
*/
function create (context) {
utils.registerTemplateBodyVisitor(context, {
'VStartTag[selfClosing=true]' (node) {
if (!utils.isSvgElementName(node.id.name)) {
const pos = node.range[1] - 2
context.report({
node,
loc: node.loc,
message: 'Self-closing should not be used.',
fix: (fixer) => fixer.removeRange([pos, pos + 1])
})
'VElement' (node) {
if (utils.isSvgElementName(node.name)) {
return
}

const sourceCode = context.parserServices.getTemplateBodyTokenStore(context)
const lastToken = sourceCode.getLastToken(node.startTag)
if (lastToken.type !== 'HTMLSelfClosingTagClose') {
return
}

context.report({
node: lastToken,
loc: lastToken.loc,
message: 'Self-closing should not be used.',
fix: (fixer) => fixer.removeRange([lastToken.range[0], lastToken.range[0] + 1])
})
}
})

Expand All @@ -49,8 +56,10 @@ module.exports = {
docs: {
description: 'disallow self-closing elements.',
category: 'Best Practices',
recommended: false
recommended: false,
replacedBy: []
},
deprecated: true,
fixable: 'code',
schema: []
}
Expand Down
4 changes: 3 additions & 1 deletion lib/rules/no-confusing-v-for-v-if.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ function isUsingIterationVar (vIf) {
function create (context) {
utils.registerTemplateBodyVisitor(context, {
"VAttribute[directive=true][key.name='if']" (node) {
if (utils.hasDirective(node.parent, 'for') && !isUsingIterationVar(node)) {
const element = node.parent.parent

if (utils.hasDirective(element, 'for') && !isUsingIterationVar(node)) {
context.report({
node,
loc: node.loc,
Expand Down
22 changes: 11 additions & 11 deletions lib/rules/no-invalid-template-root.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,24 +26,24 @@ function create (context) {

return {
Program (program) {
const node = program.templateBody
if (node == null) {
const element = program.templateBody
if (element == null) {
return
}

const hasSrc = utils.hasAttribute(node.startTag, 'src')
const hasSrc = utils.hasAttribute(element, 'src')
const rootElements = []
let extraText = null
let extraElement = null
let vIf = false
for (const child of node.children) {
for (const child of element.children) {
if (child.type === 'VElement') {
if (rootElements.length === 0 && !hasSrc) {
rootElements.push(child)
vIf = utils.hasDirective(child.startTag, 'if')
} else if (vIf && utils.hasDirective(child.startTag, 'else-if')) {
vIf = utils.hasDirective(child, 'if')
} else if (vIf && utils.hasDirective(child, 'else-if')) {
rootElements.push(child)
} else if (vIf && utils.hasDirective(child.startTag, 'else')) {
} else if (vIf && utils.hasDirective(child, 'else')) {
rootElements.push(child)
vIf = false
} else {
Expand Down Expand Up @@ -74,14 +74,14 @@ function create (context) {
})
} else if (rootElements.length === 0 && !hasSrc) {
context.report({
node,
loc: node.loc,
node: element,
loc: element.loc,
message: 'The template root requires exactly one element.'
})
} else {
for (const element of rootElements) {
const tag = element.startTag
const name = tag.id.name
const name = element.name

if (name === 'template' || name === 'slot') {
context.report({
Expand All @@ -91,7 +91,7 @@ function create (context) {
data: { name }
})
}
if (utils.hasDirective(tag, 'for')) {
if (utils.hasDirective(element, 'for')) {
context.report({
node: tag,
loc: tag.loc,
Expand Down
8 changes: 5 additions & 3 deletions lib/rules/no-invalid-v-else-if.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,23 @@ const utils = require('../utils')
function create (context) {
utils.registerTemplateBodyVisitor(context, {
"VAttribute[directive=true][key.name='else-if']" (node) {
if (!utils.prevElementHasIf(node.parent.parent)) {
const element = node.parent.parent

if (!utils.prevElementHasIf(element)) {
context.report({
node,
loc: node.loc,
message: "'v-else-if' directives require being preceded by the element which has a 'v-if' or 'v-else-if' directive."
})
}
if (utils.hasDirective(node.parent, 'if')) {
if (utils.hasDirective(element, 'if')) {
context.report({
node,
loc: node.loc,
message: "'v-else-if' and 'v-if' directives can't exist on the same element."
})
}
if (utils.hasDirective(node.parent, 'else')) {
if (utils.hasDirective(element, 'else')) {
context.report({
node,
loc: node.loc,
Expand Down
8 changes: 5 additions & 3 deletions lib/rules/no-invalid-v-else.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,23 @@ const utils = require('../utils')
function create (context) {
utils.registerTemplateBodyVisitor(context, {
"VAttribute[directive=true][key.name='else']" (node) {
if (!utils.prevElementHasIf(node.parent.parent)) {
const element = node.parent.parent

if (!utils.prevElementHasIf(element)) {
context.report({
node,
loc: node.loc,
message: "'v-else' directives require being preceded by the element which has a 'v-if' or 'v-else' directive."
})
}
if (utils.hasDirective(node.parent, 'if')) {
if (utils.hasDirective(element, 'if')) {
context.report({
node,
loc: node.loc,
message: "'v-else' and 'v-if' directives can't exist on the same element. You may want 'v-else-if' directives."
})
}
if (utils.hasDirective(node.parent, 'else-if')) {
if (utils.hasDirective(element, 'else-if')) {
context.report({
node,
loc: node.loc,
Expand Down
25 changes: 13 additions & 12 deletions lib/rules/no-invalid-v-for.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,21 @@ function isUsingIterationVar (vFor, vBindKey) {
* @param {ASTNode} element The element node to check.
*/
function checkKey (context, vFor, element) {
const startTag = element.startTag
const vBindKey = utils.getDirective(startTag, 'bind', 'key')
if (element.name === 'template') {
for (const child of element.children) {
if (child.type === 'VElement') {
checkKey(context, vFor, child)
}
}
return
}

if (utils.isCustomComponent(startTag) && vBindKey == null) {
const vBindKey = utils.getDirective(element, 'bind', 'key')

if (utils.isCustomComponent(element) && vBindKey == null) {
context.report({
node: startTag,
loc: startTag.loc,
node: element.startTag,
loc: element.startTag.loc,
message: "Custom elements in iteration require 'v-bind:key' directives."
})
}
Expand All @@ -76,13 +84,6 @@ function create (context) {
const element = node.parent.parent

checkKey(context, node, element)
if (element.startTag.id.name === 'template') {
for (const child of element.children) {
if (child.type === 'VElement') {
checkKey(context, node, child)
}
}
}

if (node.key.argument) {
context.report({
Expand Down
6 changes: 4 additions & 2 deletions lib/rules/no-invalid-v-if.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,16 @@ const utils = require('../utils')
function create (context) {
utils.registerTemplateBodyVisitor(context, {
"VAttribute[directive=true][key.name='if']" (node) {
if (utils.hasDirective(node.parent, 'else')) {
const element = node.parent.parent

if (utils.hasDirective(element, 'else')) {
context.report({
node,
loc: node.loc,
message: "'v-if' and 'v-else' directives can't exist on the same element. You may want 'v-else-if' directives."
})
}
if (utils.hasDirective(node.parent, 'else-if')) {
if (utils.hasDirective(element, 'else-if')) {
context.report({
node,
loc: node.loc,
Expand Down
26 changes: 15 additions & 11 deletions lib/rules/no-invalid-v-model.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ const VALID_MODIFIERS = new Set(['lazy', 'number', 'trim'])

/**
* Check whether the given node is valid or not.
* @param {ASTNode} node The start tag node to check.
* @param {ASTNode} node The element node to check.
* @returns {boolean} `true` if the node is valid.
*/
function isValidElement (node) {
const name = node.id.name
const name = node.name
return (
name === 'input' ||
name === 'select' ||
Expand Down Expand Up @@ -82,39 +82,44 @@ function getVariable (name, leafNode) {
function create (context) {
utils.registerTemplateBodyVisitor(context, {
"VAttribute[directive=true][key.name='model']" (node) {
if (!isValidElement(node.parent)) {
const element = node.parent.parent
const name = element.name

if (!isValidElement(element)) {
context.report({
node,
loc: node.loc,
message: "'v-model' directives aren't supported on <{{name}}> elements.",
data: node.parent.id
data: { name }
})
}
if (node.parent.id.name === 'input') {
if (utils.hasDirective(node.parent, 'bind', 'type')) {
if (name === 'input') {
if (utils.hasDirective(element, 'bind', 'type')) {
context.report({
node,
loc: node.loc,
message: "'v-model' directives don't support dynamic input types.",
data: node.parent.id
data: { name }
})
}
if (utils.hasAttribute(node.parent, 'type', 'file')) {
if (utils.hasAttribute(element, 'type', 'file')) {
context.report({
node,
loc: node.loc,
message: "'v-model' directives don't support 'file' input type.",
data: node.parent.id
data: { name }
})
}
}

if (node.key.argument) {
context.report({
node,
loc: node.loc,
message: "'v-model' directives require no argument."
})
}

for (const modifier of node.key.modifiers) {
if (!VALID_MODIFIERS.has(modifier)) {
context.report({
Expand Down Expand Up @@ -147,8 +152,7 @@ function create (context) {
continue
}

const elementNode = node.parent.parent
const variable = getVariable(id.name, elementNode)
const variable = getVariable(id.name, element)
if (variable != null) {
context.report({
node,
Expand Down
Loading