Path: blob/main/src/rules/newline-after-import.js
829 views
/**1* @fileoverview Rule to enforce new line after import not followed by another import.2* @author Radek Benkel3*/45import isStaticRequire from '../core/staticRequire';6import docsUrl from '../docsUrl';78import debug from 'debug';9const log = debug('eslint-plugin-import:rules:newline-after-import');1011//------------------------------------------------------------------------------12// Rule Definition13//------------------------------------------------------------------------------1415function containsNodeOrEqual(outerNode, innerNode) {16return outerNode.range[0] <= innerNode.range[0] && outerNode.range[1] >= innerNode.range[1];17}1819function getScopeBody(scope) {20if (scope.block.type === 'SwitchStatement') {21log('SwitchStatement scopes not supported');22return null;23}2425const { body } = scope.block;26if (body && body.type === 'BlockStatement') {27return body.body;28}2930return body;31}3233function findNodeIndexInScopeBody(body, nodeToFind) {34return body.findIndex((node) => containsNodeOrEqual(node, nodeToFind));35}3637function getLineDifference(node, nextNode) {38return nextNode.loc.start.line - node.loc.end.line;39}4041function isClassWithDecorator(node) {42return node.type === 'ClassDeclaration' && node.decorators && node.decorators.length;43}4445function isExportDefaultClass(node) {46return node.type === 'ExportDefaultDeclaration' && node.declaration.type === 'ClassDeclaration';47}4849function isExportNameClass(node) {5051return node.type === 'ExportNamedDeclaration' && node.declaration && node.declaration.type === 'ClassDeclaration';52}5354module.exports = {55meta: {56type: 'layout',57docs: {58url: docsUrl('newline-after-import'),59},60fixable: 'whitespace',61schema: [62{63'type': 'object',64'properties': {65'count': {66'type': 'integer',67'minimum': 1,68},69},70'additionalProperties': false,71},72],73},74create(context) {75let level = 0;76const requireCalls = [];7778function checkForNewLine(node, nextNode, type) {79if (isExportDefaultClass(nextNode) || isExportNameClass(nextNode)) {80const classNode = nextNode.declaration;8182if (isClassWithDecorator(classNode)) {83nextNode = classNode.decorators[0];84}85} else if (isClassWithDecorator(nextNode)) {86nextNode = nextNode.decorators[0];87}8889const options = context.options[0] || { count: 1 };90const lineDifference = getLineDifference(node, nextNode);91const EXPECTED_LINE_DIFFERENCE = options.count + 1;9293if (lineDifference < EXPECTED_LINE_DIFFERENCE) {94let column = node.loc.start.column;9596if (node.loc.start.line !== node.loc.end.line) {97column = 0;98}99100context.report({101loc: {102line: node.loc.end.line,103column,104},105message: `Expected ${options.count} empty line${options.count > 1 ? 's' : ''} \106after ${type} statement not followed by another ${type}.`,107fix: fixer => fixer.insertTextAfter(108node,109'\n'.repeat(EXPECTED_LINE_DIFFERENCE - lineDifference),110),111});112}113}114115function incrementLevel() {116level++;117}118function decrementLevel() {119level--;120}121122function checkImport(node) {123const { parent } = node;124const nodePosition = parent.body.indexOf(node);125const nextNode = parent.body[nodePosition + 1];126127// skip "export import"s128if (node.type === 'TSImportEqualsDeclaration' && node.isExport) {129return;130}131132if (nextNode && nextNode.type !== 'ImportDeclaration' && (nextNode.type !== 'TSImportEqualsDeclaration' || nextNode.isExport)) {133checkForNewLine(node, nextNode, 'import');134}135}136137return {138ImportDeclaration: checkImport,139TSImportEqualsDeclaration: checkImport,140CallExpression(node) {141if (isStaticRequire(node) && level === 0) {142requireCalls.push(node);143}144},145'Program:exit': function () {146log('exit processing for', context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename());147const scopeBody = getScopeBody(context.getScope());148log('got scope:', scopeBody);149150requireCalls.forEach(function (node, index) {151const nodePosition = findNodeIndexInScopeBody(scopeBody, node);152log('node position in scope:', nodePosition);153154const statementWithRequireCall = scopeBody[nodePosition];155const nextStatement = scopeBody[nodePosition + 1];156const nextRequireCall = requireCalls[index + 1];157158if (nextRequireCall && containsNodeOrEqual(statementWithRequireCall, nextRequireCall)) {159return;160}161162if (nextStatement &&163(!nextRequireCall || !containsNodeOrEqual(nextStatement, nextRequireCall))) {164165checkForNewLine(statementWithRequireCall, nextStatement, 'require');166}167});168},169FunctionDeclaration: incrementLevel,170FunctionExpression: incrementLevel,171ArrowFunctionExpression: incrementLevel,172BlockStatement: incrementLevel,173ObjectExpression: incrementLevel,174Decorator: incrementLevel,175'FunctionDeclaration:exit': decrementLevel,176'FunctionExpression:exit': decrementLevel,177'ArrowFunctionExpression:exit': decrementLevel,178'BlockStatement:exit': decrementLevel,179'ObjectExpression:exit': decrementLevel,180'Decorator:exit': decrementLevel,181};182},183};184185186