UNPKG

@croct/eslint-plugin

Version:

ESLint rules and presets applied to all Croct JavaScript projects.

120 lines (119 loc) 5.07 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.newlinePerChainedCall = void 0; const createRule_1 = require("../createRule"); const LINEBREAK_MATCHER = /\r\n|[\r\n\u2028\u2029]/u; exports.newlinePerChainedCall = (0, createRule_1.createRule)({ name: 'newline-per-chained-call', meta: { type: 'layout', docs: { description: 'Require a newline after each call in a method chain', recommended: 'recommended', }, fixable: 'whitespace', schema: [ { type: 'object', properties: { ignoreChainDeeperThan: { type: 'integer', minimum: 1, maximum: 10, default: 2, }, }, additionalProperties: false, }, ], messages: { expectedLineBreak: 'Expected line break before `{{propertyName}}`.', }, }, defaultOptions: [ { ignoreChainDeeperThan: 2, }, ], create: context => { const options = context.options[0] ?? {}; const ignoreChainWithDepth = options.ignoreChainDeeperThan ?? 2; const sourceCode = context.getSourceCode(); function getPropertyText(node) { const prefix = '.'; const lines = sourceCode.getText(node.property).split(LINEBREAK_MATCHER); return prefix + lines[0]; } function hasObjectAndPropertyOnSameLine(node) { return node.object.loc.end.line === node.property.loc.start.line; } function isNotClosingParenToken(token) { return token.value !== ')' || token.type !== 'Punctuator'; } function validateCallExpressionIgnoreDepth(node) { let hasCallExpression = false; if (node.type === 'CallExpression') { hasCallExpression = true; } if ((node.parent !== undefined) && node.parent.type !== 'CallExpression' && node.parent.type !== 'MemberExpression') { const memberExpressions = []; let currentNode = (node.type === 'CallExpression' ? node.callee : node); while (currentNode.type === 'CallExpression' || currentNode.type === 'MemberExpression') { if (currentNode.type === 'MemberExpression') { if (currentNode.property.type === 'Identifier' && !currentNode.computed) { memberExpressions.push(currentNode); } currentNode = currentNode.object; } else if (currentNode.type === 'CallExpression') { currentNode = currentNode.callee; } } if (memberExpressions.length > ignoreChainWithDepth && hasCallExpression && memberExpressions.some(hasObjectAndPropertyOnSameLine)) { const expressionsOnSameLine = memberExpressions .filter(hasObjectAndPropertyOnSameLine); const rootNode = expressionsOnSameLine[expressionsOnSameLine.length - 1]; if (rootNode.type === 'MemberExpression' && (rootNode.parent?.type === 'CallExpression' || rootNode.parent?.type === 'MemberExpression') && (rootNode.object.type === 'ThisExpression' || rootNode.object.type === 'Identifier')) { expressionsOnSameLine.pop(); } expressionsOnSameLine.forEach(memberExpression => { context.report({ node: memberExpression.property, loc: memberExpression.property.loc.start, messageId: 'expectedLineBreak', data: { propertyName: getPropertyText(memberExpression), }, fix: fixer => { const firstTokenAfterObject = sourceCode.getTokenAfter(memberExpression.object, isNotClosingParenToken); return fixer.insertTextBefore(firstTokenAfterObject, '\n'); }, }); }); } } } return { CallExpression: (node) => { if (node.callee?.type === 'MemberExpression') { validateCallExpressionIgnoreDepth(node); } }, MemberExpression: (node) => { validateCallExpressionIgnoreDepth(node); }, }; }, });