Socket
Socket
Sign inDemoInstall

eslint-plugin-vue

Package Overview
Dependencies
Maintainers
3
Versions
170
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

eslint-plugin-vue - npm Package Compare versions

Comparing version 3.8.0 to 3.9.0

lib/rules/attribute-hyphenation.js

4

lib/recommended-rules.js

@@ -7,2 +7,3 @@ /*

module.exports = {
"vue/attribute-hyphenation": "off",
"vue/html-end-tags": "off",

@@ -15,2 +16,3 @@ "vue/html-no-self-closing": "off",

"vue/no-confusing-v-for-v-if": "error",
"vue/no-dupe-keys": "off",
"vue/no-duplicate-attributes": "off",

@@ -32,2 +34,3 @@ "vue/no-invalid-template-root": "error",

"vue/no-parsing-error": "error",
"vue/no-reservered-keys": "off",
"vue/no-shared-component-data": "off",

@@ -39,2 +42,3 @@ "vue/no-side-effects-in-computed-properties": "off",

"vue/require-component-is": "error",
"vue/require-prop-types": "off",
"vue/require-v-for-key": "error",

@@ -41,0 +45,0 @@ "vue/return-in-computed-property": "off",

4

lib/rules/html-end-tags.js

@@ -22,3 +22,3 @@ /**

* @param {RuleContext} context - The rule context.
* @returns {object} AST event handlers.
* @returns {Object} AST event handlers.
*/

@@ -28,3 +28,3 @@ function create (context) {

VElement (node) {
const name = node.startTag.id.name
const name = node.name
const isVoid = utils.isVoidElementName(name)

@@ -31,0 +31,0 @@ const hasEndTag = node.endTag != null

@@ -22,16 +22,23 @@ /**

* @param {RuleContext} context - The rule context.
* @returns {object} AST event handlers.
* @returns {Object} AST event handlers.
*/
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.isSvgElementNode(node)) {
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])
})
}

@@ -53,4 +60,6 @@ })

category: 'Best Practices',
recommended: false
recommended: false,
replacedBy: []
},
deprecated: true,
fixable: 'code',

@@ -57,0 +66,0 @@ schema: []

@@ -22,3 +22,3 @@ /**

* @param {RuleContext} context - The rule context.
* @returns {object} AST event handlers.
* @returns {Object} AST event handlers.
*/

@@ -25,0 +25,0 @@ function create (context) {

@@ -8,40 +8,4 @@ /**

const utils = require('../utils')
const casing = require('../utils/casing')
function kebabCase (str) {
return str
.replace(/([a-z])([A-Z])/g, match => match[0] + '-' + match[1])
.replace(/[^a-zA-Z:]+/g, '-')
.toLowerCase()
}
function camelCase (str) {
return str
.replace(/(?:^\w|[A-Z]|\b\w)/g, (letter, index) => (
index === 0 ? letter.toLowerCase() : letter.toUpperCase())
)
.replace(/[^a-zA-Z:]+/g, '')
}
function pascalCase (str) {
return str
.replace(/(?:^\w|[A-Z]|\b\w)/g, (letter, index) => letter.toUpperCase())
.replace(/[^a-zA-Z:]+/g, '')
}
const allowedCaseOptions = [
'camelCase',
'kebab-case',
'PascalCase'
]
const convertersMap = {
'kebab-case': kebabCase,
'camelCase': camelCase,
'PascalCase': pascalCase
}
function getConverter (name) {
return convertersMap[name] || pascalCase
}
// ------------------------------------------------------------------------------

@@ -53,3 +17,3 @@ // Rule Definition

const options = context.options[0]
const caseType = allowedCaseOptions.indexOf(options) !== -1 ? options : 'PascalCase'
const caseType = casing.allowedCaseOptions.indexOf(options) !== -1 ? options : 'PascalCase'

@@ -62,11 +26,11 @@ // ----------------------------------------------------------------------

const node = obj.properties
.filter(item => (
.find(item => (
item.type === 'Property' &&
item.key.name === 'name' &&
item.value.type === 'Literal'
))[0]
))
if (!node) return
const value = getConverter(caseType)(node.value.value)
const value = casing.getConverter(caseType)(node.value.value)
if (value !== node.value.value) {

@@ -96,3 +60,3 @@ context.report({

{
enum: allowedCaseOptions
enum: casing.allowedCaseOptions
}

@@ -99,0 +63,0 @@ ]

@@ -37,3 +37,3 @@ /**

* @param {RuleContext} context - The rule context.
* @returns {object} AST event handlers.
* @returns {Object} AST event handlers.
*/

@@ -43,3 +43,5 @@ function create (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({

@@ -46,0 +48,0 @@ node,

@@ -37,10 +37,23 @@ /**

* @param {RuleContext} context - The rule context.
* @returns {object} AST event handlers.
* @returns {Object} AST event handlers.
*/
function create (context) {
const names = new Set()
const options = context.options[0] || {}
const allowCoexistStyle = options.allowCoexistStyle !== false
const allowCoexistClass = options.allowCoexistClass !== false
const directiveNames = new Set()
const attributeNames = new Set()
function isDuplicate (name, isDirective) {
if ((allowCoexistStyle && name === 'style') || (allowCoexistClass && name === 'class')) {
return isDirective ? directiveNames.has(name) : attributeNames.has(name)
}
return directiveNames.has(name) || attributeNames.has(name)
}
utils.registerTemplateBodyVisitor(context, {
'VStartTag' () {
names.clear()
directiveNames.clear()
attributeNames.clear()
},

@@ -53,3 +66,3 @@ 'VAttribute' (node) {

if (names.has(name)) {
if (isDuplicate(name, node.directive)) {
context.report({

@@ -62,3 +75,8 @@ node,

}
names.add(name)
if (node.directive) {
directiveNames.add(name)
} else {
attributeNames.add(name)
}
}

@@ -83,4 +101,17 @@ })

fixable: false,
schema: []
schema: [
{
type: 'object',
properties: {
allowCoexistClass: {
type: 'boolean'
},
allowCoexistStyle: {
type: 'boolean'
}
}
}
]
}
}

@@ -22,3 +22,3 @@ /**

* @param {RuleContext} context - The rule context.
* @returns {object} AST event handlers.
* @returns {Object} AST event handlers.
*/

@@ -30,8 +30,8 @@ function create (context) {

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 = []

@@ -41,10 +41,10 @@ let extraText = 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)

@@ -80,4 +80,4 @@ vIf = false

context.report({
node,
loc: node.loc,
node: element,
loc: element.loc,
message: 'The template root requires exactly one element.'

@@ -88,3 +88,3 @@ })

const tag = element.startTag
const name = tag.id.name
const name = element.name

@@ -99,3 +99,3 @@ if (name === 'template' || name === 'slot') {

}
if (utils.hasDirective(tag, 'for')) {
if (utils.hasDirective(element, 'for')) {
context.report({

@@ -102,0 +102,0 @@ node: tag,

@@ -24,3 +24,3 @@ /**

* @param {RuleContext} context - The rule context.
* @returns {object} AST event handlers.
* @returns {Object} AST event handlers.
*/

@@ -27,0 +27,0 @@ function create (context) {

@@ -22,3 +22,3 @@ /**

* @param {RuleContext} context - The rule context.
* @returns {object} AST event handlers.
* @returns {Object} AST event handlers.
*/

@@ -25,0 +25,0 @@ function create (context) {

@@ -22,3 +22,3 @@ /**

* @param {RuleContext} context - The rule context.
* @returns {object} AST event handlers.
* @returns {Object} AST event handlers.
*/

@@ -28,3 +28,5 @@ function create (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({

@@ -36,3 +38,3 @@ node,

}
if (utils.hasDirective(node.parent, 'if')) {
if (utils.hasDirective(element, 'if')) {
context.report({

@@ -44,3 +46,3 @@ node,

}
if (utils.hasDirective(node.parent, 'else')) {
if (utils.hasDirective(element, 'else')) {
context.report({

@@ -47,0 +49,0 @@ node,

@@ -22,3 +22,3 @@ /**

* @param {RuleContext} context - The rule context.
* @returns {object} AST event handlers.
* @returns {Object} AST event handlers.
*/

@@ -28,3 +28,5 @@ function create (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({

@@ -36,3 +38,3 @@ node,

}
if (utils.hasDirective(node.parent, 'if')) {
if (utils.hasDirective(element, 'if')) {
context.report({

@@ -44,3 +46,3 @@ node,

}
if (utils.hasDirective(node.parent, 'else-if')) {
if (utils.hasDirective(element, 'else-if')) {
context.report({

@@ -47,0 +49,0 @@ node,

@@ -46,9 +46,17 @@ /**

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."

@@ -70,3 +78,3 @@ })

* @param {RuleContext} context - The rule context.
* @returns {object} AST event handlers.
* @returns {Object} AST event handlers.
*/

@@ -81,9 +89,2 @@ function create (context) {

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

@@ -90,0 +91,0 @@ if (node.key.argument) {

@@ -22,3 +22,3 @@ /**

* @param {RuleContext} context - The rule context.
* @returns {object} AST event handlers.
* @returns {Object} AST event handlers.
*/

@@ -25,0 +25,0 @@ function create (context) {

@@ -22,3 +22,3 @@ /**

* @param {RuleContext} context - The rule context.
* @returns {object} AST event handlers.
* @returns {Object} AST event handlers.
*/

@@ -28,3 +28,5 @@ function create (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({

@@ -36,3 +38,3 @@ node,

}
if (utils.hasDirective(node.parent, 'else-if')) {
if (utils.hasDirective(element, 'else-if')) {
context.report({

@@ -39,0 +41,0 @@ node,

@@ -22,7 +22,7 @@ /**

* 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 (

@@ -81,3 +81,3 @@ name === 'input' ||

* @param {RuleContext} context - The rule context.
* @returns {object} AST event handlers.
* @returns {Object} AST event handlers.
*/

@@ -87,3 +87,6 @@ function create (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({

@@ -93,7 +96,7 @@ node,

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({

@@ -103,6 +106,6 @@ node,

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({

@@ -112,6 +115,7 @@ node,

message: "'v-model' directives don't support 'file' input type.",
data: node.parent.id
data: { name }
})
}
}
if (node.key.argument) {

@@ -124,2 +128,3 @@ context.report({

}
for (const modifier of node.key.modifiers) {

@@ -157,4 +162,3 @@ if (!VALID_MODIFIERS.has(modifier)) {

const elementNode = node.parent.parent
const variable = getVariable(id.name, elementNode)
const variable = getVariable(id.name, element)
if (variable != null) {

@@ -161,0 +165,0 @@ context.report({

@@ -31,3 +31,3 @@ /**

* @param {RuleContext} context - The rule context.
* @returns {object} AST event handlers.
* @returns {Object} AST event handlers.
*/

@@ -34,0 +34,0 @@ function create (context) {

@@ -22,3 +22,3 @@ /**

* @param {RuleContext} context - The rule context.
* @returns {object} AST event handlers.
* @returns {Object} AST event handlers.
*/

@@ -25,0 +25,0 @@ function create (context) {

@@ -22,3 +22,3 @@ /**

* @param {RuleContext} context - The rule context.
* @returns {object} AST event handlers.
* @returns {Object} AST event handlers.
*/

@@ -25,0 +25,0 @@ function create (context) {

@@ -22,3 +22,3 @@ /**

* @param {RuleContext} context - The rule context.
* @returns {object} AST event handlers.
* @returns {Object} AST event handlers.
*/

@@ -25,0 +25,0 @@ function create (context) {

@@ -22,3 +22,3 @@ /**

* @param {RuleContext} context - The rule context.
* @returns {object} AST event handlers.
* @returns {Object} AST event handlers.
*/

@@ -25,0 +25,0 @@ function create (context) {

@@ -9,11 +9,46 @@ /**

// ------------------------------------------------------------------------------
// Requirements
// Helpers
// ------------------------------------------------------------------------------
const utils = require('../utils')
// https://html.spec.whatwg.org/multipage/parsing.html#parse-errors
// TODO: Enable all by default.
const DEFAULT_OPTIONS = Object.freeze(Object.assign(Object.create(null), {
'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
}))
// ------------------------------------------------------------------------------
// Helpers
// ------------------------------------------------------------------------------
/**

@@ -23,23 +58,32 @@ * Creates AST event handlers for no-parsing-error.

* @param {RuleContext} context - The rule context.
* @returns {object} AST event handlers.
* @returns {Object} AST event handlers.
*/
function create (context) {
utils.registerTemplateBodyVisitor(context, {
'VExpressionContainer[syntaxError!=null]' (node) {
const message = node.syntaxError.message
const options = Object.assign({}, DEFAULT_OPTIONS, context.options[0] || {})
context.report({
node,
loc: node.loc,
message: 'Parsing error: {{message}}.',
data: {
message: message.endsWith('.')
? message.slice(0, -1)
: message
return {
Program (program) {
const node = program.templateBody
if (node == null || node.errors == null) {
return
}
for (const error of node.errors) {
if (error.code && !options[error.code]) {
continue
}
})
context.report({
node,
loc: { line: error.lineNumber, column: error.column },
message: 'Parsing error: {{message}}.',
data: {
message: error.message.endsWith('.')
? error.message.slice(0, -1)
: error.message
}
})
}
}
})
return {}
}
}

@@ -60,4 +104,13 @@

fixable: false,
schema: []
schema: [
{
type: 'object',
properties: Object.keys(DEFAULT_OPTIONS).reduce((ret, code) => {
ret[code] = { type: 'boolean' }
return ret
}, {}),
additionalProperties: false
}
]
}
}

@@ -22,7 +22,7 @@ /**

* @param {RuleContext} context - The rule context.
* @returns {object} AST event handlers.
* @returns {Object} AST event handlers.
*/
function create (context) {
utils.registerTemplateBodyVisitor(context, {
"VStartTag[id.name='template']" (node) {
"VElement[name='template']" (node) {
if (utils.hasAttribute(node, 'key') || utils.hasDirective(node, 'bind', 'key')) {

@@ -29,0 +29,0 @@ context.report({

@@ -22,7 +22,7 @@ /**

* @param {RuleContext} context - The rule context.
* @returns {object} AST event handlers.
* @returns {Object} AST event handlers.
*/
function create (context) {
utils.registerTemplateBodyVisitor(context, {
"VElement[startTag.id.name='textarea'] VExpressionContainer" (node) {
"VElement[name='textarea'] VExpressionContainer" (node) {
if (node.parent.type !== 'VElement') {

@@ -29,0 +29,0 @@ return

@@ -22,7 +22,7 @@ /**

* @param {RuleContext} context - The rule context.
* @returns {object} AST event handlers.
* @returns {Object} AST event handlers.
*/
function create (context) {
utils.registerTemplateBodyVisitor(context, {
"VStartTag[id.name='component']" (node) {
"VElement[name='component']" (node) {
if (!utils.hasDirective(node, 'bind', 'is')) {

@@ -29,0 +29,0 @@ context.report({

@@ -24,8 +24,12 @@ /**

function checkKey (context, element) {
const startTag = element.startTag
if (startTag.id.name !== 'template' && !utils.isCustomComponent(startTag) && !utils.hasDirective(startTag, 'bind', 'key')) {
if (element.name === 'template') {
for (const child of element.children) {
if (child.type === 'VElement') {
checkKey(context, child)
}
}
} else if (!utils.isCustomComponent(element) && !utils.hasDirective(element, 'bind', 'key')) {
context.report({
node: startTag,
loc: startTag.loc,
node: element.startTag,
loc: element.startTag.loc,
message: "Elements in iteration expect to have 'v-bind:key' directives."

@@ -40,3 +44,3 @@ })

* @param {RuleContext} context - The rule context.
* @returns {object} AST event handlers.
* @returns {Object} AST event handlers.
*/

@@ -46,12 +50,3 @@ function create (context) {

"VAttribute[directive=true][key.name='for']" (node) {
const element = node.parent.parent
checkKey(context, element)
if (element.startTag.id.name === 'template') {
for (const child of element.children) {
if (child.type === 'VElement') {
checkKey(context, child)
}
}
}
checkKey(context, node.parent.parent)
}

@@ -58,0 +53,0 @@ })

@@ -22,3 +22,3 @@ /**

* @param {RuleContext} context - The rule context.
* @returns {object} AST event handlers.
* @returns {Object} AST event handlers.
*/

@@ -25,0 +25,0 @@ function create (context) {

@@ -22,3 +22,3 @@ /**

* @param {RuleContext} context - The rule context.
* @returns {object} AST event handlers.
* @returns {Object} AST event handlers.
*/

@@ -25,0 +25,0 @@ function create (context) {

@@ -13,5 +13,5 @@ /**

const HTML_ELEMENT_NAMES = new Set(require('./html-elements.json'))
const SVG_ELEMENT_NAMES = new Set(require('./svg-elements.json'))
const VOID_ELEMENT_NAMES = new Set(require('./void-elements.json'))
const assert = require('assert')
const vueEslintParser = require('vue-eslint-parser')

@@ -23,11 +23,10 @@ // ------------------------------------------------------------------------------

module.exports = {
/**
* Register the given visitor to parser services.
* If the parser service of `vue-eslint-parser` was not found,
* this generates a warning.
*
* @param {RuleContext} context The rule context to use parser services.
* @param {object} visitor The visitor.
* @returns {void}
*/
/**
* Register the given visitor to parser services.
* If the parser service of `vue-eslint-parser` was not found,
* this generates a warning.
*
* @param {RuleContext} context The rule context to use parser services.
* @param {Object} visitor The visitor.
*/
registerTemplateBodyVisitor (context, visitor) {

@@ -44,26 +43,7 @@ if (context.parserServices.registerTemplateBodyVisitor == null) {

/**
* Get the token store of template bodies.
* If the parser service of `vue-eslint-parser` was not found,
* this generates a warning.
*
* @param {RuleContext} context The rule context to use parser services.
* @returns {TokenStore} The gotten token store.
*/
getTemplateBodyTokenStore (context) {
if (context.parserServices.getTemplateBodyTokenStore == null) {
context.report({
loc: { line: 1, column: 0 },
message: 'Use the latest vue-eslint-parser.'
})
return null
}
return context.parserServices.getTemplateBodyTokenStore(context)
},
/**
* Check whether the given node is the root element or not.
* @param {ASTNode} node The element node to check.
* @returns {boolean} `true` if the node is the root element.
*/
/**
* Check whether the given node is the root element or not.
* @param {ASTNode} node The element node to check.
* @returns {boolean} `true` if the node is the root element.
*/
isRootElement (node) {

@@ -73,12 +53,12 @@ assert(node && node.type === 'VElement')

return (
node.parent.type === 'Program' ||
node.parent.parent.type === 'Program'
node.parent.type === 'VDocumentFragment' ||
node.parent.parent.type === 'VDocumentFragment'
)
},
/**
* Get the previous sibling element of the given element.
* @param {ASTNode} node The element node to get the previous sibling element.
* @returns {ASTNode|null} The previous sibling element.
*/
/**
* Get the previous sibling element of the given element.
* @param {ASTNode} node The element node to get the previous sibling element.
* @returns {ASTNode|null} The previous sibling element.
*/
prevSibling (node) {

@@ -100,12 +80,12 @@ assert(node && node.type === 'VElement')

/**
* Check whether the given start tag has specific directive.
* @param {ASTNode} node The start tag node to check.
* @param {string} name The attribute name to check.
* @param {string} [value] The attribute value to check.
* @returns {boolean} `true` if the start tag has the directive.
*/
/**
* Check whether the given start tag has specific directive.
* @param {ASTNode} node The start tag node to check.
* @param {string} name The attribute name to check.
* @param {string} [value] The attribute value to check.
* @returns {boolean} `true` if the start tag has the directive.
*/
hasAttribute (node, name, value) {
assert(node && node.type === 'VStartTag')
return node.attributes.some(a =>
assert(node && node.type === 'VElement')
return node.startTag.attributes.some(a =>
!a.directive &&

@@ -120,12 +100,12 @@ a.key.name === name &&

/**
* Check whether the given start tag has specific directive.
* @param {ASTNode} node The start tag node to check.
* @param {string} name The directive name to check.
* @param {string} [argument] The directive argument to check.
* @returns {boolean} `true` if the start tag has the directive.
*/
/**
* Check whether the given start tag has specific directive.
* @param {ASTNode} node The start tag node to check.
* @param {string} name The directive name to check.
* @param {string} [argument] The directive argument to check.
* @returns {boolean} `true` if the start tag has the directive.
*/
hasDirective (node, name, argument) {
assert(node && node.type === 'VStartTag')
return node.attributes.some(a =>
assert(node && node.type === 'VElement')
return node.startTag.attributes.some(a =>
a.directive &&

@@ -137,7 +117,7 @@ a.key.name === name &&

/**
* Check whether the given attribute has their attribute value.
* @param {ASTNode} node The attribute node to check.
* @returns {boolean} `true` if the attribute has their value.
*/
/**
* Check whether the given attribute has their attribute value.
* @param {ASTNode} node The attribute node to check.
* @returns {boolean} `true` if the attribute has their value.
*/
hasAttributeValue (node) {

@@ -151,12 +131,12 @@ assert(node && node.type === 'VAttribute')

/**
* Get the attribute which has the given name.
* @param {ASTNode} node The start tag node to check.
* @param {string} name The attribute name to check.
* @param {string} [value] The attribute value to check.
* @returns {ASTNode} The found attribute.
*/
/**
* Get the attribute which has the given name.
* @param {ASTNode} node The start tag node to check.
* @param {string} name The attribute name to check.
* @param {string} [value] The attribute value to check.
* @returns {ASTNode} The found attribute.
*/
getAttribute (node, name, value) {
assert(node && node.type === 'VStartTag')
return node.attributes.find(a =>
assert(node && node.type === 'VElement')
return node.startTag.attributes.find(a =>
!a.directive &&

@@ -171,12 +151,12 @@ a.key.name === name &&

/**
* Get the directive which has the given name.
* @param {ASTNode} node The start tag node to check.
* @param {string} name The directive name to check.
* @param {string} [argument] The directive argument to check.
* @returns {ASTNode} The found directive.
*/
/**
* Get the directive which has the given name.
* @param {ASTNode} node The start tag node to check.
* @param {string} name The directive name to check.
* @param {string} [argument] The directive argument to check.
* @returns {ASTNode} The found directive.
*/
getDirective (node, name, argument) {
assert(node && node.type === 'VStartTag')
return node.attributes.find(a =>
assert(node && node.type === 'VElement')
return node.startTag.attributes.find(a =>
a.directive &&

@@ -188,7 +168,7 @@ a.key.name === name &&

/**
* Check whether the previous sibling element has `if` or `else-if` directive.
* @param {ASTNode} node The element node to check.
* @returns {boolean} `true` if the previous sibling element has `if` or `else-if` directive.
*/
/**
* Check whether the previous sibling element has `if` or `else-if` directive.
* @param {ASTNode} node The element node to check.
* @returns {boolean} `true` if the previous sibling element has `if` or `else-if` directive.
*/
prevElementHasIf (node) {

@@ -207,45 +187,55 @@ assert(node && node.type === 'VElement')

/**
* Check whether the given node is a custom component or not.
* @param {ASTNode} node The start tag node to check.
* @returns {boolean} `true` if the node is a custom component.
*/
/**
* Check whether the given node is a custom component or not.
* @param {ASTNode} node The start tag node to check.
* @returns {boolean} `true` if the node is a custom component.
*/
isCustomComponent (node) {
assert(node && node.type === 'VStartTag')
assert(node && node.type === 'VElement')
const name = node.id.name
return (
!(this.isHtmlElementName(name) || this.isSvgElementName(name)) ||
this.hasAttribute(node, 'is') ||
this.hasDirective(node, 'bind', 'is')
!(this.isKnownHtmlElementNode(node) || this.isSvgElementNode(node) || this.isMathMLElementNode(node)) ||
this.hasAttribute(node, 'is') ||
this.hasDirective(node, 'bind', 'is')
)
},
/**
* Check whether the given name is a HTML element name or not.
* @param {string} name The name to check.
* @returns {boolean} `true` if the name is a HTML element name.
*/
isHtmlElementName (name) {
assert(typeof name === 'string')
/**
* Check whether the given node is a HTML element or not.
* @param {ASTNode} node The node to check.
* @returns {boolean} `true` if the node is a HTML element.
*/
isKnownHtmlElementNode (node) {
assert(node && node.type === 'VElement')
return HTML_ELEMENT_NAMES.has(name.toLowerCase())
return node.namespace === vueEslintParser.AST.NS.HTML && HTML_ELEMENT_NAMES.has(node.name.toLowerCase())
},
/**
* Check whether the given name is a SVG element name or not.
* @param {string} name The name to check.
* @returns {boolean} `true` if the name is a SVG element name.
*/
isSvgElementName (name) {
assert(typeof name === 'string')
/**
* Check whether the given node is a SVG element or not.
* @param {ASTNode} node The node to check.
* @returns {boolean} `true` if the name is a SVG element.
*/
isSvgElementNode (node) {
assert(node && node.type === 'VElement')
return SVG_ELEMENT_NAMES.has(name.toLowerCase())
return node.namespace === vueEslintParser.AST.NS.SVG
},
/**
* Check whether the given name is a void element name or not.
* @param {string} name The name to check.
* @returns {boolean} `true` if the name is a void element name.
*/
/**
* Check whether the given name is a MathML element or not.
* @param {ASTNode} name The node to check.
* @returns {boolean} `true` if the node is a MathML element.
*/
isMathMLElementNode (node) {
assert(node && node.type === 'VElement')
return node.namespace === vueEslintParser.AST.NS.MathML
},
/**
* Check whether the given name is a void element name or not.
* @param {string} name The name to check.
* @returns {boolean} `true` if the name is a void element name.
*/
isVoidElementName (name) {

@@ -287,2 +277,44 @@ assert(typeof name === 'string')

/**
* Gets the property name of a given node.
* @param {ASTNode} node - The node to get.
* @return {string|null} The property name if static. Otherwise, null.
*/
getStaticPropertyName (node) {
let prop
switch (node && node.type) {
case 'Property':
case 'MethodDefinition':
prop = node.key
break
case 'MemberExpression':
prop = node.property
break
case 'Literal':
case 'TemplateLiteral':
case 'Identifier':
prop = node
break
// no default
}
switch (prop && prop.type) {
case 'Literal':
return String(prop.value)
case 'TemplateLiteral':
if (prop.expressions.length === 0 && prop.quasis.length === 1) {
return prop.quasis[0].value.cooked
}
break
case 'Identifier':
if (!node.computed) {
return prop.name
}
break
// no default
}
return null
},
/**
* Get all computed properties by looking at all component's properties

@@ -294,7 +326,7 @@ * @param {ObjectExpression} Object with component definition

const computedPropertiesNode = componentObject.properties
.filter(p =>
.find(p =>
p.key.type === 'Identifier' &&
p.key.name === 'computed' &&
p.value.type === 'ObjectExpression'
)[0]
)

@@ -325,10 +357,10 @@ if (!computedPropertiesNode) { return [] }

/**
* Check whether the given node is a Vue component based
* on the filename and default export type
* export default {} in .vue || .jsx
* @param {ASTNode} node Node to check
* @param {string} path File name with extension
* @returns {boolean}
*/
/**
* Check whether the given node is a Vue component based
* on the filename and default export type
* export default {} in .vue || .jsx
* @param {ASTNode} node Node to check
* @param {string} path File name with extension
* @returns {boolean}
*/
isVueComponentFile (node, path) {

@@ -341,8 +373,8 @@ const isVueFile = path.endsWith('.vue') || path.endsWith('.jsx')

/**
* Check whether given node is Vue component
* Vue.component('xxx', {}) || component('xxx', {})
* @param {ASTNode} node Node to check
* @returns {boolean}
*/
/**
* Check whether given node is Vue component
* Vue.component('xxx', {}) || component('xxx', {})
* @param {ASTNode} node Node to check
* @returns {boolean}
*/
isVueComponent (node) {

@@ -366,8 +398,8 @@ const callee = node.callee

/**
* Check whether given node is new Vue instance
* new Vue({})
* @param {ASTNode} node Node to check
* @returns {boolean}
*/
/**
* Check whether given node is new Vue instance
* new Vue({})
* @param {ASTNode} node Node to check
* @returns {boolean}
*/
isVueInstance (node) {

@@ -382,2 +414,7 @@ const callee = node.callee

/**
* Check if current file is a Vue instance or component and call callback
* @param {RuleContext} context The ESLint rule context object.
* @param {Function} cb Callback function
*/
executeOnVue (context, cb) {

@@ -390,2 +427,7 @@ return Object.assign(

/**
* Check if current file is a Vue instance (new Vue) and call callback
* @param {RuleContext} context The ESLint rule context object.
* @param {Function} cb Callback function
*/
executeOnVueInstance (context, cb) {

@@ -403,2 +445,7 @@ const _this = this

/**
* Check if current file is a Vue component and call callback
* @param {RuleContext} context The ESLint rule context object.
* @param {Function} cb Callback function
*/
executeOnVueComponent (context, cb) {

@@ -420,3 +467,70 @@ const filePath = context.getFilename()

}
},
/**
* Return generator with all properties
* @param {ASTNode} node Node to check
* @param {string} groupName Name of parent group
*/
* iterateProperties (node, groups) {
const nodes = node.properties.filter(p => p.type === 'Property' && groups.has(this.getStaticPropertyName(p.key)))
for (const item of nodes) {
const name = this.getStaticPropertyName(item.key)
if (item.value.type === 'ArrayExpression') {
yield * this.iterateArrayExpression(item.value, name)
} else if (item.value.type === 'ObjectExpression') {
yield * this.iterateObjectExpression(item.value, name)
} else if (item.value.type === 'FunctionExpression') {
yield * this.iterateFunctionExpression(item.value, name)
}
}
},
/**
* Return generator with all elements inside ArrayExpression
* @param {ASTNode} node Node to check
* @param {string} groupName Name of parent group
*/
* iterateArrayExpression (node, groupName) {
assert(node.type === 'ArrayExpression')
for (const item of node.elements) {
const name = this.getStaticPropertyName(item)
if (name) {
const obj = { name, groupName, node: item }
yield obj
}
}
},
/**
* Return generator with all elements inside ObjectExpression
* @param {ASTNode} node Node to check
* @param {string} groupName Name of parent group
*/
* iterateObjectExpression (node, groupName) {
assert(node.type === 'ObjectExpression')
for (const item of node.properties) {
const name = this.getStaticPropertyName(item)
if (name) {
const obj = { name, groupName, node: item.key }
yield obj
}
}
},
/**
* Return generator with all elements inside FunctionExpression
* @param {ASTNode} node Node to check
* @param {string} groupName Name of parent group
*/
* iterateFunctionExpression (node, groupName) {
assert(node.type === 'FunctionExpression')
if (node.body.type === 'BlockStatement') {
for (const item of node.body.body) {
if (item.type === 'ReturnStatement' && item.argument.type === 'ObjectExpression') {
yield * this.iterateObjectExpression(item.argument, groupName)
}
}
}
}
}
{
"name": "eslint-plugin-vue",
"version": "3.8.0",
"version": "3.9.0",
"description": "Official ESLint plugin for Vue.js",

@@ -9,3 +9,3 @@ "main": "lib/index.js",

"test:base": "mocha \"tests/lib/**/*.js\"",
"test:simple": "npm run test:base -- --reporter nyan",
"test:simple": "npm run test:base -- --reporter dot",
"test": "nyc npm run test:base -- \"tests/integrations/*.js\" --timeout 60000",

@@ -49,14 +49,14 @@ "lint": "eslint .",

"requireindex": "^1.1.0",
"vue-eslint-parser": "^1.1.0-7"
"vue-eslint-parser": "^2.0.0-beta.3"
},
"devDependencies": {
"@types/node": "^4.2.6",
"@types/node": "^4.2.16",
"babel-eslint": "^7.2.3",
"chai": "^4.1.0",
"eslint": "^3.18.0",
"eslint-plugin-eslint-plugin": "^0.7.1",
"eslint": "^3.19.0",
"eslint-plugin-eslint-plugin": "^0.8.0",
"eslint-plugin-vue-libs": "^1.2.0",
"mocha": "^3.2.0",
"nyc": "^10.2.0"
"nyc": "^11.1.0"
}
}

@@ -61,4 +61,17 @@ # eslint-plugin-vue

Deprecated rules witch should be used with caution and only enabled when you know what you are doing have a warning :warning: icon.
<!--RULES_TABLE_START-->
### Stylistic Issues
| | Rule ID | Description |
|:---|:--------|:------------|
| :wrench: | [attribute-hyphenation](./docs/rules/attribute-hyphenation.md) | Define a style for the props casing in templates. |
| | [html-quotes](./docs/rules/html-quotes.md) | enforce quotes style of HTML attributes. |
| :wrench: | [name-property-casing](./docs/rules/name-property-casing.md) | Requires specific casing for the name property in Vue components |
| :wrench: | [v-bind-style](./docs/rules/v-bind-style.md) | enforce `v-bind` directive style. |
| :wrench: | [v-on-style](./docs/rules/v-on-style.md) | enforce `v-on` directive style. |
### Best Practices

@@ -69,3 +82,3 @@

| :wrench: | [html-end-tags](./docs/rules/html-end-tags.md) | enforce end tag style. |
| :wrench: | [html-no-self-closing](./docs/rules/html-no-self-closing.md) | disallow self-closing elements. |
| :wrench::warning: | [html-no-self-closing](./docs/rules/html-no-self-closing.md) | disallow self-closing elements. - (deprecated) |
| | [no-async-in-computed-properties](./docs/rules/no-async-in-computed-properties.md) | Check if there are no asynchronous actions inside computed properties. |

@@ -78,15 +91,6 @@ | :white_check_mark: | [no-confusing-v-for-v-if](./docs/rules/no-confusing-v-for-v-if.md) | disallow confusing `v-for` and `v-if` on the same element. |

| :white_check_mark: | [require-component-is](./docs/rules/require-component-is.md) | require `v-bind:is` of `<component>` elements. |
| | [require-prop-types](./docs/rules/require-prop-types.md) | Prop definitions should be detailed |
| :white_check_mark: | [require-v-for-key](./docs/rules/require-v-for-key.md) | require `v-bind:key` with `v-for` directives. |
### Stylistic Issues
| | Rule ID | Description |
|:---|:--------|:------------|
| | [html-quotes](./docs/rules/html-quotes.md) | enforce quotes style of HTML attributes. |
| :wrench: | [name-property-casing](./docs/rules/name-property-casing.md) | Requires specific casing for the name property in Vue components |
| :wrench: | [v-bind-style](./docs/rules/v-bind-style.md) | enforce `v-bind` directive style. |
| :wrench: | [v-on-style](./docs/rules/v-on-style.md) | enforce `v-on` directive style. |
### Variables

@@ -103,2 +107,3 @@

|:---|:--------|:------------|
| | [no-dupe-keys](./docs/rules/no-dupe-keys.md) | Prevents duplication of field names. |
| :white_check_mark: | [no-invalid-template-root](./docs/rules/no-invalid-template-root.md) | disallow invalid template root. |

@@ -119,2 +124,3 @@ | :white_check_mark: | [no-invalid-v-bind](./docs/rules/no-invalid-v-bind.md) | disallow invalid `v-bind` directives. |

| :white_check_mark: | [no-parsing-error](./docs/rules/no-parsing-error.md) | disallow parsing errors in `<template>`. |
| | [no-reservered-keys](./docs/rules/no-reservered-keys.md) | Prevent overwrite reserved keys. |
| | [no-shared-component-data](./docs/rules/no-shared-component-data.md) | Enforces component's data property to be a function. |

@@ -121,0 +127,0 @@ | | [no-template-key](./docs/rules/no-template-key.md) | disallow `key` attribute on `<template>`. |

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc