core/quality/eslint/no-duplicate-imports.eslint.rule.ts
core/quality/eslint/no-duplicate-imports.eslint.rule.ts is a file in GovLab Quality. 107 lines of code and 31 definitions.
import type { Rule } from "eslint";
import { govlabMeta } from "#core/factories/eslint.factory";
type ImportDecl = Extract<Rule.Node, { type: "ImportDeclaration" }>;
const MIN_GROUP = 2;
const isRecord = function isRecord(value: unknown): value is Record<string, unknown> {
return value !== null && typeof value === "object";
};
const importKindOf = function importKindOf(decl: unknown): string {
if (isRecord(decl) && typeof decl["importKind"] === "string") {
return decl["importKind"];
}
return "value";
};
const sourceValue = function sourceValue(decl: ImportDecl): string {
return typeof decl.source.value === "string" ? decl.source.value : "";
};
const groupKey = function groupKey(decl: ImportDecl): string {
return `${importKindOf(decl)} ${sourceValue(decl)}`;
};
const namedOnly = function namedOnly(decl: ImportDecl): boolean {
return decl.specifiers.length > 0 && decl.specifiers.every((spec) => spec.type === "ImportSpecifier");
};
const collectNames = function collectNames(context: Rule.RuleContext, group: ImportDecl[]): string[] {
const seen = new Set<string>();
const names: string[] = [];
for (const decl of group) {
for (const spec of decl.specifiers) {
const text = context.sourceCode.getText(spec);
if (!seen.has(text)) {
seen.add(text);
names.push(text);
}
}
}
return names;
};
const mergedText = function mergedText(context: Rule.RuleContext, group: ImportDecl[]): string {
const [first] = group;
if (!first) {
return "";
}
const names = collectNames(context, group);
const kind = importKindOf(first) === "type" ? "type " : "";
const source = context.sourceCode.getText(first.source);
return `import ${kind}{ ${names.join(", ")} } from ${source};`;
};
const removeRange = function removeRange(context: Rule.RuleContext, decl: ImportDecl): [number, number] {
const range = decl.range ?? [0, 0];
const [start, end] = range;
return context.sourceCode.getText()[end] === "\n" ? [start, end + 1] : range;
};
const reportGroup = function reportGroup(context: Rule.RuleContext, group: ImportDecl[]): void {
const [head] = group;
if (!head) {
return;
}
const mergeable = group.every(namedOnly);
for (let i = 1; i < group.length; i += 1) {
const dup = group[i];
if (dup) {
const makeFix = (fixer: Rule.RuleFixer): Rule.Fix[] => [
fixer.replaceText(head, mergedText(context, group)),
...group.slice(1).map((other) => fixer.removeRange(removeRange(context, other))),
];
context.report({
data: { source: sourceValue(dup) },
...(mergeable && i === 1 ? { fix: makeFix } : {}),
messageId: "duplicate",
node: dup,
});
}
}
};
export default {
create(context: Rule.RuleContext): Rule.RuleListener {
const groups = new Map<string, ImportDecl[]>();
const onImport = (node: Rule.Node): void => {
if (node.type !== "ImportDeclaration") {
return;
}
const key = groupKey(node);
const list = groups.get(key) ?? [];
list.push(node);
groups.set(key, list);
};
const onExit = (): void => {
for (const group of groups.values()) {
if (group.length >= MIN_GROUP) {
reportGroup(context, group);
}
}
};
const handlers: [string, (node: Rule.Node) => void][] = [
["ImportDeclaration", onImport],
["Program:exit", onExit],
];
return Object.fromEntries(handlers);
},
meta: govlabMeta({
canonical: ["duplicate-code"],
description: "Disallow duplicate imports from one module; merge named-only imports into the first",
fixable: "code",
messages: { duplicate: "'{{source}}' is imported more than once; merge into a single import." },
ruleId: "no_duplicate_imports",
type: "problem",
}),
} satisfies Rule.RuleModule;