eslint-plugin-import-sorting
Version:
ESLint plugin to group and sort imports by module, à la Python isort
162 lines (161 loc) • 5.73 kB
JavaScript
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
};