UNPKG

eslint-plugin-import-sorting

Version:

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

114 lines (113 loc) 4.42 kB
"use strict"; const utils = require("@typescript-eslint/utils"); const getEslintDisabledLines = require("../utils/get-eslint-disabled-lines.cjs"); const isNodeEslintDisabled = require("../utils/is-node-eslint-disabled.cjs"); const isSortable = require("../utils/is-sortable.cjs"); const makeFixes = require("../utils/make-fixes.cjs"); const pairwise = require("../utils/pairwise.cjs"); const rangeToDiff = require("../utils/range-to-diff.cjs"); const sortNodes = require("../utils/sort-nodes.cjs"); const createRule = utils.ESLintUtils.RuleCreator( (name) => `https://github.com/stormwarning/eslint-plugin-import-sorting/blob/main/docs/rules/${name}.md` ); const specifierOrder = createRule({ name: "specifier-order", meta: { type: "suggestion", fixable: "code", docs: { description: "Consistently order named import specifiers." }, messages: { "specifier-out-of-order": "{{right}} should occur before {{left}}" }, schema: [] }, defaultOptions: [], create(context) { let { sourceCode } = context; let options = { ignoreAlias: true, ignoreCase: false, groupKind: "mixed", order: "asc", type: "natural" }; return { ImportDeclaration(node) { let specifiers = node.specifiers.filter( ({ type }) => type === utils.AST_NODE_TYPES.ImportSpecifier ); if (!isSortable.isSortable(specifiers)) return; let eslintDisabledLines = getEslintDisabledLines.getEslintDisabledLines({ ruleName: context.id, sourceCode }); let formattedMembers = [[]]; for (let specifier of specifiers) { let { name } = specifier.local; if (specifier.type === utils.AST_NODE_TYPES.ImportSpecifier && options.ignoreAlias) { name = specifier.imported.type === utils.AST_NODE_TYPES.Identifier ? specifier.imported.name : ( // @ts-expect-error -- `value` possibly missing from type declarations. specifier.imported.value ); } let sortingNode = { groupKind: specifier.type === utils.AST_NODE_TYPES.ImportSpecifier && specifier.importKind === "type" ? "type" : "value", isEslintDisabled: isNodeEslintDisabled.isNodeEslintDisabled(specifier, eslintDisabledLines), size: rangeToDiff.rangeToDiff(specifier, sourceCode), node: specifier, name }; formattedMembers.at(-1).push(sortingNode); } let groupKindOrder; if (options.groupKind === "values-first") { groupKindOrder = ["value", "type"]; } else if (options.groupKind === "types-first") { groupKindOrder = ["type", "value"]; } else { groupKindOrder = ["any"]; } for (let nodes of formattedMembers) { let filteredGroupKindNodes = groupKindOrder.map( (groupKind) => nodes.filter( (currentNode) => groupKind === "any" || currentNode.groupKind === groupKind ) ); let sortNodesExcludingEslintDisabled = (ignoreEslintDisabledNodes) => filteredGroupKindNodes.flatMap( (groupedNodes) => sortNodes.sortNodes(groupedNodes, options, { ignoreEslintDisabledNodes }) ); let sortedNodes = sortNodesExcludingEslintDisabled(false); let sortedNodesExcludingEslintDisabled = sortNodesExcludingEslintDisabled(true); pairwise.pairwise(nodes, (left, right) => { let indexOfLeft = sortedNodes.indexOf(left); let indexOfRight = sortedNodes.indexOf(right); let indexOfRightExcludingEslintDisabled = sortedNodesExcludingEslintDisabled.indexOf(right); if (indexOfLeft < indexOfRight && indexOfLeft < indexOfRightExcludingEslintDisabled) { return; } context.report({ fix: (fixer) => makeFixes.makeFixes({ fixer, nodes, sortedNodes: sortedNodesExcludingEslintDisabled, sourceCode }), data: { right: right.name, left: left.name }, messageId: "specifier-out-of-order", node: right.node }); }); } } }; } }); module.exports = specifierOrder;