Socket
Socket
Sign inDemoInstall

eslint-plugin-vue

Package Overview
Dependencies
Maintainers
5
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 7.0.0-alpha.5 to 7.0.0-alpha.6

lib/rules/func-call-spacing.js

4

lib/configs/no-layout-rules.js

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

'vue/dot-location': 'off',
'vue/func-call-spacing': 'off',
'vue/html-closing-bracket-newline': 'off',

@@ -34,3 +35,6 @@ 'vue/html-closing-bracket-spacing': 'off',

'vue/no-spaces-around-equal-signs-in-attribute': 'off',
'vue/object-curly-newline': 'off',
'vue/object-curly-spacing': 'off',
'vue/object-property-newline': 'off',
'vue/operator-linebreak': 'off',
'vue/padding-line-between-blocks': 'off',

@@ -37,0 +41,0 @@ 'vue/script-indent': 'off',

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

'vue/component-tags-order': 'warn',
'vue/no-multiple-slot-args': 'warn',
'vue/no-v-html': 'warn',

@@ -13,0 +14,0 @@ 'vue/order-in-components': 'warn',

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

'vue/no-deprecated-dollar-listeners-api': 'error',
'vue/no-deprecated-dollar-scopedslots-api': 'error',
'vue/no-deprecated-events-api': 'error',

@@ -46,2 +47,3 @@ 'vue/no-deprecated-filter': 'error',

'vue/require-render-return': 'error',
'vue/require-slots-as-functions': 'error',
'vue/require-toggle-inside-transition': 'error',

@@ -48,0 +50,0 @@ 'vue/require-v-for-key': 'error',

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

'vue/component-tags-order': 'warn',
'vue/no-multiple-slot-args': 'warn',
'vue/no-v-html': 'warn',

@@ -13,0 +14,0 @@ 'vue/order-in-components': 'warn',

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

eqeqeq: require('./rules/eqeqeq'),
'func-call-spacing': require('./rules/func-call-spacing'),
'html-closing-bracket-newline': require('./rules/html-closing-bracket-newline'),

@@ -49,2 +50,3 @@ 'html-closing-bracket-spacing': require('./rules/html-closing-bracket-spacing'),

'no-async-in-computed-properties': require('./rules/no-async-in-computed-properties'),
'no-bare-strings-in-template': require('./rules/no-bare-strings-in-template'),
'no-boolean-default': require('./rules/no-boolean-default'),

@@ -55,2 +57,3 @@ 'no-confusing-v-for-v-if': require('./rules/no-confusing-v-for-v-if'),

'no-deprecated-dollar-listeners-api': require('./rules/no-deprecated-dollar-listeners-api'),
'no-deprecated-dollar-scopedslots-api': require('./rules/no-deprecated-dollar-scopedslots-api'),
'no-deprecated-events-api': require('./rules/no-deprecated-events-api'),

@@ -76,2 +79,3 @@ 'no-deprecated-filter': require('./rules/no-deprecated-filter'),

'no-multi-spaces': require('./rules/no-multi-spaces'),
'no-multiple-slot-args': require('./rules/no-multiple-slot-args'),
'no-multiple-template-root': require('./rules/no-multiple-template-root'),

@@ -84,3 +88,5 @@ 'no-mutating-props': require('./rules/no-mutating-props'),

'no-reserved-keys': require('./rules/no-reserved-keys'),
'no-restricted-static-attribute': require('./rules/no-restricted-static-attribute'),
'no-restricted-syntax': require('./rules/no-restricted-syntax'),
'no-restricted-v-bind': require('./rules/no-restricted-v-bind'),
'no-setup-props-destructure': require('./rules/no-setup-props-destructure'),

@@ -102,7 +108,12 @@ 'no-shared-component-data': require('./rules/no-shared-component-data'),

'no-useless-concat': require('./rules/no-useless-concat'),
'no-useless-mustaches': require('./rules/no-useless-mustaches'),
'no-useless-v-bind': require('./rules/no-useless-v-bind'),
'no-v-html': require('./rules/no-v-html'),
'no-v-model-argument': require('./rules/no-v-model-argument'),
'no-watch-after-await': require('./rules/no-watch-after-await'),
'object-curly-newline': require('./rules/object-curly-newline'),
'object-curly-spacing': require('./rules/object-curly-spacing'),
'object-property-newline': require('./rules/object-property-newline'),
'one-component-per-file': require('./rules/one-component-per-file'),
'operator-linebreak': require('./rules/operator-linebreak'),
'order-in-components': require('./rules/order-in-components'),

@@ -120,2 +131,3 @@ 'padding-line-between-blocks': require('./rules/padding-line-between-blocks'),

'require-render-return': require('./rules/require-render-return'),
'require-slots-as-functions': require('./rules/require-slots-as-functions'),
'require-toggle-inside-transition': require('./rules/require-toggle-inside-transition'),

@@ -122,0 +134,0 @@ 'require-v-for-key': require('./rules/require-v-for-key'),

46

lib/rules/component-tags-order.js

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

const DEFAULT_ORDER = Object.freeze(['script', 'template', 'style'])
const DEFAULT_ORDER = Object.freeze([['script', 'template'], 'style'])

@@ -29,10 +29,20 @@ // ------------------------------------------------------------------------------

fixable: null,
schema: {
type: 'array',
properties: {
order: {
type: 'array'
schema: [
{
type: 'object',
properties: {
order: {
type: 'array',
items: {
anyOf: [
{ type: 'string' },
{ type: 'array', items: { type: 'string' }, uniqueItems: true }
]
},
uniqueItems: true,
additionalItems: false
}
}
}
},
],
messages: {

@@ -44,4 +54,16 @@ unexpected:

create(context) {
const order =
(context.options[0] && context.options[0].order) || DEFAULT_ORDER
/** @type {Map<string, number} */
const orderMap = new Map()
;(
(context.options[0] && context.options[0].order) ||
DEFAULT_ORDER
).forEach((nameOrNames, index) => {
if (Array.isArray(nameOrNames)) {
for (const name of nameOrNames) {
orderMap.set(name, index)
}
} else {
orderMap.set(nameOrNames, index)
}
})
const documentFragment =

@@ -82,3 +104,3 @@ context.parserServices.getDocumentFragment &&

elements.forEach((element, index) => {
const expectedIndex = order.indexOf(element.name)
const expectedIndex = orderMap.get(element.name)
if (expectedIndex < 0) {

@@ -89,5 +111,5 @@ return

.slice(0, index)
.filter((e) => expectedIndex < order.indexOf(e.name))
.filter((e) => expectedIndex < orderMap.get(e.name))
.sort(
(e1, e2) => order.indexOf(e1.name) - order.indexOf(e2.name)
(e1, e2) => orderMap.get(e1.name) - orderMap.get(e2.name)
)[0]

@@ -94,0 +116,0 @@ if (firstUnordered) {

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

const { ReferenceTracker, findVariable } = require('eslint-utils')
const utils = require('../utils')

@@ -22,3 +23,3 @@ module.exports = {

requireDotValue:
'Must use `.value` to read or write the value wrapped by `ref()`.'
'Must use `.value` to read or write the value wrapped by `{{method}}()`.'
}

@@ -30,3 +31,4 @@ },

function reportIfRefWrapped(node) {
if (!refReferenceIds.has(node)) {
const data = refReferenceIds.get(node)
if (!data) {
return

@@ -36,3 +38,6 @@ }

node,
messageId: 'requireDotValue'
messageId: 'requireDotValue',
data: {
method: data.method
}
})

@@ -48,2 +53,14 @@ }

[ReferenceTracker.CALL]: true
},
computed: {
[ReferenceTracker.CALL]: true
},
toRef: {
[ReferenceTracker.CALL]: true
},
customRef: {
[ReferenceTracker.CALL]: true
},
shallowRef: {
[ReferenceTracker.CALL]: true
}

@@ -53,3 +70,3 @@ }

for (const { node } of tracker.iterateEsmReferences(traceMap)) {
for (const { node, path } of tracker.iterateEsmReferences(traceMap)) {
const variableDeclarator = node.parent

@@ -82,3 +99,4 @@ if (

variableDeclarator,
variableDeclaration
variableDeclaration,
method: path[1]
})

@@ -118,9 +136,9 @@ }

// Report only constants.
const info = refReferenceIds.get(node)
if (!info) {
const data = refReferenceIds.get(node)
if (!data) {
return
}
if (
!info.variableDeclaration ||
info.variableDeclaration.kind !== 'const'
!data.variableDeclaration ||
data.variableDeclaration.kind !== 'const'
) {

@@ -137,2 +155,22 @@ return

reportIfRefWrapped(node)
},
// `${refValue}`
'TemplateLiteral>Identifier'(node) {
reportIfRefWrapped(node)
},
// refValue.x
'MemberExpression>Identifier'(node) {
if (node.parent.object !== node) {
return
}
const name = utils.getStaticPropertyName(node.parent)
if (
name === 'value' ||
name == null ||
// WritableComputedRef
name === 'effect'
) {
return
}
reportIfRefWrapped(node)
}

@@ -139,0 +177,0 @@ }

@@ -19,6 +19,11 @@ /**

* @typedef {import('vue-eslint-parser').AST.ESLintIdentifier} Identifier
* @typedef {import('vue-eslint-parser').AST.ESLintPattern} Pattern
* @typedef {import('vue-eslint-parser').AST.ESLintThisExpression} ThisExpression
* @typedef {import('vue-eslint-parser').AST.ESLintMemberExpression} MemberExpression
* @typedef {import('vue-eslint-parser').AST.ESLintFunctionExpression} FunctionExpression
* @typedef {import('vue-eslint-parser').AST.ESLintArrowFunctionExpression} ArrowFunctionExpression
* @typedef {import('vue-eslint-parser').AST.ESLintFunctionDeclaration} FunctionDeclaration
* @typedef {import('vue-eslint-parser').AST.VAttribute} VAttribute
* @typedef {import('vue-eslint-parser').AST.VIdentifier} VIdentifier
* @typedef {import('vue-eslint-parser').AST.VExpressionContainer} VExpressionContainer
* @typedef {import('eslint').Scope.Variable} Variable

@@ -28,7 +33,17 @@ * @typedef {import('eslint').Rule.RuleContext} RuleContext

/**
* @typedef { { name: string, groupName: string, node: ASTNode } } PropertyData
* @typedef { { usedNames: Set<string> } } TemplatePropertiesContainer
* @typedef { { properties: Array<PropertyData>, usedNames: Set<string>, unknown: boolean, usedPropsNames: Set<string>, unknownProps: boolean } } VueComponentPropertiesContainer
* @typedef {import('../utils').ComponentPropertyData} ComponentPropertyData
*/
/**
* @typedef {object} TemplatePropertiesContainer
* @property {Set<string>} usedNames
* @property {Set<string>} refNames
* @typedef {object} VueComponentPropertiesContainer
* @property {ComponentPropertyData[]} properties
* @property {Set<string>} usedNames
* @property {boolean} unknown
* @property {Set<string>} usedPropsNames
* @property {boolean} unknownProps
* @typedef { { node: FunctionExpression | ArrowFunctionExpression | FunctionDeclaration, index: number } } CallIdAndParamIndex
* @typedef { { usedNames: Set<string>, unknown: boolean } } UsedProperties
* @typedef { { usedNames: UsedNames, unknown: boolean } } UsedProperties
* @typedef { (context: RuleContext) => UsedProps } UsedPropsTracker
*/

@@ -105,2 +120,47 @@

class UsedNames {
constructor() {
/** @type {Map<string, UsedPropsTracker[]>} */
this.map = new Map()
}
/**
* @returns {IterableIterator<string>}
*/
names() {
return this.map.keys()
}
/**
* @param {string} name
* @returns {UsedPropsTracker[]}
*/
get(name) {
return this.map.get(name) || []
}
/**
* @param {string} name
* @param {UsedPropsTracker} tracker
*/
add(name, tracker) {
const list = this.map.get(name)
if (list) {
list.push(tracker)
} else {
this.map.set(name, [tracker])
}
}
/**
* @param {UsedNames} other
*/
addAll(other) {
other.map.forEach((trackers, name) => {
const list = this.map.get(name)
if (list) {
list.push(...trackers)
} else {
this.map.set(name, trackers)
}
})
}
}
/**

@@ -111,6 +171,7 @@ * @param {ObjectPattern} node

function extractObjectPatternProperties(node) {
const usedNames = new Set()
const usedNames = new UsedNames()
for (const prop of node.properties) {
if (prop.type === 'Property') {
usedNames.add(utils.getStaticPropertyName(prop))
const name = utils.getStaticPropertyName(prop)
usedNames.add(name, getObjectPatternPropertyPatternTracker(prop.value))
} else {

@@ -131,8 +192,52 @@ // If use RestElement, everything is used!

/**
* @param {Identifier | ThisExpression} node
* @param {Pattern} pattern
* @returns {UsedPropsTracker}
*/
function getObjectPatternPropertyPatternTracker(pattern) {
if (pattern.type === 'ObjectPattern') {
return () => {
const result = new UsedProps()
const { usedNames, unknown } = extractObjectPatternProperties(pattern)
result.usedNames.addAll(usedNames)
result.unknown = unknown
return result
}
}
if (pattern.type === 'Identifier') {
return (context) => {
const result = new UsedProps()
const variable = findVariable(context, pattern)
if (!variable) {
return result
}
for (const reference of variable.references) {
/** @type {Identifier} */
// @ts-ignore
const id = reference.identifier
const { usedNames, unknown, calls } = extractPatternOrThisProperties(
id,
context
)
result.usedNames.addAll(usedNames)
result.unknown = result.unknown || unknown
result.calls.push(...calls)
}
return result
}
} else if (pattern.type === 'AssignmentPattern') {
return getObjectPatternPropertyPatternTracker(pattern.left)
}
return () => {
const result = new UsedProps()
result.unknown = true
return result
}
}
/**
* @param {Identifier | MemberExpression | ThisExpression} node
* @param {RuleContext} context
* @returns {UsedProps}
*/
function extractIdOrThisProperties(node, context) {
/** @type {UsedProps} */
function extractPatternOrThisProperties(node, context) {
const result = new UsedProps()

@@ -144,12 +249,34 @@ const parent = node.parent

const { usedNames, unknown } = extractObjectPatternProperties(parent.left)
usedNames.forEach((name) => result.usedNames.add(name))
result.usedNames.addAll(usedNames)
result.unknown = result.unknown || unknown
}
return result
} else if (parent.type === 'VariableDeclarator') {
if (parent.init === node && parent.id.type === 'ObjectPattern') {
// `const {foo} = arg`
const { usedNames, unknown } = extractObjectPatternProperties(parent.id)
usedNames.forEach((name) => result.usedNames.add(name))
result.unknown = result.unknown || unknown
if (parent.init === node) {
if (parent.id.type === 'ObjectPattern') {
// `const {foo} = arg`
const { usedNames, unknown } = extractObjectPatternProperties(parent.id)
result.usedNames.addAll(usedNames)
result.unknown = result.unknown || unknown
} else if (parent.id.type === 'Identifier') {
// `const foo = arg`
const variable = findVariable(context, parent.id)
if (!variable) {
return result
}
for (const reference of variable.references) {
/** @type {Identifier} */
// @ts-ignore
const id = reference.identifier
const { usedNames, unknown, calls } = extractPatternOrThisProperties(
id,
context
)
result.usedNames.addAll(usedNames)
result.unknown = result.unknown || unknown
result.calls.push(...calls)
}
}
}
return result
} else if (parent.type === 'MemberExpression') {

@@ -160,3 +287,5 @@ if (parent.object === node) {

if (name) {
result.usedNames.add(name)
result.usedNames.add(name, () =>
extractPatternOrThisProperties(parent, context)
)
} else {

@@ -166,3 +295,5 @@ result.unknown = true

}
return result
} else if (parent.type === 'CallExpression') {
// @ts-ignore
const argIndex = parent.arguments.indexOf(node)

@@ -206,4 +337,3 @@ if (argIndex > -1 && parent.callee.type === 'Identifier') {

constructor() {
/** @type {Set<string>} */
this.usedNames = new Set()
this.usedNames = new UsedNames()
/** @type {CallIdAndParamIndex[]} */

@@ -232,3 +362,3 @@ this.calls = []

const { usedNames, unknown } = extractObjectPatternProperties(paramNode)
usedNames.forEach((name) => this.usedNames.add(name))
this.usedNames.addAll(usedNames)
this.unknown = this.unknown || unknown

@@ -245,7 +375,7 @@ return

const id = reference.identifier
const { usedNames, unknown, calls } = extractIdOrThisProperties(
const { usedNames, unknown, calls } = extractPatternOrThisProperties(
id,
context
)
usedNames.forEach((name) => this.usedNames.add(name))
this.usedNames.addAll(usedNames)
this.unknown = this.unknown || unknown

@@ -339,3 +469,4 @@ this.calls.push(...calls)

const templatePropertiesContainer = {
usedNames: new Set()
usedNames: new Set(),
refNames: new Set()
}

@@ -383,2 +514,3 @@ /** @type {Map<ASTNode, VueComponentPropertiesContainer>} */

if (container.unknown) {
// unknown
continue

@@ -391,2 +523,3 @@ }

) {
// used
continue

@@ -399,4 +532,12 @@ }

) {
// used props
continue
}
if (
property.groupName === 'setup' &&
templatePropertiesContainer.refNames.has(property.name)
) {
// used template refs
continue
}
context.report({

@@ -417,3 +558,3 @@ node: property.node,

* @param {Map<ASTNode,Set<number>>} already
* @returns {Generator<UsedProps>}
* @returns {IterableIterator<UsedProps>}
*/

@@ -442,2 +583,18 @@ function* iterateUsedProps(usedProps, already = new Map()) {

/**
* @param {VueComponentPropertiesContainer} container
* @param {UsedProps} baseUseProps
*/
function processParamPropsUsed(container, baseUseProps) {
for (const { usedNames, unknown } of iterateUsedProps(baseUseProps)) {
if (unknown) {
container.unknownProps = true
return
}
for (const name of usedNames.names()) {
container.usedPropsNames.add(name)
}
}
}
const scriptVisitor = Object.assign(

@@ -468,19 +625,41 @@ {},

const container = getVueComponentPropertiesContainer(vueData.node)
const propsParam = node.params[0]
if (!propsParam) {
// no arguments
return
if (node.params[0]) {
const paramsUsedProps = getParamsUsedProps(node)
const paramUsedProps = paramsUsedProps.getParam(0)
processParamPropsUsed(container, paramUsedProps)
}
const paramsUsedProps = getParamsUsedProps(node)
const paramUsedProps = paramsUsedProps.getParam(0)
},
onRenderFunctionEnter(node, vueData) {
const container = getVueComponentPropertiesContainer(vueData.node)
if (node.params[0]) {
// for Vue 3.x render
const paramsUsedProps = getParamsUsedProps(node)
const paramUsedProps = paramsUsedProps.getParam(0)
for (const { usedNames, unknown } of iterateUsedProps(
paramUsedProps
)) {
if (unknown) {
container.unknownProps = true
processParamPropsUsed(container, paramUsedProps)
if (container.unknownProps) {
return
}
for (const name of usedNames) {
container.usedPropsNames.add(name)
}
if (vueData.functional && node.params[1]) {
// for Vue 2.x render & functional
const paramsUsedProps = getParamsUsedProps(node)
const paramUsedProps = paramsUsedProps.getParam(1)
for (const { usedNames, unknown } of iterateUsedProps(
paramUsedProps
)) {
if (unknown) {
container.unknownProps = true
return
}
for (const usedPropsTracker of usedNames.get('props')) {
const propUsedProps = usedPropsTracker(context)
processParamPropsUsed(container, propUsedProps)
if (container.unknownProps) {
return
}
}
}

@@ -494,3 +673,3 @@ }

const container = getVueComponentPropertiesContainer(vueData.node)
const usedProps = extractIdOrThisProperties(node, context)
const usedProps = extractPatternOrThisProperties(node, context)

@@ -502,3 +681,3 @@ for (const { usedNames, unknown } of iterateUsedProps(usedProps)) {

}
for (const name of usedNames) {
for (const name of usedNames.names()) {
container.usedNames.add(name)

@@ -519,2 +698,5 @@ }

const templateVisitor = {
/**
* @param {VExpressionContainer} node
*/
VExpressionContainer(node) {

@@ -525,2 +707,10 @@ for (const name of getReferencesNames(node.references)) {

},
/**
* @param {VAttribute} node
*/
'VAttribute[directive=false]'(node) {
if (node.key.name === 'ref' && node.value != null) {
templatePropertiesContainer.refNames.add(node.value.value)
}
},
"VElement[parent.type!='VElement']:exit"() {

@@ -527,0 +717,0 @@ reportUnusedProperties()

@@ -11,21 +11,50 @@ /**

const defaultOrder = [
// Side Effects (triggers effects outside the component)
'el',
// Global Awareness (requires knowledge beyond the component)
'name',
'parent',
// Component Type (changes the type of the component)
'functional',
// Template Modifiers (changes the way templates are compiled)
['delimiters', 'comments'],
// Template Dependencies (assets used in the template)
['components', 'directives', 'filters'],
// Composition (merges properties into the options)
'extends',
'mixins',
['provide', 'inject'], // for Vue.js 2.2.0+
// Interface (the interface to the component)
'inheritAttrs',
'model',
['props', 'propsData'],
'fetch',
'asyncData',
'emits', // for Vue.js 3.x
// Note:
// The `setup` option is included in the "Composition" category,
// but the behavior of the `setup` option requires the definition of "Interface",
// so we prefer to put the `setup` option after the "Interface".
'setup', // for Vue 3.x
// Local State (local reactive properties)
'fetch', // for Nuxt
'asyncData', // for Nuxt
'data',
'computed',
// Events (callbacks triggered by reactive events)
'watch',
'LIFECYCLE_HOOKS',
// Non-Reactive Properties (instance properties independent of the reactivity system)
'methods',
'head',
// Rendering (the declarative description of the component output)
'head', // for Nuxt
['template', 'render'],

@@ -45,4 +74,9 @@ 'renderError'

'deactivated',
'beforeUnmount', // for Vue.js 3.x
'unmounted', // for Vue.js 3.x
'beforeDestroy',
'destroyed'
'destroyed',
'renderTracked', // for Vue.js 3.x
'renderTriggered', // for Vue.js 3.x
'errorCaptured' // for Vue.js 2.5.0+
]

@@ -69,3 +103,3 @@ }

const ARITHMETIC_OPERATORS = ['+', '-', '*', '/', '%', '**']
const ARITHMETIC_OPERATORS = ['+', '-', '*', '/', '%', '**'/* es2016 */]
const BITWISE_OPERATORS = ['&', '|', '^', '~', '<<', '>>', '>>>']

@@ -80,3 +114,3 @@ const COMPARISON_OPERATORS = ['==', '!=', '===', '!==', '>', '>=', '<', '<=']

)
const LOGICAL_OPERATORS = ['&&', '||']
const LOGICAL_OPERATORS = ['&&', '||', '??'/* es2020 */]

@@ -101,7 +135,7 @@ /*

let result = true
const noSideEffectsNodes = new Set()
let skipNode = false
traverseNodes(node, {
visitorKeys,
enterNode (node, parent) {
if (!result) {
enterNode (node) {
if (!result || skipNode) {
return

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

if (
// parent has no side effects
noSideEffectsNodes.has(parent) ||
// no side effects node

@@ -122,3 +154,3 @@ node.type === 'FunctionExpression' ||

) {
noSideEffectsNodes.add(node)
skipNode = node
} else if (

@@ -141,3 +173,7 @@ node.type !== 'Property' &&

},
leaveNode () {}
leaveNode (node) {
if (skipNode === node) {
skipNode = null
}
}
})

@@ -144,0 +180,0 @@

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

/**
* @typedef {import('vue-eslint-parser').AST.ESLintExportDefaultDeclaration} ExportDefaultDeclaration
* @typedef {import('vue-eslint-parser').AST.ESLintDeclaration} Declaration
* @typedef {import('vue-eslint-parser').AST.ESLintExpression} Expression
* @typedef {import('vue-eslint-parser').AST.ESLintReturnStatement} ReturnStatement
*
*/
// ------------------------------------------------------------------------------

@@ -23,3 +30,9 @@ // Rule Definition

fixable: null, // or "code" or "whitespace"
schema: []
schema: [{
type: 'object',
properties: {
disallowFunctionalComponentFunction: { type: 'boolean' }
},
additionalProperties: false
}]
},

@@ -29,21 +42,71 @@

const filePath = context.getFilename()
if (!utils.isVueFile(filePath)) return {}
return {
'ExportDefaultDeclaration:exit' (node) {
if (!utils.isVueFile(filePath)) return
const disallowFunctional = (context.options[0] || {}).disallowFunctionalComponentFunction
const isObjectExpression = (
node.type === 'ExportDefaultDeclaration' &&
node.declaration.type === 'ObjectExpression'
)
let maybeVue3Functional
let scopeStack = null
if (!isObjectExpression) {
context.report({
node,
message: `Expected the component literal to be directly exported.`
})
return {
/** @param {Declaration | Expression} node */
'ExportDefaultDeclaration > *' (node) {
if (node.type === 'ObjectExpression') {
// OK
return
}
}
if (!disallowFunctional) {
if (node.type === 'ArrowFunctionExpression') {
if (node.body.type !== 'BlockStatement') {
// OK
return
}
maybeVue3Functional = {
body: node.body
}
return
}
if (node.type === 'FunctionExpression' || node.type === 'FunctionDeclaration') {
maybeVue3Functional = {
body: node.body
}
return
}
}
context.report({
node: node.parent,
message: `Expected the component literal to be directly exported.`
})
},
...(disallowFunctional ? {} : {
':function > BlockStatement' (node) {
if (!maybeVue3Functional) {
return
}
scopeStack = { upper: scopeStack, withinVue3FunctionalBody: maybeVue3Functional.body === node }
},
/** @param {ReturnStatement} node */
ReturnStatement (node) {
if (scopeStack && scopeStack.withinVue3FunctionalBody && node.argument) {
maybeVue3Functional.hasReturnArgument = true
}
},
':function > BlockStatement:exit' (node) {
scopeStack = scopeStack && scopeStack.upper
},
/** @param {ExportDefaultDeclaration} node */
'ExportDefaultDeclaration:exit' (node) {
if (!maybeVue3Functional) {
return
}
if (!maybeVue3Functional.hasReturnArgument) {
context.report({
node,
message: `Expected the component literal to be directly exported.`
})
}
}
})
}
}
}

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

const FIX_EMITS_AFTER_OPTIONS = [
'props',
'propsData',
'setup',

@@ -48,4 +46,9 @@ 'data',

'deactivated',
'beforeUnmount',
'unmounted',
'beforeDestroy',
'destroyed'
'destroyed',
'renderTracked',
'renderTriggered',
'errorCaptured'
]

@@ -52,0 +55,0 @@

@@ -40,6 +40,3 @@ /**

function isLhs(node) {
return (
Boolean(node) &&
(node.type === 'Identifier' || node.type === 'MemberExpression')
)
return node.type === 'Identifier' || node.type === 'MemberExpression'
}

@@ -89,26 +86,28 @@

if (node.value) {
if (!isLhs(node.value.expression)) {
if (!node.value || !node.value.expression) {
return
}
if (!isLhs(node.value.expression)) {
context.report({
node,
loc: node.loc,
messageId: 'unexpectedNonLhsExpression'
})
}
for (const reference of node.value.references) {
const id = reference.id
if (id.parent.type !== 'VExpressionContainer') {
continue
}
const variable = reference.variable
if (variable) {
context.report({
node,
loc: node.loc,
messageId: 'unexpectedNonLhsExpression'
messageId: 'unexpectedUpdateIterationVariable',
data: { varName: id.name }
})
}
for (const reference of node.value.references) {
const id = reference.id
if (id.parent.type !== 'VExpressionContainer') {
continue
}
const variable = reference.variable
if (variable) {
context.report({
node,
loc: node.loc,
messageId: 'unexpectedUpdateIterationVariable',
data: { varName: id.name }
})
}
}
}

@@ -115,0 +114,0 @@ }

@@ -51,3 +51,3 @@ /**

if (!utils.hasAttributeValue(node)) {
if (!node.value || utils.isEmptyValueDirective(node, context)) {
context.report({

@@ -54,0 +54,0 @@ node,

@@ -73,3 +73,3 @@ /**

}
if (!utils.hasAttributeValue(node)) {
if (!node.value || utils.isEmptyValueDirective(node, context)) {
context.report({

@@ -76,0 +76,0 @@ node,

@@ -73,3 +73,3 @@ /**

}
if (utils.hasAttributeValue(node)) {
if (node.value) {
context.report({

@@ -76,0 +76,0 @@ node,

@@ -140,3 +140,3 @@ /**

}
if (!utils.hasAttributeValue(node)) {
if (!node.value || utils.isEmptyValueDirective(node, context)) {
context.report({

@@ -143,0 +143,0 @@ node,

@@ -47,3 +47,3 @@ /**

}
if (!utils.hasAttributeValue(node)) {
if (!node.value || utils.isEmptyValueDirective(node, context)) {
context.report({

@@ -50,0 +50,0 @@ node,

@@ -65,3 +65,3 @@ /**

}
if (!utils.hasAttributeValue(node)) {
if (!node.value || utils.isEmptyValueDirective(node, context)) {
context.report({

@@ -68,0 +68,0 @@ node,

@@ -45,6 +45,3 @@ /**

function isLhs(node) {
return (
node != null &&
(node.type === 'Identifier' || node.type === 'MemberExpression')
)
return node.type === 'Identifier' || node.type === 'MemberExpression'
}

@@ -137,3 +134,3 @@

if (!utils.hasAttributeValue(node)) {
if (!node.value || utils.isEmptyValueDirective(node, context)) {
context.report({

@@ -144,5 +141,25 @@ node,

})
return
}
if (node.value) {
if (!isLhs(node.value.expression)) {
if (!node.value.expression) {
// Parsing error
return
}
if (!isLhs(node.value.expression)) {
context.report({
node,
loc: node.loc,
message:
"'v-model' directives require the attribute value which is valid as LHS."
})
}
for (const reference of node.value.references) {
const id = reference.id
if (id.parent.type !== 'VExpressionContainer') {
continue
}
const variable = getVariable(id.name, element)
if (variable != null) {
context.report({

@@ -152,23 +169,6 @@ node,

message:
"'v-model' directives require the attribute value which is valid as LHS."
"'v-model' directives cannot update the iteration variable '{{varName}}' itself.",
data: { varName: id.name }
})
}
for (const reference of node.value.references) {
const id = reference.id
if (id.parent.type !== 'VExpressionContainer') {
continue
}
const variable = getVariable(id.name, element)
if (variable != null) {
context.report({
node,
loc: node.loc,
message:
"'v-model' directives cannot update the iteration variable '{{varName}}' itself.",
data: { varName: id.name }
})
}
}
}

@@ -175,0 +175,0 @@ }

@@ -111,3 +111,3 @@ /**

if (
!utils.hasAttributeValue(node) &&
(!node.value || !node.value.expression) &&
!node.key.modifiers.some((modifier) =>

@@ -117,11 +117,21 @@ VERB_MODIFIERS.has(modifier.name)

) {
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 }
})
if (node.value && !utils.isEmptyValueDirective(node, context)) {
const valueText = sourceCode.getText(node.value)
let innerText = valueText
if (
(valueText[0] === '"' || valueText[0] === "'") &&
valueText[0] === valueText[valueText.length - 1]
) {
// quoted
innerText = valueText.slice(1, -1)
}
if (/^\w+$/.test(innerText)) {
context.report({
node,
loc: node.loc,
message:
'Avoid using JavaScript keyword as "v-on" value: {{value}}.',
data: { value: valueText }
})
}
} else {

@@ -128,0 +138,0 @@ context.report({

@@ -47,3 +47,3 @@ /**

}
if (!utils.hasAttributeValue(node)) {
if (!node.value || utils.isEmptyValueDirective(node, context)) {
context.report({

@@ -50,0 +50,0 @@ node,

@@ -267,3 +267,5 @@ /**

isDefaultSlot &&
!utils.hasAttributeValue(node)
(!node.value ||
utils.isEmptyValueDirective(node, context) ||
utils.isEmptyExpressionValueDirective(node, context))
) {

@@ -270,0 +272,0 @@ context.report({

@@ -47,3 +47,3 @@ /**

}
if (!utils.hasAttributeValue(node)) {
if (!node.value || utils.isEmptyValueDirective(node, context)) {
context.report({

@@ -50,0 +50,0 @@ node,

@@ -17,7 +17,94 @@ /**

const KNOWN_NODES = new Set(['ArrayExpression', 'ArrayPattern', 'ArrowFunctionExpression', 'AssignmentExpression', 'AssignmentPattern', 'AwaitExpression', 'BinaryExpression', 'BlockStatement', 'BreakStatement', 'CallExpression', 'CatchClause', 'ClassBody', 'ClassDeclaration', 'ClassExpression', 'ConditionalExpression', 'ContinueStatement', 'DebuggerStatement', 'DoWhileStatement', 'EmptyStatement', 'ExperimentalRestProperty', 'ExperimentalSpreadProperty', 'ExportAllDeclaration', 'ExportDefaultDeclaration', 'ExportNamedDeclaration', 'ExportSpecifier', 'ExpressionStatement', 'ForInStatement', 'ForOfStatement', 'ForStatement', 'FunctionDeclaration', 'FunctionExpression', 'Identifier', 'IfStatement', 'ImportDeclaration', 'ImportDefaultSpecifier', 'ImportNamespaceSpecifier', 'ImportSpecifier', 'LabeledStatement', 'Literal', 'LogicalExpression', 'MemberExpression', 'MetaProperty', 'MethodDefinition', 'NewExpression', 'ObjectExpression', 'ObjectPattern', 'Program', 'Property', 'RestElement', 'ReturnStatement', 'SequenceExpression', 'SpreadElement', 'Super', 'SwitchCase', 'SwitchStatement', 'TaggedTemplateExpression', 'TemplateElement', 'TemplateLiteral', 'ThisExpression', 'ThrowStatement', 'TryStatement', 'UnaryExpression', 'UpdateExpression', 'VariableDeclaration', 'VariableDeclarator', 'WhileStatement', 'WithStatement', 'YieldExpression', 'VAttribute', 'VDirectiveKey', 'VDocumentFragment', 'VElement', 'VEndTag', 'VExpressionContainer', 'VFilter', 'VFilterSequenceExpression', 'VForExpression', 'VIdentifier', 'VLiteral', 'VOnExpression', 'VSlotScopeExpression', 'VStartTag', 'VText'])
const KNOWN_NODES = new Set([
'ArrayExpression',
'ArrayPattern',
'ArrowFunctionExpression',
'AssignmentExpression',
'AssignmentPattern',
'AwaitExpression',
'BinaryExpression',
'BlockStatement',
'BreakStatement',
'CallExpression',
'CatchClause',
'ClassBody',
'ClassDeclaration',
'ClassExpression',
'ConditionalExpression',
'ContinueStatement',
'DebuggerStatement',
'DoWhileStatement',
'EmptyStatement',
'ExperimentalRestProperty',
'ExperimentalSpreadProperty',
'ExportAllDeclaration',
'ExportDefaultDeclaration',
'ExportNamedDeclaration',
'ExportSpecifier',
'ExpressionStatement',
'ForInStatement',
'ForOfStatement',
'ForStatement',
'FunctionDeclaration',
'FunctionExpression',
'Identifier',
'IfStatement',
'ImportDeclaration',
'ImportDefaultSpecifier',
'ImportNamespaceSpecifier',
'ImportSpecifier',
'LabeledStatement',
'Literal',
'LogicalExpression',
'MemberExpression',
'MetaProperty',
'MethodDefinition',
'NewExpression',
'ObjectExpression',
'ObjectPattern',
'Program',
'Property',
'RestElement',
'ReturnStatement',
'SequenceExpression',
'SpreadElement',
'Super',
'SwitchCase',
'SwitchStatement',
'TaggedTemplateExpression',
'TemplateElement',
'TemplateLiteral',
'ThisExpression',
'ThrowStatement',
'TryStatement',
'UnaryExpression',
'UpdateExpression',
'VariableDeclaration',
'VariableDeclarator',
'WhileStatement',
'WithStatement',
'YieldExpression',
'VAttribute',
'VDirectiveKey',
'VDocumentFragment',
'VElement',
'VEndTag',
'VExpressionContainer',
'VFilter',
'VFilterSequenceExpression',
'VForExpression',
'VIdentifier',
'VLiteral',
'VOnExpression',
'VSlotScopeExpression',
'VStartTag',
'VText'
])
const LT_CHAR = /[\r\n\u2028\u2029]/
const LINES = /[^\r\n\u2028\u2029]+(?:$|\r\n|[\r\n\u2028\u2029])/g
const BLOCK_COMMENT_PREFIX = /^\s*\*/
const ITERATION_OPTS = Object.freeze({ includeComments: true, filter: isNotWhitespace })
const ITERATION_OPTS = Object.freeze({
includeComments: true,
filter: isNotWhitespace
})
const PREFORMATTED_ELEMENT_NAMES = ['pre', 'textarea']

@@ -46,17 +133,20 @@

*/
function parseOptions (type, options, defaultOptions) {
const ret = Object.assign({
indentChar: ' ',
indentSize: 2,
baseIndent: 0,
attribute: 1,
closeBracket: {
startTag: 0,
endTag: 0,
selfClosingTag: 0
function parseOptions(type, options, defaultOptions) {
const ret = Object.assign(
{
indentChar: ' ',
indentSize: 2,
baseIndent: 0,
attribute: 1,
closeBracket: {
startTag: 0,
endTag: 0,
selfClosingTag: 0
},
switchCase: 0,
alignAttributesVertically: true,
ignores: []
},
switchCase: 0,
alignAttributesVertically: true,
ignores: []
}, defaultOptions)
defaultOptions
)

@@ -112,3 +202,3 @@ if (Number.isSafeInteger(type)) {

*/
function isArrow (token) {
function isArrow(token) {
return token != null && token.type === 'Punctuator' && token.value === '=>'

@@ -122,3 +212,3 @@ }

*/
function isLeftParen (token) {
function isLeftParen(token) {
return token != null && token.type === 'Punctuator' && token.value === '('

@@ -132,3 +222,3 @@ }

*/
function isNotLeftParen (token) {
function isNotLeftParen(token) {
return token != null && (token.type !== 'Punctuator' || token.value !== '(')

@@ -142,3 +232,3 @@ }

*/
function isRightParen (token) {
function isRightParen(token) {
return token != null && token.type === 'Punctuator' && token.value === ')'

@@ -152,3 +242,3 @@ }

*/
function isNotRightParen (token) {
function isNotRightParen(token) {
return token != null && (token.type !== 'Punctuator' || token.value !== ')')

@@ -162,3 +252,3 @@ }

*/
function isLeftBrace (token) {
function isLeftBrace(token) {
return token != null && token.type === 'Punctuator' && token.value === '{'

@@ -172,3 +262,3 @@ }

*/
function isRightBrace (token) {
function isRightBrace(token) {
return token != null && token.type === 'Punctuator' && token.value === '}'

@@ -182,3 +272,3 @@ }

*/
function isLeftBracket (token) {
function isLeftBracket(token) {
return token != null && token.type === 'Punctuator' && token.value === '['

@@ -192,3 +282,3 @@ }

*/
function isRightBracket (token) {
function isRightBracket(token) {
return token != null && token.type === 'Punctuator' && token.value === ']'

@@ -202,3 +292,3 @@ }

*/
function isSemicolon (token) {
function isSemicolon(token) {
return token != null && token.type === 'Punctuator' && token.value === ';'

@@ -212,3 +302,3 @@ }

*/
function isComma (token) {
function isComma(token) {
return token != null && token.type === 'Punctuator' && token.value === ','

@@ -222,3 +312,3 @@ }

*/
function isNotWhitespace (token) {
function isNotWhitespace(token) {
return token != null && token.type !== 'HTMLWhitespace'

@@ -232,4 +322,10 @@ }

*/
function isComment (token) {
return token != null && (token.type === 'Block' || token.type === 'Line' || token.type === 'Shebang' || token.type.endsWith('Comment'))
function isComment(token) {
return (
token != null &&
(token.type === 'Block' ||
token.type === 'Line' ||
token.type === 'Shebang' ||
token.type.endsWith('Comment'))
)
}

@@ -242,4 +338,10 @@

*/
function isNotComment (token) {
return token != null && token.type !== 'Block' && token.type !== 'Line' && token.type !== 'Shebang' && !token.type.endsWith('Comment')
function isNotComment(token) {
return (
token != null &&
token.type !== 'Block' &&
token.type !== 'Line' &&
token.type !== 'Shebang' &&
!token.type.endsWith('Comment')
)
}

@@ -252,3 +354,3 @@

*/
function isNotEmptyTextNode (node) {
function isNotEmptyTextNode(node) {
return !(node.type === 'VText' && node.value.trim() === '')

@@ -262,3 +364,3 @@ }

*/
function isPipeOperator (token) {
function isPipeOperator(token) {
return token != null && token.type === 'Punctuator' && token.value === '|'

@@ -272,3 +374,3 @@ }

*/
function last (xs) {
function last(xs) {
return xs.length === 0 ? undefined : xs[xs.length - 1]

@@ -284,3 +386,3 @@ }

*/
function isBeginningOfLine (node, index, nodes) {
function isBeginningOfLine(node, index, nodes) {
if (node != null) {

@@ -304,14 +406,9 @@ for (let i = index - 1; i >= 0; --i) {

*/
function isClosingToken (token) {
return token != null && (
token.type === 'HTMLEndTagOpen' ||
token.type === 'VExpressionEnd' ||
(
token.type === 'Punctuator' &&
(
token.value === ')' ||
token.value === '}' ||
token.value === ']'
)
)
function isClosingToken(token) {
return (
token != null &&
(token.type === 'HTMLEndTagOpen' ||
token.type === 'VExpressionEnd' ||
(token.type === 'Punctuator' &&
(token.value === ')' || token.value === '}' || token.value === ']')))
)

@@ -328,6 +425,14 @@ }

*/
module.exports.defineVisitor = function create (context, tokenStore, defaultOptions) {
module.exports.defineVisitor = function create(
context,
tokenStore,
defaultOptions
) {
if (!context.getFilename().endsWith('.vue')) return {}
const options = parseOptions(context.options[0], context.options[1] || {}, defaultOptions)
const options = parseOptions(
context.options[0],
context.options[1] || {},
defaultOptions
)
const sourceCode = context.getSourceCode()

@@ -345,3 +450,3 @@ const offsets = new Map()

*/
function setOffset (token, offset, baseToken) {
function setOffset(token, offset, baseToken) {
assert(baseToken != null, "'baseToken' should not be null or undefined.")

@@ -373,3 +478,3 @@

*/
function setBaseline (token) {
function setBaseline(token) {
const offsetInfo = offsets.get(token)

@@ -386,16 +491,22 @@ if (offsetInfo != null) {

*/
function setPreformattedTokens (node) {
const endToken = (node.endTag && tokenStore.getFirstToken(node.endTag)) || tokenStore.getTokenAfter(node)
function setPreformattedTokens(node) {
const endToken =
(node.endTag && tokenStore.getFirstToken(node.endTag)) ||
tokenStore.getTokenAfter(node)
const option = {
includeComments: true,
filter: token => token != null && (
token.type === 'HTMLText' ||
token.type === 'HTMLRCDataText' ||
token.type === 'HTMLTagOpen' ||
token.type === 'HTMLEndTagOpen' ||
token.type === 'HTMLComment'
)
filter: (token) =>
token != null &&
(token.type === 'HTMLText' ||
token.type === 'HTMLRCDataText' ||
token.type === 'HTMLTagOpen' ||
token.type === 'HTMLEndTagOpen' ||
token.type === 'HTMLComment')
}
for (const token of tokenStore.getTokensBetween(node.startTag, endToken, option)) {
for (const token of tokenStore.getTokensBetween(
node.startTag,
endToken,
option
)) {
ignoreTokens.add(token)

@@ -413,3 +524,3 @@ }

*/
function getFirstAndLastTokens (node, borderOffset) {
function getFirstAndLastTokens(node, borderOffset) {
borderOffset |= 0

@@ -422,3 +533,9 @@

let t, u
while ((t = tokenStore.getTokenBefore(firstToken)) != null && (u = tokenStore.getTokenAfter(lastToken)) != null && isLeftParen(t) && isRightParen(u) && t.range[0] >= borderOffset) {
while (
(t = tokenStore.getTokenBefore(firstToken)) != null &&
(u = tokenStore.getTokenAfter(lastToken)) != null &&
isLeftParen(t) &&
isRightParen(u) &&
t.range[0] >= borderOffset
) {
firstToken = t

@@ -442,3 +559,3 @@ lastToken = u

*/
function processNodeList (nodeList, left, right, offset, alignVertically) {
function processNodeList(nodeList, left, right, offset, alignVertically) {
let t

@@ -460,3 +577,6 @@ const leftToken = (left && tokenStore.getFirstToken(left)) || left

}
const elementTokens = getFirstAndLastTokens(node, lastToken != null ? lastToken.range[1] : 0)
const elementTokens = getFirstAndLastTokens(
node,
lastToken != null ? lastToken.range[1] : 0
)

@@ -540,3 +660,3 @@ // Collect comma/comment tokens between the last token of the previous node and the first token of this node.

*/
function processMaybeBlock (node, baseToken) {
function processMaybeBlock(node, baseToken) {
const firstToken = getFirstAndLastTokens(node).firstToken

@@ -551,3 +671,3 @@ setOffset(firstToken, isLeftBrace(firstToken) ? 0 : 1, baseToken)

*/
function getPrefixTokens (node) {
function getPrefixTokens(node) {
const prefixes = []

@@ -572,3 +692,3 @@

*/
function getChainHeadToken (node) {
function getChainHeadToken(node) {
const type = node.type

@@ -599,3 +719,3 @@ while (node.parent.type === type) {

*/
function isBeginningOfElement (token, belongingNode) {
function isBeginningOfElement(token, belongingNode) {
let node = belongingNode

@@ -621,16 +741,24 @@

if (t === 'CallExpression' || t === 'NewExpression') {
const openParen = tokenStore.getTokenAfter(parent.callee, isNotRightParen)
return parent.arguments.some(param =>
getFirstAndLastTokens(param, openParen.range[1]).firstToken.range[0] === token.range[0]
const openParen = tokenStore.getTokenAfter(
parent.callee,
isNotRightParen
)
return parent.arguments.some(
(param) =>
getFirstAndLastTokens(param, openParen.range[1]).firstToken
.range[0] === token.range[0]
)
}
if (t === 'ArrayExpression') {
return parent.elements.some(element =>
element != null &&
getFirstAndLastTokens(element).firstToken.range[0] === token.range[0]
return parent.elements.some(
(element) =>
element != null &&
getFirstAndLastTokens(element).firstToken.range[0] ===
token.range[0]
)
}
if (t === 'SequenceExpression') {
return parent.expressions.some(expr =>
getFirstAndLastTokens(expr).firstToken.range[0] === token.range[0]
return parent.expressions.some(
(expr) =>
getFirstAndLastTokens(expr).firstToken.range[0] === token.range[0]
)

@@ -651,3 +779,3 @@ }

*/
function processTopLevelNode (node, expectedIndent) {
function processTopLevelNode(node, expectedIndent) {
const token = tokenStore.getFirstToken(node)

@@ -658,3 +786,8 @@ const offsetInfo = offsets.get(token)

} else {
offsets.set(token, { baseToken: null, offset: 0, baseline: false, expectedIndent })
offsets.set(token, {
baseToken: null,
offset: 0,
baseline: false,
expectedIndent
})
}

@@ -668,3 +801,3 @@ }

*/
function ignore (node) {
function ignore(node) {
for (const token of tokenStore.getTokens(node)) {

@@ -681,3 +814,3 @@ offsets.delete(token)

*/
function processIgnores (visitor) {
function processIgnores(visitor) {
for (const ignorePattern of options.ignores) {

@@ -706,3 +839,3 @@ const key = `${ignorePattern}:exit`

*/
function getExpectedIndents (tokens) {
function getExpectedIndents(tokens) {
const expectedIndents = []

@@ -719,4 +852,11 @@

const baseOffsetInfo = offsets.get(offsetInfo.baseToken)
if (baseOffsetInfo != null && baseOffsetInfo.expectedIndent != null && (i === 0 || !baseOffsetInfo.baseline)) {
expectedIndents.push(baseOffsetInfo.expectedIndent + (offsetInfo.offset * options.indentSize))
if (
baseOffsetInfo != null &&
baseOffsetInfo.expectedIndent != null &&
(i === 0 || !baseOffsetInfo.baseline)
) {
expectedIndents.push(
baseOffsetInfo.expectedIndent +
offsetInfo.offset * options.indentSize
)
if (baseOffsetInfo.baseline) {

@@ -744,3 +884,3 @@ break

*/
function getIndentText (firstToken) {
function getIndentText(firstToken) {
const text = sourceCode.text

@@ -763,3 +903,3 @@ let i = firstToken.range[0] - 1

*/
function defineFix (token, actualIndent, expectedIndent) {
function defineFix(token, actualIndent, expectedIndent) {
if (token.type === 'Block' && token.loc.start.line !== token.loc.end.line) {

@@ -769,4 +909,4 @@ // Fix indentation in multiline block comments.

const firstLine = lines.shift()
if (lines.every(l => BLOCK_COMMENT_PREFIX.test(l))) {
return fixer => {
if (lines.every((l) => BLOCK_COMMENT_PREFIX.test(l))) {
return (fixer) => {
const range = [token.range[0] - actualIndent, token.range[1]]

@@ -777,3 +917,5 @@ const indent = options.indentChar.repeat(expectedIndent)

range,
`${indent}${firstLine}${lines.map(l => l.replace(BLOCK_COMMENT_PREFIX, `${indent} *`)).join('')}`
`${indent}${firstLine}${lines
.map((l) => l.replace(BLOCK_COMMENT_PREFIX, `${indent} *`))
.join('')}`
)

@@ -784,3 +926,3 @@ }

return fixer => {
return (fixer) => {
const range = [token.range[0] - actualIndent, token.range[0]]

@@ -799,3 +941,3 @@ const indent = options.indentChar.repeat(expectedIndent)

*/
function validateCore (token, expectedIndent, optionalExpectedIndents) {
function validateCore(token, expectedIndent, optionalExpectedIndents) {
const line = token.loc.start.line

@@ -812,3 +954,3 @@ const indentText = getIndentText(token)

const actualIndent = token.loc.start.column
const unit = (options.indentChar === '\t' ? 'tab' : 'space')
const unit = options.indentChar === '\t' ? 'tab' : 'space'

@@ -822,3 +964,4 @@ for (let i = 0; i < indentText.length; ++i) {

},
message: 'Expected {{expected}} character, but found {{actual}} character.',
message:
'Expected {{expected}} character, but found {{actual}} character.',
data: {

@@ -834,3 +977,7 @@ expected: JSON.stringify(options.indentChar),

if (actualIndent !== expectedIndent && (optionalExpectedIndents == null || !optionalExpectedIndents.includes(actualIndent))) {
if (
actualIndent !== expectedIndent &&
(optionalExpectedIndents == null ||
!optionalExpectedIndents.includes(actualIndent))
) {
context.report({

@@ -841,3 +988,4 @@ loc: {

},
message: 'Expected indentation of {{expectedIndent}} {{unit}}{{expectedIndentPlural}} but found {{actualIndent}} {{unit}}{{actualIndentPlural}}.',
message:
'Expected indentation of {{expectedIndent}} {{unit}}{{expectedIndentPlural}} but found {{actualIndent}} {{unit}}{{actualIndentPlural}}.',
data: {

@@ -847,4 +995,4 @@ expectedIndent,

unit,
expectedIndentPlural: (expectedIndent === 1) ? '' : 's',
actualIndentPlural: (actualIndent === 1) ? '' : 's'
expectedIndentPlural: expectedIndent === 1 ? '' : 's',
actualIndentPlural: actualIndent === 1 ? '' : 's'
},

@@ -863,3 +1011,7 @@ fix: defineFix(token, actualIndent, expectedIndent)

*/
function getCommentExpectedIndents (nextToken, nextExpectedIndent, lastExpectedIndent) {
function getCommentExpectedIndents(
nextToken,
nextExpectedIndent,
lastExpectedIndent
) {
if (typeof lastExpectedIndent === 'number' && isClosingToken(nextToken)) {

@@ -897,3 +1049,3 @@ if (nextExpectedIndent === lastExpectedIndent) {

*/
function validate (tokens, comments, lastToken) {
function validate(tokens, comments, lastToken) {
// Calculate and save expected indentation.

@@ -932,7 +1084,11 @@ const firstToken = tokens[0]

if (options.indentChar === ' ') {
offsetInfo.expectedIndent = Math.max(0, token.loc.start.column + expectedBaseIndent - 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 = expectedBaseIndent + (token === tokens[0] ? 0 : 1)
offsetInfo.expectedIndent =
expectedBaseIndent + (token === tokens[0] ? 0 : 1)
}

@@ -942,3 +1098,5 @@ baseline.add(token)

// The base token is a baseline token on this line, so inherit it.
offsetInfo.expectedIndent = offsets.get(offsetInfo.baseToken).expectedIndent
offsetInfo.expectedIndent = offsets.get(
offsetInfo.baseToken
).expectedIndent
baseline.add(token)

@@ -961,3 +1119,7 @@ } else {

const lastExpectedIndent = lastOffsetInfo && lastOffsetInfo.expectedIndent
const commentOptionalExpectedIndents = getCommentExpectedIndents(firstToken, expectedIndent, lastExpectedIndent)
const commentOptionalExpectedIndents = getCommentExpectedIndents(
firstToken,
expectedIndent,
lastExpectedIndent
)

@@ -967,8 +1129,11 @@ // Validate.

const commentExpectedIndents = getExpectedIndents([comment])
const commentExpectedIndent =
commentExpectedIndents
? commentExpectedIndents.expectedIndent
: commentOptionalExpectedIndents[0]
const commentExpectedIndent = commentExpectedIndents
? commentExpectedIndents.expectedIndent
: commentOptionalExpectedIndents[0]
validateCore(comment, commentExpectedIndent, commentOptionalExpectedIndents)
validateCore(
comment,
commentExpectedIndent,
commentOptionalExpectedIndents
)
}

@@ -983,3 +1148,3 @@ validateCore(firstToken, expectedIndent)

return processIgnores({
VAttribute (node) {
VAttribute(node) {
const keyToken = tokenStore.getFirstToken(node)

@@ -998,7 +1163,13 @@ const eqToken = tokenStore.getTokenAfter(node.key)

VElement (node) {
VElement(node) {
if (!PREFORMATTED_ELEMENT_NAMES.includes(node.name)) {
const isTopLevel = node.parent.type !== 'VElement'
const offset = isTopLevel ? options.baseIndent : 1
processNodeList(node.children.filter(isNotEmptyTextNode), node.startTag, node.endTag, offset, false)
processNodeList(
node.children.filter(isNotEmptyTextNode),
node.startTag,
node.endTag,
offset,
false
)
} else {

@@ -1012,3 +1183,3 @@ const startTagToken = tokenStore.getFirstToken(node)

VEndTag (node) {
VEndTag(node) {
const element = node.parent

@@ -1023,4 +1194,7 @@ const startTagOpenToken = tokenStore.getFirstToken(element.startTag)

VExpressionContainer (node) {
if (node.expression != null && node.range[0] !== node.expression.range[0]) {
VExpressionContainer(node) {
if (
node.expression != null &&
node.range[0] !== node.expression.range[0]
) {
const startQuoteToken = tokenStore.getFirstToken(node)

@@ -1035,3 +1209,3 @@ const endQuoteToken = tokenStore.getLastToken(node)

VFilter (node) {
VFilter(node) {
const idToken = tokenStore.getFirstToken(node)

@@ -1046,3 +1220,3 @@ const lastToken = tokenStore.getLastToken(node)

VFilterSequenceExpression (node) {
VFilterSequenceExpression(node) {
if (node.filters.length === 0) {

@@ -1065,3 +1239,3 @@ return

VForExpression (node) {
VForExpression(node) {
const firstToken = tokenStore.getFirstToken(node)

@@ -1080,7 +1254,7 @@ const lastOfLeft = last(node.left) || firstToken

VOnExpression (node) {
VOnExpression(node) {
processNodeList(node.body, null, null, 0)
},
VStartTag (node) {
VStartTag(node) {
const openToken = tokenStore.getFirstToken(node)

@@ -1097,5 +1271,6 @@ const closeToken = tokenStore.getLastToken(node)

if (closeToken != null && closeToken.type.endsWith('TagClose')) {
const offset = closeToken.type !== 'HTMLSelfClosingTagClose'
? options.closeBracket.startTag
: options.closeBracket.selfClosingTag
const offset =
closeToken.type !== 'HTMLSelfClosingTagClose'
? options.closeBracket.startTag
: options.closeBracket.selfClosingTag
setOffset(closeToken, offset, openToken)

@@ -1105,3 +1280,3 @@ }

VText (node) {
VText(node) {
const tokens = tokenStore.getTokens(node, isNotWhitespace)

@@ -1115,7 +1290,12 @@ const firstTokenInfo = offsets.get(tokenStore.getFirstToken(node))

'ArrayExpression, ArrayPattern' (node) {
processNodeList(node.elements, tokenStore.getFirstToken(node), tokenStore.getLastToken(node), 1)
'ArrayExpression, ArrayPattern'(node) {
processNodeList(
node.elements,
tokenStore.getFirstToken(node),
tokenStore.getLastToken(node),
1
)
},
ArrowFunctionExpression (node) {
ArrowFunctionExpression(node) {
const firstToken = tokenStore.getFirstToken(node)

@@ -1130,3 +1310,6 @@ const secondToken = tokenStore.getTokenAfter(firstToken)

if (isLeftParen(leftToken)) {
const rightToken = tokenStore.getTokenAfter(last(node.params) || leftToken, isRightParen)
const rightToken = tokenStore.getTokenAfter(
last(node.params) || leftToken,
isRightParen
)
processNodeList(node.params, leftToken, rightToken, 1)

@@ -1139,3 +1322,5 @@ }

'AssignmentExpression, AssignmentPattern, BinaryExpression, LogicalExpression' (node) {
'AssignmentExpression, AssignmentPattern, BinaryExpression, LogicalExpression'(
node
) {
const leftToken = getChainHeadToken(node)

@@ -1145,7 +1330,6 @@ const opToken = tokenStore.getTokenAfter(node.left, isNotRightParen)

const prevToken = tokenStore.getTokenBefore(leftToken)
const shouldIndent = (
const shouldIndent =
prevToken == null ||
prevToken.loc.end.line === leftToken.loc.start.line ||
isBeginningOfElement(leftToken, node)
)

@@ -1155,3 +1339,3 @@ setOffset([opToken, rightToken], shouldIndent ? 1 : 0, leftToken)

'AwaitExpression, RestElement, SpreadElement, UnaryExpression' (node) {
'AwaitExpression, RestElement, SpreadElement, UnaryExpression'(node) {
const firstToken = tokenStore.getFirstToken(node)

@@ -1163,7 +1347,12 @@ const nextToken = tokenStore.getTokenAfter(firstToken)

'BlockStatement, ClassBody' (node) {
processNodeList(node.body, tokenStore.getFirstToken(node), tokenStore.getLastToken(node), 1)
'BlockStatement, ClassBody'(node) {
processNodeList(
node.body,
tokenStore.getFirstToken(node),
tokenStore.getLastToken(node),
1
)
},
'BreakStatement, ContinueStatement, ReturnStatement, ThrowStatement' (node) {
'BreakStatement, ContinueStatement, ReturnStatement, ThrowStatement'(node) {
if (node.argument != null || node.label != null) {

@@ -1177,3 +1366,3 @@ const firstToken = tokenStore.getFirstToken(node)

CallExpression (node) {
CallExpression(node) {
const firstToken = tokenStore.getFirstToken(node)

@@ -1187,3 +1376,3 @@ const rightToken = tokenStore.getLastToken(node)

CatchClause (node) {
CatchClause(node) {
const firstToken = tokenStore.getFirstToken(node)

@@ -1202,3 +1391,3 @@ const bodyToken = tokenStore.getFirstToken(node.body)

'ClassDeclaration, ClassExpression' (node) {
'ClassDeclaration, ClassExpression'(node) {
const firstToken = tokenStore.getFirstToken(node)

@@ -1219,3 +1408,3 @@ const bodyToken = tokenStore.getFirstToken(node.body)

ConditionalExpression (node) {
ConditionalExpression(node) {
const prevToken = tokenStore.getTokenBefore(node)

@@ -1225,3 +1414,6 @@ const firstToken = tokenStore.getFirstToken(node)

const consequentToken = tokenStore.getTokenAfter(questionToken)
const colonToken = tokenStore.getTokenAfter(node.consequent, isNotRightParen)
const colonToken = tokenStore.getTokenAfter(
node.consequent,
isNotRightParen
)
const alternateToken = tokenStore.getTokenAfter(colonToken)

@@ -1234,3 +1426,7 @@ const isFlat =

if (isFlat) {
setOffset([questionToken, consequentToken, colonToken, alternateToken], 0, firstToken)
setOffset(
[questionToken, consequentToken, colonToken, alternateToken],
0,
firstToken
)
} else {

@@ -1242,3 +1438,3 @@ setOffset([questionToken, colonToken], 1, firstToken)

DoWhileStatement (node) {
DoWhileStatement(node) {
const doToken = tokenStore.getFirstToken(node)

@@ -1249,3 +1445,5 @@ const whileToken = tokenStore.getTokenAfter(node.body, isNotRightParen)

const lastToken = tokenStore.getLastToken(node)
const rightToken = isSemicolon(lastToken) ? tokenStore.getTokenBefore(lastToken) : lastToken
const rightToken = isSemicolon(lastToken)
? tokenStore.getTokenBefore(lastToken)
: lastToken

@@ -1259,3 +1457,3 @@ processMaybeBlock(node.body, doToken)

ExportAllDeclaration (node) {
ExportAllDeclaration(node) {
const tokens = tokenStore.getTokens(node)

@@ -1269,10 +1467,11 @@ const firstToken = tokens.shift()

ExportDefaultDeclaration (node) {
ExportDefaultDeclaration(node) {
const exportToken = tokenStore.getFirstToken(node)
const defaultToken = tokenStore.getFirstToken(node, 1)
const declarationToken = getFirstAndLastTokens(node.declaration).firstToken
const declarationToken = getFirstAndLastTokens(node.declaration)
.firstToken
setOffset([defaultToken, declarationToken], 1, exportToken)
},
ExportNamedDeclaration (node) {
ExportNamedDeclaration(node) {
const exportToken = tokenStore.getFirstToken(node)

@@ -1291,3 +1490,6 @@ if (node.declaration) {

const maybeFromToken = tokenStore.getTokenAfter(rightParenToken)
if (maybeFromToken != null && sourceCode.getText(maybeFromToken) === 'from') {
if (
maybeFromToken != null &&
sourceCode.getText(maybeFromToken) === 'from'
) {
const fromToken = maybeFromToken

@@ -1300,3 +1502,3 @@ const nameToken = tokenStore.getTokenAfter(fromToken)

ExportSpecifier (node) {
ExportSpecifier(node) {
const tokens = tokenStore.getTokens(node)

@@ -1307,3 +1509,3 @@ const firstToken = tokens.shift()

'ForInStatement, ForOfStatement' (node) {
'ForInStatement, ForOfStatement'(node) {
const forToken = tokenStore.getFirstToken(node)

@@ -1314,3 +1516,6 @@ const leftParenToken = tokenStore.getTokenAfter(forToken)

const rightToken = tokenStore.getTokenAfter(inToken)
const rightParenToken = tokenStore.getTokenBefore(node.body, isNotLeftParen)
const rightParenToken = tokenStore.getTokenBefore(
node.body,
isNotLeftParen
)

@@ -1325,13 +1530,21 @@ setOffset(leftParenToken, 1, forToken)

ForStatement (node) {
ForStatement(node) {
const forToken = tokenStore.getFirstToken(node)
const leftParenToken = tokenStore.getTokenAfter(forToken)
const rightParenToken = tokenStore.getTokenBefore(node.body, isNotLeftParen)
const rightParenToken = tokenStore.getTokenBefore(
node.body,
isNotLeftParen
)
setOffset(leftParenToken, 1, forToken)
processNodeList([node.init, node.test, node.update], leftParenToken, rightParenToken, 1)
processNodeList(
[node.init, node.test, node.update],
leftParenToken,
rightParenToken,
1
)
processMaybeBlock(node.body, forToken)
},
'FunctionDeclaration, FunctionExpression' (node) {
'FunctionDeclaration, FunctionExpression'(node) {
const firstToken = tokenStore.getFirstToken(node)

@@ -1341,3 +1554,6 @@ if (isLeftParen(firstToken)) {

const leftToken = firstToken
const rightToken = tokenStore.getTokenAfter(last(node.params) || leftToken, isRightParen)
const rightToken = tokenStore.getTokenAfter(
last(node.params) || leftToken,
isRightParen
)
const bodyToken = tokenStore.getFirstToken(node.body)

@@ -1349,7 +1565,16 @@

// Normal functions.
const functionToken = node.async ? tokenStore.getTokenAfter(firstToken) : firstToken
const starToken = node.generator ? tokenStore.getTokenAfter(functionToken) : null
const functionToken = node.async
? tokenStore.getTokenAfter(firstToken)
: firstToken
const starToken = node.generator
? tokenStore.getTokenAfter(functionToken)
: null
const idToken = node.id && tokenStore.getFirstToken(node.id)
const leftToken = tokenStore.getTokenAfter(idToken || starToken || functionToken)
const rightToken = tokenStore.getTokenAfter(last(node.params) || leftToken, isRightParen)
const leftToken = tokenStore.getTokenAfter(
idToken || starToken || functionToken
)
const rightToken = tokenStore.getTokenAfter(
last(node.params) || leftToken,
isRightParen
)
const bodyToken = tokenStore.getFirstToken(node.body)

@@ -1372,6 +1597,9 @@

IfStatement (node) {
IfStatement(node) {
const ifToken = tokenStore.getFirstToken(node)
const ifLeftParenToken = tokenStore.getTokenAfter(ifToken)
const ifRightParenToken = tokenStore.getTokenBefore(node.consequent, isRightParen)
const ifRightParenToken = tokenStore.getTokenBefore(
node.consequent,
isRightParen
)

@@ -1383,3 +1611,6 @@ setOffset(ifLeftParenToken, 1, ifToken)

if (node.alternate != null) {
const elseToken = tokenStore.getTokenAfter(node.consequent, isNotRightParen)
const elseToken = tokenStore.getTokenAfter(
node.consequent,
isNotRightParen
)

@@ -1391,3 +1622,3 @@ setOffset(elseToken, 0, ifToken)

ImportDeclaration (node) {
ImportDeclaration(node) {
const firstSpecifier = node.specifiers[0]

@@ -1418,3 +1649,6 @@ const secondSpecifier = node.specifiers[1]

} else if (firstSpecifier.type === 'ImportDefaultSpecifier') {
if (secondSpecifier && secondSpecifier.type === 'ImportNamespaceSpecifier') {
if (
secondSpecifier &&
secondSpecifier.type === 'ImportNamespaceSpecifier'
) {
// There is a pattern:

@@ -1478,3 +1712,3 @@ // import Foo, * as foo from "foo"

ImportSpecifier (node) {
ImportSpecifier(node) {
if (node.local.range[0] !== node.imported.range[0]) {

@@ -1487,3 +1721,3 @@ const tokens = tokenStore.getTokens(node)

ImportNamespaceSpecifier (node) {
ImportNamespaceSpecifier(node) {
const tokens = tokenStore.getTokens(node)

@@ -1494,3 +1728,3 @@ const firstToken = tokens.shift()

LabeledStatement (node) {
LabeledStatement(node) {
const labelToken = tokenStore.getFirstToken(node)

@@ -1503,8 +1737,14 @@ const colonToken = tokenStore.getTokenAfter(labelToken)

'MemberExpression, MetaProperty' (node) {
'MemberExpression, MetaProperty'(node) {
const objectToken = tokenStore.getFirstToken(node)
if (node.computed) {
const leftBracketToken = tokenStore.getTokenBefore(node.property, isLeftBracket)
const leftBracketToken = tokenStore.getTokenBefore(
node.property,
isLeftBracket
)
const propertyToken = tokenStore.getTokenAfter(leftBracketToken)
const rightBracketToken = tokenStore.getTokenAfter(node.property, isRightBracket)
const rightBracketToken = tokenStore.getTokenAfter(
node.property,
isRightBracket
)

@@ -1522,4 +1762,4 @@ setOffset(leftBracketToken, 1, objectToken)

'MethodDefinition, Property' (node) {
const isMethod = (node.type === 'MethodDefinition' || node.method === true)
'MethodDefinition, Property'(node) {
const isMethod = node.type === 'MethodDefinition' || node.method === true
const prefixTokens = getPrefixTokens(node)

@@ -1536,3 +1776,6 @@ const hasPrefix = prefixTokens.length >= 1

const keyToken = tokenStore.getTokenAfter(keyLeftToken)
const keyRightToken = lastKeyToken = tokenStore.getTokenAfter(node.key, isRightBracket)
const keyRightToken = (lastKeyToken = tokenStore.getTokenAfter(
node.key,
isRightBracket
))

@@ -1545,3 +1788,3 @@ if (hasPrefix) {

} else {
const idToken = lastKeyToken = tokenStore.getFirstToken(node.key)
const idToken = (lastKeyToken = tokenStore.getFirstToken(node.key))

@@ -1565,3 +1808,3 @@ if (hasPrefix) {

NewExpression (node) {
NewExpression(node) {
const newToken = tokenStore.getFirstToken(node)

@@ -1581,11 +1824,16 @@ const calleeToken = tokenStore.getTokenAfter(newToken)

'ObjectExpression, ObjectPattern' (node) {
processNodeList(node.properties, tokenStore.getFirstToken(node), tokenStore.getLastToken(node), 1)
'ObjectExpression, ObjectPattern'(node) {
processNodeList(
node.properties,
tokenStore.getFirstToken(node),
tokenStore.getLastToken(node),
1
)
},
SequenceExpression (node) {
SequenceExpression(node) {
processNodeList(node.expressions, null, null, 0)
},
SwitchCase (node) {
SwitchCase(node) {
const caseToken = tokenStore.getFirstToken(node)

@@ -1604,3 +1852,6 @@

if (node.consequent.length === 1 && node.consequent[0].type === 'BlockStatement') {
if (
node.consequent.length === 1 &&
node.consequent[0].type === 'BlockStatement'
) {
setOffset(tokenStore.getFirstToken(node.consequent[0]), 0, caseToken)

@@ -1613,7 +1864,10 @@ } else if (node.consequent.length >= 1) {

SwitchStatement (node) {
SwitchStatement(node) {
const switchToken = tokenStore.getFirstToken(node)
const leftParenToken = tokenStore.getTokenAfter(switchToken)
const discriminantToken = tokenStore.getTokenAfter(leftParenToken)
const leftBraceToken = tokenStore.getTokenAfter(node.discriminant, isLeftBrace)
const leftBraceToken = tokenStore.getTokenAfter(
node.discriminant,
isLeftBrace
)
const rightParenToken = tokenStore.getTokenBefore(leftBraceToken)

@@ -1626,6 +1880,11 @@ const rightBraceToken = tokenStore.getLastToken(node)

setOffset(leftBraceToken, 0, switchToken)
processNodeList(node.cases, leftBraceToken, rightBraceToken, options.switchCase)
processNodeList(
node.cases,
leftBraceToken,
rightBraceToken,
options.switchCase
)
},
TaggedTemplateExpression (node) {
TaggedTemplateExpression(node) {
const tagTokens = getFirstAndLastTokens(node.tag, node.range[0])

@@ -1637,6 +1896,10 @@ const quasiToken = tokenStore.getTokenAfter(tagTokens.lastToken)

TemplateLiteral (node) {
TemplateLiteral(node) {
const firstToken = tokenStore.getFirstToken(node)
const quasiTokens = node.quasis.slice(1).map(n => tokenStore.getFirstToken(n))
const expressionToken = node.quasis.slice(0, -1).map(n => tokenStore.getTokenAfter(n))
const quasiTokens = node.quasis
.slice(1)
.map((n) => tokenStore.getFirstToken(n))
const expressionToken = node.quasis
.slice(0, -1)
.map((n) => tokenStore.getTokenAfter(n))

@@ -1647,3 +1910,3 @@ setOffset(quasiTokens, 0, firstToken)

TryStatement (node) {
TryStatement(node) {
const tryToken = tokenStore.getFirstToken(node)

@@ -1668,3 +1931,3 @@ const tryBlockToken = tokenStore.getFirstToken(node.block)

UpdateExpression (node) {
UpdateExpression(node) {
const firstToken = tokenStore.getFirstToken(node)

@@ -1676,7 +1939,12 @@ const nextToken = tokenStore.getTokenAfter(firstToken)

VariableDeclaration (node) {
processNodeList(node.declarations, tokenStore.getFirstToken(node), null, 1)
VariableDeclaration(node) {
processNodeList(
node.declarations,
tokenStore.getFirstToken(node),
null,
1
)
},
VariableDeclarator (node) {
VariableDeclarator(node) {
if (node.init != null) {

@@ -1691,3 +1959,3 @@ const idToken = tokenStore.getFirstToken(node)

'WhileStatement, WithStatement' (node) {
'WhileStatement, WithStatement'(node) {
const firstToken = tokenStore.getFirstToken(node)

@@ -1702,3 +1970,3 @@ const leftParenToken = tokenStore.getTokenAfter(firstToken)

YieldExpression (node) {
YieldExpression(node) {
if (node.argument != null) {

@@ -1715,3 +1983,3 @@ const yieldToken = tokenStore.getFirstToken(node)

// Process semicolons.
':statement' (node) {
':statement'(node) {
const firstToken = tokenStore.getFirstToken(node)

@@ -1729,3 +1997,7 @@ const lastToken = tokenStore.getLastToken(node)

const prevToken = tokenStore.getTokenBefore(firstToken)
if (info != null && isSemicolon(prevToken) && prevToken.loc.end.line === firstToken.loc.start.line) {
if (
info != null &&
isSemicolon(prevToken) &&
prevToken.loc.end.line === firstToken.loc.start.line
) {
offsets.set(prevToken, info)

@@ -1737,3 +2009,3 @@ }

// `:expression` does not match with MetaProperty and TemplateLiteral as a bug: https://github.com/estools/esquery/pull/59
':expression, MetaProperty, TemplateLiteral' (node) {
':expression, MetaProperty, TemplateLiteral'(node) {
let leftToken = tokenStore.getTokenBefore(node)

@@ -1754,3 +2026,3 @@ let rightToken = tokenStore.getTokenAfter(node)

// Ignore tokens of unknown nodes.
'*:exit' (node) {
'*:exit'(node) {
if (!KNOWN_NODES.has(node.type)) {

@@ -1762,11 +2034,11 @@ ignore(node)

// Top-level process.
Program (node) {
Program(node) {
const firstToken = node.tokens[0]
const isScriptTag = (
const isScriptTag =
firstToken != null &&
firstToken.type === 'Punctuator' &&
firstToken.value === '<script>'
)
const baseIndent =
isScriptTag ? (options.indentSize * options.baseIndent) : 0
const baseIndent = isScriptTag
? options.indentSize * options.baseIndent
: 0

@@ -1777,3 +2049,3 @@ for (const statement of node.body) {

},
"VElement[parent.type!='VElement']" (node) {
"VElement[parent.type!='VElement']"(node) {
processTopLevelNode(node, 0)

@@ -1783,3 +2055,3 @@ },

// Do validation.
":matches(Program, VElement[parent.type!='VElement']):exit" (node) {
":matches(Program, VElement[parent.type!='VElement']):exit"(node) {
let comments = []

@@ -1792,3 +2064,6 @@ let tokensOnSameLine = []

for (const token of tokenStore.getTokens(node, ITERATION_OPTS)) {
if (tokensOnSameLine.length === 0 || tokensOnSameLine[0].loc.start.line === token.loc.start.line) {
if (
tokensOnSameLine.length === 0 ||
tokensOnSameLine[0].loc.start.line === token.loc.start.line
) {
// This is on the same line (or the first token).

@@ -1800,3 +2075,4 @@ tokensOnSameLine.push(token)

comments.push(tokensOnSameLine[0])
isBesideMultilineToken = last(tokensOnSameLine).loc.end.line === token.loc.start.line
isBesideMultilineToken =
last(tokensOnSameLine).loc.end.line === token.loc.start.line
tokensOnSameLine = [token]

@@ -1809,3 +2085,4 @@ } else {

}
isBesideMultilineToken = last(tokensOnSameLine).loc.end.line === token.loc.start.line
isBesideMultilineToken =
last(tokensOnSameLine).loc.end.line === token.loc.start.line
tokensOnSameLine = [token]

@@ -1812,0 +2089,0 @@ comments = []

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

* @typedef {import('vue-eslint-parser').AST.ESLintNode} ESLintNode
*
* @typedef {import('vue-eslint-parser').AST.ESLintArrowFunctionExpression | { type: 'ArrowFunctionExpression', body: BlockStatement | Expression } } ArrowFunctionExpression
*/

@@ -41,2 +43,7 @@

*/
/**
* @typedef { { name: string, groupName: string, node: Literal | TemplateLiteral } } ComponentArrayPropertyData
* @typedef { { name: string, groupName: string, node: Identifier | Literal | TemplateLiteral } } ComponentObjectPropertyData
* @typedef { ComponentArrayPropertyData | ComponentObjectPropertyData } ComponentPropertyData
*/

@@ -295,15 +302,72 @@ // ------------------------------------------------------------------------------

/**
* Check whether the given attribute has their attribute value.
* @param {ASTNode} node The attribute node to check.
* @returns {boolean} `true` if the attribute has their value.
* Check whether the given directive attribute has their empty value (`=""`).
* @param {ASTNode} node The directive attribute node to check.
* @param {RuleContext} context The rule context to use parser services.
* @returns {boolean} `true` if the directive attribute has their empty value (`=""`).
*/
hasAttributeValue (node) {
isEmptyValueDirective (node, context) {
assert(node && node.type === 'VAttribute')
return (
node.value != null &&
(node.value.expression != null || node.value.syntaxError != null)
)
if (node.value == null) {
return false
}
if (node.value.expression != null) {
return false
}
let valueText = context.getSourceCode().getText(node.value)
if ((valueText[0] === '"' || valueText[0] === "'") && valueText[0] === valueText[valueText.length - 1]) {
// quoted
valueText = valueText.slice(1, -1)
}
if (!valueText) {
// empty
return true
}
return false
},
/**
* Check whether the given directive attribute has their empty expression value (e.g. `=" "`, `="/* &ast;/"`).
* @param {ASTNode} node The directive attribute node to check.
* @param {RuleContext} context The rule context to use parser services.
* @returns {boolean} `true` if the directive attribute has their empty expression value.
*/
isEmptyExpressionValueDirective (node, context) {
assert(node && node.type === 'VAttribute')
if (node.value == null) {
return false
}
if (node.value.expression != null) {
return false
}
const valueNode = node.value
const tokenStore = context.parserServices.getTemplateBodyTokenStore()
let quote1 = null
let quote2 = null
// `node.value` may be only comments, so cannot get the correct tokens with `tokenStore.getTokens(node.value)`.
for (const token of tokenStore.getTokens(node)) {
if (token.range[1] <= valueNode.range[0]) {
continue
}
if (valueNode.range[1] <= token.range[0]) {
// empty
return true
}
if (!quote1 && token.type === 'Punctuator' && (token.value === '"' || token.value === "'")) {
quote1 = token
continue
}
if (!quote2 && quote1 && token.type === 'Punctuator' && (token.value === quote1.value)) {
quote2 = token
continue
}
// not empty
return false
}
// empty
return true
},
/**
* Get the attribute which has the given name.

@@ -640,2 +704,3 @@ * @param {ASTNode} node The start tag node to check.

* - `onSetupFunctionEnter` ... Event when setup function found.
* - `onRenderFunctionEnter` ... Event when render function found.
*

@@ -659,6 +724,31 @@ * @param {RuleContext} context The ESLint rule context object.

/**
* @param {ObjectExpression} node
*/
vueVisitor.ObjectExpression = (node) => {
const type = getVueObjectType(context, node)
if (type) {
vueStack = { node, type, parent: vueStack }
vueStack = {
node,
type,
parent: vueStack,
get functional () {
/** @type {Property} */
const functional = node.properties.find(
(p) =>
p.type === 'Property' &&
getStaticPropertyName(p) === 'functional'
)
if (!functional) {
return false
}
if (
functional.value.type === 'Literal' &&
functional.value.value === false
) {
return false
}
return true
}
}
callVisitor('onVueObjectEnter', node)

@@ -675,12 +765,20 @@ }

}
if (visitor.onSetupFunctionEnter) {
vueVisitor['Property[value.type=/^(Arrow)?FunctionExpression$/] > :function'] = (node) => {
if (visitor.onSetupFunctionEnter || visitor.onRenderFunctionEnter) {
vueVisitor[
'Property[value.type=/^(Arrow)?FunctionExpression$/] > :function'
] = (node) => {
/** @type {Property} */
const prop = node.parent
if (vueStack && prop.parent === vueStack.node) {
if (getStaticPropertyName(prop) === 'setup' && prop.value === node) {
if (vueStack && prop.parent === vueStack.node && prop.value === node) {
const name = getStaticPropertyName(prop)
if (name === 'setup') {
callVisitor('onSetupFunctionEnter', node)
} else if (name === 'render') {
callVisitor('onRenderFunctionEnter', node)
}
}
callVisitor('Property[value.type=/^(Arrow)?FunctionExpression$/] > :function', node)
callVisitor(
'Property[value.type=/^(Arrow)?FunctionExpression$/] > :function',
node
)
}

@@ -751,10 +849,13 @@ }

* Return generator with all properties
* @param {ASTNode} node Node to check
* @param {Set} groups Name of parent group
* @param {ObjectExpression} node Node to check
* @param {Set<string>} groups Name of parent group
* @returns {IterableIterator<ComponentPropertyData>}
*/
* iterateProperties (node, groups) {
const nodes = node.properties.filter(p => p.type === 'Property' && groups.has(getStaticPropertyName(p.key)))
for (const item of nodes) {
const name = getStaticPropertyName(item.key)
if (!name) continue
for (const item of node.properties) {
if (item.type !== 'Property') {
continue
}
const name = getStaticPropertyName(item)
if (!name || !groups.has(name)) continue

@@ -775,4 +876,5 @@ if (item.value.type === 'ArrayExpression') {

* Return generator with all elements inside ArrayExpression
* @param {ASTNode} node Node to check
* @param {ArrayExpression} node Node to check
* @param {string} groupName Name of parent group
* @returns {IterableIterator<ComponentArrayPropertyData>}
*/

@@ -782,6 +884,7 @@ * iterateArrayExpression (node, groupName) {

for (const item of node.elements) {
const name = getStaticPropertyName(item)
if (name) {
const obj = { name, groupName, node: item }
yield obj
if (item.type === 'Literal' || item.type === 'TemplateLiteral') {
const name = getStaticPropertyName(item)
if (name) {
yield { name, groupName, node: item }
}
}

@@ -793,12 +896,35 @@ }

* Return generator with all elements inside ObjectExpression
* @param {ASTNode} node Node to check
* @param {ObjectExpression} node Node to check
* @param {string} groupName Name of parent group
* @returns {IterableIterator<ComponentObjectPropertyData>}
*/
* iterateObjectExpression (node, groupName) {
assert(node.type === 'ObjectExpression')
let usedGetter
for (const item of node.properties) {
const name = getStaticPropertyName(item)
if (name) {
const obj = { name, groupName, node: item.key }
yield obj
if (item.type === 'Property') {
const key = item.key
if (key.type === 'Identifier' || key.type === 'Literal' || key.type === 'TemplateLiteral') {
const name = getStaticPropertyName(item)
if (name) {
if (item.kind === 'set') {
// find getter pair
if (!usedGetter) { usedGetter = new Set() }
if (node.properties.some(item2 => {
if (item2.type === 'Property' && item2.kind === 'get' && !usedGetter.has(item2)) {
const getterName = getStaticPropertyName(item2)
if (getterName === name) {
usedGetter.add(item2)
return true
}
}
return false
})) {
// has getter pair
continue
}
}
yield { name, groupName, node: key }
}
}
}

@@ -810,4 +936,5 @@ }

* Return generator with all elements inside FunctionExpression
* @param {ASTNode} node Node to check
* @param {FunctionExpression} node Node to check
* @param {string} groupName Name of parent group
* @returns {IterableIterator<ComponentObjectPropertyData>}
*/

@@ -827,9 +954,11 @@ * iterateFunctionExpression (node, groupName) {

* Return generator with all elements inside ArrowFunctionExpression
* @param {ASTNode} node Node to check
* @param {ArrowFunctionExpression} node Node to check
* @param {string} groupName Name of parent group
* @returns {IterableIterator<ComponentObjectPropertyData>}
*/
* iterateArrowFunctionExpression (node, groupName) {
assert(node.type === 'ArrowFunctionExpression')
if (node.body.type === 'BlockStatement') {
for (const item of node.body.body) {
const body = node.body
if (body.type === 'BlockStatement') {
for (const item of body.body) {
if (item.type === 'ReturnStatement' && item.argument && item.argument.type === 'ObjectExpression') {

@@ -839,4 +968,4 @@ yield * this.iterateObjectExpression(item.argument, groupName)

}
} else if (node.body.type === 'ObjectExpression') {
yield * this.iterateObjectExpression(node.body, groupName)
} else if (body.type === 'ObjectExpression') {
yield * this.iterateObjectExpression(body, groupName)
}

@@ -843,0 +972,0 @@ },

@@ -25,3 +25,3 @@ const RE_REGEXP_CHAR = /[\\^$.*+?()[\]{}|]/gu

* @param {string} string The string to convert.
* @returns {string} Returns the `RegExp`.
* @returns {RegExp} Returns the `RegExp`.
*/

@@ -36,5 +36,15 @@ function toRegExp(string) {

/**
* Checks whether given string is regexp string
* @param {string} string
* @returns {boolean}
*/
function isRegExp(string) {
return Boolean(RE_REGEXP_STR.exec(string))
}
module.exports = {
escape,
toRegExp
toRegExp,
isRegExp
}
{
"name": "eslint-plugin-vue",
"version": "7.0.0-alpha.5",
"version": "7.0.0-alpha.6",
"description": "Official ESLint plugin for Vue.js",

@@ -11,2 +11,4 @@ "main": "lib/index.js",

"debug": "mocha --inspect-brk \"tests/lib/**/*.js\" --reporter dot --timeout 60000",
"cover": "npm run cover:test && npm run cover:report",
"cover:test": "nyc npm run test:base -- --timeout 60000",
"cover:report": "nyc report --reporter=html",

@@ -13,0 +15,0 @@ "lint": "eslint . --rulesdir eslint-internal-rules",

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