/** * @fileoverview Enforce new lines between multi-line properties in Vue components. * @author IWANABETHATGUY */ 'use strict' const utils = require('../utils') /** * @param {Token} node */ function isComma(node) { return node.type === 'Punctuator' && node.value === ',' } /** * Check whether the between given nodes has empty line. * @param {SourceCode} sourceCode * @param {ASTNode} pre * @param {ASTNode} cur */ function* iterateBetweenTokens(sourceCode, pre, cur) { yield sourceCode.getLastToken(pre) yield* sourceCode.getTokensBetween(pre, cur, { includeComments: true }) yield sourceCode.getFirstToken(cur) } /** * Check whether the between given nodes has empty line. * @param {SourceCode} sourceCode * @param {ASTNode} pre * @param {ASTNode} cur */ function hasEmptyLine(sourceCode, pre, cur) { /** @type {Token|null} */ let preToken = null for (const token of iterateBetweenTokens(sourceCode, pre, cur)) { if (preToken && token.loc.start.line - preToken.loc.end.line >= 2) { return true } preToken = token } return false } // ------------------------------------------------------------------------------ // Rule Definition // ------------------------------------------------------------------------------ module.exports = { meta: { type: 'layout', docs: { description: 'enforce new lines between multi-line properties in Vue components', categories: undefined, url: 'https://eslint.vuejs.org/rules/new-line-between-multi-line-property.html' }, fixable: 'whitespace', // or "code" or "whitespace" schema: [ { type: 'object', properties: { // number of line you want to insert after multi-line property minLineOfMultilineProperty: { type: 'number', minimum: 2 } }, additionalProperties: false } ] }, /** @param {RuleContext} context */ create(context) { let minLineOfMultilineProperty = 2 if ( context.options && context.options[0] && context.options[0].minLineOfMultilineProperty ) { minLineOfMultilineProperty = context.options[0].minLineOfMultilineProperty } /** @type {CallExpression[]} */ const callStack = [] const sourceCode = context.getSourceCode() return Object.assign( utils.defineVueVisitor(context, { CallExpression(node) { callStack.push(node) }, 'CallExpression:exit'() { callStack.pop() }, /** * @param {ObjectExpression} node */ ObjectExpression(node) { if (callStack.length) { return } const properties = node.properties for (let i = 1; i < properties.length; i++) { const cur = properties[i] const pre = properties[i - 1] const lineCountOfPreProperty = pre.loc.end.line - pre.loc.start.line + 1 if (lineCountOfPreProperty < minLineOfMultilineProperty) { continue } if (hasEmptyLine(sourceCode, pre, cur)) { continue } context.report({ node: pre, loc: { start: pre.loc.end, end: cur.loc.start }, message: 'Enforce new lines between multi-line properties in Vue components.', fix(fixer) { /** @type {Token|null} */ let preToken = null for (const token of iterateBetweenTokens( sourceCode, pre, cur )) { if ( preToken && preToken.loc.end.line < token.loc.start.line ) { return fixer.insertTextAfter(preToken, '\n') } preToken = token } const commaToken = sourceCode.getTokenAfter(pre, isComma) return fixer.insertTextAfter(commaToken || pre, '\n\n') } }) } } }) ) } }