Socket
Socket
Sign inDemoInstall

eslint-plugin-vue

Package Overview
Dependencies
Maintainers
4
Versions
170
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

eslint-plugin-vue - npm Package Compare versions

Comparing version 5.0.0-beta.3 to 5.0.0-beta.4

lib/rules/use-v-on-exact.js

1

lib/index.js

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

'this-in-template': require('./rules/this-in-template'),
'use-v-on-exact': require('./rules/use-v-on-exact'),
'v-bind-style': require('./rules/v-bind-style'),

@@ -57,0 +58,0 @@ 'v-on-style': require('./rules/v-on-style'),

2

lib/rules/attribute-hyphenation.js

@@ -19,3 +19,3 @@ /**

category: 'strongly-recommended',
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/attribute-hyphenation.md'
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/attribute-hyphenation.md'
},

@@ -22,0 +22,0 @@ fixable: 'code',

@@ -80,21 +80,6 @@ /**

// If we can upgrade requirements to `eslint@>4.1.0`, this code can be replaced by:
// return shiftAttrs.map((attr, i) => {
// const text = attr === previousNode ? sourceCode.getText(node) : sourceCode.getText(shiftAttrs[i - 1])
// return fixer.replaceText(attr, text)
// })
const replaceDataList = shiftAttrs.map((attr, i) => {
return shiftAttrs.map((attr, i) => {
const text = attr === previousNode ? sourceCode.getText(node) : sourceCode.getText(shiftAttrs[i - 1])
return {
range: attr.range,
text
}
return fixer.replaceText(attr, text)
})
const replaceRange = [previousNode.range[0], node.range[1]]
let text = sourceCode.text.slice(replaceRange[0], replaceRange[1])
replaceDataList.reverse().forEach((data) => {
const textRange = data.range.map(r => r - replaceRange[0])
text = text.slice(0, textRange[0]) + data.text + text.slice(textRange[1], text.length)
})
return fixer.replaceTextRange(replaceRange, text)
}

@@ -125,3 +110,3 @@ })

category: 'recommended',
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/attributes-order.md'
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/attributes-order.md'
},

@@ -128,0 +113,0 @@ fixable: 'code',

@@ -112,3 +112,3 @@ /**

category: 'base',
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/comment-directive.md'
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/comment-directive.md'
},

@@ -115,0 +115,0 @@ schema: []

@@ -26,3 +26,3 @@ /**

category: undefined, // strongly-recommended
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/component-name-in-template-casing.md'
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/component-name-in-template-casing.md'
},

@@ -55,3 +55,2 @@ fixable: 'code',

const tokens = context.parserServices.getTemplateBodyTokenStore && context.parserServices.getTemplateBodyTokenStore()
const sourceCode = context.getSourceCode()

@@ -66,3 +65,7 @@ let hasInvalidEOF = false

if (!utils.isCustomComponent(node)) {
if (
(!utils.isHtmlElementNode(node) && !utils.isSvgElementNode(node)) ||
utils.isHtmlWellKnownElementName(node.rawName) ||
utils.isSvgWellKnownElementName(node.rawName)
) {
return

@@ -94,9 +97,6 @@ }

const endTagOpen = tokens.getFirstToken(endTag)
// If we can upgrade requirements to `eslint@>4.1.0`, this code can be replaced by:
// return [
// fixer.replaceText(open, `<${casingName}`),
// fixer.replaceText(endTagOpen, `</${casingName}`)
// ]
const code = `<${casingName}${sourceCode.text.slice(open.range[1], endTagOpen.range[0])}</${casingName}`
return fixer.replaceTextRange([open.range[0], endTagOpen.range[1]], code)
return [
fixer.replaceText(open, `<${casingName}`),
fixer.replaceText(endTagOpen, `</${casingName}`)
]
}

@@ -103,0 +103,0 @@ })

@@ -35,3 +35,3 @@ /**

category: 'strongly-recommended',
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/html-closing-bracket-newline.md'
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/html-closing-bracket-newline.md'
},

@@ -38,0 +38,0 @@ fixable: 'whitespace',

@@ -56,3 +56,3 @@ /**

category: 'strongly-recommended',
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/html-closing-bracket-spacing.md'
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/html-closing-bracket-spacing.md'
},

@@ -59,0 +59,0 @@ schema: [{

@@ -23,3 +23,3 @@ /**

category: 'strongly-recommended',
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/html-end-tags.md'
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/html-end-tags.md'
},

@@ -26,0 +26,0 @@ fixable: 'code',

@@ -32,3 +32,3 @@ /**

category: 'strongly-recommended',
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/html-indent.md'
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/html-indent.md'
},

@@ -35,0 +35,0 @@ fixable: 'whitespace',

@@ -23,3 +23,3 @@ /**

category: 'recommended',
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/html-quotes.md'
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/html-quotes.md'
},

@@ -26,0 +26,0 @@ fixable: 'code',

@@ -91,3 +91,3 @@ /**

category: 'strongly-recommended',
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.2/docs/rules/html-self-closing.md'
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/html-self-closing.md'
},

@@ -169,3 +169,7 @@ fixable: 'code',

}
return fixer.replaceText(close, `></${node.rawName}>`)
// If only `close` is targeted for replacement, it conflicts with `component-name-in-template-casing`,
// so replace the entire element.
// return fixer.replaceText(close, `></${node.rawName}>`)
const elementPart = sourceCode.text.slice(node.range[0], close.range[0])
return fixer.replaceText(node, elementPart + `></${node.rawName}>`)
}

@@ -172,0 +176,0 @@ })

@@ -42,3 +42,3 @@ // the following rule is based on yannickcr/eslint-plugin-react

category: 'base',
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/jsx-uses-vars.md'
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/jsx-uses-vars.md'
},

@@ -45,0 +45,0 @@ schema: []

@@ -17,3 +17,3 @@ /**

category: 'strongly-recommended',
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/max-attributes-per-line.md'
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/max-attributes-per-line.md'
},

@@ -74,2 +74,3 @@ fixable: 'whitespace', // or "code" or "whitespace"

const canHaveFirstLine = configuration.allowFirstLine
const template = context.parserServices.getTemplateBodyTokenStore && context.parserServices.getTemplateBodyTokenStore()

@@ -83,3 +84,3 @@ return utils.defineTemplateBodyVisitor(context, {

if (utils.isSingleLine(node) && numberOfAttributes > singlelinemMaximum) {
showErrors(node.attributes.slice(singlelinemMaximum), node)
showErrors(node.attributes.slice(singlelinemMaximum))
}

@@ -89,3 +90,3 @@

if (!canHaveFirstLine && node.attributes[0].loc.start.line === node.loc.start.line) {
showErrors([node.attributes[0]], node)
showErrors([node.attributes[0]])
}

@@ -95,3 +96,3 @@

.filter(attrs => attrs.length > multilineMaximum)
.forEach(attrs => showErrors(attrs.splice(multilineMaximum), node))
.forEach(attrs => showErrors(attrs.splice(multilineMaximum)))
}

@@ -136,12 +137,41 @@ }

function showErrors (attributes, node) {
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) => {
if (i !== 0) return null
// Find the closest token before the current prop
// that is not a white space
const prevToken = template.getTokenBefore(prop, {
filter: (token) => token.type !== 'HTMLWhitespace'
})
const range = [prevToken.range[1], prop.range[0]]
return fixer.replaceTextRange(range, '\n')
}
context.report({
node: prop,
loc: prop.loc,
message: 'Attribute "{{propName}}" should be on a new line.',
data: {
propName: prop.key.name
},
fix: i === 0 ? (fixer) => fixer.insertTextBefore(prop, '\n') : undefined
message: '{{propType}} "{{propName}}" should be on a new line.',
data: getPropData(prop),
fix
})

@@ -148,0 +178,0 @@ })

@@ -55,3 +55,3 @@ /**

category: undefined,
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/multiline-html-element-content-newline.md'
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/multiline-html-element-content-newline.md'
},

@@ -58,0 +58,0 @@ fixable: 'whitespace',

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

category: 'strongly-recommended',
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/mustache-interpolation-spacing.md'
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/mustache-interpolation-spacing.md'
},

@@ -25,0 +25,0 @@ fixable: 'whitespace',

@@ -20,3 +20,3 @@ /**

category: 'strongly-recommended',
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/name-property-casing.md'
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/name-property-casing.md'
},

@@ -23,0 +23,0 @@ fixable: 'code', // or "code" or "whitespace"

@@ -67,3 +67,3 @@ /**

category: 'essential',
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/no-async-in-computed-properties.md'
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/no-async-in-computed-properties.md'
},

@@ -70,0 +70,0 @@ fixable: null,

@@ -42,3 +42,3 @@ /**

category: 'recommended',
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/no-confusing-v-for-v-if.md',
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/no-confusing-v-for-v-if.md',
replacedBy: ['no-use-v-if-with-v-for']

@@ -45,0 +45,0 @@ },

@@ -20,3 +20,3 @@ /**

category: 'essential',
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/no-dupe-keys.md'
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/no-dupe-keys.md'
},

@@ -23,0 +23,0 @@ fixable: null, // or "code" or "whitespace"

@@ -42,3 +42,3 @@ /**

category: 'essential',
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/no-duplicate-attributes.md'
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/no-duplicate-attributes.md'
},

@@ -45,0 +45,0 @@ fixable: null,

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

const isProperty = (context, node) => {
const sourceCode = context.getSourceCode()
return node.type === 'Punctuator' && sourceCode.getText(node) === ':'
}
module.exports = {

@@ -17,6 +22,14 @@ meta: {

category: 'strongly-recommended',
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/no-multi-spaces.md'
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/no-multi-spaces.md'
},
fixable: 'whitespace', // or "code" or "whitespace"
schema: []
schema: [{
type: 'object',
properties: {
ignoreProperties: {
type: 'boolean'
}
},
additionalProperties: false
}]
},

@@ -29,5 +42,4 @@

create (context) {
// ----------------------------------------------------------------------
// Public
// ----------------------------------------------------------------------
const options = context.options[0] || {}
const ignoreProperties = options.ignoreProperties === true

@@ -53,3 +65,6 @@ return {

const spaces = token.range[0] - prevToken.range[1]
if (spaces > 1 && token.loc.start.line === prevToken.loc.start.line) {
const shouldIgnore = ignoreProperties && (
isProperty(context, token) || isProperty(context, prevToken)
)
if (spaces > 1 && token.loc.start.line === prevToken.loc.start.line && !shouldIgnore) {
context.report({

@@ -56,0 +71,0 @@ node: token,

@@ -61,3 +61,3 @@ /**

category: 'essential',
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/no-parsing-error.md'
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/no-parsing-error.md'
},

@@ -64,0 +64,0 @@ fixable: null,

@@ -21,3 +21,3 @@ /**

category: 'essential',
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/no-reserved-keys.md'
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/no-reserved-keys.md'
},

@@ -24,0 +24,0 @@ fixable: null,

@@ -43,3 +43,3 @@ /**

category: 'essential',
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/no-shared-component-data.md'
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/no-shared-component-data.md'
},

@@ -70,12 +70,6 @@ fixable: 'code',

// If we can upgrade requirements to `eslint@>4.1.0`, this code can be replaced by:
// return [
// fixer.insertTextBefore(tokens.first, 'function() {\nreturn '),
// fixer.insertTextAfter(tokens.last, ';\n}')
// ]
// See: https://eslint.org/blog/2017/06/eslint-v4.1.0-released#applying-multiple-autofixes-simultaneously
const range = [tokens.first.range[0], tokens.last.range[1]]
const valueText = sourceCode.text.slice(range[0], range[1])
const replacement = `function() {\nreturn ${valueText};\n}`
return fixer.replaceTextRange(range, replacement)
return [
fixer.insertTextBefore(tokens.first, 'function() {\nreturn '),
fixer.insertTextAfter(tokens.last, ';\n}')
]
}

@@ -82,0 +76,0 @@ })

@@ -18,3 +18,3 @@ /**

category: 'essential',
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/no-side-effects-in-computed-properties.md'
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/no-side-effects-in-computed-properties.md'
},

@@ -21,0 +21,0 @@ fixable: null,

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

category: undefined,
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/no-spaces-around-equal-signs-in-attribute.md'
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/no-spaces-around-equal-signs-in-attribute.md'
},

@@ -25,0 +25,0 @@ fixable: 'whitespace',

@@ -23,3 +23,3 @@ /**

category: 'essential',
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/no-template-key.md'
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/no-template-key.md'
},

@@ -26,0 +26,0 @@ fixable: null,

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

category: 'strongly-recommended',
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/no-template-shadow.md'
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/no-template-shadow.md'
},

@@ -27,0 +27,0 @@ fixable: null,

@@ -23,3 +23,3 @@ /**

category: 'essential',
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/no-textarea-mustache.md'
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/no-textarea-mustache.md'
},

@@ -26,0 +26,0 @@ fixable: null,

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

category: 'essential',
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/no-unused-components.md'
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/no-unused-components.md'
},
fixable: null,
schema: []
schema: [{
type: 'object',
properties: {
ignoreWhenBindingPresent: {
type: 'boolean'
}
},
additionalProperties: false
}]
},
create (context) {
const usedComponents = []
const options = context.options[0] || {}
const ignoreWhenBindingPresent = options.ignoreWhenBindingPresent !== undefined ? options.ignoreWhenBindingPresent : true
const usedComponents = new Set()
let registeredComponents = []
let ignoreReporting = false
let templateLocation

@@ -37,23 +48,24 @@

VElement (node) {
if (!utils.isCustomComponent(node)) return
let usedComponentName
if (utils.hasAttribute(node, 'is')) {
usedComponentName = utils.findAttribute(node, 'is').value.value
} else if (utils.hasDirective(node, 'bind', 'is')) {
const directiveNode = utils.findDirective(node, 'bind', 'is')
if (
directiveNode.value.type === 'VExpressionContainer' &&
directiveNode.value.expression.type === 'Literal'
) {
usedComponentName = directiveNode.value.expression.value
}
} else {
usedComponentName = node.rawName
if (
(!utils.isHtmlElementNode(node) && !utils.isSvgElementNode(node)) ||
utils.isHtmlWellKnownElementName(node.rawName) ||
utils.isSvgWellKnownElementName(node.rawName)
) {
return
}
if (usedComponentName) {
usedComponents.push(usedComponentName)
usedComponents.add(node.rawName)
},
"VAttribute[directive=true][key.name='bind'][key.argument='is']" (node) {
if (node.value.type !== 'VExpressionContainer') return
if (node.value.expression.type === 'Literal') {
usedComponents.add(node.value.expression.value)
} else if (ignoreWhenBindingPresent) {
ignoreReporting = true
}
},
"VAttribute[directive=false][key.name='is']" (node) {
usedComponents.add(node.value.value)
},
"VElement[name='template']" (rootNode) {

@@ -63,13 +75,17 @@ templateLocation = templateLocation || rootNode.loc.start

"VElement[name='template']:exit" (rootNode) {
if (rootNode.loc.start !== templateLocation) return
if (
rootNode.loc.start !== templateLocation ||
ignoreReporting ||
utils.hasAttribute(rootNode, 'src')
) return
registeredComponents
.filter(({ name }) => {
// If the component name is PascalCase
// it can be used in varoious of ways inside template,
// If the component name is PascalCase or camelCase
// it can be used in various of ways inside template,
// like "theComponent", "The-component" etc.
// but except snake_case
if (casing.pascalCase(name) === name) {
return !usedComponents.some(n => {
return n.indexOf('_') === -1 && name === casing.pascalCase(n)
if (casing.pascalCase(name) === name || casing.camelCase(name) === name) {
return ![...usedComponents].some(n => {
return n.indexOf('_') === -1 && (name === casing.pascalCase(n) || casing.camelCase(n) === name)
})

@@ -79,3 +95,3 @@ } else {

// the registered name
return usedComponents.indexOf(name) === -1
return !usedComponents.has(name)
}

@@ -82,0 +98,0 @@ })

@@ -18,3 +18,3 @@ /**

category: 'essential',
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/no-unused-vars.md'
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/no-unused-vars.md'
},

@@ -21,0 +21,0 @@ fixable: null,

@@ -55,3 +55,3 @@ /**

category: 'essential',
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/no-use-v-if-with-v-for.md'
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/no-use-v-if-with-v-for.md'
},

@@ -58,0 +58,0 @@ fixable: null,

@@ -17,3 +17,3 @@ /**

category: 'recommended',
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/no-v-html.md'
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/no-v-html.md'
},

@@ -20,0 +20,0 @@ fixable: null,

@@ -138,3 +138,3 @@ /**

category: 'recommended',
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/order-in-components.md'
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/order-in-components.md'
},

@@ -197,18 +197,18 @@ fixable: 'code', // null or "code" or "whitespace"

}
const comma = sourceCode.getTokenAfter(propertyNode)
const hasAfterComma = isComma(comma)
const afterComma = sourceCode.getTokenAfter(propertyNode)
const hasAfterComma = isComma(afterComma)
const codeStart = sourceCode.getTokenBefore(propertyNode).range[1] // to include comments
const codeEnd = hasAfterComma ? comma.range[1] : propertyNode.range[1]
const beforeComma = sourceCode.getTokenBefore(propertyNode)
const codeStart = beforeComma.range[1] // to include comments
const codeEnd = hasAfterComma ? afterComma.range[1] : propertyNode.range[1]
const propertyCode = sourceCode.text.slice(codeStart, codeEnd) + (hasAfterComma ? '' : ',')
const insertTarget = sourceCode.getTokenBefore(firstUnorderedPropertyNode)
// If we can upgrade requirements to `eslint@>4.1.0`, this code can be replaced by:
// return [
// fixer.removeRange([codeStart, codeEnd]),
// fixer.insertTextAfter(insertTarget, propertyCode)
// ]
const insertStart = insertTarget.range[1]
const newCode = propertyCode + sourceCode.text.slice(insertStart, codeStart)
return fixer.replaceTextRange([insertStart, codeEnd], newCode)
const removeStart = hasAfterComma ? codeStart : beforeComma.range[0]
return [
fixer.removeRange([removeStart, codeEnd]),
fixer.insertTextAfter(insertTarget, propertyCode)
]
}

@@ -215,0 +215,0 @@ })

@@ -11,3 +11,3 @@ /**

function canFixPropertyName (node, originalName) {
function canFixPropertyName (node, key, originalName) {
// Can not fix of computed property names & shorthand

@@ -17,3 +17,3 @@ if (node.computed || node.shorthand) {

}
const key = node.key
// Can not fix of unknown types

@@ -41,32 +41,15 @@ if (key.type !== 'Literal' && key.type !== 'Identifier') {

return utils.executeOnVue(context, (obj) => {
const node = obj.properties.find(p =>
p.type === 'Property' &&
p.key.type === 'Identifier' &&
p.key.name === 'props' &&
(p.value.type === 'ObjectExpression' || p.value.type === 'ArrayExpression')
)
const props = utils.getComponentProps(obj)
.filter(prop => prop.key && prop.key.type === 'Literal' || (prop.key.type === 'Identifier' && !prop.node.computed))
if (!node) return
const items = node.value.type === 'ObjectExpression' ? node.value.properties : node.value.elements
for (const item of items) {
if (item.type !== 'Property') {
return
for (const item of props) {
const propName = item.key.type === 'Literal' ? item.key.value : item.key.name
if (typeof propName !== 'string') {
// (boolean | null | number | RegExp) Literal
continue
}
if (item.computed) {
if (item.key.type !== 'Literal') {
// TemplateLiteral | Identifier(variable) | Expression(s)
return
}
if (typeof item.key.value !== 'string') {
// (boolean | null | number | RegExp) Literal
return
}
}
const propName = item.key.type === 'Literal' ? item.key.value : item.key.name
const convertedName = converter(propName)
if (convertedName !== propName) {
context.report({
node: item,
node: item.node,
message: 'Prop "{{name}}" is not in {{caseType}}.',

@@ -77,3 +60,3 @@ data: {

},
fix: canFixPropertyName(item, propName) ? fixer => {
fix: canFixPropertyName(item.node, item.key, propName) ? fixer => {
return item.key.type === 'Literal'

@@ -98,3 +81,3 @@ ? fixer.replaceText(item.key, item.key.raw.replace(item.key.value, convertedName))

category: 'strongly-recommended',
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/prop-name-casing.md'
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/prop-name-casing.md'
},

@@ -101,0 +84,0 @@ fixable: 'code', // null or "code" or "whitespace"

@@ -23,3 +23,3 @@ /**

category: 'essential',
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/require-component-is.md'
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/require-component-is.md'
},

@@ -26,0 +26,0 @@ fixable: null,

@@ -9,2 +9,12 @@ /**

const NATIVE_TYPES = new Set([
'String',
'Number',
'Boolean',
'Function',
'Object',
'Array',
'Symbol'
])
// ------------------------------------------------------------------------------

@@ -19,3 +29,3 @@ // Rule Definition

category: 'strongly-recommended',
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/require-default-prop.md'
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/require-default-prop.md'
},

@@ -26,3 +36,3 @@ fixable: null, // or "code" or "whitespace"

create: function (context) {
create (context) {
// ----------------------------------------------------------------------

@@ -66,11 +76,10 @@ // Helpers

* Finds all props that don't have a default value set
* @param {Property} propsNode - Vue component's "props" node
* @param {Array} props - Vue component's "props" node
* @return {Array} Array of props without "default" value
*/
function findPropsWithoutDefaultValue (propsNode) {
return propsNode.value.properties
.filter(prop => prop.type === 'Property')
function findPropsWithoutDefaultValue (props) {
return props
.filter(prop => {
if (prop.value.type !== 'ObjectExpression') {
return true
return (prop.value.type !== 'CallExpression' && prop.value.type !== 'Identifier') || NATIVE_TYPES.has(prop.value.name)
}

@@ -105,3 +114,3 @@

function isBooleanProp (prop) {
const value = prop.value
const value = utils.unwrapTypes(prop.value)

@@ -132,18 +141,11 @@ return isValueNodeOfBooleanType(value) || (

return utils.executeOnVue(context, (obj) => {
const propsNode = obj.properties
.find(p =>
p.type === 'Property' &&
p.key.type === 'Identifier' &&
p.key.name === 'props' &&
p.value.type === 'ObjectExpression'
)
const props = utils.getComponentProps(obj)
.filter(prop => prop.key && prop.value && !prop.node.shorthand)
if (!propsNode) return
const propsWithoutDefault = findPropsWithoutDefaultValue(propsNode)
const propsWithoutDefault = findPropsWithoutDefaultValue(props)
const propsToReport = excludeBooleanProps(propsWithoutDefault)
propsToReport.forEach(prop => {
for (const prop of propsToReport) {
context.report({
node: prop,
node: prop.node,
message: `Prop '{{propName}}' requires default value to be set.`,

@@ -154,5 +156,5 @@ data: {

})
})
}
})
}
}

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

const isForbiddenType = nodeType => forbiddenTypes.indexOf(nodeType) > -1
const isForbiddenType = node => forbiddenTypes.indexOf(node.type) > -1 && node.raw !== 'null'

@@ -30,3 +30,3 @@ module.exports = {

category: 'essential',
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/require-prop-type-constructor.md'
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/require-prop-type-constructor.md'
},

@@ -50,15 +50,15 @@ fixable: 'code', // or "code" or "whitespace"

const checkPropertyNode = (p) => {
if (isForbiddenType(p.value.type)) {
const checkPropertyNode = (key, node) => {
if (isForbiddenType(node)) {
context.report({
node: p.value,
node: node,
message,
data: {
name: utils.getStaticPropertyName(p.key)
name: utils.getStaticPropertyName(key)
},
fix: fix(p.value)
fix: fix(node)
})
} else if (p.value.type === 'ArrayExpression') {
p.value.elements
.filter(prop => isForbiddenType(prop.type))
} else if (node.type === 'ArrayExpression') {
node.elements
.filter(prop => isForbiddenType(prop))
.forEach(prop => context.report({

@@ -68,3 +68,3 @@ node: prop,

data: {
name: utils.getStaticPropertyName(p.key)
name: utils.getStaticPropertyName(key)
},

@@ -77,27 +77,21 @@ fix: fix(prop)

return utils.executeOnVueComponent(context, (obj) => {
const node = obj.properties.find(p =>
p.type === 'Property' &&
p.key.type === 'Identifier' &&
p.key.name === 'props' &&
p.value.type === 'ObjectExpression'
)
const props = utils.getComponentProps(obj)
.filter(prop => prop.key && prop.value)
if (!node) return
node.value.properties.forEach(p => {
if (isForbiddenType(p.value.type) || p.value.type === 'ArrayExpression') {
checkPropertyNode(p)
} else if (p.value.type === 'ObjectExpression') {
const typeProperty = p.value.properties.find(prop =>
prop.type === 'Property' &&
prop.key.name === 'type'
for (const prop of props) {
if (isForbiddenType(prop.value) || prop.value.type === 'ArrayExpression') {
checkPropertyNode(prop.key, prop.value)
} else if (prop.value.type === 'ObjectExpression') {
const typeProperty = prop.value.properties.find(property =>
property.type === 'Property' &&
property.key.name === 'type'
)
if (!typeProperty) return
if (!typeProperty) continue
checkPropertyNode(typeProperty)
checkPropertyNode(prop.key, typeProperty.value)
}
})
}
})
}
}

@@ -18,3 +18,3 @@ /**

category: 'strongly-recommended',
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/require-prop-types.md'
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/require-prop-types.md'
},

@@ -46,25 +46,23 @@ fixable: null, // or "code" or "whitespace"

function checkProperties (items) {
for (const cp of items) {
if (cp.type !== 'Property') {
return
}
let hasType = true
if (cp.value.type === 'ObjectExpression') { // foo: {
hasType = objectHasType(cp.value)
} else if (cp.value.type === 'ArrayExpression') { // foo: [
hasType = cp.value.elements.length > 0
} else if (cp.value.type === 'FunctionExpression' || cp.value.type === 'ArrowFunctionExpression') {
hasType = false
}
if (!hasType) {
context.report({
node: cp,
message: 'Prop "{{name}}" should define at least its type.',
data: {
name: cp.key.name
}
})
}
function checkProperty (key, value, node) {
let hasType = true
if (!value) {
hasType = false
} else if (value.type === 'ObjectExpression') { // foo: {
hasType = objectHasType(value)
} else if (value.type === 'ArrayExpression') { // foo: [
hasType = value.elements.length > 0
} else if (value.type === 'FunctionExpression' || value.type === 'ArrowFunctionExpression') {
hasType = false
}
if (!hasType) {
context.report({
node,
message: 'Prop "{{name}}" should define at least its type.',
data: {
name: utils.getStaticPropertyName(key || node) || 'Unknown prop'
}
})
}
}

@@ -77,23 +75,9 @@

return utils.executeOnVue(context, (obj) => {
const node = obj.properties
.find(p =>
p.type === 'Property' &&
p.key.type === 'Identifier' &&
p.key.name === 'props'
)
const props = utils.getComponentProps(obj)
if (!node) return
if (node.value.type === 'ObjectExpression') {
checkProperties(node.value.properties)
for (const prop of props) {
checkProperty(prop.key, prop.value, prop.node)
}
if (node.value.type === 'ArrayExpression') {
context.report({
node,
message: 'Props should at least define their types.'
})
}
})
}
}

@@ -18,3 +18,3 @@ /**

category: 'essential',
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/require-render-return.md'
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/require-render-return.md'
},

@@ -21,0 +21,0 @@ fixable: null, // or "code" or "whitespace"

@@ -23,3 +23,3 @@ /**

category: 'essential',
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/require-v-for-key.md'
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/require-v-for-key.md'
},

@@ -26,0 +26,0 @@ fixable: null,

@@ -27,3 +27,3 @@ /**

category: 'essential',
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/require-valid-default-prop.md'
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/require-valid-default-prop.md'
},

@@ -92,15 +92,6 @@ fixable: null,

return utils.executeOnVue(context, obj => {
const props = obj.properties.find(p =>
isPropertyIdentifier(p) &&
p.key.name === 'props' &&
p.value.type === 'ObjectExpression'
)
if (!props) return
const props = utils.getComponentProps(obj)
.filter(prop => prop.key && prop.value && prop.value.type === 'ObjectExpression')
const properties = props.value.properties.filter(p =>
isPropertyIdentifier(p) &&
p.value.type === 'ObjectExpression'
)
for (const prop of properties) {
for (const prop of props) {
const type = getPropertyNode(prop.value, 'type')

@@ -107,0 +98,0 @@ if (!type) continue

@@ -18,3 +18,3 @@ /**

category: 'essential',
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/return-in-computed-property.md'
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/return-in-computed-property.md'
},

@@ -21,0 +21,0 @@ fixable: null, // or "code" or "whitespace"

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

category: undefined,
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/script-indent.md'
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/script-indent.md'
},

@@ -25,0 +25,0 @@ fixable: 'whitespace',

@@ -50,3 +50,3 @@ /**

category: undefined,
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/singleline-html-element-content-newline.md'
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/singleline-html-element-content-newline.md'
},

@@ -53,0 +53,0 @@ fixable: 'whitespace',

@@ -23,3 +23,3 @@ /**

category: 'recommended',
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/this-in-template.md'
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/this-in-template.md'
},

@@ -26,0 +26,0 @@ fixable: null,

@@ -23,3 +23,3 @@ /**

category: 'strongly-recommended',
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/v-bind-style.md'
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/v-bind-style.md'
},

@@ -26,0 +26,0 @@ fixable: 'code',

@@ -23,3 +23,3 @@ /**

category: 'strongly-recommended',
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/v-on-style.md'
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/v-on-style.md'
},

@@ -26,0 +26,0 @@ fixable: 'code',

@@ -23,3 +23,3 @@ /**

category: 'essential',
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/valid-template-root.md'
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/valid-template-root.md'
},

@@ -26,0 +26,0 @@ fixable: null,

@@ -29,3 +29,3 @@ /**

category: 'essential',
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/valid-v-bind.md'
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/valid-v-bind.md'
},

@@ -32,0 +32,0 @@ fixable: null,

@@ -23,3 +23,3 @@ /**

category: 'essential',
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/valid-v-cloak.md'
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/valid-v-cloak.md'
},

@@ -26,0 +26,0 @@ fixable: null,

@@ -23,3 +23,3 @@ /**

category: 'essential',
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/valid-v-else-if.md'
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/valid-v-else-if.md'
},

@@ -26,0 +26,0 @@ fixable: null,

@@ -23,3 +23,3 @@ /**

category: 'essential',
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/valid-v-else.md'
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/valid-v-else.md'
},

@@ -26,0 +26,0 @@ fixable: null,

@@ -109,3 +109,3 @@ /**

category: 'essential',
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/valid-v-for.md'
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/valid-v-for.md'
},

@@ -112,0 +112,0 @@ fixable: null,

@@ -23,3 +23,3 @@ /**

category: 'essential',
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/valid-v-html.md'
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/valid-v-html.md'
},

@@ -26,0 +26,0 @@ fixable: null,

@@ -23,3 +23,3 @@ /**

category: 'essential',
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/valid-v-if.md'
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/valid-v-if.md'
},

@@ -26,0 +26,0 @@ fixable: null,

@@ -85,3 +85,3 @@ /**

category: 'essential',
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/valid-v-model.md'
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/valid-v-model.md'
},

@@ -151,3 +151,3 @@ fixable: null,

const id = reference.id
if (id.parent.type === 'MemberExpression' || id.parent.type === 'BinaryExpression') {
if (id.parent.type !== 'VExpressionContainer') {
continue

@@ -161,3 +161,4 @@ }

loc: node.loc,
message: "'v-model' directives cannot update the iteration variable 'x' itself."
message: "'v-model' directives cannot update the iteration variable '{{varName}}' itself.",
data: { varName: id.name }
})

@@ -164,0 +165,0 @@ }

@@ -96,3 +96,3 @@ /**

function isValidModifier (modifier) {
function isValidModifier (modifier, customModifiers) {
return (

@@ -106,3 +106,5 @@ // built-in aliases

// keyAlias (special keys)
KEY_ALIASES.has(modifier)
KEY_ALIASES.has(modifier) ||
// custom modifiers
customModifiers.has(modifier)
)

@@ -120,13 +122,27 @@ }

category: 'essential',
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/valid-v-on.md'
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/valid-v-on.md'
},
fixable: null,
schema: []
schema: [
{
type: 'object',
properties: {
modifiers: {
type: 'array'
}
},
additionalProperties: false
}
]
},
create (context) {
const options = context.options[0] || {}
const customModifiers = new Set(options.modifiers || [])
const sourceCode = context.getSourceCode()
return utils.defineTemplateBodyVisitor(context, {
"VAttribute[directive=true][key.name='on']" (node) {
for (const modifier of node.key.modifiers) {
if (!isValidModifier(modifier)) {
if (!isValidModifier(modifier, customModifiers)) {
context.report({

@@ -140,8 +156,22 @@ node,

}
if (!utils.hasAttributeValue(node) && !node.key.modifiers.some(VERB_MODIFIERS.has, VERB_MODIFIERS)) {
context.report({
node,
loc: node.loc,
message: "'v-on' directives require that attribute value or verb modifiers."
})
if (
!utils.hasAttributeValue(node) &&
!node.key.modifiers.some(VERB_MODIFIERS.has, VERB_MODIFIERS)
) {
if (node.value && sourceCode.getText(node.value.expression)) {
const value = sourceCode.getText(node.value)
context.report({
node,
loc: node.loc,
message: 'Avoid using JavaScript keyword as "v-on" value: {{value}}.',
data: { value }
})
} else {
context.report({
node,
loc: node.loc,
message: "'v-on' directives require a value or verb modifier (like 'stop' or 'prevent')."
})
}
}

@@ -148,0 +178,0 @@ }

@@ -23,3 +23,3 @@ /**

category: 'essential',
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/valid-v-once.md'
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/valid-v-once.md'
},

@@ -26,0 +26,0 @@ fixable: null,

@@ -23,3 +23,3 @@ /**

category: 'essential',
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/valid-v-pre.md'
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/valid-v-pre.md'
},

@@ -26,0 +26,0 @@ fixable: null,

@@ -23,3 +23,3 @@ /**

category: 'essential',
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/valid-v-show.md'
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/valid-v-show.md'
},

@@ -26,0 +26,0 @@ fixable: null,

@@ -23,3 +23,3 @@ /**

category: 'essential',
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/valid-v-text.md'
url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/valid-v-text.md'
},

@@ -26,0 +26,0 @@ fixable: null,

@@ -12,3 +12,5 @@ const assert = require('assert')

return str
.replace(/([a-z])([A-Z])/g, match => match[0] + '-' + match[1])
.replace(/[A-Z]/g, match => '-' + match)
.replace(/([^a-zA-Z])-([A-Z])/g, match => match[0] + match[2])
.replace(/^-/, '')
.replace(invalidChars, '-')

@@ -25,3 +27,5 @@ .toLowerCase()

return str
.replace(/([a-z])([A-Z])/g, match => match[0] + '_' + match[1])
.replace(/[A-Z]/g, match => '_' + match)
.replace(/([^a-zA-Z])_([A-Z])/g, match => match[0] + match[2])
.replace(/^_/, '')
.replace(invalidChars, '_')

@@ -28,0 +32,0 @@ .toLowerCase()

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

const BLOCK_COMMENT_PREFIX = /^\s*\*/
const TRIVIAL_PUNCTUATOR = /^[(){}[\],;]$/

@@ -250,17 +249,2 @@ /**

/**
* Check whether a given token is trivial or not.
* @param {Token} token The token to check.
* @returns {boolean} `true` if the token is trivial.
*/
function isTrivialToken (token) {
return token != null && (
(token.type === 'Punctuator' && TRIVIAL_PUNCTUATOR.test(token.value)) ||
token.type === 'HTMLTagOpen' ||
token.type === 'HTMLEndTagOpen' ||
token.type === 'HTMLTagClose' ||
token.type === 'HTMLSelfClosingTagClose'
)
}
/**
* Creates AST event handlers for html-indent.

@@ -569,7 +553,6 @@ *

* @param {Token[]} tokens Tokens which are on the same line.
* @returns {number} Correct indentation. If it failed to calculate then `Number.MAX_SAFE_INTEGER`.
* @returns {object|null} Correct indentation. If it failed to calculate then `null`.
*/
function getExpectedIndent (tokens) {
const trivial = isTrivialToken(tokens[0])
let expectedIndent = Number.MAX_SAFE_INTEGER
function getExpectedIndents (tokens) {
const expectedIndents = []

@@ -580,10 +563,9 @@ for (let i = 0; i < tokens.length; ++i) {

// If the first token is not trivial then ignore trivial following tokens.
if (offsetInfo != null && (trivial || !isTrivialToken(token))) {
if (offsetInfo != null) {
if (offsetInfo.expectedIndent != null) {
expectedIndent = Math.min(expectedIndent, offsetInfo.expectedIndent)
expectedIndents.push(offsetInfo.expectedIndent)
} else {
const baseOffsetInfo = offsets.get(offsetInfo.baseToken)
if (baseOffsetInfo != null && baseOffsetInfo.expectedIndent != null && (i === 0 || !baseOffsetInfo.baseline)) {
expectedIndent = Math.min(expectedIndent, baseOffsetInfo.expectedIndent + offsetInfo.offset * options.indentSize)
expectedIndents.push(baseOffsetInfo.expectedIndent + offsetInfo.offset * options.indentSize)
if (baseOffsetInfo.baseline) {

@@ -596,4 +578,10 @@ break

}
if (!expectedIndents.length) {
return null
}
return expectedIndent
return {
expectedIndent: expectedIndents[0],
expectedBaseIndent: expectedIndents.reduce((a, b) => Math.min(a, b))
}
}

@@ -754,7 +742,10 @@

const actualIndent = firstToken.loc.start.column
const expectedIndent = getExpectedIndent(tokens)
if (expectedIndent === Number.MAX_SAFE_INTEGER) {
const expectedIndents = getExpectedIndents(tokens)
if (!expectedIndents) {
return
}
const expectedBaseIndent = expectedIndents.expectedBaseIndent
const expectedIndent = expectedIndents.expectedIndent
// Debug log

@@ -782,7 +773,7 @@ // console.log('line', firstToken.loc.start.line, '=', { actualIndent, expectedIndent }, 'from:')

if (options.indentChar === ' ') {
offsetInfo.expectedIndent = Math.max(0, token.loc.start.column + expectedIndent - actualIndent)
offsetInfo.expectedIndent = Math.max(0, token.loc.start.column + expectedBaseIndent - actualIndent)
} else {
// In hard-tabs mode, it cannot align tokens strictly, so use one additional offset.
// But the additional offset isn't needed if it's at the beginning of the line.
offsetInfo.expectedIndent = expectedIndent + (token === tokens[0] ? 0 : 1)
offsetInfo.expectedIndent = expectedBaseIndent + (token === tokens[0] ? 0 : 1)
}

@@ -796,3 +787,3 @@ baseline.add(token)

// Otherwise, set the expected indent of this line.
offsetInfo.expectedIndent = expectedIndent
offsetInfo.expectedIndent = expectedBaseIndent
}

@@ -799,0 +790,0 @@ }

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

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'))

@@ -237,3 +238,3 @@ const assert = require('assert')

return (
(this.isHtmlElementNode(node) && !this.isHtmlWellKnownElementName(node.name)) ||
(this.isHtmlElementNode(node) && !this.isHtmlWellKnownElementName(node.rawName)) ||
this.hasAttribute(node, 'is') ||

@@ -285,6 +286,16 @@ this.hasDirective(node, 'bind', 'is')

return HTML_ELEMENT_NAMES.has(name.toLowerCase())
return HTML_ELEMENT_NAMES.has(name)
},
/**
* Check whether the given name is an well-known SVG element or not.
* @param {string} name The name to check.
* @returns {boolean} `true` if the name is an well-known SVG element name.
*/
isSvgWellKnownElementName (name) {
assert(typeof name === 'string')
return SVG_ELEMENT_NAMES.has(name)
},
/**
* Check whether the given name is a void element name or not.

@@ -297,6 +308,26 @@ * @param {string} name The name to check.

return VOID_ELEMENT_NAMES.has(name.toLowerCase())
return VOID_ELEMENT_NAMES.has(name)
},
/**
* Check whether the given attribute node is a binding
* @param {ASTNode} name 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

@@ -373,4 +404,41 @@ * @param {ASTNode} MemberExpression

/**
* Get all props by looking at all component's properties
* @param {ObjectExpression} componentObject Object with component definition
* @return {Array} Array of component props in format: [{key?: String, value?: ASTNode, node: ASTNod}]
*/
getComponentProps (componentObject) {
const propsNode = componentObject.properties
.find(p =>
p.type === 'Property' &&
p.key.type === 'Identifier' &&
p.key.name === 'props' &&
(p.value.type === 'ObjectExpression' || p.value.type === 'ArrayExpression')
)
if (!propsNode) {
return []
}
let props
if (propsNode.value.type === 'ObjectExpression') {
props = propsNode.value.properties
.filter(prop => prop.type === 'Property')
.map(prop => {
return { key: prop.key, value: this.unwrapTypes(prop.value), node: prop }
})
} else {
props = propsNode.value.elements
.map(prop => {
const key = prop.type === 'Literal' && typeof prop.value === 'string' ? prop : null
return { key, value: null, node: prop }
})
}
return props
},
/**
* Get all computed properties by looking at all component's properties
* @param {ObjectExpression} Object with component definition
* @param {ObjectExpression} componentObject Object with component definition
* @return {Array} Array of computed properties in format: [{key: String, value: ASTNode}]

@@ -434,20 +502,28 @@ */

isVueComponent (node) {
const callee = node.callee
if (node.type === 'CallExpression') {
const callee = node.callee
const isFullVueComponent = node.type === 'CallExpression' &&
callee.type === 'MemberExpression' &&
callee.object.type === 'Identifier' &&
callee.object.name === 'Vue' &&
callee.property.type === 'Identifier' &&
['component', 'mixin', 'extend'].indexOf(callee.property.name) > -1 &&
node.arguments.length >= 1 &&
node.arguments.slice(-1)[0].type === 'ObjectExpression'
if (callee.type === 'MemberExpression') {
const calleeObject = this.unwrapTypes(callee.object)
const isDestructedVueComponent = node.type === 'CallExpression' &&
callee.type === 'Identifier' &&
callee.name === 'component' &&
node.arguments.length >= 1 &&
node.arguments.slice(-1)[0].type === 'ObjectExpression'
const isFullVueComponent = calleeObject.type === 'Identifier' &&
calleeObject.name === 'Vue' &&
callee.property.type === 'Identifier' &&
['component', 'mixin', 'extend'].indexOf(callee.property.name) > -1 &&
node.arguments.length >= 1 &&
node.arguments.slice(-1)[0].type === 'ObjectExpression'
return isFullVueComponent || isDestructedVueComponent
return isFullVueComponent
}
if (callee.type === 'Identifier') {
const isDestructedVueComponent = callee.name === 'component' &&
node.arguments.length >= 1 &&
node.arguments.slice(-1)[0].type === 'ObjectExpression'
return isDestructedVueComponent
}
}
return false
},

@@ -680,3 +756,3 @@

*
* @param {Object} node The node to parse (MemberExpression | CallExpression)
* @param {ASTNode} node The node to parse (MemberExpression | CallExpression)
* @return {String} eg. 'this.asd.qwe().map().filter().test.reduce()'

@@ -713,3 +789,12 @@ */

return parsedCallee.reverse().join('.').replace(/\.\[/g, '[')
},
/**
* Unwrap typescript types like "X as F"
* @param {ASTNode} node
* @return {ASTNode}
*/
unwrapTypes (node) {
return node.type === 'TSAsExpression' ? node.expression : node
}
}
{
"name": "eslint-plugin-vue",
"version": "5.0.0-beta.3",
"version": "5.0.0-beta.4",
"description": "Official ESLint plugin for Vue.js",

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

"eslint-plugin-eslint-plugin": "^1.4.0",
"eslint-plugin-html": "^4.0.1",
"eslint-plugin-vue-libs": "^3.0.0",
"lodash": "^4.17.4",
"mocha": "^5.2.0",
"nyc": "^12.0.2"
"nyc": "^12.0.2",
"typescript": "^3.1.3",
"typescript-eslint-parser": "^20.0.0"
}
}

@@ -5,3 +5,4 @@ # eslint-plugin-vue

[![NPM downloads](https://img.shields.io/npm/dm/eslint-plugin-vue.svg?style=flat)](https://npmjs.org/package/eslint-plugin-vue)
[![CircleCI](https://circleci.com/gh/vuejs/eslint-plugin-vue.svg?style=svg)](https://circleci.com/gh/vuejs/eslint-plugin-vue)
[![CircleCI](https://img.shields.io/circleci/project/github/vuejs/eslint-plugin-vue/master.svg?style=flat)](https://circleci.com/gh/vuejs/eslint-plugin-vue)
[![License](https://img.shields.io/github/license/vuejs/eslint-plugin-vue.svg?style=flat)](https://github.com/vuejs/eslint-plugin-vue/blob/master/LICENSE.md)

@@ -240,2 +241,3 @@ > Official ESLint plugin for Vue.js

| :wrench: | [vue/singleline-html-element-content-newline](./docs/rules/singleline-html-element-content-newline.md) | require a line break before and after the contents of a singleline element |
| | [vue/use-v-on-exact](./docs/rules/use-v-on-exact.md) | enforce usage of `exact` modifier on `v-on` |

@@ -289,4 +291,16 @@ ### Deprecated

2. Make sure your tool is set to lint `.vue` files.
- CLI targets only `.js` files by default. You have to specify additional extensions by `--ext` option or glob patterns. E.g. `eslint "src/**/*.{js,vue}"` or `eslint src --ext .vue`.
- CLI targets only `.js` files by default. You have to specify additional extensions by `--ext` option or glob patterns. E.g. `eslint "src/**/*.{js,vue}"` or `eslint src --ext .vue`. If you use `@vue/cli-plugin-eslint` and the `vue-cli-service lint` command - you don't have to worry about it.
- VSCode targets only JavaScript or HTML files by default. You have to add `"vue"` to the `"eslint.validate"` array in vscode settings. e.g. `"eslint.validate": [ "javascript", "javascriptreact", "vue" ]`
- If you use `Vetur` plugin in VSCode - set `"vetur.validation.template": false` to avoid default Vetur template validation. Check out [vetur documentation](https://github.com/vuejs/vetur/blob/master/docs/linting-error.md) for more info.
- For Atom editor, you need to go into Settings -> Packages -> linter-eslint, under the option “List of scopes to run eslint on”, add `text.html.vue`.
- For Sublime Text, you need to open command-pallete via cmd+shift+p (cmd => ctrl for windows) and type "Preferences: SublimeLinter Settings", paste to the config on the right side:
```json
{
"linters": {
"eslint": {
"selector": "source.js, text.html.vue"
}
}
}
```

@@ -293,0 +307,0 @@ ## :anchor: Semantic Versioning Policy

SocketSocket SOC 2 Logo

Product

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

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc