UNPKG

eslint-plugin-import-sorting

Version:

ESLint plugin to group and sort imports by module, à la Python isort

162 lines (161 loc) 5.73 kB
import { ESLintUtils, AST_NODE_TYPES } from "@typescript-eslint/utils"; import { computeGroup } from "../utils/compute-group.js"; import { getEslintDisabledLines } from "../utils/get-eslint-disabled-lines.js"; import { getGroupNumber } from "../utils/get-group-number.js"; import { getNewlineErrors } from "../utils/get-newline-errors.js"; import { isNodeEslintDisabled } from "../utils/is-node-eslint-disabled.js"; import { makeFixes } from "../utils/make-fixes.js"; import { makeNewlineFixes } from "../utils/make-newline-fixes.js"; import { pairwise } from "../utils/pairwise.js"; import { rangeToDiff } from "../utils/range-to-diff.js"; import { sortNodesByGroups } from "../utils/sort-nodes-by-groups.js"; const IMPORT_GROUPS = [ "unassigned", "builtin", "framework", "external", "internal", "local", "style", "object", "unknown" ]; const createRule = ESLintUtils.RuleCreator( (name) => `https://github.com/stormwarning/eslint-plugin-import-sorting/blob/main/docs/rules/${name}.md` ); const order = createRule({ name: "order", meta: { type: "suggestion", fixable: "code", docs: { description: "Consistently order `import` statements." }, messages: { "needs-newline": "There should be at least one empty line between {{left}} and {{right}}", "extra-newline": "There should be no empty line between {{left}} and {{right}}", "out-of-order": "{{right}} should occur before {{left}}" }, schema: [] }, defaultOptions: [], create(context) { let { settings, sourceCode } = context; let options = { ignoreCase: false, newlinesBetween: "always", order: "asc", type: "natural" }; let eslintDisabledLines = getEslintDisabledLines({ ruleName: context.id, sourceCode }); let nodes = []; function registerNode(node) { let name; if (node.type === AST_NODE_TYPES.ImportDeclaration) { name = node.source.value; } else if (node.type === AST_NODE_TYPES.TSImportEqualsDeclaration) { name = node.moduleReference.type === AST_NODE_TYPES.TSExternalModuleReference ? node.moduleReference.expression.value : sourceCode.getText(node.moduleReference); } else { let decl = node.declarations[0].init; let { value } = decl.arguments[0]; name = value.toString(); } nodes.push({ group: computeGroup(node, settings, sourceCode), isEslintDisabled: isNodeEslintDisabled(node, eslintDisabledLines), name, node, shouldAddSafetySemicolonWhenInline: true, size: rangeToDiff(node, sourceCode) }); } return { TSImportEqualsDeclaration: registerNode, ImportDeclaration: registerNode, VariableDeclaration(node) { var _a; if (node.declarations[0].init && node.declarations[0].init.type === AST_NODE_TYPES.CallExpression && node.declarations[0].init.callee.type === AST_NODE_TYPES.Identifier && node.declarations[0].init.callee.name === "require" && ((_a = node.declarations[0].init.arguments[0]) == null ? void 0 : _a.type) === AST_NODE_TYPES.Literal) { registerNode(node); } }, // eslint-disable-next-line @typescript-eslint/naming-convention "Program:exit"() { var _a; function hasContentBetweenNodes(left, right) { return sourceCode.getTokensBetween(left.node, right.node, { includeComments: false }).length > 0; } let formattedNodes = [[]]; for (let node of nodes) { let lastNode = (_a = formattedNodes.at(-1)) == null ? void 0 : _a.at(-1); if (lastNode && hasContentBetweenNodes(lastNode, node)) { formattedNodes.push([node]); } else { formattedNodes.at(-1).push(node); } } for (let nodeList of formattedNodes) { let sortedNodes = sortNodesByGroups(nodeList, options, {}); pairwise(nodeList, (left, right) => { let leftNumber = getGroupNumber(IMPORT_GROUPS, left); let rightNumber = getGroupNumber(IMPORT_GROUPS, right); let indexOfLeft = sortedNodes.indexOf(left); let indexOfRight = sortedNodes.indexOf(right); let messages = []; if (indexOfLeft > indexOfRight) { messages.push(leftNumber === rightNumber ? "out-of-order" : "out-of-order"); } messages = [ ...messages, ...getNewlineErrors({ missingLineError: "needs-newline", extraLineError: "extra-newline", left, leftNumber, right, rightNumber, sourceCode, options }) ]; for (let message of messages) { context.report({ fix: (fixer) => [ ...makeFixes({ fixer, nodes: nodeList, sortedNodes, sourceCode }), ...makeNewlineFixes({ fixer, nodes: nodeList, sortedNodes, sourceCode, options }) ], data: { rightGroup: right.group, leftGroup: left.group, right: right.name, left: left.name }, node: right.node, messageId: message }); } }); } } }; } }); export { IMPORT_GROUPS, order as default };