From c6c49275aea8c6ef92b3ea9179963f9688503efa Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Fri, 8 Feb 2019 07:03:46 +0900 Subject: [PATCH 01/11] Chore: update vue-eslint-parser --- lib/rules/attribute-hyphenation.js | 5 +- lib/rules/attributes-order.js | 6 +- lib/rules/max-attributes-per-line.js | 22 +------- lib/rules/no-confusing-v-for-v-if.js | 2 +- lib/rules/no-duplicate-attributes.js | 4 +- lib/rules/no-unused-components.js | 2 +- lib/rules/no-use-v-if-with-v-for.js | 2 +- lib/rules/no-v-html.js | 2 +- lib/rules/require-v-for-key.js | 2 +- lib/rules/use-v-on-exact.js | 6 +- lib/rules/v-bind-style.js | 21 ++++--- lib/rules/v-on-function-call.js | 4 +- lib/rules/v-on-style.js | 11 ++-- lib/rules/valid-v-bind.js | 6 +- lib/rules/valid-v-cloak.js | 2 +- lib/rules/valid-v-else-if.js | 2 +- lib/rules/valid-v-else.js | 2 +- lib/rules/valid-v-for.js | 2 +- lib/rules/valid-v-html.js | 2 +- lib/rules/valid-v-if.js | 2 +- lib/rules/valid-v-model.js | 6 +- lib/rules/valid-v-on.js | 9 +-- lib/rules/valid-v-once.js | 2 +- lib/rules/valid-v-pre.js | 2 +- lib/rules/valid-v-show.js | 2 +- lib/rules/valid-v-text.js | 2 +- lib/utils/indent-common.js | 8 +-- lib/utils/index.js | 65 ++-------------------- package.json | 2 +- tests/lib/rules/max-attributes-per-line.js | 34 +++++------ tests/lib/rules/no-multi-spaces.js | 4 +- 31 files changed, 92 insertions(+), 151 deletions(-) diff --git a/lib/rules/attribute-hyphenation.js b/lib/rules/attribute-hyphenation.js index f2c95a52f..2ead228b7 100644 --- a/lib/rules/attribute-hyphenation.js +++ b/lib/rules/attribute-hyphenation.js @@ -92,7 +92,10 @@ module.exports = { VAttribute (node) { if (!utils.isCustomComponent(node.parent.parent)) return - const name = !node.directive ? node.key.rawName : node.key.name === 'bind' ? node.key.raw.argument : false + const name = + !node.directive ? node.key.rawName + : node.key.name.name === 'bind' ? node.key.argument && node.key.argument.rawName + : /* otherwise */ false if (!name || isIgnoredAttribute(name)) return reportIssue(node, name) diff --git a/lib/rules/attributes-order.js b/lib/rules/attributes-order.js index 8aec48c00..f6df3ac34 100644 --- a/lib/rules/attributes-order.js +++ b/lib/rules/attributes-order.js @@ -39,9 +39,9 @@ function getAttributeType (name, isDirective) { } } function getPosition (attribute, attributePosition) { - const attributeType = attribute.directive && attribute.key.name === 'bind' - ? getAttributeType(attribute.key.argument, false) - : getAttributeType(attribute.key.name, attribute.directive) + const attributeType = attribute.directive && attribute.key.name.name === 'bind' + ? getAttributeType(attribute.key.argument && attribute.key.argument.name, false) + : getAttributeType(attribute.directive ? attribute.key.name.name : attribute.key.name, attribute.directive) return attributePosition.hasOwnProperty(attributeType) ? attributePosition[attributeType] : -1 } diff --git a/lib/rules/max-attributes-per-line.js b/lib/rules/max-attributes-per-line.js index f9f4deb84..eca53345e 100644 --- a/lib/rules/max-attributes-per-line.js +++ b/lib/rules/max-attributes-per-line.js @@ -67,6 +67,7 @@ module.exports = { }, create: function (context) { + const sourceCode = context.getSourceCode() const configuration = parseOptions(context.options[0]) const multilineMaximum = configuration.multiline const singlelinemMaximum = configuration.singleline @@ -130,23 +131,6 @@ module.exports = { return defaults } - function getPropData (prop) { - let propType = 'Attribute' - let propName = prop.key.name - - if (utils.isBindingAttribute(prop)) { - propType = 'Binding' - propName = prop.key.raw.argument - } else if (utils.isEventAttribute(prop)) { - propType = 'Event' - propName = prop.key.raw.argument - } else if (prop.directive) { - propType = 'Directive' - } - - return { propType, propName } - } - function showErrors (attributes) { attributes.forEach((prop, i) => { const fix = (fixer) => { @@ -166,8 +150,8 @@ module.exports = { context.report({ node: prop, loc: prop.loc, - message: '{{propType}} "{{propName}}" should be on a new line.', - data: getPropData(prop), + message: '\'{{name}}\' should be on a new line.', + data: { name: sourceCode.getText(prop.key) }, fix }) }) diff --git a/lib/rules/no-confusing-v-for-v-if.js b/lib/rules/no-confusing-v-for-v-if.js index 2ab0188b6..0102ad55c 100644 --- a/lib/rules/no-confusing-v-for-v-if.js +++ b/lib/rules/no-confusing-v-for-v-if.js @@ -50,7 +50,7 @@ module.exports = { create (context) { return utils.defineTemplateBodyVisitor(context, { - "VAttribute[directive=true][key.name='if']" (node) { + "VAttribute[directive=true][key.name.name='if']" (node) { const element = node.parent.parent if (utils.hasDirective(element, 'for') && !isUsingIterationVar(node)) { diff --git a/lib/rules/no-duplicate-attributes.js b/lib/rules/no-duplicate-attributes.js index a0bd3f901..d3c20c6ec 100644 --- a/lib/rules/no-duplicate-attributes.js +++ b/lib/rules/no-duplicate-attributes.js @@ -24,8 +24,8 @@ function getName (attribute) { if (!attribute.directive) { return attribute.key.name } - if (attribute.key.name === 'bind') { - return attribute.key.argument || null + if (attribute.key.name.name === 'bind') { + return (attribute.key.argument && attribute.key.argument.name) || null } return null } diff --git a/lib/rules/no-unused-components.js b/lib/rules/no-unused-components.js index a12b7d43d..bf012d558 100644 --- a/lib/rules/no-unused-components.js +++ b/lib/rules/no-unused-components.js @@ -55,7 +55,7 @@ module.exports = { usedComponents.add(node.rawName) }, - "VAttribute[directive=true][key.name='bind'][key.argument='is']" (node) { + "VAttribute[directive=true][key.name.name='bind'][key.argument.name='is']" (node) { if ( !node.value || // `` node.value.type !== 'VExpressionContainer' || diff --git a/lib/rules/no-use-v-if-with-v-for.js b/lib/rules/no-use-v-if-with-v-for.js index ef3a19aaf..754086f7e 100644 --- a/lib/rules/no-use-v-if-with-v-for.js +++ b/lib/rules/no-use-v-if-with-v-for.js @@ -70,7 +70,7 @@ module.exports = { const options = context.options[0] || {} const allowUsingIterationVar = options.allowUsingIterationVar === true // default false return utils.defineTemplateBodyVisitor(context, { - "VAttribute[directive=true][key.name='if']" (node) { + "VAttribute[directive=true][key.name.name='if']" (node) { const element = node.parent.parent if (utils.hasDirective(element, 'for')) { diff --git a/lib/rules/no-v-html.js b/lib/rules/no-v-html.js index 17a47921f..878bbbd02 100644 --- a/lib/rules/no-v-html.js +++ b/lib/rules/no-v-html.js @@ -22,7 +22,7 @@ module.exports = { }, create (context) { return utils.defineTemplateBodyVisitor(context, { - "VAttribute[directive=true][key.name='html']" (node) { + "VAttribute[directive=true][key.name.name='html']" (node) { context.report({ node, loc: node.loc, diff --git a/lib/rules/require-v-for-key.js b/lib/rules/require-v-for-key.js index 6de38f7ed..6a71e3aca 100644 --- a/lib/rules/require-v-for-key.js +++ b/lib/rules/require-v-for-key.js @@ -49,7 +49,7 @@ module.exports = { } return utils.defineTemplateBodyVisitor(context, { - "VAttribute[directive=true][key.name='for']" (node) { + "VAttribute[directive=true][key.name.name='for']" (node) { checkKey(node.parent.parent) } }) diff --git a/lib/rules/use-v-on-exact.js b/lib/rules/use-v-on-exact.js index 3e110ec2a..047d84ed3 100644 --- a/lib/rules/use-v-on-exact.js +++ b/lib/rules/use-v-on-exact.js @@ -26,12 +26,12 @@ function getEventDirectives (attributes) { return attributes .filter(attribute => attribute.directive && - attribute.key.name === 'on' + attribute.key.name.name === 'on' ) .map(attribute => ({ - name: attribute.key.argument, + name: attribute.key.argument && attribute.key.argument.name, node: attribute.key, - modifiers: attribute.key.modifiers + modifiers: attribute.key.modifiers.map(modifier => modifier.name) })) } diff --git a/lib/rules/v-bind-style.js b/lib/rules/v-bind-style.js index e2e813611..9da7df315 100644 --- a/lib/rules/v-bind-style.js +++ b/lib/rules/v-bind-style.js @@ -30,23 +30,30 @@ module.exports = { }, create (context) { - const shorthand = context.options[0] !== 'longform' + const preferShorthand = context.options[0] !== 'longform' return utils.defineTemplateBodyVisitor(context, { - "VAttribute[directive=true][key.name='bind'][key.argument!=null]" (node) { - if (node.key.shorthand === shorthand) { + "VAttribute[directive=true][key.name.name='bind'][key.argument!=null]" (node) { + const shorthand = node.key.name.rawName === ':' || node.key.name.rawName === '.' + if (shorthand === preferShorthand) { return } context.report({ node, loc: node.loc, - message: shorthand + message: preferShorthand ? "Unexpected 'v-bind' before ':'." : "Expected 'v-bind' before ':'.", - fix: (fixer) => shorthand - ? fixer.removeRange([node.range[0], node.range[0] + 6]) - : fixer.insertTextBefore(node, 'v-bind') + fix: (fixer) => { + if (preferShorthand) { + return fixer.removeRange([node.range[0], node.range[0] + 6]) + } + if (node.key.name.rawName === ':') { + return fixer.insertTextBefore(node, 'v-bind') + } + return null + } }) } }) diff --git a/lib/rules/v-on-function-call.js b/lib/rules/v-on-function-call.js index 5dd76b2e1..e044cf134 100644 --- a/lib/rules/v-on-function-call.js +++ b/lib/rules/v-on-function-call.js @@ -31,7 +31,7 @@ module.exports = { const always = context.options[0] === 'always' return utils.defineTemplateBodyVisitor(context, { - "VAttribute[directive=true][key.name='on'][key.argument!=null] > VExpressionContainer > Identifier" (node) { + "VAttribute[directive=true][key.name.name='on'][key.argument!=null] > VExpressionContainer > Identifier" (node) { if (!always) return context.report({ node, @@ -40,7 +40,7 @@ module.exports = { }) }, - "VAttribute[directive=true][key.name='on'][key.argument!=null] VOnExpression > ExpressionStatement > *" (node) { + "VAttribute[directive=true][key.name.name='on'][key.argument!=null] VOnExpression > ExpressionStatement > *" (node) { if (!always && node.type === 'CallExpression' && node.arguments.length === 0) { context.report({ node, diff --git a/lib/rules/v-on-style.js b/lib/rules/v-on-style.js index a767ef1bc..191a12fcb 100644 --- a/lib/rules/v-on-style.js +++ b/lib/rules/v-on-style.js @@ -30,11 +30,12 @@ module.exports = { }, create (context) { - const shorthand = context.options[0] !== 'longform' + const preferShorthand = context.options[0] !== 'longform' return utils.defineTemplateBodyVisitor(context, { - "VAttribute[directive=true][key.name='on'][key.argument!=null]" (node) { - if (node.key.shorthand === shorthand) { + "VAttribute[directive=true][key.name.name='on'][key.argument!=null]" (node) { + const shorthand = node.key.name.rawName === '@' + if (shorthand === preferShorthand) { return } @@ -42,10 +43,10 @@ module.exports = { context.report({ node, loc: node.loc, - message: shorthand + message: preferShorthand ? "Expected '@' instead of 'v-on:'." : "Expected 'v-on:' instead of '@'.", - fix: (fixer) => shorthand + fix: (fixer) => preferShorthand ? fixer.replaceTextRange([pos, pos + 5], '@') : fixer.replaceTextRange([pos, pos + 1], 'v-on:') }) diff --git a/lib/rules/valid-v-bind.js b/lib/rules/valid-v-bind.js index ce47a1db8..f8bc3519f 100644 --- a/lib/rules/valid-v-bind.js +++ b/lib/rules/valid-v-bind.js @@ -35,14 +35,14 @@ module.exports = { create (context) { return utils.defineTemplateBodyVisitor(context, { - "VAttribute[directive=true][key.name='bind']" (node) { + "VAttribute[directive=true][key.name.name='bind']" (node) { for (const modifier of node.key.modifiers) { - if (!VALID_MODIFIERS.has(modifier)) { + if (!VALID_MODIFIERS.has(modifier.name)) { context.report({ node, loc: node.key.loc, message: "'v-bind' directives don't support the modifier '{{name}}'.", - data: { name: modifier } + data: { name: modifier.name } }) } } diff --git a/lib/rules/valid-v-cloak.js b/lib/rules/valid-v-cloak.js index b64e6c6b6..b1676a083 100644 --- a/lib/rules/valid-v-cloak.js +++ b/lib/rules/valid-v-cloak.js @@ -29,7 +29,7 @@ module.exports = { create (context) { return utils.defineTemplateBodyVisitor(context, { - "VAttribute[directive=true][key.name='cloak']" (node) { + "VAttribute[directive=true][key.name.name='cloak']" (node) { if (node.key.argument) { context.report({ node, diff --git a/lib/rules/valid-v-else-if.js b/lib/rules/valid-v-else-if.js index 67c5a05c2..a4b5987ed 100644 --- a/lib/rules/valid-v-else-if.js +++ b/lib/rules/valid-v-else-if.js @@ -29,7 +29,7 @@ module.exports = { create (context) { return utils.defineTemplateBodyVisitor(context, { - "VAttribute[directive=true][key.name='else-if']" (node) { + "VAttribute[directive=true][key.name.name='else-if']" (node) { const element = node.parent.parent if (!utils.prevElementHasIf(element)) { diff --git a/lib/rules/valid-v-else.js b/lib/rules/valid-v-else.js index d2d63a9ee..e54122377 100644 --- a/lib/rules/valid-v-else.js +++ b/lib/rules/valid-v-else.js @@ -29,7 +29,7 @@ module.exports = { create (context) { return utils.defineTemplateBodyVisitor(context, { - "VAttribute[directive=true][key.name='else']" (node) { + "VAttribute[directive=true][key.name.name='else']" (node) { const element = node.parent.parent if (!utils.prevElementHasIf(element)) { diff --git a/lib/rules/valid-v-for.js b/lib/rules/valid-v-for.js index 75edaf07c..28cf30c1a 100644 --- a/lib/rules/valid-v-for.js +++ b/lib/rules/valid-v-for.js @@ -117,7 +117,7 @@ module.exports = { const sourceCode = context.getSourceCode() return utils.defineTemplateBodyVisitor(context, { - "VAttribute[directive=true][key.name='for']" (node) { + "VAttribute[directive=true][key.name.name='for']" (node) { const element = node.parent.parent checkKey(context, node, element) diff --git a/lib/rules/valid-v-html.js b/lib/rules/valid-v-html.js index 9ec65933b..1aeaeef15 100644 --- a/lib/rules/valid-v-html.js +++ b/lib/rules/valid-v-html.js @@ -29,7 +29,7 @@ module.exports = { create (context) { return utils.defineTemplateBodyVisitor(context, { - "VAttribute[directive=true][key.name='html']" (node) { + "VAttribute[directive=true][key.name.name='html']" (node) { if (node.key.argument) { context.report({ node, diff --git a/lib/rules/valid-v-if.js b/lib/rules/valid-v-if.js index e08ccac2f..40e9259c2 100644 --- a/lib/rules/valid-v-if.js +++ b/lib/rules/valid-v-if.js @@ -29,7 +29,7 @@ module.exports = { create (context) { return utils.defineTemplateBodyVisitor(context, { - "VAttribute[directive=true][key.name='if']" (node) { + "VAttribute[directive=true][key.name.name='if']" (node) { const element = node.parent.parent if (utils.hasDirective(element, 'else')) { diff --git a/lib/rules/valid-v-model.js b/lib/rules/valid-v-model.js index b9e16ab3a..d969f2c0e 100644 --- a/lib/rules/valid-v-model.js +++ b/lib/rules/valid-v-model.js @@ -91,7 +91,7 @@ module.exports = { create (context) { return utils.defineTemplateBodyVisitor(context, { - "VAttribute[directive=true][key.name='model']" (node) { + "VAttribute[directive=true][key.name.name='model']" (node) { const element = node.parent.parent const name = element.name @@ -121,12 +121,12 @@ module.exports = { } for (const modifier of node.key.modifiers) { - if (!VALID_MODIFIERS.has(modifier)) { + if (!VALID_MODIFIERS.has(modifier.name)) { context.report({ node, loc: node.loc, message: "'v-model' directives don't support the modifier '{{name}}'.", - data: { name: modifier } + data: { name: modifier.name } }) } } diff --git a/lib/rules/valid-v-on.js b/lib/rules/valid-v-on.js index 76034844d..37e531f51 100644 --- a/lib/rules/valid-v-on.js +++ b/lib/rules/valid-v-on.js @@ -27,7 +27,8 @@ const VERB_MODIFIERS = new Set([ // https://www.w3.org/TR/uievents-key/ const KEY_ALIASES = new Set(keyAliases) -function isValidModifier (modifier, customModifiers) { +function isValidModifier (modifierNode, customModifiers) { + const modifier = modifierNode.name return ( // built-in aliases VALID_MODIFIERS.has(modifier) || @@ -74,21 +75,21 @@ module.exports = { const sourceCode = context.getSourceCode() return utils.defineTemplateBodyVisitor(context, { - "VAttribute[directive=true][key.name='on']" (node) { + "VAttribute[directive=true][key.name.name='on']" (node) { for (const modifier of node.key.modifiers) { if (!isValidModifier(modifier, customModifiers)) { context.report({ node, loc: node.loc, message: "'v-on' directives don't support the modifier '{{modifier}}'.", - data: { modifier } + data: { modifier: modifier.name } }) } } if ( !utils.hasAttributeValue(node) && - !node.key.modifiers.some(VERB_MODIFIERS.has, VERB_MODIFIERS) + !node.key.modifiers.some(modifier => VERB_MODIFIERS.has(modifier.name)) ) { if (node.value && sourceCode.getText(node.value.expression)) { const value = sourceCode.getText(node.value) diff --git a/lib/rules/valid-v-once.js b/lib/rules/valid-v-once.js index f294a8098..e0ada5558 100644 --- a/lib/rules/valid-v-once.js +++ b/lib/rules/valid-v-once.js @@ -29,7 +29,7 @@ module.exports = { create (context) { return utils.defineTemplateBodyVisitor(context, { - "VAttribute[directive=true][key.name='once']" (node) { + "VAttribute[directive=true][key.name.name='once']" (node) { if (node.key.argument) { context.report({ node, diff --git a/lib/rules/valid-v-pre.js b/lib/rules/valid-v-pre.js index eaf4ef038..40231a462 100644 --- a/lib/rules/valid-v-pre.js +++ b/lib/rules/valid-v-pre.js @@ -29,7 +29,7 @@ module.exports = { create (context) { return utils.defineTemplateBodyVisitor(context, { - "VAttribute[directive=true][key.name='pre']" (node) { + "VAttribute[directive=true][key.name.name='pre']" (node) { if (node.key.argument) { context.report({ node, diff --git a/lib/rules/valid-v-show.js b/lib/rules/valid-v-show.js index 0855e92cc..6eb5183ca 100644 --- a/lib/rules/valid-v-show.js +++ b/lib/rules/valid-v-show.js @@ -29,7 +29,7 @@ module.exports = { create (context) { return utils.defineTemplateBodyVisitor(context, { - "VAttribute[directive=true][key.name='show']" (node) { + "VAttribute[directive=true][key.name.name='show']" (node) { if (node.key.argument) { context.report({ node, diff --git a/lib/rules/valid-v-text.js b/lib/rules/valid-v-text.js index d1f823da3..c23d1f1de 100644 --- a/lib/rules/valid-v-text.js +++ b/lib/rules/valid-v-text.js @@ -29,7 +29,7 @@ module.exports = { create (context) { return utils.defineTemplateBodyVisitor(context, { - "VAttribute[directive=true][key.name='text']" (node) { + "VAttribute[directive=true][key.name.name='text']" (node) { if (node.key.argument) { context.report({ node, diff --git a/lib/utils/indent-common.js b/lib/utils/indent-common.js index 28e68b0a8..227e85adc 100644 --- a/lib/utils/indent-common.js +++ b/lib/utils/indent-common.js @@ -879,13 +879,13 @@ module.exports.defineVisitor = function create (context, tokenStore, defaultOpti return processIgnores({ VAttribute (node) { const keyToken = tokenStore.getFirstToken(node) - const eqToken = tokenStore.getFirstToken(node, 1) + const eqToken = tokenStore.getTokenAfter(node.key) - if (eqToken != null) { + if (eqToken != null && eqToken.range[1] <= node.range[1]) { setOffset(eqToken, 1, keyToken) - const valueToken = tokenStore.getFirstToken(node, 2) - if (valueToken != null) { + const valueToken = tokenStore.getTokenAfter(eqToken) + if (valueToken != null && valueToken.range[1] <= node.range[1]) { setOffset(valueToken, 1, keyToken) } } diff --git a/lib/utils/index.js b/lib/utils/index.js index e2af0257f..a4129d98f 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -140,25 +140,6 @@ module.exports = { return null }, - /** - * Finds attribute in the given start tag - * @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} attribute node - */ - findAttribute (node, name, value) { - assert(node && node.type === 'VElement') - return node.startTag.attributes.find(attr => ( - !attr.directive && - attr.key.name === name && - ( - value === undefined || - (attr.value != null && attr.value.value === value) - ) - )) - }, - /** * Check whether the given start tag has specific directive. * @param {ASTNode} node The start tag node to check. @@ -168,23 +149,7 @@ module.exports = { */ hasAttribute (node, name, value) { assert(node && node.type === 'VElement') - return Boolean(this.findAttribute(node, name, value)) - }, - - /** - * Finds directive in the given start tag - * @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} directive node - */ - findDirective (node, name, argument) { - assert(node && node.type === 'VElement') - return node.startTag.attributes.find(a => - a.directive && - a.key.name === name && - (argument === undefined || a.key.argument === argument) - ) + return Boolean(this.getAttribute(node, name, value)) }, /** @@ -196,7 +161,7 @@ module.exports = { */ hasDirective (node, name, argument) { assert(node && node.type === 'VElement') - return Boolean(this.findDirective(node, name, argument)) + return Boolean(this.getDirective(node, name, argument)) }, /** @@ -242,8 +207,8 @@ module.exports = { assert(node && node.type === 'VElement') return node.startTag.attributes.find(a => a.directive && - a.key.name === name && - (argument === undefined || a.key.argument === argument) + a.key.name.name === name && + (argument === undefined || (a.key.argument && a.key.argument.name) === argument) ) }, @@ -284,7 +249,7 @@ module.exports = { prev != null && prev.startTag.attributes.some(a => a.directive && - (a.key.name === 'if' || a.key.name === 'else-if') + (a.key.name.name === 'if' || a.key.name.name === 'else-if') ) ) }, @@ -369,26 +334,6 @@ module.exports = { return VOID_ELEMENT_NAMES.has(name) }, - /** - * Check whether the given attribute node is a binding - * @param {ASTNode} attribute The attribute to check. - * @returns {boolean} - */ - isBindingAttribute (attribute) { - return attribute.directive && - attribute.key.name === 'bind' && - attribute.key.argument - }, - - /** - * Check whether the given attribute node is an event - * @param {ASTNode} name The attribute to check. - * @returns {boolean} - */ - isEventAttribute (attribute) { - return attribute.directive && attribute.key.name === 'on' - }, - /** * Parse member expression node to get array with all of its parts * @param {ASTNode} node MemberExpression diff --git a/package.json b/package.json index afa4e7569..7da80ac64 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "eslint": "^5.0.0" }, "dependencies": { - "vue-eslint-parser": "^5.0.0" + "vue-eslint-parser": "^6.0.0" }, "devDependencies": { "@types/node": "^4.2.16", diff --git a/tests/lib/rules/max-attributes-per-line.js b/tests/lib/rules/max-attributes-per-line.js index ce4d45ffe..382492659 100644 --- a/tests/lib/rules/max-attributes-per-line.js +++ b/tests/lib/rules/max-attributes-per-line.js @@ -93,43 +93,43 @@ ruleTester.run('max-attributes-per-line', rule, { code: ``, output: ``, - errors: ['Attribute "age" should be on a new line.'] + errors: ['\'age\' should be on a new line.'] }, { code: ``, output: ``, - errors: ['Binding "age" should be on a new line.'] + errors: ['\':age\' should be on a new line.'] }, { code: ``, output: ``, - errors: ['Directive "bind" should be on a new line.'] + errors: ['\'v-bind\' should be on a new line.'] }, { code: ``, output: ``, - errors: ['Event "buy" should be on a new line.'] + errors: ['\'@buy\' should be on a new line.'] }, { code: ``, output: ``, - errors: ['Event "click" should be on a new line.'] + errors: ['\'@click.stop\' should be on a new line.'] }, { code: ``, output: ``, - errors: ['Directive "if" should be on a new line.'] + errors: ['\'v-if\' should be on a new line.'] }, { code: ``, output: ``, - errors: ['Binding "age" should be on a new line.'] + errors: ['\'v-bind:age\' should be on a new line.'] }, { code: ``, errors: [{ - message: 'Attribute "job" should be on a new line.', + message: '\'job\' should be on a new line.', type: 'VAttribute', line: 1 }] @@ -155,7 +155,7 @@ job="Vet" output: ``, errors: [{ - message: 'Attribute "job" should be on a new line.', + message: '\'job\' should be on a new line.', type: 'VAttribute', line: 1 }] @@ -166,11 +166,11 @@ job="Vet">`, output: ``, errors: [{ - message: 'Attribute "age" should be on a new line.', + message: '\'age\' should be on a new line.', type: 'VAttribute', line: 1 }, { - message: 'Attribute "job" should be on a new line.', + message: '\'job\' should be on a new line.', type: 'VAttribute', line: 1 }] @@ -187,7 +187,7 @@ name="John Doe" `, errors: [{ - message: 'Attribute "name" should be on a new line.', + message: '\'name\' should be on a new line.', type: 'VAttribute', line: 1 }] @@ -206,7 +206,7 @@ age="30" `, errors: [{ - message: 'Attribute "age" should be on a new line.', + message: '\'age\' should be on a new line.', type: 'VAttribute', line: 2 }] @@ -225,7 +225,7 @@ age="30" `, errors: [{ - message: 'Attribute "age" should be on a new line.', + message: '\'age\' should be on a new line.', type: 'VAttribute', line: 2 }] @@ -244,7 +244,7 @@ petname="Snoopy"> `, errors: [{ - message: 'Attribute "petname" should be on a new line.', + message: '\'petname\' should be on a new line.', type: 'VAttribute', line: 3 }] @@ -263,11 +263,11 @@ petname="Snoopy" extra="foo"> `, errors: [{ - message: 'Attribute "petname" should be on a new line.', + message: '\'petname\' should be on a new line.', type: 'VAttribute', line: 3 }, { - message: 'Attribute "extra" should be on a new line.', + message: '\'extra\' should be on a new line.', type: 'VAttribute', line: 3 }] diff --git a/tests/lib/rules/no-multi-spaces.js b/tests/lib/rules/no-multi-spaces.js index 68433af44..225f81c08 100644 --- a/tests/lib/rules/no-multi-spaces.js +++ b/tests/lib/rules/no-multi-spaces.js @@ -118,8 +118,8 @@ ruleTester.run('no-multi-spaces', rule, { output: '', errors: [ { - message: "Multiple spaces found before ':class'.", - type: 'HTMLIdentifier' + message: "Multiple spaces found before ':'.", + type: 'Punctuator' }, { message: "Multiple spaces found before '/>'.", From 6037a9f9236110e6c7fd302e7d0f420a7741543e Mon Sep 17 00:00:00 2001 From: ota Date: Fri, 8 Feb 2019 10:32:48 +0900 Subject: [PATCH 02/11] Exclude with dynamic arguments from core rule wrapper validation target --- lib/rules/array-bracket-spacing.js | 7 +++++-- lib/utils/index.js | 20 ++++++++++++++++++-- tests/lib/rules/array-bracket-spacing.js | 10 ++++++++++ 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/lib/rules/array-bracket-spacing.js b/lib/rules/array-bracket-spacing.js index e03abeb08..359a29c72 100644 --- a/lib/rules/array-bracket-spacing.js +++ b/lib/rules/array-bracket-spacing.js @@ -5,5 +5,8 @@ const { wrapCoreRule } = require('../utils') -// eslint-disable-next-line -module.exports = wrapCoreRule(require('eslint/lib/rules/array-bracket-spacing')) +// eslint-disable-next-line no-invalid-meta +module.exports = wrapCoreRule( + require('eslint/lib/rules/array-bracket-spacing'), + { skipDynamicArguments: true } +) diff --git a/lib/utils/index.js b/lib/utils/index.js index a4129d98f..5a02620b3 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -65,10 +65,13 @@ module.exports = { /** * Wrap a given core rule to apply it to Vue.js template. * @param {Rule} coreRule The core rule implementation to wrap. - * @param {string|undefined} category The category of this rule. + * @param {Object|undefined} options The option of this rule. + * @param {string|undefined} options.category The category of this rule. + * @param {boolean|undefined} options.skipDynamicArguments If `true`, skip validation within dynamic arguments. * @returns {Rule} The wrapped rule implementation. */ - wrapCoreRule (coreRule, category) { + wrapCoreRule (coreRule, options) { + const { category, skipDynamicArguments } = options || {} return { create (context) { const tokenStore = @@ -92,6 +95,19 @@ module.exports = { delete handlers['Program:exit'] } + if (skipDynamicArguments) { + let withinDynamicArguments = false + for (const name of Object.keys(handlers)) { + const original = handlers[name] + handlers[name] = (...args) => { + if (withinDynamicArguments) return + original(...args) + } + } + handlers['VDirectiveKey > VExpressionContainer'] = () => { withinDynamicArguments = true } + handlers['VDirectiveKey > VExpressionContainer:exit'] = () => { withinDynamicArguments = false } + } + // Apply the handlers to templates. return module.exports.defineTemplateBodyVisitor(context, handlers) }, diff --git a/tests/lib/rules/array-bracket-spacing.js b/tests/lib/rules/array-bracket-spacing.js index 509ebb1d1..fa0edc6b5 100644 --- a/tests/lib/rules/array-bracket-spacing.js +++ b/tests/lib/rules/array-bracket-spacing.js @@ -21,6 +21,16 @@ tester.run('array-bracket-spacing', rule, { { code: '', options: ['always'] + }, + '', + { + code: '', + options: ['always'] + }, + '', + { + code: '', + options: ['always'] } ], invalid: [ From 00f6db9fb466046fa6b8c43aad2020ac9b082aa4 Mon Sep 17 00:00:00 2001 From: ota Date: Fri, 8 Feb 2019 16:55:21 +0900 Subject: [PATCH 03/11] Changed to use skipDynamicArguments - vue/block-spacing - vue/brace-style - vue/key-spacing - vue/object-curly-spacing - vue/space-infix-ops - vue/space-unary-ops Add testcases - vue/array-bracket-spacing - vue/comma-dangle - vue/no-restricted-syntax --- lib/rules/block-spacing.js | 7 ++-- lib/rules/brace-style.js | 8 +++-- lib/rules/key-spacing.js | 7 ++-- lib/rules/object-curly-spacing.js | 7 ++-- lib/rules/space-infix-ops.js | 7 ++-- lib/rules/space-unary-ops.js | 7 ++-- tests/lib/rules/array-bracket-spacing.js | 18 ++++++++++ tests/lib/rules/block-spacing.js | 24 ++++++++++++- tests/lib/rules/brace-style.js | 16 ++++++++- tests/lib/rules/comma-dangle.js | 44 ++++++++++++++++++++++++ tests/lib/rules/key-spacing.js | 17 ++++++++- tests/lib/rules/no-restricted-syntax.js | 26 ++++++++++++++ tests/lib/rules/object-curly-spacing.js | 23 +++++++++++++ tests/lib/rules/space-infix-ops.js | 11 +++++- tests/lib/rules/space-unary-ops.js | 24 ++++++++++++- 15 files changed, 229 insertions(+), 17 deletions(-) diff --git a/lib/rules/block-spacing.js b/lib/rules/block-spacing.js index 7cee52355..6877ce313 100644 --- a/lib/rules/block-spacing.js +++ b/lib/rules/block-spacing.js @@ -5,5 +5,8 @@ const { wrapCoreRule } = require('../utils') -// eslint-disable-next-line -module.exports = wrapCoreRule(require('eslint/lib/rules/block-spacing')) +// eslint-disable-next-line no-invalid-meta +module.exports = wrapCoreRule( + require('eslint/lib/rules/block-spacing'), + { skipDynamicArguments: true } +) diff --git a/lib/rules/brace-style.js b/lib/rules/brace-style.js index c64880ba5..4d0999dda 100644 --- a/lib/rules/brace-style.js +++ b/lib/rules/brace-style.js @@ -5,5 +5,9 @@ const { wrapCoreRule } = require('../utils') -// eslint-disable-next-line -module.exports = wrapCoreRule(require('eslint/lib/rules/brace-style')) +// eslint-disable-next-line no-invalid-meta +module.exports = wrapCoreRule( + require('eslint/lib/rules/brace-style'), + { skipDynamicArguments: true } +) + diff --git a/lib/rules/key-spacing.js b/lib/rules/key-spacing.js index c7c46fcf6..5dd81f5fc 100644 --- a/lib/rules/key-spacing.js +++ b/lib/rules/key-spacing.js @@ -5,5 +5,8 @@ const { wrapCoreRule } = require('../utils') -// eslint-disable-next-line -module.exports = wrapCoreRule(require('eslint/lib/rules/key-spacing')) +// eslint-disable-next-line no-invalid-meta +module.exports = wrapCoreRule( + require('eslint/lib/rules/key-spacing'), + { skipDynamicArguments: true } +) diff --git a/lib/rules/object-curly-spacing.js b/lib/rules/object-curly-spacing.js index fc41b0d36..db049bbf1 100644 --- a/lib/rules/object-curly-spacing.js +++ b/lib/rules/object-curly-spacing.js @@ -5,5 +5,8 @@ const { wrapCoreRule } = require('../utils') -// eslint-disable-next-line -module.exports = wrapCoreRule(require('eslint/lib/rules/object-curly-spacing')) +// eslint-disable-next-line no-invalid-meta +module.exports = wrapCoreRule( + require('eslint/lib/rules/object-curly-spacing'), + { skipDynamicArguments: true } +) diff --git a/lib/rules/space-infix-ops.js b/lib/rules/space-infix-ops.js index fb94b74b2..251004681 100644 --- a/lib/rules/space-infix-ops.js +++ b/lib/rules/space-infix-ops.js @@ -5,5 +5,8 @@ const { wrapCoreRule } = require('../utils') -// eslint-disable-next-line -module.exports = wrapCoreRule(require('eslint/lib/rules/space-infix-ops')) +// eslint-disable-next-line no-invalid-meta +module.exports = wrapCoreRule( + require('eslint/lib/rules/space-infix-ops'), + { skipDynamicArguments: true } +) diff --git a/lib/rules/space-unary-ops.js b/lib/rules/space-unary-ops.js index 419607929..787aae6b3 100644 --- a/lib/rules/space-unary-ops.js +++ b/lib/rules/space-unary-ops.js @@ -5,5 +5,8 @@ const { wrapCoreRule } = require('../utils') -// eslint-disable-next-line -module.exports = wrapCoreRule(require('eslint/lib/rules/space-unary-ops')) +// eslint-disable-next-line no-invalid-meta +module.exports = wrapCoreRule( + require('eslint/lib/rules/space-unary-ops'), + { skipDynamicArguments: true } +) diff --git a/tests/lib/rules/array-bracket-spacing.js b/tests/lib/rules/array-bracket-spacing.js index fa0edc6b5..2aafe9ba5 100644 --- a/tests/lib/rules/array-bracket-spacing.js +++ b/tests/lib/rules/array-bracket-spacing.js @@ -36,14 +36,17 @@ tester.run('array-bracket-spacing', rule, { invalid: [ { code: '', + output: '', errors: ["There should be no space after '['."] }, { code: '', + output: '', errors: ["There should be no space before ']'."] }, { code: '', + output: '', errors: [ "There should be no space after '['.", "There should be no space before ']'." @@ -52,16 +55,19 @@ tester.run('array-bracket-spacing', rule, { { code: '', options: ['never'], + output: '', errors: ["There should be no space after '['."] }, { code: '', options: ['never'], + output: '', errors: ["There should be no space before ']'."] }, { code: '', options: ['never'], + output: '', errors: [ "There should be no space after '['.", "There should be no space before ']'." @@ -70,16 +76,28 @@ tester.run('array-bracket-spacing', rule, { { code: '', options: ['always'], + output: '', errors: ["A space is required before ']'."] }, { code: '', options: ['always'], + output: '', errors: ["A space is required after '['."] }, { code: '', options: ['always'], + output: '', + errors: [ + "A space is required after '['.", + "A space is required before ']'." + ] + }, + { + code: '', + options: ['always'], + output: '', errors: [ "A space is required after '['.", "A space is required before ']'." diff --git a/tests/lib/rules/block-spacing.js b/tests/lib/rules/block-spacing.js index 8ffc55d62..ff3b44002 100644 --- a/tests/lib/rules/block-spacing.js +++ b/tests/lib/rules/block-spacing.js @@ -17,7 +17,8 @@ tester.run('block-spacing', rule, { { code: '', options: ['never'] - } + }, + '' ], invalid: [ { @@ -110,6 +111,27 @@ tester.run('block-spacing', rule, { line: 3 } ] + }, + { + code: '', + output: '', + errors: [ + { + messageId: 'missing', + data: { + location: 'after', + token: '{' + } + // message: 'Requires a space after \'{\'', + }, + { + messageId: 'missing', + data: { + location: 'before', + token: '}' + } + // message: 'Requires a space before \'}\'', + }] } ] }) diff --git a/tests/lib/rules/brace-style.js b/tests/lib/rules/brace-style.js index cd4f3d36d..d8278e774 100644 --- a/tests/lib/rules/brace-style.js +++ b/tests/lib/rules/brace-style.js @@ -19,7 +19,8 @@ tester.run('brace-style', rule, { { code: ``, options: ['1tbs', { 'allowSingleLine': true }] - } + }, + `` ], invalid: [ { @@ -64,6 +65,19 @@ tester.run('brace-style', rule, { line: 3 } ] + }, + { + code: '', + output: ``, + errors: [ + { + message: 'Statement inside of curly braces should be on next line.' + }, + { + message: 'Closing curly brace should be on the same line as opening curly brace or on the line after the previous block.' + }] } ] }) diff --git a/tests/lib/rules/comma-dangle.js b/tests/lib/rules/comma-dangle.js index aca710198..1d5c3e7b1 100644 --- a/tests/lib/rules/comma-dangle.js +++ b/tests/lib/rules/comma-dangle.js @@ -33,6 +33,19 @@ tester.run('comma-dangle', rule, { options: [{ 'arrays': 'ignore' }] + }, + { + code: ` + ` + }, + { + code: ` + `, + options: ['always'] } ], invalid: [ @@ -103,6 +116,37 @@ tester.run('comma-dangle', rule, { line: 7 } ] + }, + { + code: ` + `, + output: ` + `, + errors: [ + { + message: 'Unexpected trailing comma.' + } + ] + }, + { + code: ` + `, + output: ` + `, + options: ['always'], + errors: [ + { + message: 'Missing trailing comma.' + } + ] } ] }) diff --git a/tests/lib/rules/key-spacing.js b/tests/lib/rules/key-spacing.js index 52adaaf9d..167e1f381 100644 --- a/tests/lib/rules/key-spacing.js +++ b/tests/lib/rules/key-spacing.js @@ -13,15 +13,30 @@ const tester = new RuleTester({ tester.run('key-spacing', rule, { valid: [ - '' + '', + '', + { + code: '', + options: [{ 'beforeColon': true }] + } ], invalid: [ { code: '', + output: '', errors: [ "Extra space after key 'a'.", "Missing space before value for key 'a'." ] + }, + { + code: '', + options: [{ 'beforeColon': true }], + output: '', + errors: [ + "Missing space after key 'a'.", + "Missing space before value for key 'a'." + ] } ] }) diff --git a/tests/lib/rules/no-restricted-syntax.js b/tests/lib/rules/no-restricted-syntax.js index 3fcdbc24a..36d7b9a02 100644 --- a/tests/lib/rules/no-restricted-syntax.js +++ b/tests/lib/rules/no-restricted-syntax.js @@ -24,6 +24,18 @@ tester.run('no-restricted-syntax', rule, { 'message': 'Call expressions are not allowed.' } ] + }, + { + code: ` + `, + options: [ + { + 'selector': 'CallExpression', + 'message': 'Call expressions are not allowed.' + } + ] } ], invalid: [ @@ -113,6 +125,20 @@ tester.run('no-restricted-syntax', rule, { endColumn: 53 } ] + }, + + { + code: ` + `, + options: [ + { + 'selector': 'CallExpression', + 'message': 'Call expressions are not allowed.' + } + ], + errors: ['Call expressions are not allowed.', 'Call expressions are not allowed.'] } ] }) diff --git a/tests/lib/rules/object-curly-spacing.js b/tests/lib/rules/object-curly-spacing.js index bf6cbc8a8..6471f3afc 100644 --- a/tests/lib/rules/object-curly-spacing.js +++ b/tests/lib/rules/object-curly-spacing.js @@ -21,19 +21,27 @@ tester.run('object-curly-spacing', rule, { { code: '', options: ['always'] + }, + '', + { + code: '', + options: ['always'] } ], invalid: [ { code: '', + output: '', errors: ["There should be no space after '{'."] }, { code: '', + output: '', errors: ["There should be no space before '}'."] }, { code: '', + output: '', errors: [ "There should be no space after '{'.", "There should be no space before '}'." @@ -42,16 +50,19 @@ tester.run('object-curly-spacing', rule, { { code: '', options: ['never'], + output: '', errors: ["There should be no space after '{'."] }, { code: '', options: ['never'], + output: '', errors: ["There should be no space before '}'."] }, { code: '', options: ['never'], + output: '', errors: [ "There should be no space after '{'.", "There should be no space before '}'." @@ -60,16 +71,28 @@ tester.run('object-curly-spacing', rule, { { code: '', options: ['always'], + output: '', errors: ["A space is required before '}'."] }, { code: '', options: ['always'], + output: '', errors: ["A space is required after '{'."] }, { code: '', options: ['always'], + output: '', + errors: [ + "A space is required after '{'.", + "A space is required before '}'." + ] + }, + { + code: '', + options: ['always'], + output: '', errors: [ "A space is required after '{'.", "A space is required before '}'." diff --git a/tests/lib/rules/space-infix-ops.js b/tests/lib/rules/space-infix-ops.js index ba67575ff..2c3da144a 100644 --- a/tests/lib/rules/space-infix-ops.js +++ b/tests/lib/rules/space-infix-ops.js @@ -19,20 +19,29 @@ const message = semver.lt(CLIEngine.version, '5.10.0') tester.run('space-infix-ops', rule, { valid: [ '', - '' + '', + '' ], invalid: [ { code: '', + output: '', errors: [message('+')] }, { code: '', + output: '', errors: [message('?')] }, { code: '', + output: '', errors: [message(':')] + }, + { + code: '', + output: '', + errors: [message('+')] } ] }) diff --git a/tests/lib/rules/space-unary-ops.js b/tests/lib/rules/space-unary-ops.js index 2f36302bb..f6accb202 100644 --- a/tests/lib/rules/space-unary-ops.js +++ b/tests/lib/rules/space-unary-ops.js @@ -14,16 +14,38 @@ const tester = new RuleTester({ tester.run('space-unary-ops', rule, { valid: [ '', - '' + '', + '', + { + code: '', + options: [{ nonwords: true }] + }, + { + code: '', + options: [{ nonwords: true }] + } ], invalid: [ { code: '', + output: '', errors: ['Unexpected space after unary operator \'-\'.'] }, { code: '', + output: '', errors: ['Unary word operator \'typeof\' must be followed by whitespace.'] + }, + { + code: '', + output: '', + errors: ['Unary word operator \'typeof\' must be followed by whitespace.'] + }, + { + code: '', + options: [{ nonwords: true }], + output: '', + errors: ['Unary operator \'!\' must be followed by whitespace.'] } ] }) From af7a8ff7df9dfb6b0bbfd56ee25f3ad2e7556c80 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Sun, 10 Feb 2019 00:38:38 +0900 Subject: [PATCH 04/11] fix attributes-order - handle dynamic argument names - handle v-slot directive --- lib/rules/attributes-order.js | 22 +++++++++------ tests/lib/rules/attributes-order.js | 43 +++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 8 deletions(-) diff --git a/lib/rules/attributes-order.js b/lib/rules/attributes-order.js index f6df3ac34..1808d3817 100644 --- a/lib/rules/attributes-order.js +++ b/lib/rules/attributes-order.js @@ -9,8 +9,13 @@ const utils = require('../utils') // Rule Definition // ------------------------------------------------------------------------------ -function getAttributeType (name, isDirective) { - if (isDirective) { +function getAttributeType (attribute, sourceCode) { + const isBind = attribute.directive && attribute.key.name.name === 'bind' + const name = isBind + ? (attribute.key.argument ? sourceCode.getText(attribute.key.argument) : '') + : (attribute.directive ? attribute.key.name.name : attribute.key.name) + + if (attribute.directive && !isBind) { if (name === 'for') { return 'LIST_RENDERING' } else if (name === 'if' || name === 'else-if' || name === 'else' || name === 'show' || name === 'cloak') { @@ -23,6 +28,8 @@ function getAttributeType (name, isDirective) { return 'EVENTS' } else if (name === 'html' || name === 'text') { return 'CONTENT' + } else if (name === 'slot') { + return 'UNIQUE' } else { return 'OTHER_DIRECTIVES' } @@ -38,10 +45,9 @@ function getAttributeType (name, isDirective) { } } } -function getPosition (attribute, attributePosition) { - const attributeType = attribute.directive && attribute.key.name.name === 'bind' - ? getAttributeType(attribute.key.argument && attribute.key.argument.name, false) - : getAttributeType(attribute.directive ? attribute.key.name.name : attribute.key.name, attribute.directive) + +function getPosition (attribute, attributePosition, sourceCode) { + const attributeType = getAttributeType(attribute, sourceCode) return attributePosition.hasOwnProperty(attributeType) ? attributePosition[attributeType] : -1 } @@ -91,8 +97,8 @@ function create (context) { previousNode = null }, 'VAttribute' (node) { - if ((currentPosition === -1) || (currentPosition <= getPosition(node, attributePosition))) { - currentPosition = getPosition(node, attributePosition) + if ((currentPosition === -1) || (currentPosition <= getPosition(node, attributePosition, sourceCode))) { + currentPosition = getPosition(node, attributePosition, sourceCode) previousNode = node } else { reportIssue(node, previousNode) diff --git a/tests/lib/rules/attributes-order.js b/tests/lib/rules/attributes-order.js index d36294411..48e70e60e 100644 --- a/tests/lib/rules/attributes-order.js +++ b/tests/lib/rules/attributes-order.js @@ -276,6 +276,23 @@ tester.run('attributes-order', rule, { 'GLOBAL' ] }] + }, + { + filename: 'test.vue', + code: + `` } ], @@ -584,6 +601,32 @@ tester.run('attributes-order', rule, { nodeType: 'VIdentifier' } ] + }, + { + code: + ``, + output: + ``, + errors: [ + { + message: 'Attribute "v-slot" should go before "v-model".', + nodeType: 'VIdentifier' + } + ] } ] }) From 58a8d7efc15a22e473ed9d551ed30290806265d3 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Sun, 10 Feb 2019 00:53:21 +0900 Subject: [PATCH 05/11] add attribute-hyphenation tests --- tests/lib/rules/attribute-hyphenation.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/lib/rules/attribute-hyphenation.js b/tests/lib/rules/attribute-hyphenation.js index 433a33823..e5aad05e0 100644 --- a/tests/lib/rules/attribute-hyphenation.js +++ b/tests/lib/rules/attribute-hyphenation.js @@ -47,6 +47,16 @@ ruleTester.run('attribute-hyphenation', rule, { filename: 'test.vue', code: '', options: ['never', { 'ignore': ['custom-hyphen', 'second-custom'] }] + }, + { + filename: 'test.vue', + code: '', + options: ['always'] + }, + { + filename: 'test.vue', + code: '', + options: ['never'] } ], From 88fea6381a4367fe06d419c06017035e793b4002 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Sun, 10 Feb 2019 01:29:10 +0900 Subject: [PATCH 06/11] add no-duplicate-attributes tests --- tests/lib/rules/no-duplicate-attributes.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/lib/rules/no-duplicate-attributes.js b/tests/lib/rules/no-duplicate-attributes.js index 03cd71c74..aeef7fa8a 100644 --- a/tests/lib/rules/no-duplicate-attributes.js +++ b/tests/lib/rules/no-duplicate-attributes.js @@ -52,6 +52,14 @@ tester.run('no-duplicate-attributes', rule, { filename: 'test.vue', code: '', options: [{ allowCoexistStyle: true }] + }, + { + filename: 'test.vue', + code: '' + }, + { + filename: 'test.vue', + code: '' } ], invalid: [ From 3b1fc94369855156340e36f9455d815cae7213ab Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Sun, 10 Feb 2019 01:36:08 +0900 Subject: [PATCH 07/11] fix use-v-on-exact - support dynamic arguments --- lib/rules/use-v-on-exact.js | 9 ++++++--- tests/lib/rules/use-v-on-exact.js | 17 +++++++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/lib/rules/use-v-on-exact.js b/lib/rules/use-v-on-exact.js index 047d84ed3..bf194fd8e 100644 --- a/lib/rules/use-v-on-exact.js +++ b/lib/rules/use-v-on-exact.js @@ -20,16 +20,17 @@ const SYSTEM_MODIFIERS = new Set(['ctrl', 'shift', 'alt', 'meta']) * Finds and returns all keys for event directives * * @param {array} attributes Element attributes + * @param {SourceCode} sourceCode The source code object. * @returns {array[object]} [{ name, node, modifiers }] */ -function getEventDirectives (attributes) { +function getEventDirectives (attributes, sourceCode) { return attributes .filter(attribute => attribute.directive && attribute.key.name.name === 'on' ) .map(attribute => ({ - name: attribute.key.argument && attribute.key.argument.name, + name: attribute.key.argument ? sourceCode.getText(attribute.key.argument) : '', node: attribute.key, modifiers: attribute.key.modifiers.map(modifier => modifier.name) })) @@ -149,12 +150,14 @@ module.exports = { * @returns {Object} AST event handlers. */ create (context) { + const sourceCode = context.getSourceCode() + return utils.defineTemplateBodyVisitor(context, { VStartTag (node) { if (node.attributes.length === 0) return const isCustomComponent = utils.isCustomComponent(node.parent) - let events = getEventDirectives(node.attributes) + let events = getEventDirectives(node.attributes, sourceCode) if (isCustomComponent) { // For components consider only events with `native` modifier diff --git a/tests/lib/rules/use-v-on-exact.js b/tests/lib/rules/use-v-on-exact.js index be90196b1..2bb990403 100644 --- a/tests/lib/rules/use-v-on-exact.js +++ b/tests/lib/rules/use-v-on-exact.js @@ -152,6 +152,12 @@ ruleTester.run('use-v-on-exact', rule, { }, { code: `` + }, + { + code: `` + }, + { + code: `` } ], @@ -272,6 +278,17 @@ ruleTester.run('use-v-on-exact', rule, { { message: "Consider to use '.exact' modifier.", line: 3 }, { message: "Consider to use '.exact' modifier.", line: 4 } ] + }, + { + code: ``, + errors: [ + { message: "Consider to use '.exact' modifier.", line: 3 } + ] } ] }) From 4085728e9816f688d77b1e3fc089ee5452d8a492 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Sun, 10 Feb 2019 02:22:04 +0900 Subject: [PATCH 08/11] fix v-bind-style - supports autofix to make .prop shortohand to long-form. --- lib/rules/v-bind-style.js | 32 +++++++++++++++------- package.json | 2 +- tests/lib/rules/v-bind-style.js | 48 +++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 11 deletions(-) diff --git a/lib/rules/v-bind-style.js b/lib/rules/v-bind-style.js index 9da7df315..a5025ee65 100644 --- a/lib/rules/v-bind-style.js +++ b/lib/rules/v-bind-style.js @@ -34,7 +34,8 @@ module.exports = { return utils.defineTemplateBodyVisitor(context, { "VAttribute[directive=true][key.name.name='bind'][key.argument!=null]" (node) { - const shorthand = node.key.name.rawName === ':' || node.key.name.rawName === '.' + const shorthandProp = node.key.name.rawName === '.' + const shorthand = node.key.name.rawName === ':' || shorthandProp if (shorthand === preferShorthand) { return } @@ -42,17 +43,28 @@ module.exports = { context.report({ node, loc: node.loc, - message: preferShorthand - ? "Unexpected 'v-bind' before ':'." - : "Expected 'v-bind' before ':'.", - fix: (fixer) => { + message: + preferShorthand ? "Unexpected 'v-bind' before ':'." + : shorthandProp ? "Expected 'v-bind:' instead of '.'." + /* otherwise */ : "Expected 'v-bind' before ':'.", + * fix (fixer) { if (preferShorthand) { - return fixer.removeRange([node.range[0], node.range[0] + 6]) - } - if (node.key.name.rawName === ':') { - return fixer.insertTextBefore(node, 'v-bind') + yield fixer.remove(node.key.name) + } else { + yield fixer.insertTextBefore(node, 'v-bind') + + if (shorthandProp) { + // Replace `.` by `:`. + yield fixer.replaceText(node.key.name, ':') + + // Insert `.prop` modifier if it doesn't exist. + const modifier = node.key.modifiers[0] + const isAutoGeneratedPropModifier = modifier.name === 'prop' && modifier.rawName === '' + if (isAutoGeneratedPropModifier) { + yield fixer.insertTextBefore(modifier, '.prop') + } + } } - return null } }) } diff --git a/package.json b/package.json index 7da80ac64..c19bb8d58 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "eslint": "^5.0.0" }, "dependencies": { - "vue-eslint-parser": "^6.0.0" + "vue-eslint-parser": "^6.0.1" }, "devDependencies": { "@types/node": "^4.2.16", diff --git a/tests/lib/rules/v-bind-style.js b/tests/lib/rules/v-bind-style.js index eeab4b9ab..bc9afb971 100644 --- a/tests/lib/rules/v-bind-style.js +++ b/tests/lib/rules/v-bind-style.js @@ -44,6 +44,26 @@ tester.run('v-bind-style', rule, { filename: 'test.vue', code: '', options: ['longform'] + }, + + // Don't enforce `.prop` shorthand because of experimental. + { + filename: 'test.vue', + code: '' + }, + { + filename: 'test.vue', + code: '' + }, + { + filename: 'test.vue', + code: '', + options: ['shorthand'] + }, + { + filename: 'test.vue', + code: '', + options: ['shorthand'] } ], invalid: [ @@ -66,6 +86,34 @@ tester.run('v-bind-style', rule, { code: '', output: '', errors: ["Expected 'v-bind' before ':'."] + }, + { + filename: 'test.vue', + options: ['longform'], + code: '', + output: '', + errors: ["Expected 'v-bind:' instead of '.'."] + }, + { + filename: 'test.vue', + options: ['longform'], + code: '', + output: '', + errors: ["Expected 'v-bind:' instead of '.'."] + }, + { + filename: 'test.vue', + options: ['longform'], + code: '', + output: '', + errors: ["Expected 'v-bind:' instead of '.'."] + }, + { + filename: 'test.vue', + options: ['longform'], + code: '', + output: '', + errors: ["Expected 'v-bind:' instead of '.'."] } ] }) From 56f2017e9a49838c7247774ecb41733582ba3499 Mon Sep 17 00:00:00 2001 From: yosuke ota Date: Sun, 10 Feb 2019 10:16:26 +0900 Subject: [PATCH 09/11] Update document of `attributes-order.md`. --- docs/rules/attributes-order.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/rules/attributes-order.md b/docs/rules/attributes-order.md index cbd38eee0..2f41dfa63 100644 --- a/docs/rules/attributes-order.md +++ b/docs/rules/attributes-order.md @@ -25,7 +25,7 @@ This rule aims to enforce ordering of component attributes. The default order is - `GLOBAL` ex: 'id' - `UNIQUE` - ex: 'ref', 'key', 'slot' + ex: 'ref', 'key', 'v-slot', 'slot' - `TWO_WAY_BINDING` ex: 'v-model' - `OTHER_DIRECTIVES` From e227f997fff7ffb4b3f3f0696cc79d151355dcf8 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Sun, 10 Feb 2019 18:09:31 +0900 Subject: [PATCH 10/11] add no-unused-vars tests - update vue-eslint-parser to fix a bug about references. --- package.json | 2 +- tests/lib/rules/no-unused-vars.js | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index c19bb8d58..195060c87 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "eslint": "^5.0.0" }, "dependencies": { - "vue-eslint-parser": "^6.0.1" + "vue-eslint-parser": "^6.0.2" }, "devDependencies": { "@types/node": "^4.2.16", diff --git a/tests/lib/rules/no-unused-vars.js b/tests/lib/rules/no-unused-vars.js index f717e7af9..663740e92 100644 --- a/tests/lib/rules/no-unused-vars.js +++ b/tests/lib/rules/no-unused-vars.js @@ -49,6 +49,9 @@ tester.run('no-unused-vars', rule, { }, { code: '' + }, + { + code: '' } ], invalid: [ From 5c784a0762b6071cfcf8ffed7b7aa6e50aa863e0 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Sun, 10 Feb 2019 18:10:16 +0900 Subject: [PATCH 11/11] fix this-in-template to avoid making invalid syntax in autofix --- lib/rules/this-in-template.js | 5 +++++ tests/lib/rules/this-in-template.js | 13 +++++++++++++ 2 files changed, 18 insertions(+) diff --git a/lib/rules/this-in-template.js b/lib/rules/this-in-template.js index 0f96402eb..6e972ee96 100644 --- a/lib/rules/this-in-template.js +++ b/lib/rules/this-in-template.js @@ -84,6 +84,11 @@ module.exports = { } : { 'VExpressionContainer' (node) { + if (node.parent.type === 'VDirectiveKey') { + // We cannot use `.` in dynamic arguments because the right of the `.` becomes a modifier. + // For example, In `:[this.prop]` case, `:[this` is an argument and `prop]` is a modifier. + return + } if (node.references) { for (const reference of node.references) { if (!scope.nodes.some(el => el.name === reference.id.name)) { diff --git a/tests/lib/rules/this-in-template.js b/tests/lib/rules/this-in-template.js index 1f7c7c69b..294882941 100644 --- a/tests/lib/rules/this-in-template.js +++ b/tests/lib/rules/this-in-template.js @@ -106,6 +106,12 @@ function createValidTests (prefix, options) { `, options + }, + + // We cannot use `.` in dynamic arguments because the right of the `.` becomes a modifier. + { + code: ``, + options } ] } @@ -153,6 +159,13 @@ function createInvalidTests (prefix, options, message, type) { errors: [{ message, type }], options } + + // We cannot use `.` in dynamic arguments because the right of the `.` becomes a modifier. + // { + // code: ``, + // errors: [{ message, type }], + // options + // } ].concat(options[0] === 'always' ? [] : [