Skip to content

Commit

Permalink
Breaking: support new syntax in Vue.js 2.6 (#807)
Browse files Browse the repository at this point in the history
  • Loading branch information
mysticatea committed Feb 27, 2019
1 parent db700ff commit 5d26e94
Show file tree
Hide file tree
Showing 56 changed files with 532 additions and 182 deletions.
2 changes: 1 addition & 1 deletion docs/rules/attributes-order.md
Expand Up @@ -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`
Expand Down
7 changes: 5 additions & 2 deletions lib/rules/array-bracket-spacing.js
Expand Up @@ -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 }
)
5 changes: 4 additions & 1 deletion lib/rules/attribute-hyphenation.js
Expand Up @@ -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)
Expand Down
22 changes: 14 additions & 8 deletions lib/rules/attributes-order.js
Expand Up @@ -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') {
Expand All @@ -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'
}
Expand All @@ -38,10 +45,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)

function getPosition (attribute, attributePosition, sourceCode) {
const attributeType = getAttributeType(attribute, sourceCode)
return attributePosition.hasOwnProperty(attributeType) ? attributePosition[attributeType] : -1
}

Expand Down Expand Up @@ -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)
Expand Down
7 changes: 5 additions & 2 deletions lib/rules/block-spacing.js
Expand Up @@ -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 }
)
8 changes: 6 additions & 2 deletions lib/rules/brace-style.js
Expand Up @@ -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 }
)

7 changes: 5 additions & 2 deletions lib/rules/key-spacing.js
Expand Up @@ -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 }
)
22 changes: 3 additions & 19 deletions lib/rules/max-attributes-per-line.js
Expand Up @@ -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
Expand Down Expand Up @@ -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) => {
Expand All @@ -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
})
})
Expand Down
2 changes: 1 addition & 1 deletion lib/rules/no-confusing-v-for-v-if.js
Expand Up @@ -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)) {
Expand Down
4 changes: 2 additions & 2 deletions lib/rules/no-duplicate-attributes.js
Expand Up @@ -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
}
Expand Down
2 changes: 1 addition & 1 deletion lib/rules/no-unused-components.js
Expand Up @@ -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 || // `<component :is>`
node.value.type !== 'VExpressionContainer' ||
Expand Down
2 changes: 1 addition & 1 deletion lib/rules/no-use-v-if-with-v-for.js
Expand Up @@ -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')) {
Expand Down
2 changes: 1 addition & 1 deletion lib/rules/no-v-html.js
Expand Up @@ -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,
Expand Down
7 changes: 5 additions & 2 deletions lib/rules/object-curly-spacing.js
Expand Up @@ -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 }
)
2 changes: 1 addition & 1 deletion lib/rules/require-v-for-key.js
Expand Up @@ -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)
}
})
Expand Down
7 changes: 5 additions & 2 deletions lib/rules/space-infix-ops.js
Expand Up @@ -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 }
)
7 changes: 5 additions & 2 deletions lib/rules/space-unary-ops.js
Expand Up @@ -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 }
)
5 changes: 5 additions & 0 deletions lib/rules/this-in-template.js
Expand Up @@ -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)) {
Expand Down
13 changes: 8 additions & 5 deletions lib/rules/use-v-on-exact.js
Expand Up @@ -20,18 +20,19 @@ 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 === 'on'
attribute.key.name.name === 'on'
)
.map(attribute => ({
name: attribute.key.argument,
name: attribute.key.argument ? sourceCode.getText(attribute.key.argument) : '',
node: attribute.key,
modifiers: attribute.key.modifiers
modifiers: attribute.key.modifiers.map(modifier => modifier.name)
}))
}

Expand Down Expand Up @@ -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
Expand Down
37 changes: 28 additions & 9 deletions lib/rules/v-bind-style.js
Expand Up @@ -30,23 +30,42 @@ 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 shorthandProp = node.key.name.rawName === '.'
const shorthand = node.key.name.rawName === ':' || shorthandProp
if (shorthand === preferShorthand) {
return
}

context.report({
node,
loc: node.loc,
message: shorthand
? "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')
message:
preferShorthand ? "Unexpected 'v-bind' before ':'."
: shorthandProp ? "Expected 'v-bind:' instead of '.'."
/* otherwise */ : "Expected 'v-bind' before ':'.",
* fix (fixer) {
if (preferShorthand) {
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')
}
}
}
}
})
}
})
Expand Down
4 changes: 2 additions & 2 deletions lib/rules/v-on-function-call.js
Expand Up @@ -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,
Expand All @@ -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,
Expand Down
11 changes: 6 additions & 5 deletions lib/rules/v-on-style.js
Expand Up @@ -30,22 +30,23 @@ 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
}

const pos = node.range[0]
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:')
})
Expand Down

0 comments on commit 5d26e94

Please sign in to comment.