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.1.0 to 5.2.0

lib/rules/array-bracket-spacing.js

11

lib/configs/no-layout-rules.js

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

rules: {
'vue/array-bracket-spacing': 'off',
'vue/arrow-spacing': 'off',
'vue/block-spacing': 'off',
'vue/brace-style': 'off',
'vue/comma-dangle': 'off',
'vue/html-closing-bracket-newline': 'off',

@@ -14,2 +19,3 @@ 'vue/html-closing-bracket-spacing': 'off',

'vue/html-self-closing': 'off',
'vue/key-spacing': 'off',
'vue/max-attributes-per-line': 'off',

@@ -20,5 +26,8 @@ 'vue/multiline-html-element-content-newline': 'off',

'vue/no-spaces-around-equal-signs-in-attribute': 'off',
'vue/object-curly-spacing': 'off',
'vue/script-indent': 'off',
'vue/singleline-html-element-content-newline': 'off'
'vue/singleline-html-element-content-newline': 'off',
'vue/space-infix-ops': 'off',
'vue/space-unary-ops': 'off'
}
}

@@ -10,6 +10,13 @@ /*

rules: {
'array-bracket-spacing': require('./rules/array-bracket-spacing'),
'arrow-spacing': require('./rules/arrow-spacing'),
'attribute-hyphenation': require('./rules/attribute-hyphenation'),
'attributes-order': require('./rules/attributes-order'),
'block-spacing': require('./rules/block-spacing'),
'brace-style': require('./rules/brace-style'),
'camelcase': require('./rules/camelcase'),
'comma-dangle': require('./rules/comma-dangle'),
'comment-directive': require('./rules/comment-directive'),
'component-name-in-template-casing': require('./rules/component-name-in-template-casing'),
'eqeqeq': require('./rules/eqeqeq'),
'html-closing-bracket-newline': require('./rules/html-closing-bracket-newline'),

@@ -22,2 +29,4 @@ 'html-closing-bracket-spacing': require('./rules/html-closing-bracket-spacing'),

'jsx-uses-vars': require('./rules/jsx-uses-vars'),
'key-spacing': require('./rules/key-spacing'),
'match-component-file-name': require('./rules/match-component-file-name'),
'max-attributes-per-line': require('./rules/max-attributes-per-line'),

@@ -28,2 +37,3 @@ 'multiline-html-element-content-newline': require('./rules/multiline-html-element-content-newline'),

'no-async-in-computed-properties': require('./rules/no-async-in-computed-properties'),
'no-boolean-default': require('./rules/no-boolean-default'),
'no-confusing-v-for-v-if': require('./rules/no-confusing-v-for-v-if'),

@@ -35,2 +45,3 @@ 'no-dupe-keys': require('./rules/no-dupe-keys'),

'no-reserved-keys': require('./rules/no-reserved-keys'),
'no-restricted-syntax': require('./rules/no-restricted-syntax'),
'no-shared-component-data': require('./rules/no-shared-component-data'),

@@ -46,2 +57,3 @@ 'no-side-effects-in-computed-properties': require('./rules/no-side-effects-in-computed-properties'),

'no-v-html': require('./rules/no-v-html'),
'object-curly-spacing': require('./rules/object-curly-spacing'),
'order-in-components': require('./rules/order-in-components'),

@@ -51,2 +63,3 @@ 'prop-name-casing': require('./rules/prop-name-casing'),

'require-default-prop': require('./rules/require-default-prop'),
'require-direct-export': require('./rules/require-direct-export'),
'require-prop-type-constructor': require('./rules/require-prop-type-constructor'),

@@ -60,5 +73,8 @@ 'require-prop-types': require('./rules/require-prop-types'),

'singleline-html-element-content-newline': require('./rules/singleline-html-element-content-newline'),
'space-infix-ops': require('./rules/space-infix-ops'),
'space-unary-ops': require('./rules/space-unary-ops'),
'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'),
'v-on-function-call': require('./rules/v-on-function-call'),
'v-on-style': require('./rules/v-on-style'),

@@ -65,0 +81,0 @@ 'valid-template-root': require('./rules/valid-template-root'),

2

lib/rules/attribute-hyphenation.js

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

category: 'strongly-recommended',
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/attribute-hyphenation.html'
url: 'https://eslint.vuejs.org/rules/attribute-hyphenation.html'
},

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

@@ -110,3 +110,3 @@ /**

category: 'recommended',
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/attributes-order.html'
url: 'https://eslint.vuejs.org/rules/attributes-order.html'
},

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

@@ -113,3 +113,3 @@ /**

category: 'base',
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/comment-directive.html'
url: 'https://eslint.vuejs.org/rules/comment-directive.html'
},

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

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

const casing = require('../utils/casing')
const { toRegExp } = require('../utils/regexp')
// -----------------------------------------------------------------------------
// Helpers
// -----------------------------------------------------------------------------
const allowedCaseOptions = ['PascalCase', 'kebab-case']

@@ -28,3 +33,3 @@ const defaultCase = 'PascalCase'

category: undefined,
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/component-name-in-template-casing.html'
url: 'https://eslint.vuejs.org/rules/component-name-in-template-casing.html'
},

@@ -44,2 +49,5 @@ fixable: 'code',

additionalItems: false
},
registeredComponentsOnly: {
type: 'boolean'
}

@@ -56,5 +64,39 @@ },

const caseType = allowedCaseOptions.indexOf(caseOption) !== -1 ? caseOption : defaultCase
const ignores = options.ignores || []
const ignores = (options.ignores || []).map(toRegExp)
const registeredComponentsOnly = options.registeredComponentsOnly !== false
const tokens = context.parserServices.getTemplateBodyTokenStore && context.parserServices.getTemplateBodyTokenStore()
const registeredComponents = []
/**
* Checks whether the given node is the verification target node.
* @param {VElement} node element node
* @returns {boolean} `true` if the given node is the verification target node.
*/
function isVerifyTarget (node) {
if (ignores.some(re => re.test(node.rawName))) {
// ignore
return false
}
if (!registeredComponentsOnly) {
// If the user specifies registeredComponentsOnly as false, it checks all component tags.
if ((!utils.isHtmlElementNode(node) && !utils.isSvgElementNode(node)) ||
utils.isHtmlWellKnownElementName(node.rawName) ||
utils.isSvgWellKnownElementName(node.rawName)
) {
return false
}
return true
}
// We only verify the components registered in the component.
if (registeredComponents
.filter(name => casing.pascalCase(name) === name) // When defining a component with PascalCase, you can use either case
.some(name => node.rawName === name || casing.pascalCase(node.rawName) === name)) {
return true
}
return false
}
let hasInvalidEOF = false

@@ -68,7 +110,3 @@

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

@@ -78,5 +116,2 @@ }

const name = node.rawName
if (ignores.indexOf(name) >= 0) {
return
}
const casingName = casing.getConverter(caseType)(name)

@@ -109,8 +144,16 @@ if (casingName !== name) {

}
}, {
Program (node) {
hasInvalidEOF = utils.hasInvalidEOF(node)
}
})
},
Object.assign(
{
Program (node) {
hasInvalidEOF = utils.hasInvalidEOF(node)
}
},
registeredComponentsOnly
? utils.executeOnVue(context, (obj) => {
registeredComponents.push(...utils.getRegisteredComponents(obj).map(n => n.name))
})
: {}
))
}
}

@@ -36,3 +36,3 @@ /**

category: 'strongly-recommended',
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/html-closing-bracket-newline.html'
url: 'https://eslint.vuejs.org/rules/html-closing-bracket-newline.html'
},

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

@@ -57,3 +57,3 @@ /**

category: 'strongly-recommended',
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/html-closing-bracket-spacing.html'
url: 'https://eslint.vuejs.org/rules/html-closing-bracket-spacing.html'
},

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

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

category: 'strongly-recommended',
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/html-end-tags.html'
url: 'https://eslint.vuejs.org/rules/html-end-tags.html'
},

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

@@ -33,3 +33,3 @@ /**

category: 'strongly-recommended',
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/html-indent.html'
url: 'https://eslint.vuejs.org/rules/html-indent.html'
},

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

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

category: 'strongly-recommended',
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/html-quotes.html'
url: 'https://eslint.vuejs.org/rules/html-quotes.html'
},

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

@@ -92,3 +92,3 @@ /**

category: 'strongly-recommended',
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/html-self-closing.html'
url: 'https://eslint.vuejs.org/rules/html-self-closing.html'
},

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

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

category: 'base',
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/jsx-uses-vars.html'
url: 'https://eslint.vuejs.org/rules/jsx-uses-vars.html'
},

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

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

category: 'strongly-recommended',
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/max-attributes-per-line.html'
url: 'https://eslint.vuejs.org/rules/max-attributes-per-line.html'
},

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

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

const casing = require('../utils/casing')
const INLINE_ELEMENTS = require('../utils/inline-non-void-elements.json')

@@ -25,4 +26,5 @@ // ------------------------------------------------------------------------------

return Object.assign({
ignores: ['pre', 'textarea'],
ignoreWhenEmpty: true
ignores: ['pre', 'textarea'].concat(INLINE_ELEMENTS),
ignoreWhenEmpty: true,
allowEmptyLines: false
}, options)

@@ -60,3 +62,3 @@ }

category: 'strongly-recommended',
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/multiline-html-element-content-newline.html'
url: 'https://eslint.vuejs.org/rules/multiline-html-element-content-newline.html'
},

@@ -75,2 +77,5 @@ fixable: 'whitespace',

additionalItems: false
},
allowEmptyLines: {
type: 'boolean'
}

@@ -90,2 +95,3 @@ },

const ignoreWhenEmpty = options.ignoreWhenEmpty
const allowEmptyLines = options.allowEmptyLines
const template = context.parserServices.getTemplateBodyTokenStore && context.parserServices.getTemplateBodyTokenStore()

@@ -102,2 +108,10 @@ const sourceCode = context.getSourceCode()

function isInvalidLineBreaks (lineBreaks) {
if (allowEmptyLines) {
return lineBreaks === 0
} else {
return lineBreaks !== 1
}
}
return utils.defineTemplateBodyVisitor(context, {

@@ -136,3 +150,3 @@ 'VElement' (node) {

const afterLineBreaks = node.endTag.loc.start.line - contentLast.loc.end.line
if (beforeLineBreaks !== 1) {
if (isInvalidLineBreaks(beforeLineBreaks)) {
context.report({

@@ -160,3 +174,3 @@ node: template.getLastToken(node.startTag),

if (afterLineBreaks !== 1) {
if (isInvalidLineBreaks(afterLineBreaks)) {
context.report({

@@ -163,0 +177,0 @@ node: template.getFirstToken(node.endTag),

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

category: 'strongly-recommended',
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/mustache-interpolation-spacing.html'
url: 'https://eslint.vuejs.org/rules/mustache-interpolation-spacing.html'
},

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

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

category: 'strongly-recommended',
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/name-property-casing.html'
url: 'https://eslint.vuejs.org/rules/name-property-casing.html'
},

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

@@ -68,3 +68,3 @@ /**

category: 'essential',
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/no-async-in-computed-properties.html'
url: 'https://eslint.vuejs.org/rules/no-async-in-computed-properties.html'
},

@@ -77,2 +77,3 @@ fixable: null,

const forbiddenNodes = []
const allowedScopes = []

@@ -93,2 +94,4 @@ const expressionTypes = {

})
} else if (node.parent.type === 'ReturnStatement') {
allowedScopes.push(node)
}

@@ -111,2 +114,4 @@ }

})
} else if (node.parent.type === 'ReturnStatement') {
allowedScopes.push(node)
}

@@ -121,4 +126,3 @@ },

})
}
if (isTimedFunction(node)) {
} else if (isTimedFunction(node)) {
forbiddenNodes.push({

@@ -128,2 +132,4 @@ node: node,

})
} else if (node.parent.type === 'ReturnStatement') {
allowedScopes.push(node)
}

@@ -137,2 +143,11 @@ },

})
},
'ReturnStatement' (node) {
if (
node.argument.type === 'ObjectExpression' ||
node.argument.type === 'ArrayExpression'
) {
allowedScopes.push(node.argument)
}
}

@@ -148,3 +163,7 @@ },

el.node.loc.start.line >= cp.value.loc.start.line &&
el.node.loc.end.line <= cp.value.loc.end.line
el.node.loc.end.line <= cp.value.loc.end.line &&
!allowedScopes.some(scope =>
scope.range[0] < el.node.range[0] &&
scope.range[1] > el.node.range[1]
)
) {

@@ -151,0 +170,0 @@ context.report({

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

category: 'recommended',
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/no-confusing-v-for-v-if.html',
url: 'https://eslint.vuejs.org/rules/no-confusing-v-for-v-if.html',
replacedBy: ['no-use-v-if-with-v-for']

@@ -46,0 +46,0 @@ },

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

category: 'essential',
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/no-dupe-keys.html'
url: 'https://eslint.vuejs.org/rules/no-dupe-keys.html'
},

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

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

category: 'essential',
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/no-duplicate-attributes.html'
url: 'https://eslint.vuejs.org/rules/no-duplicate-attributes.html'
},

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

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

category: 'strongly-recommended',
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/no-multi-spaces.html'
url: 'https://eslint.vuejs.org/rules/no-multi-spaces.html'
},

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

@@ -62,3 +62,3 @@ /**

category: 'essential',
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/no-parsing-error.html'
url: 'https://eslint.vuejs.org/rules/no-parsing-error.html'
},

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

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

category: 'essential',
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/no-reserved-keys.html'
url: 'https://eslint.vuejs.org/rules/no-reserved-keys.html'
},

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

@@ -44,3 +44,3 @@ /**

category: 'essential',
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/no-shared-component-data.html'
url: 'https://eslint.vuejs.org/rules/no-shared-component-data.html'
},

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

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

category: 'essential',
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/no-side-effects-in-computed-properties.html'
url: 'https://eslint.vuejs.org/rules/no-side-effects-in-computed-properties.html'
},

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

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

category: 'strongly-recommended',
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/no-spaces-around-equal-signs-in-attribute.html'
url: 'https://eslint.vuejs.org/rules/no-spaces-around-equal-signs-in-attribute.html'
},

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

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

category: 'essential',
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/no-template-key.html'
url: 'https://eslint.vuejs.org/rules/no-template-key.html'
},

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

@@ -25,3 +25,3 @@ /**

category: 'strongly-recommended',
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/no-template-shadow.html'
url: 'https://eslint.vuejs.org/rules/no-template-shadow.html'
},

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

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

category: 'essential',
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/no-textarea-mustache.html'
url: 'https://eslint.vuejs.org/rules/no-textarea-mustache.html'
},

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

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

category: 'essential',
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/no-unused-components.html'
url: 'https://eslint.vuejs.org/rules/no-unused-components.html'
},

@@ -60,3 +60,7 @@ fixable: null,

"VAttribute[directive=true][key.name='bind'][key.argument='is']" (node) {
if (node.value.type !== 'VExpressionContainer') return
if (
!node.value || // `<component :is>`
node.value.type !== 'VExpressionContainer' ||
!node.value.expression // `<component :is="">`
) return

@@ -63,0 +67,0 @@ if (node.value.expression.type === 'Literal') {

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

category: 'essential',
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/no-unused-vars.html'
url: 'https://eslint.vuejs.org/rules/no-unused-vars.html'
},

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

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

category: 'essential',
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/no-use-v-if-with-v-for.html'
url: 'https://eslint.vuejs.org/rules/no-use-v-if-with-v-for.html'
},

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

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

category: 'recommended',
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/no-v-html.html'
url: 'https://eslint.vuejs.org/rules/no-v-html.html'
},

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

@@ -25,2 +25,4 @@ /**

'watch',
'asyncData',
'fetch',
'LIFECYCLE_HOOKS',

@@ -140,3 +142,3 @@ 'methods',

category: 'recommended',
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/order-in-components.html'
url: 'https://eslint.vuejs.org/rules/order-in-components.html'
},

@@ -143,0 +145,0 @@ fixable: 'code', // null or "code" or "whitespace"

@@ -78,3 +78,3 @@ /**

category: 'strongly-recommended',
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/prop-name-casing.html'
url: 'https://eslint.vuejs.org/rules/prop-name-casing.html'
},

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

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

category: 'essential',
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/require-component-is.html'
url: 'https://eslint.vuejs.org/rules/require-component-is.html'
},

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

/**
* @fileoverview Require default value for props
* @author Michał Sajnóg <msajnog93@gmail.com> (http://github.com/michalsnik)
* @author Michał Sajnóg <msajnog93@gmail.com> (https://github.com/michalsnik)
*/

@@ -29,3 +29,3 @@ 'use strict'

category: 'strongly-recommended',
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/require-default-prop.html'
url: 'https://eslint.vuejs.org/rules/require-default-prop.html'
},

@@ -145,2 +145,4 @@ fixable: null, // or "code" or "whitespace"

for (const prop of propsToReport) {
const propName = prop.propName != null ? prop.propName : `[${context.getSourceCode().getText(prop.key)}]`
context.report({

@@ -150,3 +152,3 @@ node: prop.node,

data: {
propName: prop.key.name
propName
}

@@ -153,0 +155,0 @@ })

@@ -30,3 +30,3 @@ /**

category: 'essential',
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/require-prop-type-constructor.html'
url: 'https://eslint.vuejs.org/rules/require-prop-type-constructor.html'
},

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

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

category: 'strongly-recommended',
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/require-prop-types.html'
url: 'https://eslint.vuejs.org/rules/require-prop-types.html'
},

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

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

category: 'essential',
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/require-render-return.html'
url: 'https://eslint.vuejs.org/rules/require-render-return.html'
},

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

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

category: 'essential',
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/require-v-for-key.html'
url: 'https://eslint.vuejs.org/rules/require-v-for-key.html'
},

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

@@ -28,3 +28,3 @@ /**

category: 'essential',
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/require-valid-default-prop.html'
url: 'https://eslint.vuejs.org/rules/require-valid-default-prop.html'
},

@@ -113,2 +113,3 @@ fixable: null,

const propName = prop.propName != null ? prop.propName : `[${context.getSourceCode().getText(prop.key)}]`
context.report({

@@ -118,3 +119,3 @@ node: def,

data: {
name: prop.key.name,
name: propName,
types: Array.from(typeNames).join(' or ').toLowerCase()

@@ -121,0 +122,0 @@ }

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

category: 'essential',
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/return-in-computed-property.html'
url: 'https://eslint.vuejs.org/rules/return-in-computed-property.html'
},

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

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

category: undefined,
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/script-indent.html'
url: 'https://eslint.vuejs.org/rules/script-indent.html'
},

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

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

const casing = require('../utils/casing')
const INLINE_ELEMENTS = require('../utils/inline-non-void-elements.json')

@@ -25,3 +26,3 @@ // ------------------------------------------------------------------------------

return Object.assign({
ignores: ['pre', 'textarea'],
ignores: ['pre', 'textarea'].concat(INLINE_ELEMENTS),
ignoreWhenNoAttributes: true,

@@ -55,3 +56,3 @@ ignoreWhenEmpty: true

category: 'strongly-recommended',
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/singleline-html-element-content-newline.html'
url: 'https://eslint.vuejs.org/rules/singleline-html-element-content-newline.html'
},

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

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

category: 'recommended',
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/this-in-template.html'
url: 'https://eslint.vuejs.org/rules/this-in-template.html'
},

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

@@ -13,6 +13,122 @@ /**

const SYSTEM_MODIFIERS = new Set(['ctrl', 'shift', 'alt', 'meta'])
// ------------------------------------------------------------------------------
// Rule Definition
// Helpers
// ------------------------------------------------------------------------------
/**
* Finds and returns all keys for event directives
*
* @param {array} attributes Element attributes
* @returns {array[object]} [{ name, node, modifiers }]
*/
function getEventDirectives (attributes) {
return attributes
.filter(attribute =>
attribute.directive &&
attribute.key.name === 'on'
)
.map(attribute => ({
name: attribute.key.argument,
node: attribute.key,
modifiers: attribute.key.modifiers
}))
}
/**
* Checks whether given modifier is system one
*
* @param {string} modifier
* @returns {boolean}
*/
function isSystemModifier (modifier) {
return SYSTEM_MODIFIERS.has(modifier)
}
/**
* Checks whether given any of provided modifiers
* has system modifier
*
* @param {array} modifiers
* @returns {boolean}
*/
function hasSystemModifier (modifiers) {
return modifiers.some(isSystemModifier)
}
/**
* Groups all events in object,
* with keys represinting each event name
*
* @param {array} events
* @returns {object} { click: [], keypress: [] }
*/
function groupEvents (events) {
return events.reduce((acc, event) => {
if (acc[event.name]) {
acc[event.name].push(event)
} else {
acc[event.name] = [event]
}
return acc
}, {})
}
/**
* Creates alphabetically sorted string with system modifiers
*
* @param {array[string]} modifiers
* @returns {string} e.g. "alt,ctrl,del,shift"
*/
function getSystemModifiersString (modifiers) {
return modifiers.filter(isSystemModifier).sort().join(',')
}
/**
* Compares two events based on their modifiers
* to detect possible event leakeage
*
* @param {object} baseEvent
* @param {object} event
* @returns {boolean}
*/
function hasConflictedModifiers (baseEvent, event) {
if (
event.node === baseEvent.node ||
event.modifiers.includes('exact')
) return false
const eventModifiers = getSystemModifiersString(event.modifiers)
const baseEventModifiers = getSystemModifiersString(baseEvent.modifiers)
return (
baseEvent.modifiers.length >= 1 &&
baseEventModifiers !== eventModifiers &&
baseEventModifiers.indexOf(eventModifiers) > -1
)
}
/**
* Searches for events that might conflict with each other
*
* @param {array} events
* @returns {array} conflicted events, without duplicates
*/
function findConflictedEvents (events) {
return events.reduce((acc, event) => {
return [
...acc,
...events
.filter(evt => !acc.find(e => evt === e)) // No duplicates
.filter(hasConflictedModifiers.bind(null, event))
]
}, [])
}
// ------------------------------------------------------------------------------
// Rule details
// ------------------------------------------------------------------------------
module.exports = {

@@ -24,3 +140,3 @@ meta: {

category: 'essential',
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/use-v-on-exact.html'
url: 'https://eslint.vuejs.org/rules/use-v-on-exact.html'
},

@@ -40,27 +156,32 @@ fixable: null,

VStartTag (node) {
if (node.attributes.length > 1) {
const groups = node.attributes
.map(item => item.key)
.filter(item => item && item.type === 'VDirectiveKey' && item.name === 'on')
.reduce((rv, item) => {
(rv[item.argument] = rv[item.argument] || []).push(item)
return rv
}, {})
if (node.attributes.length === 0) return
const directives = Object.keys(groups).map(key => groups[key])
// const directives = Object.values(groups) // Uncomment after node 6 is dropped
.filter(item => item.length > 1)
for (const group of directives) {
if (group.some(item => item.modifiers.length > 0)) { // check if there are directives with modifiers
const invalid = group.filter(item => item.modifiers.length === 0)
for (const node of invalid) {
context.report({
node,
loc: node.loc,
message: "Consider to use '.exact' modifier."
})
}
}
}
const isCustomComponent = utils.isCustomComponent(node.parent)
let events = getEventDirectives(node.attributes)
if (isCustomComponent) {
// For components consider only events with `native` modifier
events = events.filter(event => event.modifiers.includes('native'))
}
const grouppedEvents = groupEvents(events)
Object.keys(grouppedEvents).forEach(eventName => {
const eventsInGroup = grouppedEvents[eventName]
const hasEventWithKeyModifier = eventsInGroup.some(event =>
hasSystemModifier(event.modifiers)
)
if (!hasEventWithKeyModifier) return
const conflictedEvents = findConflictedEvents(eventsInGroup)
conflictedEvents.forEach(e => {
context.report({
node: e.node,
loc: e.node.loc,
message: "Consider to use '.exact' modifier."
})
})
})
}

@@ -67,0 +188,0 @@ })

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

category: 'strongly-recommended',
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/v-bind-style.html'
url: 'https://eslint.vuejs.org/rules/v-bind-style.html'
},

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

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

category: 'strongly-recommended',
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/v-on-style.html'
url: 'https://eslint.vuejs.org/rules/v-on-style.html'
},

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

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

category: 'essential',
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/valid-template-root.html'
url: 'https://eslint.vuejs.org/rules/valid-template-root.html'
},

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

@@ -30,3 +30,3 @@ /**

category: 'essential',
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/valid-v-bind.html'
url: 'https://eslint.vuejs.org/rules/valid-v-bind.html'
},

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

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

category: 'essential',
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/valid-v-cloak.html'
url: 'https://eslint.vuejs.org/rules/valid-v-cloak.html'
},

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

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

category: 'essential',
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/valid-v-else-if.html'
url: 'https://eslint.vuejs.org/rules/valid-v-else-if.html'
},

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

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

category: 'essential',
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/valid-v-else.html'
url: 'https://eslint.vuejs.org/rules/valid-v-else.html'
},

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

@@ -110,3 +110,3 @@ /**

category: 'essential',
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/valid-v-for.html'
url: 'https://eslint.vuejs.org/rules/valid-v-for.html'
},

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

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

category: 'essential',
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/valid-v-html.html'
url: 'https://eslint.vuejs.org/rules/valid-v-html.html'
},

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

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

category: 'essential',
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/valid-v-if.html'
url: 'https://eslint.vuejs.org/rules/valid-v-if.html'
},

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

@@ -86,3 +86,3 @@ /**

category: 'essential',
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/valid-v-model.html'
url: 'https://eslint.vuejs.org/rules/valid-v-model.html'
},

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

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

const utils = require('../utils')
const keyAliases = require('../utils/key-aliases.json')

@@ -28,70 +29,3 @@ // ------------------------------------------------------------------------------

// https://www.w3.org/TR/uievents-key/
const KEY_ALIASES = new Set([
'unidentified', 'alt', 'alt-graph', 'caps-lock', 'control', 'fn', 'fn-lock',
'meta', 'num-lock', 'scroll-lock', 'shift', 'symbol', 'symbol-lock', 'hyper',
'super', 'enter', 'tab', 'arrow-down', 'arrow-left', 'arrow-right',
'arrow-up', 'end', 'home', 'page-down', 'page-up', 'backspace', 'clear',
'copy', 'cr-sel', 'cut', 'delete', 'erase-eof', 'ex-sel', 'insert', 'paste',
'redo', 'undo', 'accept', 'again', 'attn', 'cancel', 'context-menu', 'escape',
'execute', 'find', 'help', 'pause', 'select', 'zoom-in', 'zoom-out',
'brightness-down', 'brightness-up', 'eject', 'log-off', 'power',
'print-screen', 'hibernate', 'standby', 'wake-up', 'all-candidates',
'alphanumeric', 'code-input', 'compose', 'convert', 'dead', 'final-mode',
'group-first', 'group-last', 'group-next', 'group-previous', 'mode-change',
'next-candidate', 'non-convert', 'previous-candidate', 'process',
'single-candidate', 'hangul-mode', 'hanja-mode', 'junja-mode', 'eisu',
'hankaku', 'hiragana', 'hiragana-katakana', 'kana-mode', 'kanji-mode',
'katakana', 'romaji', 'zenkaku', 'zenkaku-hankaku', 'f1', 'f2', 'f3', 'f4',
'f5', 'f6', 'f7', 'f8', 'f9', 'f10', 'f11', 'f12', 'soft1', 'soft2', 'soft3',
'soft4', 'channel-down', 'channel-up', 'close', 'mail-forward', 'mail-reply',
'mail-send', 'media-close', 'media-fast-forward', 'media-pause',
'media-play-pause', 'media-record', 'media-rewind', 'media-stop',
'media-track-next', 'media-track-previous', 'new', 'open', 'print', 'save',
'spell-check', 'key11', 'key12', 'audio-balance-left', 'audio-balance-right',
'audio-bass-boost-down', 'audio-bass-boost-toggle', 'audio-bass-boost-up',
'audio-fader-front', 'audio-fader-rear', 'audio-surround-mode-next',
'audio-treble-down', 'audio-treble-up', 'audio-volume-down',
'audio-volume-up', 'audio-volume-mute', 'microphone-toggle',
'microphone-volume-down', 'microphone-volume-up', 'microphone-volume-mute',
'speech-correction-list', 'speech-input-toggle', 'launch-application1',
'launch-application2', 'launch-calendar', 'launch-contacts', 'launch-mail',
'launch-media-player', 'launch-music-player', 'launch-phone',
'launch-screen-saver', 'launch-spreadsheet', 'launch-web-browser',
'launch-web-cam', 'launch-word-processor', 'browser-back',
'browser-favorites', 'browser-forward', 'browser-home', 'browser-refresh',
'browser-search', 'browser-stop', 'app-switch', 'call', 'camera',
'camera-focus', 'end-call', 'go-back', 'go-home', 'headset-hook',
'last-number-redial', 'notification', 'manner-mode', 'voice-dial', 't-v',
't-v3-d-mode', 't-v-antenna-cable', 't-v-audio-description',
't-v-audio-description-mix-down', 't-v-audio-description-mix-up',
't-v-contents-menu', 't-v-data-service', 't-v-input', 't-v-input-component1',
't-v-input-component2', 't-v-input-composite1', 't-v-input-composite2',
't-v-input-h-d-m-i1', 't-v-input-h-d-m-i2', 't-v-input-h-d-m-i3',
't-v-input-h-d-m-i4', 't-v-input-v-g-a1', 't-v-media-context', 't-v-network',
't-v-number-entry', 't-v-power', 't-v-radio-service', 't-v-satellite',
't-v-satellite-b-s', 't-v-satellite-c-s', 't-v-satellite-toggle',
't-v-terrestrial-analog', 't-v-terrestrial-digital', 't-v-timer',
'a-v-r-input', 'a-v-r-power', 'color-f0-red', 'color-f1-green',
'color-f2-yellow', 'color-f3-blue', 'color-f4-grey', 'color-f5-brown',
'closed-caption-toggle', 'dimmer', 'display-swap', 'd-v-r', 'exit',
'favorite-clear0', 'favorite-clear1', 'favorite-clear2', 'favorite-clear3',
'favorite-recall0', 'favorite-recall1', 'favorite-recall2',
'favorite-recall3', 'favorite-store0', 'favorite-store1', 'favorite-store2',
'favorite-store3', 'guide', 'guide-next-day', 'guide-previous-day', 'info',
'instant-replay', 'link', 'list-program', 'live-content', 'lock',
'media-apps', 'media-last', 'media-skip-backward', 'media-skip-forward',
'media-step-backward', 'media-step-forward', 'media-top-menu', 'navigate-in',
'navigate-next', 'navigate-out', 'navigate-previous', 'next-favorite-channel',
'next-user-profile', 'on-demand', 'pairing', 'pin-p-down', 'pin-p-move',
'pin-p-toggle', 'pin-p-up', 'play-speed-down', 'play-speed-reset',
'play-speed-up', 'random-toggle', 'rc-low-battery', 'record-speed-next',
'rf-bypass', 'scan-channels-toggle', 'screen-mode-next', 'settings',
'split-screen-toggle', 's-t-b-input', 's-t-b-power', 'subtitle', 'teletext',
'video-mode-next', 'wink', 'zoom-toggle', 'audio-volume-down',
'audio-volume-up', 'audio-volume-mute', 'browser-back', 'browser-forward',
'channel-down', 'channel-up', 'context-menu', 'eject', 'end', 'enter', 'home',
'media-fast-forward', 'media-play', 'media-play-pause', 'media-record',
'media-rewind', 'media-stop', 'media-next-track', 'media-pause',
'media-previous-track', 'power', 'unidentified'
])
const KEY_ALIASES = new Set(keyAliases)

@@ -123,3 +57,3 @@ function isValidModifier (modifier, customModifiers) {

category: 'essential',
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/valid-v-on.html'
url: 'https://eslint.vuejs.org/rules/valid-v-on.html'
},

@@ -126,0 +60,0 @@ fixable: null,

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

category: 'essential',
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/valid-v-once.html'
url: 'https://eslint.vuejs.org/rules/valid-v-once.html'
},

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

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

category: 'essential',
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/valid-v-pre.html'
url: 'https://eslint.vuejs.org/rules/valid-v-pre.html'
},

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

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

category: 'essential',
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/valid-v-show.html'
url: 'https://eslint.vuejs.org/rules/valid-v-show.html'
},

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

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

category: 'essential',
url: 'https://vuejs.github.io/eslint-plugin-vue/rules/valid-v-text.html'
url: 'https://eslint.vuejs.org/rules/valid-v-text.html'
},

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

@@ -16,4 +16,25 @@ /**

const assert = require('assert')
const path = require('path')
const vueEslintParser = require('vue-eslint-parser')
/**
* Wrap the rule context object to override methods which access to tokens (such as getTokenAfter).
* @param {RuleContext} context The rule context object.
* @param {TokenStore} tokenStore The token store object for template.
*/
function wrapContextToOverrideTokenMethods (context, tokenStore) {
const sourceCode = new Proxy(context.getSourceCode(), {
get (object, key) {
return key in tokenStore ? tokenStore[key] : object[key]
}
})
return {
__proto__: context,
getSourceCode () {
return sourceCode
}
}
}
// ------------------------------------------------------------------------------

@@ -46,2 +67,45 @@ // 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.
* @returns {Rule} The wrapped rule implementation.
*/
wrapCoreRule (coreRule, category) {
return {
create (context) {
const tokenStore =
context.parserServices.getTemplateBodyTokenStore &&
context.parserServices.getTemplateBodyTokenStore()
// The `context.getSourceCode()` cannot access the tokens of templates.
// So override the methods which access to tokens by the `tokenStore`.
if (tokenStore) {
context = wrapContextToOverrideTokenMethods(context, tokenStore)
}
// Move `Program` handlers to `VElement[parent.type!='VElement']`
const handlers = coreRule.create(context)
if (handlers.Program) {
handlers["VElement[parent.type!='VElement']"] = handlers.Program
delete handlers.Program
}
if (handlers['Program:exit']) {
handlers["VElement[parent.type!='VElement']:exit"] = handlers['Program:exit']
delete handlers['Program:exit']
}
// Apply the handlers to templates.
return module.exports.defineTemplateBodyVisitor(context, handlers)
},
meta: Object.assign({}, coreRule.meta, {
docs: Object.assign({}, coreRule.meta.docs, {
category,
url: `https://vuejs.github.io/eslint-plugin-vue/rules/${path.basename(coreRule.meta.docs.url || '')}.html`
})
})
}
},
/**
* Check whether the given node is the root element or not.

@@ -206,6 +270,7 @@ * @param {ASTNode} node The element node to check.

.filter(p => p.type === 'Property')
.map(node => ({
node,
name: this.getStaticPropertyName(node.key)
}))
.map(node => {
const name = this.getStaticPropertyName(node)
return name ? { node, name } : null
})
.filter(comp => comp != null)
},

@@ -426,3 +491,6 @@

.map(prop => {
return { key: prop.key, value: this.unwrapTypes(prop.value), node: prop }
return {
key: prop.key, value: this.unwrapTypes(prop.value), node: prop,
propName: this.getStaticPropertyName(prop)
}
})

@@ -433,3 +501,3 @@ } else {

const key = prop.type === 'Literal' && typeof prop.value === 'string' ? prop : null
return { key, value: null, node: prop }
return { key, value: null, node: prop, propName: key != null ? prop.value : null }
})

@@ -480,2 +548,6 @@ }

isVueFile (path) {
return path.endsWith('.vue') || path.endsWith('.jsx')
},
/**

@@ -490,4 +562,3 @@ * Check whether the given node is a Vue component based

isVueComponentFile (node, path) {
const isVueFile = path.endsWith('.vue') || path.endsWith('.jsx')
return isVueFile &&
return this.isVueFile(path) &&
node.type === 'ExportDefaultDeclaration' &&

@@ -494,0 +565,0 @@ node.declaration.type === 'ObjectExpression'

{
"name": "eslint-plugin-vue",
"version": "5.1.0",
"version": "5.2.0",
"description": "Official ESLint plugin for Vue.js",

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

"docs:watch": "vuepress dev docs",
"docs:build": "vuepress build docs",
"docs:deploy": "./tools/deploy.sh"
"docs:build": "vuepress build docs"
},

@@ -23,3 +22,3 @@ "files": [

],
"homepage": "https://vuejs.github.io/eslint-plugin-vue/",
"homepage": "https://eslint.vuejs.org",
"keywords": [

@@ -35,3 +34,3 @@ "eslint",

"contributors": [
"Michał Sajnóg <msajnog93@gmail.com> (http://github.com/michalsnik)"
"Michał Sajnóg <msajnog93@gmail.com> (https://github.com/michalsnik)"
],

@@ -53,3 +52,3 @@ "license": "MIT",

"dependencies": {
"vue-eslint-parser": "^4.0.2"
"vue-eslint-parser": "^5.0.0"
},

@@ -67,2 +66,3 @@ "devDependencies": {

"nyc": "^12.0.2",
"semver": "^5.6.0",
"typescript": "^3.1.3",

@@ -69,0 +69,0 @@ "typescript-eslint-parser": "^20.0.0",

@@ -12,27 +12,27 @@ # eslint-plugin-vue

See https://vuejs.github.io/eslint-plugin-vue/
See [the official website](https://eslint.vuejs.org).
## :anchor: Versioning Policy
This plugin is following [Semantic Versioning](http://semver.org/) and [ESLint's Semantic Versioning Policy](https://github.com/eslint/eslint#semantic-versioning-policy).
This plugin is following [Semantic Versioning](https://semver.org/) and [ESLint's Semantic Versioning Policy](https://github.com/eslint/eslint#semantic-versioning-policy).
## :newspaper: Changelog
We're using [GitHub Releases](https://github.com/vuejs/eslint-plugin-vue/releases).
This project uses [GitHub Releases](https://github.com/vuejs/eslint-plugin-vue/releases).
## :beers: Contribution guide
## :beers: Contribution Guide
Contributing is welcome!
Contribution is welcome!
See https://vuejs.github.io/eslint-plugin-vue/developer-guide/
See [The ESLint Vue Plugin Developer Guide](https://eslint.vuejs.org/developer-guide/).
### Working with rules
### Working with Rules
Before you start writing new rule, please read the [official ESLint guide](https://eslint.org/docs/developer-guide/working-with-rules).
Before you start writing a new rule, please read [the official ESLint guide](https://eslint.org/docs/developer-guide/working-with-rules).
Next in order to get an idea how does the AST of the code that you want to check looks like, you can use one of the following applications:
- [astexplorer.net](http://astexplorer.net/) - best tool to inspect ASTs, but it doesn't support Vue templates yet
- [ast.js.org](https://ast.js.org/) - not fully featured, but supports Vue templates syntax
Next, in order to get an idea how does the AST of the code that you want to check looks like, use one of the following applications:
- [astexplorer.net](https://astexplorer.net/) - the best tool to inspect ASTs, but it doesn't support Vue template yet
- [ast.js.org](https://ast.js.org/) - not fully featured, but supports Vue template syntax
Since single file components in Vue are not plain JavaScript, we can't use the default parser, and we had to introduce additional one: `vue-eslint-parser`, that generates enhanced AST with nodes that represent specific parts of the template syntax, as well as what's inside the `<script>` tag.
Since single file components in Vue are not plain JavaScript, the default parser couldn't be used, so a new one was introduced. `vue-eslint-parser` generates enhanced AST with nodes that represent specific parts of the template syntax, as well as what's inside the `<script>` tag.

@@ -43,11 +43,11 @@ To know more about certain nodes in produced ASTs, go here:

The `vue-eslint-parser` provides few useful parser services, to help traverse the produced AST and access tokens of the template:
The `vue-eslint-parser` provides a few useful parser services that help traverse the produced AST and access tokens of the template:
- `context.parserServices.defineTemplateBodyVisitor(visitor, scriptVisitor)`
- `context.parserServices.getTemplateBodyTokenStore()`
Check out an [example rule](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/mustache-interpolation-spacing.js) to get a better understanding of how these work.
Check out [an example rule](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/mustache-interpolation-spacing.js) to get a better understanding of how these work.
Please be aware that regarding what kind of code examples you'll write in tests, you'll have to accordingly setup the parser in `RuleTester` (you can do it on per test case basis though). [See an example here](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/attribute-hyphenation.js#L19)
Please be aware that regarding what kind of code examples you'll write in tests, you'll have to accordingly set up the parser in `RuleTester` (you can do it on a per test case basis). See an example [here](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/attribute-hyphenation.js#L19).
If you'll stuck, remember there are plenty of rules you can learn from already, and if you can't find the right solution - don't hesitate to reach out in issues. We're happy to help!
If you'll stuck, remember there are plenty of rules you can learn from already. If you can't find the right solution, don't hesitate to reach out in [issues](https://github.com/vuejs/eslint-plugin-vue/issues) – we're happy to help!

@@ -54,0 +54,0 @@ ## :lock: License

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