eslint-plugin-vue
Advanced tools
Comparing version 3.8.0 to 3.9.0
@@ -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", |
@@ -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>`. | |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
117824
49
3374
176
+ Addedvue-eslint-parser@2.0.3(transitive)
- Removed@types/node@22.7.5(transitive)
- Removeddebug@2.6.9(transitive)
- Removedlodash.sortedindex@4.1.0(transitive)
- Removedlodash.sortedindexby@4.6.0(transitive)
- Removedms@2.0.0(transitive)
- Removedparse5@3.0.3(transitive)
- Removedundici-types@6.19.8(transitive)
- Removedvue-eslint-parser@1.1.0-9(transitive)