101 lines
No EOL
4 KiB
JavaScript
101 lines
No EOL
4 KiB
JavaScript
import { a as createRule } from "../utils.mjs";
|
|
|
|
//#region src/rules/first/first.ts
|
|
function getImportValue(node) {
|
|
return node.type === "ImportDeclaration" ? node.source.value : "moduleReference" in node && "expression" in node.moduleReference && "value" in node.moduleReference.expression && node.moduleReference.expression.value;
|
|
}
|
|
function isPossibleDirective(node) {
|
|
return node.type === "ExpressionStatement" && node.expression.type === "Literal" && typeof node.expression.value === "string";
|
|
}
|
|
var first_default = createRule({
|
|
name: "first",
|
|
meta: {
|
|
type: "suggestion",
|
|
docs: { description: "Ensure all imports appear before other statements." },
|
|
fixable: "code",
|
|
schema: [{
|
|
type: "string",
|
|
enum: ["absolute-first", "disable-absolute-first"]
|
|
}],
|
|
messages: {
|
|
absolute: "Absolute imports should come before relative imports.",
|
|
order: "Import in body of module; reorder to top."
|
|
}
|
|
},
|
|
defaultOptions: [],
|
|
create(context, options) {
|
|
return { Program(n) {
|
|
const body = n.body;
|
|
if (!body?.length) return;
|
|
const absoluteFirst = options[0] === "absolute-first";
|
|
const { sourceCode } = context;
|
|
const originSourceCode = sourceCode.getText();
|
|
let nonImportCount = 0;
|
|
let anyExpressions = false;
|
|
let anyRelative = false;
|
|
let lastLegalImp = null;
|
|
const errorInfos = [];
|
|
let shouldSort = true;
|
|
let lastSortNodesIndex = 0;
|
|
for (const [index, node] of body.entries()) {
|
|
if (!anyExpressions && isPossibleDirective(node)) continue;
|
|
anyExpressions = true;
|
|
if (node.type === "ImportDeclaration" || node.type === "TSImportEqualsDeclaration") {
|
|
if (absoluteFirst) {
|
|
const importValue = getImportValue(node);
|
|
if (typeof importValue === "string" && /^\./.test(importValue)) anyRelative = true;
|
|
else if (anyRelative) context.report({
|
|
node: node.type === "ImportDeclaration" ? node.source : node.moduleReference,
|
|
messageId: "absolute"
|
|
});
|
|
}
|
|
if (nonImportCount > 0) {
|
|
/** @see https://eslint.org/docs/next/use/migrate-to-9.0.0#-removed-multiple-context-methods */
|
|
for (const variable of sourceCode.getDeclaredVariables(node)) {
|
|
if (!shouldSort) break;
|
|
for (const reference of variable.references) if (reference.identifier.range[0] < node.range[1]) {
|
|
shouldSort = false;
|
|
break;
|
|
}
|
|
}
|
|
if (shouldSort) lastSortNodesIndex = errorInfos.length;
|
|
errorInfos.push({
|
|
node,
|
|
range: [body[index - 1].range[1], node.range[1]]
|
|
});
|
|
} else lastLegalImp = node;
|
|
} else nonImportCount++;
|
|
}
|
|
if (errorInfos.length === 0) return;
|
|
for (const [index, { node }] of errorInfos.entries()) {
|
|
let fix;
|
|
if (index < lastSortNodesIndex) fix = (fixer) => fixer.insertTextAfter(node, "");
|
|
else if (index === lastSortNodesIndex) {
|
|
const sortNodes = errorInfos.slice(0, lastSortNodesIndex + 1);
|
|
fix = (fixer) => {
|
|
const removeFixers = sortNodes.map(({ range: range$1 }) => fixer.removeRange(range$1));
|
|
const range = [0, removeFixers[removeFixers.length - 1].range[1]];
|
|
let insertSourceCode = sortNodes.map(({ range: range$1 }) => {
|
|
const nodeSourceCode = originSourceCode.slice(...range$1);
|
|
if (/\S/.test(nodeSourceCode[0])) return `\n${nodeSourceCode}`;
|
|
return nodeSourceCode;
|
|
}).join("");
|
|
let replaceSourceCode = "";
|
|
if (!lastLegalImp) insertSourceCode = insertSourceCode.trim() + insertSourceCode.match(/^(\s+)/)[0];
|
|
const fixers = [lastLegalImp ? fixer.insertTextAfter(lastLegalImp, insertSourceCode) : fixer.insertTextBefore(body[0], insertSourceCode), ...removeFixers];
|
|
for (const [i, computedFixer] of fixers.entries()) replaceSourceCode += originSourceCode.slice(fixers[i - 1] ? fixers[i - 1].range[1] : 0, computedFixer.range[0]) + computedFixer.text;
|
|
return fixer.replaceTextRange(range, replaceSourceCode);
|
|
};
|
|
}
|
|
context.report({
|
|
node,
|
|
messageId: "order",
|
|
fix
|
|
});
|
|
}
|
|
} };
|
|
}
|
|
});
|
|
|
|
//#endregion
|
|
export { first_default as t }; |