routie dev init since i didn't adhere to any proper guidance up until now

This commit is contained in:
2026-04-29 22:27:29 -06:00
commit e1dabb71e2
15301 changed files with 3562618 additions and 0 deletions
@@ -0,0 +1,101 @@
import { a as createRule, i as isCommaToken, r as getValue } from "../utils.mjs";
//#region src/rules/consistent-type-specifier-style/consistent-type-specifier-style.ts
function getImportText(node, sourceCode, specifiers) {
const sourceString = sourceCode.getText(node.source);
if (specifiers.length === 0) return "";
return `import type {${specifiers.map((s) => {
const importedName = getValue(s.imported);
if (importedName === s.local.name) return importedName;
return `${importedName} as ${s.local.name}`;
}).join(", ")}} from ${sourceString};`;
}
function hasResolutionModeAttribute(node) {
return node.attributes?.some((attr) => attr.key.type === "Literal" && attr.key.value === "resolution-mode");
}
var consistent_type_specifier_style_default = createRule({
name: "consistent-type-specifier-style",
meta: {
type: "suggestion",
docs: { description: "Enforce or ban the use of inline type-only markers for named imports." },
fixable: "code",
schema: [{
type: "string",
enum: [
"top-level",
"inline",
"prefer-top-level"
],
default: "top-level"
}],
messages: {
inline: "Prefer using inline {{kind}} specifiers instead of a top-level {{kind}}-only import.",
topLevel: "Prefer using a top-level {{kind}}-only import instead of inline {{kind}} specifiers."
}
},
defaultOptions: ["top-level"],
create(context, [options]) {
const { sourceCode } = context;
if (options === "inline") return { ImportDeclaration(node) {
if (node.importKind === "value" || node.importKind == null) return;
if (hasResolutionModeAttribute(node)) return;
if (node.specifiers.length === 0 || node.specifiers.length === 1 && (node.specifiers[0].type === "ImportDefaultSpecifier" || node.specifiers[0].type === "ImportNamespaceSpecifier")) return;
context.report({
node,
messageId: "inline",
data: { kind: node.importKind },
fix(fixer) {
const kindToken = sourceCode.getFirstToken(node, { skip: 1 });
return [kindToken ? fixer.remove(kindToken) : [], node.specifiers.map((specifier) => fixer.insertTextBefore(specifier, `${node.importKind} `))].flat();
}
});
} };
return { ImportDeclaration(node) {
if (node.importKind === "type" || node.specifiers.length === 0 || node.specifiers.length === 1 && (node.specifiers[0].type === "ImportDefaultSpecifier" || node.specifiers[0].type === "ImportNamespaceSpecifier")) return;
const typeSpecifiers = [];
const valueSpecifiers = [];
let defaultSpecifier = null;
for (const specifier of node.specifiers) {
if (specifier.type === "ImportDefaultSpecifier") {
defaultSpecifier = specifier;
continue;
}
if (!("importKind" in specifier)) continue;
if (specifier.importKind === "type") typeSpecifiers.push(specifier);
else if (specifier.importKind === "value" || specifier.importKind == null) valueSpecifiers.push(specifier);
}
const typeImport = getImportText(node, sourceCode, typeSpecifiers);
if (typeSpecifiers.length === node.specifiers.length) context.report({
node,
messageId: "topLevel",
data: { kind: "type" },
fix(fixer) {
return fixer.replaceText(node, typeImport);
}
});
else if (options === "top-level") for (const specifier of typeSpecifiers) context.report({
node: specifier,
messageId: "topLevel",
data: { kind: specifier.importKind },
fix(fixer) {
const fixes = [];
if (valueSpecifiers.length > 0) {
for (const specifier of typeSpecifiers) {
const token = sourceCode.getTokenAfter(specifier);
if (token && isCommaToken(token)) fixes.push(fixer.remove(token));
fixes.push(fixer.remove(specifier));
}
const maybeComma = sourceCode.getTokenAfter(valueSpecifiers.at(-1));
if (isCommaToken(maybeComma)) fixes.push(fixer.remove(maybeComma));
} else if (defaultSpecifier) {
const comma = sourceCode.getTokenAfter(defaultSpecifier, isCommaToken);
const closingBrace = sourceCode.getTokenAfter(node.specifiers.at(-1), (token) => token.type === "Punctuator" && token.value === "}");
fixes.push(fixer.removeRange([comma.range[0], closingBrace.range[1]]));
}
return [...fixes, fixer.insertTextAfter(node, `\n${typeImport}`)];
}
});
} };
}
});
//#endregion
export { consistent_type_specifier_style_default as t };
@@ -0,0 +1,28 @@
import { a as createRule } from "../utils.mjs";
//#region src/rules/exports-last/exports-last.ts
function isNonExportStatement({ type }) {
return type !== "ExportDefaultDeclaration" && type !== "ExportNamedDeclaration" && type !== "ExportAllDeclaration";
}
var exports_last_default = createRule({
name: "exports-last",
meta: {
type: "suggestion",
docs: { description: "Ensure all exports appear after other statements." },
schema: [],
messages: { end: "Export statements should appear at the end of the file" }
},
defaultOptions: [],
create(context) {
return { Program({ body }) {
const lastNonExportStatementIndex = body.findLastIndex(isNonExportStatement);
if (lastNonExportStatementIndex !== -1) {
for (const node of body.slice(0, lastNonExportStatementIndex)) if (!isNonExportStatement(node)) context.report({
node,
messageId: "end"
});
}
} };
}
});
//#endregion
export { exports_last_default as t };
+102
View File
@@ -0,0 +1,102 @@
import { a as createRule } from "../utils.mjs";
//#region src/rules/first/first.ts
const RELATIVE_PATTERN = /^\./;
const NON_WHITESPACE_PATTERN = /\S/;
const LEADING_WHITESPACE_PATTERN = /^(\s+)/;
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" && RELATIVE_PATTERN.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 }) => fixer.removeRange(range));
const range = [0, removeFixers.at(-1).range[1]];
let insertSourceCode = sortNodes.map(({ range }) => {
const nodeSourceCode = originSourceCode.slice(...range);
if (NON_WHITESPACE_PATTERN.test(nodeSourceCode[0])) return `\n${nodeSourceCode}`;
return nodeSourceCode;
}).join("");
let replaceSourceCode = "";
if (!lastLegalImp) insertSourceCode = insertSourceCode.trim() + insertSourceCode.match(LEADING_WHITESPACE_PATTERN)[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 };
@@ -0,0 +1,168 @@
import { a as createRule } from "../utils.mjs";
//#region src/rules/newline-after-import/newline-after-import.ts
function isStaticRequire(node) {
return node && node.callee && node.callee.type === "Identifier" && node.callee.name === "require" && node.arguments.length === 1 && node.arguments[0].type === "Literal" && typeof node.arguments[0].value === "string";
}
function containsNodeOrEqual(outerNode, innerNode) {
return outerNode.range[0] <= innerNode.range[0] && outerNode.range[1] >= innerNode.range[1];
}
function getScopeBody(scope) {
if (scope.block.type === "SwitchStatement") {
console.log("SwitchStatement scopes not supported");
return [];
}
const body = "body" in scope.block ? scope.block.body : null;
if (body && "type" in body && body.type === "BlockStatement") return body.body;
return Array.isArray(body) ? body : [];
}
function findNodeIndexInScopeBody(body, nodeToFind) {
return body.findIndex((node) => containsNodeOrEqual(node, nodeToFind));
}
function getLineDifference(node, nextNode) {
return nextNode.loc.start.line - node.loc.end.line;
}
function isClassWithDecorator(node) {
return node.type === "ClassDeclaration" && !!node.decorators?.length;
}
function isExportDefaultClass(node) {
return node.type === "ExportDefaultDeclaration" && node.declaration.type === "ClassDeclaration";
}
function isExportNameClass(node) {
return node.type === "ExportNamedDeclaration" && node.declaration?.type === "ClassDeclaration";
}
var newline_after_import_default = createRule({
name: "newline-after-import",
meta: {
type: "layout",
docs: { description: "Enforce a newline after import statements." },
fixable: "whitespace",
schema: [{
type: "object",
properties: {
count: {
type: "integer",
minimum: 1
},
exactCount: { type: "boolean" },
considerComments: { type: "boolean" }
},
additionalProperties: false
}],
messages: { newline: "Expected {{count}} empty line{{lineSuffix}} after {{type}} statement not followed by another {{type}}." }
},
defaultOptions: [{
count: 1,
exactCount: false,
considerComments: false
}],
create(context, [options]) {
const { count = 1, exactCount = false, considerComments = false } = options || {};
let level = 0;
const requireCalls = [];
function checkForNewLine(node, nextNode, type) {
if (isExportDefaultClass(nextNode) || isExportNameClass(nextNode)) {
const classNode = nextNode.declaration;
if (isClassWithDecorator(classNode)) nextNode = classNode.decorators[0];
} else if (isClassWithDecorator(nextNode)) nextNode = nextNode.decorators[0];
const lineDifference = getLineDifference(node, nextNode);
const EXPECTED_LINE_DIFFERENCE = count + 1;
if (lineDifference < EXPECTED_LINE_DIFFERENCE || exactCount && lineDifference !== EXPECTED_LINE_DIFFERENCE) {
let column = node.loc.start.column;
if (node.loc.start.line !== node.loc.end.line) column = 0;
context.report({
loc: {
line: node.loc.end.line,
column
},
messageId: "newline",
data: {
count,
lineSuffix: count > 1 ? "s" : "",
type
},
fix: exactCount && EXPECTED_LINE_DIFFERENCE < lineDifference ? void 0 : (fixer) => fixer.insertTextAfter(node, "\n".repeat(EXPECTED_LINE_DIFFERENCE - lineDifference))
});
}
}
function commentAfterImport(node, nextComment, type) {
const lineDifference = getLineDifference(node, nextComment);
const EXPECTED_LINE_DIFFERENCE = count + 1;
if (lineDifference < EXPECTED_LINE_DIFFERENCE) {
let column = node.loc.start.column;
if (node.loc.start.line !== node.loc.end.line) column = 0;
context.report({
loc: {
line: node.loc.end.line,
column
},
messageId: "newline",
data: {
count,
lineSuffix: count > 1 ? "s" : "",
type
},
fix: exactCount && EXPECTED_LINE_DIFFERENCE < lineDifference ? void 0 : (fixer) => fixer.insertTextAfter(node, "\n".repeat(EXPECTED_LINE_DIFFERENCE - lineDifference))
});
}
}
function incrementLevel() {
level++;
}
function decrementLevel() {
level--;
}
function checkImport(node) {
const { parent } = node;
if (!parent || !("body" in parent) || !parent.body) return;
const root = parent;
const nodePosition = root.body.indexOf(node);
const nextNode = root.body[nodePosition + 1];
const endLine = node.loc.end.line;
let nextComment;
if (root.comments !== void 0 && considerComments) nextComment = root.comments.find((o) => o.loc.start.line >= endLine && o.loc.start.line <= endLine + count + 1);
if (node.type === "TSImportEqualsDeclaration" && node.isExport) return;
if (nextComment) commentAfterImport(node, nextComment, "import");
else if (nextNode && nextNode.type !== "ImportDeclaration" && (nextNode.type !== "TSImportEqualsDeclaration" || nextNode.isExport)) checkForNewLine(node, nextNode, "import");
}
return {
"ImportDeclaration": checkImport,
"TSImportEqualsDeclaration": checkImport,
CallExpression(node) {
if (isStaticRequire(node) && level === 0) requireCalls.push(node);
},
"Program:exit": function(node) {
const scopeBody = getScopeBody(context.sourceCode.getScope(node));
for (const [index, node] of requireCalls.entries()) {
const nodePosition = findNodeIndexInScopeBody(scopeBody, node);
const statementWithRequireCall = scopeBody[nodePosition];
const nextStatement = scopeBody[nodePosition + 1];
const nextRequireCall = requireCalls[index + 1];
if (nextRequireCall && containsNodeOrEqual(statementWithRequireCall, nextRequireCall)) continue;
if (nextStatement && (!nextRequireCall || !containsNodeOrEqual(nextStatement, nextRequireCall))) {
let nextComment;
if ("comments" in statementWithRequireCall.parent && statementWithRequireCall.parent.comments !== void 0 && considerComments) {
const endLine = node.loc.end.line;
nextComment = statementWithRequireCall.parent.comments.find((o) => o.loc.start.line >= endLine && o.loc.start.line <= endLine + count + 1);
}
if (nextComment && nextComment !== void 0) commentAfterImport(statementWithRequireCall, nextComment, "require");
else checkForNewLine(statementWithRequireCall, nextStatement, "require");
}
}
},
"FunctionDeclaration": incrementLevel,
"FunctionExpression": incrementLevel,
"ArrowFunctionExpression": incrementLevel,
"BlockStatement": incrementLevel,
"ObjectExpression": incrementLevel,
"Decorator": incrementLevel,
"FunctionDeclaration:exit": decrementLevel,
"FunctionExpression:exit": decrementLevel,
"ArrowFunctionExpression:exit": decrementLevel,
"BlockStatement:exit": decrementLevel,
"ObjectExpression:exit": decrementLevel,
"Decorator:exit": decrementLevel
};
}
});
//#endregion
export { newline_after_import_default as t };
@@ -0,0 +1,47 @@
import { a as createRule, n as sourceType, r as getValue } from "../utils.mjs";
//#region src/rules/no-default-export/no-default-export.ts
var no_default_export_default = createRule({
name: "no-default-export",
meta: {
type: "suggestion",
docs: { description: "Forbid default exports." },
schema: [],
messages: {
preferNamed: "Prefer named exports.",
noAliasDefault: "Do not alias `{{local}}` as `default`. Just export `{{local}}` itself instead."
}
},
defaultOptions: [],
create(context) {
if (sourceType(context) !== "module") return {};
const { sourceCode } = context;
return {
ExportDefaultDeclaration(node) {
const { loc } = sourceCode.getFirstTokens(node)[1] || {};
context.report({
node,
messageId: "preferNamed",
loc
});
},
ExportNamedDeclaration(node) {
for (const specifier of node.specifiers.filter((specifier) => getValue(specifier.exported) === "default")) {
const { loc } = sourceCode.getFirstTokens(node)[1] || {};
if (specifier.type === "ExportDefaultSpecifier") context.report({
node,
messageId: "preferNamed",
loc
});
else if (specifier.type === "ExportSpecifier") context.report({
node,
messageId: "noAliasDefault",
data: { local: getValue(specifier.local) },
loc
});
}
}
};
}
});
//#endregion
export { no_default_export_default as t };
@@ -0,0 +1,241 @@
import { a as createRule, t as resolve } from "../utils.mjs";
//#region src/rules/no-duplicates/no-duplicates.ts
const LEADING_WHITESPACE_PATTERN = /^(\s*)/;
function checkImports(imported, context) {
imported.forEach((nodes, module) => {
if (nodes.length <= 1) return;
for (let i = 0, len = nodes.length; i < len; i++) {
const node = nodes[i];
context.report({
node: node.source,
messageId: "duplicate",
data: { module },
fix: i === 0 ? getFix(nodes, context.sourceCode, context) : null
});
}
});
}
function getSpecifiersByKind(node) {
const typeSpecs = [];
const valueSpecs = [];
for (const spec of node.specifiers) {
if (spec.type !== "ImportSpecifier") continue;
const name = spec.imported.type === "Identifier" ? spec.imported.name : spec.imported.value;
const localName = spec.local.name;
("importKind" in spec && spec.importKind === "type" ? typeSpecs : valueSpecs).push({
name,
localName
});
}
return {
typeSpecs,
valueSpecs
};
}
function formatSpecifier(s) {
return s.name !== s.localName ? `${s.name} as ${s.localName}` : s.name;
}
function getFix(nodes, sourceCode, context) {
const first = nodes[0];
const isTypeOnlyImport = first.importKind === "type";
if (hasProblematicComments(first, sourceCode) || hasNamespace(first)) return null;
const defaultImportNames = new Set(nodes.flatMap((x) => getDefaultImportName(x) || []));
if (defaultImportNames.size > 1) return null;
const restWithoutCommentsAndNamespaces = nodes.slice(1).filter((node) => !hasProblematicComments(node, sourceCode) && !hasNamespace(node));
const restWithoutCommentsAndNamespacesHasSpecifiers = restWithoutCommentsAndNamespaces.map(hasSpecifiers);
const specifiers = restWithoutCommentsAndNamespaces.reduce((acc, node, nodeIndex) => {
const tokens = sourceCode.getTokens(node);
const openBrace = tokens.find((token) => isPunctuator(token, "{"));
const closeBrace = tokens.find((token) => isPunctuator(token, "}"));
if (openBrace == null || closeBrace == null) return acc;
const entry = {
importNode: node,
identifiers: sourceCode.text.slice(openBrace.range[1], closeBrace.range[0]).split(","),
isEmpty: !restWithoutCommentsAndNamespacesHasSpecifiers[nodeIndex]
};
if (isTypeOnlyImport && node.importKind !== "type") {
const { typeSpecs, valueSpecs } = getSpecifiersByKind(node);
if (typeSpecs.length > 0 && valueSpecs.length > 0) {
entry.typeSpecs = typeSpecs;
entry.valueSpecs = valueSpecs;
}
}
acc.push(entry);
return acc;
}, []);
const unnecessaryImports = restWithoutCommentsAndNamespaces.filter((node, nodeIndex) => !restWithoutCommentsAndNamespacesHasSpecifiers[nodeIndex] && !specifiers.some((specifier) => specifier.importNode === node));
const shouldAddSpecifiers = specifiers.length > 0;
const shouldRemoveUnnecessary = unnecessaryImports.length > 0;
const shouldAddDefault = getDefaultImportName(first) == null && defaultImportNames.size === 1;
if (!shouldAddSpecifiers && !shouldRemoveUnnecessary && !shouldAddDefault) return null;
const preferInline = context.options[0] && context.options[0]["prefer-inline"];
return (fixer) => {
const tokens = sourceCode.getTokens(first);
const openBrace = tokens.find((token) => isPunctuator(token, "{"));
const closeBrace = tokens.find((token) => isPunctuator(token, "}"));
const firstToken = sourceCode.getFirstToken(first);
const [defaultImportName] = defaultImportNames;
const firstHasTrailingComma = closeBrace != null && isPunctuator(sourceCode.getTokenBefore(closeBrace), ",");
const firstIsEmpty = !hasSpecifiers(first);
const firstExistingIdentifiers = firstIsEmpty ? /* @__PURE__ */ new Set() : new Set(sourceCode.text.slice(openBrace.range[1], closeBrace.range[0]).split(",").map((x) => x.split(" as ")[0].trim()));
const [specifiersText] = specifiers.reduce(([result, needsComma, existingIdentifiers], specifier) => {
if (specifier.typeSpecs) {
const newSpecs = specifier.typeSpecs.filter((ts) => !existingIdentifiers.has(ts.name));
if (newSpecs.length === 0) return [
result,
needsComma,
existingIdentifiers
];
const text = newSpecs.map(formatSpecifier).join(", ");
const updatedSet = new Set(existingIdentifiers);
newSpecs.forEach((ts) => updatedSet.add(ts.name));
return [
needsComma ? `${result}, ${text}` : `${result}${text}`,
true,
updatedSet
];
}
const isTypeSpecifier = "importNode" in specifier && specifier.importNode.importKind === "type";
const [specifierText, updatedExistingIdentifiers] = specifier.identifiers.reduce(([text, set], cur) => {
const trimmed = cur.trim();
if (trimmed.length === 0 || existingIdentifiers.has(trimmed)) return [text, set];
const curWithType = preferInline && isTypeSpecifier ? cur.replace(LEADING_WHITESPACE_PATTERN, "$1type ") : cur;
return [text.length > 0 ? `${text},${curWithType}` : curWithType, set.add(trimmed)];
}, ["", existingIdentifiers]);
return [
needsComma && !specifier.isEmpty && specifierText.length > 0 ? `${result},${specifierText}` : `${result}${specifierText}`,
specifier.isEmpty ? needsComma : true,
updatedExistingIdentifiers
];
}, [
"",
!firstHasTrailingComma && !firstIsEmpty,
firstExistingIdentifiers
]);
const fixes = [];
if (shouldAddSpecifiers && preferInline && first.importKind === "type") {
const typeIdentifierToken = tokens.find((token) => token.type === "Identifier" && token.value === "type");
if (typeIdentifierToken) fixes.push(fixer.removeRange([typeIdentifierToken.range[0], typeIdentifierToken.range[1] + 1]));
for (const identifier of tokens.filter((token) => firstExistingIdentifiers.has(token.value))) fixes.push(fixer.replaceTextRange([identifier.range[0], identifier.range[1]], `type ${identifier.value}`));
}
if (openBrace == null && shouldAddSpecifiers && shouldAddDefault) fixes.push(fixer.insertTextAfter(firstToken, ` ${defaultImportName}, {${specifiersText}} from`));
else if (openBrace == null && !shouldAddSpecifiers && shouldAddDefault) fixes.push(fixer.insertTextAfter(firstToken, ` ${defaultImportName} from`));
else if (openBrace != null && closeBrace != null && shouldAddDefault) {
fixes.push(fixer.insertTextAfter(firstToken, ` ${defaultImportName},`));
if (shouldAddSpecifiers) fixes.push(fixer.insertTextBefore(closeBrace, specifiersText));
} else if (openBrace == null && shouldAddSpecifiers && !shouldAddDefault) if (first.specifiers.length === 0) fixes.push(fixer.insertTextAfter(firstToken, ` {${specifiersText}} from`));
else fixes.push(fixer.insertTextAfter(first.specifiers[0], `, {${specifiersText}}`));
else if (openBrace != null && closeBrace != null && !shouldAddDefault) {
const tokenBefore = sourceCode.getTokenBefore(closeBrace);
fixes.push(fixer.insertTextAfter(tokenBefore, specifiersText));
}
for (const specifier of specifiers) {
const importNode = specifier.importNode;
if (specifier.valueSpecs) {
const nodeTokens = sourceCode.getTokens(importNode);
const nodeOpenBrace = nodeTokens.find((token) => isPunctuator(token, "{"));
const nodeCloseBrace = nodeTokens.find((token) => isPunctuator(token, "}"));
if (nodeOpenBrace && nodeCloseBrace) fixes.push(fixer.replaceTextRange([nodeOpenBrace.range[1], nodeCloseBrace.range[0]], ` ${specifier.valueSpecs.map(formatSpecifier).join(", ")} `));
continue;
}
fixes.push(fixer.remove(importNode));
const charAfterImportRange = [importNode.range[1], importNode.range[1] + 1];
if (sourceCode.text.slice(charAfterImportRange[0], charAfterImportRange[1]) === "\n") fixes.push(fixer.removeRange(charAfterImportRange));
}
for (const node of unnecessaryImports) {
fixes.push(fixer.remove(node));
const charAfterImportRange = [node.range[1], node.range[1] + 1];
if (sourceCode.text.slice(charAfterImportRange[0], charAfterImportRange[1]) === "\n") fixes.push(fixer.removeRange(charAfterImportRange));
}
return fixes;
};
}
function isPunctuator(node, value) {
return node.type === "Punctuator" && node.value === value;
}
function getDefaultImportName(node) {
return node.specifiers.find((specifier) => specifier.type === "ImportDefaultSpecifier")?.local.name;
}
function hasNamespace(node) {
return node.specifiers.some((specifier) => specifier.type === "ImportNamespaceSpecifier");
}
function hasSpecifiers(node) {
return node.specifiers.some((specifier) => specifier.type === "ImportSpecifier");
}
function hasProblematicComments(node, sourceCode) {
return hasCommentBefore(node, sourceCode) || hasCommentAfter(node, sourceCode) || hasCommentInsideNonSpecifiers(node, sourceCode);
}
function hasCommentBefore(node, sourceCode) {
return sourceCode.getCommentsBefore(node).some((comment) => comment.loc.end.line >= node.loc.start.line - 1);
}
function hasCommentAfter(node, sourceCode) {
return sourceCode.getCommentsAfter(node).some((comment) => comment.loc.start.line === node.loc.end.line);
}
function hasCommentInsideNonSpecifiers(node, sourceCode) {
const tokens = sourceCode.getTokens(node);
const openBraceIndex = tokens.findIndex((token) => isPunctuator(token, "{"));
const closeBraceIndex = tokens.findIndex((token) => isPunctuator(token, "}"));
return (openBraceIndex !== -1 && closeBraceIndex !== -1 ? [...tokens.slice(1, openBraceIndex + 1), ...tokens.slice(closeBraceIndex + 1)] : tokens.slice(1)).some((token) => sourceCode.getCommentsBefore(token).length > 0);
}
var no_duplicates_default = createRule({
name: "no-duplicates",
meta: {
type: "problem",
docs: {
recommended: true,
description: "Forbid repeated import of the same module in multiple places."
},
fixable: "code",
schema: [{
type: "object",
properties: { "prefer-inline": { type: "boolean" } },
additionalProperties: false
}],
messages: { duplicate: "'{{module}}' imported multiple times." }
},
defaultOptions: [],
create(context) {
const preferInline = context.options[0]?.["prefer-inline"];
const moduleMaps = /* @__PURE__ */ new Map();
function getImportMap(n) {
const parent = n.parent;
let map;
if (moduleMaps.has(parent)) map = moduleMaps.get(parent);
else {
map = {
imported: /* @__PURE__ */ new Map(),
nsImported: /* @__PURE__ */ new Map(),
defaultTypesImported: /* @__PURE__ */ new Map(),
namespaceTypesImported: /* @__PURE__ */ new Map(),
namedTypesImported: /* @__PURE__ */ new Map()
};
moduleMaps.set(parent, map);
}
if (n.importKind === "type") {
if (n.specifiers.length > 0 && n.specifiers[0].type === "ImportDefaultSpecifier") return map.defaultTypesImported;
if (n.specifiers.length > 0 && n.specifiers[0].type === "ImportNamespaceSpecifier") return map.namespaceTypesImported;
if (!preferInline) return map.namedTypesImported;
}
if (!preferInline && n.specifiers.some((spec) => "importKind" in spec && spec.importKind === "type")) return map.namedTypesImported;
return hasNamespace(n) ? map.nsImported : map.imported;
}
return {
ImportDeclaration(n) {
const resolvedPath = resolve(n.source.value);
const importMap = getImportMap(n);
if (importMap.has(resolvedPath)) importMap.get(resolvedPath).push(n);
else importMap.set(resolvedPath, [n]);
},
"Program:exit": function() {
for (const map of moduleMaps.values()) {
checkImports(map.imported, context);
checkImports(map.nsImported, context);
checkImports(map.defaultTypesImported, context);
checkImports(map.namedTypesImported, context);
}
}
};
}
});
//#endregion
export { no_duplicates_default as t };
@@ -0,0 +1,39 @@
import { a as createRule } from "../utils.mjs";
//#region src/rules/no-mutable-exports/no-mutable-exports.ts
var no_mutable_exports_default = createRule({
name: "no-mutable-exports",
meta: {
type: "suggestion",
docs: { description: "Forbid the use of mutable exports with `var` or `let`." },
schema: [],
messages: { noMutable: "Exporting mutable '{{kind}}' binding, use 'const' instead." }
},
defaultOptions: [],
create(context) {
function checkDeclaration(node) {
if ("kind" in node && (node.kind === "var" || node.kind === "let")) context.report({
node,
messageId: "noMutable",
data: { kind: node.kind }
});
}
function checkDeclarationsInScope({ variables }, name) {
for (const variable of variables) if (variable.name === name) {
for (const def of variable.defs) if (def.type === "Variable" && def.parent) checkDeclaration(def.parent);
}
}
return {
ExportDefaultDeclaration(node) {
const scope = context.sourceCode.getScope(node);
if ("name" in node.declaration) checkDeclarationsInScope(scope, node.declaration.name);
},
ExportNamedDeclaration(node) {
const scope = context.sourceCode.getScope(node);
if (node.declaration) checkDeclaration(node.declaration);
else if (!node.source) for (const specifier of node.specifiers) checkDeclarationsInScope(scope, specifier.local.name);
}
};
}
});
//#endregion
export { no_mutable_exports_default as t };
@@ -0,0 +1,26 @@
import { a as createRule, r as getValue } from "../utils.mjs";
//#region src/rules/no-named-default/no-named-default.ts
var no_named_default_default = createRule({
name: "no-named-default",
meta: {
type: "suggestion",
docs: { description: "Forbid named default exports." },
schema: [],
messages: { default: `Use default import syntax to import '{{importName}}'.` }
},
defaultOptions: [],
create(context) {
return { ImportDeclaration(node) {
for (const im of node.specifiers) {
if ("importKind" in im && im.importKind === "type") continue;
if (im.type === "ImportSpecifier" && getValue(im.imported) === "default") context.report({
node: im.local,
messageId: "default",
data: { importName: im.local.name }
});
}
} };
}
});
//#endregion
export { no_named_default_default as t };
@@ -0,0 +1,79 @@
import { a as createRule, r as getValue } from "../utils.mjs";
//#region src/rules/prefer-default-export/prefer-default-export.ts
var prefer_default_export_default = createRule({
name: "prefer-default-export",
meta: {
type: "suggestion",
docs: { description: "Prefer a default export if module exports a single name or multiple names." },
schema: [{
type: "object",
properties: { target: {
type: "string",
enum: ["single", "any"],
default: "single"
} },
additionalProperties: false
}],
messages: {
single: "Prefer default export on a file with single export.",
any: "Prefer default export to be present on every file that has export."
}
},
defaultOptions: [{ target: "single" }],
create(context, [options]) {
let specifierExportCount = 0;
let hasDefaultExport = false;
let hasStarExport = false;
let hasTypeExport = false;
let namedExportNode;
const { target } = options;
function captureDeclaration(identifierOrPattern) {
if (identifierOrPattern?.type === "ObjectPattern") for (const property of identifierOrPattern.properties) captureDeclaration(property.value);
else if (identifierOrPattern?.type === "ArrayPattern") for (const el of identifierOrPattern.elements) captureDeclaration(el);
else specifierExportCount++;
}
return {
ExportDefaultSpecifier() {
hasDefaultExport = true;
},
ExportSpecifier(node) {
if (getValue(node.exported) === "default") hasDefaultExport = true;
else {
specifierExportCount++;
namedExportNode = node;
}
},
ExportNamedDeclaration(node) {
if (!node.declaration) return;
const { type } = node.declaration;
if (type === "TSTypeAliasDeclaration" || type === "TSInterfaceDeclaration") {
specifierExportCount++;
hasTypeExport = true;
return;
}
if ("declarations" in node.declaration && node.declaration.declarations) for (const declaration of node.declaration.declarations) captureDeclaration(declaration.id);
else specifierExportCount++;
namedExportNode = node;
},
ExportDefaultDeclaration() {
hasDefaultExport = true;
},
ExportAllDeclaration() {
hasStarExport = true;
},
"Program:exit": function() {
if (hasDefaultExport || hasStarExport || hasTypeExport) return;
if (target === "single" && specifierExportCount === 1) context.report({
node: namedExportNode,
messageId: "single"
});
else if (target === "any" && specifierExportCount > 0) context.report({
node: namedExportNode,
messageId: "any"
});
}
};
}
});
//#endregion
export { prefer_default_export_default as t };