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
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 vida xie
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+53
View File
@@ -0,0 +1,53 @@
# eslint-plugin-import-lite
[![Open on npmx.dev](https://npmx.dev/api/registry/badge/version/eslint-plugin-import-lite)](https://npmx.dev/package/eslint-plugin-import-lite)
[![Open on npmx.dev](https://npmx.dev/api/registry/badge/size/eslint-plugin-import-lite)](https://npmx.dev/package/eslint-plugin-import-lite)
[![License](https://npmx.dev/api/registry/badge/license/eslint-plugin-import-lite)](https://npmx.dev/package/eslint-plugin-import-lite)
## Feature
- Zero dependencies.
- Port some useful rules that don't require a resolver from [`eslint-plugin-import-x`](https://github.com/un-ts/eslint-plugin-import-x).
- No need for a resolver and settings like those in [`eslint-plugin-import-x`](https://github.com/un-ts/eslint-plugin-import-x).
- Drop babel and flow support.
> [!NOTE]
>
> This plugin intentionally does **NOT** include binary resolvers — but contributions are welcome if you'd like to implement resolver support!
See all rules in [`src/rules`](./src/rules)
## Available Rules
- [consistent-type-specifier-style](./src/rules/consistent-type-specifier-style/README.md)
- [exports-last](./src/rules/exports-last/README.md)
- [first](./src/rules/first/README.md)
- [newline-after-import](./src/rules/newline-after-import/README.md)
- [no-default-export](./src/rules/no-default-export/README.md)
- [no-duplicates](./src/rules/no-duplicates/README.md)
- [no-mutable-exports](./src/rules/no-mutable-exports/README.md)
- [no-named-default](./src/rules/no-named-default/README.md)
- [prefer-default-export](./src/rules/prefer-default-export/README.md)
## Motivation
I extend [my own ESLint config](https://github.com/9romise/eslint-config) from [`@antfu/eslint-config`](https://github.com/antfu/eslint-config).
Recently this config dropped [`eslint-plugin-import-x`](https://github.com/un-ts/eslint-plugin-import-x) because it introduces built-in binary resolvers and makes it heavy.
In a [discussion](https://github.com/9romise/eslint-import-resolver-oxc/issues/87#issuecomment-2945162572) about the built-in resolver, the maintainer plans to keep it as a dependency, which makes it impossible to keep the package lightweight.
But there are some useful rules and [some people (including me) want to bring the plugin back](https://github.com/antfu/eslint-config/issues/720).
## See Also
- [eslint-plugin-fast-import](https://npmx.dev/package/eslint-plugin-fast-import) - An ESLint plugin using a novel algorithm combined with the OXC Rust parser.
## Credits
- [eslint-plugin-import-x](https://github.com/un-ts/eslint-plugin-import-x) - source codes [MIT](https://github.com/un-ts/eslint-plugin-import-x/blob/master/LICENSE)
- [eslint-stylistic](https://github.com/eslint-stylistic/eslint-stylistic) - project structure and scripts [MIT](https://github.com/eslint-stylistic/eslint-stylistic/blob/main/LICENSE)
## License
[MIT](./LICENSE) License © 2025-PRESENT [Vida Xie](https://github.com/9romise)
+27
View File
@@ -0,0 +1,27 @@
import { RuleOptions } from "./rule-options.js";
import { ESLint, Linter, Rule } from "eslint";
//#region src/dts/configs.d.ts
declare const configs: {
/**
* The default recommended config in Flat Config Format
*/
recommended: Linter.Config;
/**
* Enable all rules, in Flat Config Format
*/
all: Linter.Config;
};
type Configs = typeof configs;
//#endregion
//#region src/dts/rules.d.ts
type RuleName<K extends string> = K extends `${string}/${infer Name}` ? RuleName<Name> : K;
type Rules = Required<{ [K in keyof RuleOptions as RuleName<K>]: Rule.RuleModule }>;
//#endregion
//#region src/dts/index.d.ts
declare const plugin: {
rules: Rules;
configs: ESLint.Plugin['configs'] & Configs;
};
//#endregion
export { type Configs, type RuleOptions, type Rules, plugin as default };
@@ -0,0 +1,67 @@
import { Linter } from "eslint";
//#region src/dts/rule-options.d.ts
interface RuleOptions {
/**
* Enforce or ban the use of inline type-only markers for named imports.
* @see https://github.com/9romise/eslint-plugin-import-lite/blob/main/src/rules/consistent-type-specifier-style/README.md
*/
'import-lite/consistent-type-specifier-style'?: Linter.RuleEntry<ImportLiteConsistentTypeSpecifierStyle>;
/**
* Ensure all exports appear after other statements.
* @see https://github.com/9romise/eslint-plugin-import-lite/blob/main/src/rules/exports-last/README.md
*/
'import-lite/exports-last'?: Linter.RuleEntry<[]>;
/**
* Ensure all imports appear before other statements.
* @see https://github.com/9romise/eslint-plugin-import-lite/blob/main/src/rules/first/README.md
*/
'import-lite/first'?: Linter.RuleEntry<ImportLiteFirst>;
/**
* Enforce a newline after import statements.
* @see https://github.com/9romise/eslint-plugin-import-lite/blob/main/src/rules/newline-after-import/README.md
*/
'import-lite/newline-after-import'?: Linter.RuleEntry<ImportLiteNewlineAfterImport>;
/**
* Forbid default exports.
* @see https://github.com/9romise/eslint-plugin-import-lite/blob/main/src/rules/no-default-export/README.md
*/
'import-lite/no-default-export'?: Linter.RuleEntry<[]>;
/**
* Forbid repeated import of the same module in multiple places.
* @see https://github.com/9romise/eslint-plugin-import-lite/blob/main/src/rules/no-duplicates/README.md
*/
'import-lite/no-duplicates'?: Linter.RuleEntry<ImportLiteNoDuplicates>;
/**
* Forbid the use of mutable exports with `var` or `let`.
* @see https://github.com/9romise/eslint-plugin-import-lite/blob/main/src/rules/no-mutable-exports/README.md
*/
'import-lite/no-mutable-exports'?: Linter.RuleEntry<[]>;
/**
* Forbid named default exports.
* @see https://github.com/9romise/eslint-plugin-import-lite/blob/main/src/rules/no-named-default/README.md
*/
'import-lite/no-named-default'?: Linter.RuleEntry<[]>;
/**
* Prefer a default export if module exports a single name or multiple names.
* @see https://github.com/9romise/eslint-plugin-import-lite/blob/main/src/rules/prefer-default-export/README.md
*/
'import-lite/prefer-default-export'?: Linter.RuleEntry<ImportLitePreferDefaultExport>;
}
/* ======= Declarations ======= */
// ----- import-lite/consistent-type-specifier-style -----
type ImportLiteConsistentTypeSpecifierStyle = [] | [("top-level" | "inline" | "prefer-top-level")]; // ----- import-lite/first -----
type ImportLiteFirst = [] | [("absolute-first" | "disable-absolute-first")]; // ----- import-lite/newline-after-import -----
type ImportLiteNewlineAfterImport = [] | [{
count?: number;
exactCount?: boolean;
considerComments?: boolean;
}]; // ----- import-lite/no-duplicates -----
type ImportLiteNoDuplicates = [] | [{
"prefer-inline"?: boolean;
}]; // ----- import-lite/prefer-default-export -----
type ImportLitePreferDefaultExport = [] | [{
target?: ("single" | "any");
}];
//#endregion
export { RuleOptions };
+44
View File
@@ -0,0 +1,44 @@
import { t as consistent_type_specifier_style_default } from "./rules/consistent-type-specifier-style.mjs";
import { t as exports_last_default } from "./rules/exports-last.mjs";
import { t as first_default } from "./rules/first.mjs";
import { t as newline_after_import_default } from "./rules/newline-after-import.mjs";
import { t as no_default_export_default } from "./rules/no-default-export.mjs";
import { t as no_duplicates_default } from "./rules/no-duplicates.mjs";
import { t as no_mutable_exports_default } from "./rules/no-mutable-exports.mjs";
import { t as no_named_default_default } from "./rules/no-named-default.mjs";
import { t as prefer_default_export_default } from "./rules/prefer-default-export.mjs";
//#region src/rules/index.ts
const rules = {
"consistent-type-specifier-style": consistent_type_specifier_style_default,
"exports-last": exports_last_default,
"first": first_default,
"newline-after-import": newline_after_import_default,
"no-default-export": no_default_export_default,
"no-duplicates": no_duplicates_default,
"no-mutable-exports": no_mutable_exports_default,
"no-named-default": no_named_default_default,
"prefer-default-export": prefer_default_export_default
};
//#endregion
//#region src/index.ts
const pluginName = "import-lite";
function generateConfig(name, filter = () => true) {
const ruleMeta = Object.entries(rules).filter(([ruleName, rule]) => !rule.meta?.deprecated && filter(ruleName, rule));
return {
name: `${pluginName}/${name}`,
plugins: { [pluginName]: {
name: pluginName,
rules
} },
rules: Object.fromEntries(ruleMeta.map(([ruleName]) => [`${pluginName}/${ruleName}`, "error"]))
};
}
var src_default = {
rules,
configs: {
recommended: generateConfig("recommended", (_, rule) => !!rule.meta?.docs?.recommended),
all: generateConfig("all")
}
};
//#endregion
export { src_default as default, pluginName };
@@ -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 };
+46
View File
@@ -0,0 +1,46 @@
import { n as isPlainObject, t as toMerged } from "./vender.mjs";
//#region src/utils/index.ts
function createRule({ name, create, defaultOptions = [], meta }) {
return {
create: ((context) => {
const optionsCount = Math.max(context.options.length, defaultOptions.length);
return create(context, Array.from({ length: optionsCount }, (_, i) => {
if (isPlainObject(context.options[i]) && isPlainObject(defaultOptions[i])) return toMerged(defaultOptions[i], context.options[i]);
return context.options[i] ?? defaultOptions[i];
}));
}),
defaultOptions,
meta: {
...meta,
docs: {
...meta.docs,
url: `https://github.com/9romise/eslint-plugin-import-lite/blob/main/src/rules/${name}/README.md`
}
}
};
}
//#endregion
//#region src/utils/ast.ts
function isCommaToken(token) {
return token.type === "Punctuator" && token.value === ",";
}
function getValue(node) {
switch (node.type) {
case "Identifier": return node.name;
case "Literal": return node.value;
default: throw new Error(`Unsupported node type: ${node.type}`);
}
}
//#endregion
//#region src/utils/compat.ts
function sourceType(context) {
if (context.parserOptions && "sourceType" in context.parserOptions) return context.parserOptions.sourceType;
if (context.languageOptions) return context.languageOptions.sourceType;
}
//#endregion
//#region src/utils/resolve.ts
function resolve(path) {
return path;
}
//#endregion
export { createRule as a, isCommaToken as i, sourceType as n, getValue as r, resolve as t };
+86
View File
@@ -0,0 +1,86 @@
//#region node_modules/.pnpm/es-toolkit@1.45.1/node_modules/es-toolkit/dist/predicate/isPrimitive.mjs
function isPrimitive(value) {
return value == null || typeof value !== "object" && typeof value !== "function";
}
//#endregion
//#region node_modules/.pnpm/es-toolkit@1.45.1/node_modules/es-toolkit/dist/predicate/isTypedArray.mjs
function isTypedArray(x) {
return ArrayBuffer.isView(x) && !(x instanceof DataView);
}
//#endregion
//#region node_modules/.pnpm/es-toolkit@1.45.1/node_modules/es-toolkit/dist/object/clone.mjs
function clone(obj) {
if (isPrimitive(obj)) return obj;
if (Array.isArray(obj) || isTypedArray(obj) || obj instanceof ArrayBuffer || typeof SharedArrayBuffer !== "undefined" && obj instanceof SharedArrayBuffer) return obj.slice(0);
const prototype = Object.getPrototypeOf(obj);
if (prototype == null) return Object.assign(Object.create(prototype), obj);
const Constructor = prototype.constructor;
if (obj instanceof Date || obj instanceof Map || obj instanceof Set) return new Constructor(obj);
if (obj instanceof RegExp) {
const newRegExp = new Constructor(obj);
newRegExp.lastIndex = obj.lastIndex;
return newRegExp;
}
if (obj instanceof DataView) return new Constructor(obj.buffer.slice(0));
if (obj instanceof Error) {
let newError;
if (obj instanceof AggregateError) newError = new Constructor(obj.errors, obj.message, { cause: obj.cause });
else newError = new Constructor(obj.message, { cause: obj.cause });
newError.stack = obj.stack;
Object.assign(newError, obj);
return newError;
}
if (typeof File !== "undefined" && obj instanceof File) return new Constructor([obj], obj.name, {
type: obj.type,
lastModified: obj.lastModified
});
if (typeof obj === "object") {
const newObject = Object.create(prototype);
return Object.assign(newObject, obj);
}
return obj;
}
//#endregion
//#region node_modules/.pnpm/es-toolkit@1.45.1/node_modules/es-toolkit/dist/predicate/isPlainObject.mjs
function isPlainObject(value) {
if (!value || typeof value !== "object") return false;
const proto = Object.getPrototypeOf(value);
if (!(proto === null || proto === Object.prototype || Object.getPrototypeOf(proto) === null)) return false;
return Object.prototype.toString.call(value) === "[object Object]";
}
//#endregion
//#region node_modules/.pnpm/es-toolkit@1.45.1/node_modules/es-toolkit/dist/_internal/isUnsafeProperty.mjs
function isUnsafeProperty(key) {
return key === "__proto__";
}
//#endregion
//#region node_modules/.pnpm/es-toolkit@1.45.1/node_modules/es-toolkit/dist/object/mergeWith.mjs
function mergeWith(target, source, merge) {
const sourceKeys = Object.keys(source);
for (let i = 0; i < sourceKeys.length; i++) {
const key = sourceKeys[i];
if (isUnsafeProperty(key)) continue;
const sourceValue = source[key];
const targetValue = target[key];
const merged = merge(targetValue, sourceValue, key, target, source);
if (merged !== void 0) target[key] = merged;
else if (Array.isArray(sourceValue)) if (Array.isArray(targetValue)) target[key] = mergeWith(targetValue, sourceValue, merge);
else target[key] = mergeWith([], sourceValue, merge);
else if (isPlainObject(sourceValue)) if (isPlainObject(targetValue)) target[key] = mergeWith(targetValue, sourceValue, merge);
else target[key] = mergeWith({}, sourceValue, merge);
else if (targetValue === void 0 || sourceValue !== void 0) target[key] = sourceValue;
}
return target;
}
//#endregion
//#region node_modules/.pnpm/es-toolkit@1.45.1/node_modules/es-toolkit/dist/object/toMerged.mjs
function toMerged(target, source) {
return mergeWith(clone(target), source, function mergeRecursively(targetValue, sourceValue) {
if (Array.isArray(sourceValue)) if (Array.isArray(targetValue)) return mergeWith(clone(targetValue), sourceValue, mergeRecursively);
else return mergeWith([], sourceValue, mergeRecursively);
else if (isPlainObject(sourceValue)) if (isPlainObject(targetValue)) return mergeWith(clone(targetValue), sourceValue, mergeRecursively);
else return mergeWith({}, sourceValue, mergeRecursively);
});
}
//#endregion
export { isPlainObject as n, toMerged as t };
+86
View File
@@ -0,0 +1,86 @@
{
"name": "eslint-plugin-import-lite",
"type": "module",
"version": "0.6.0",
"description": "A lightweight ESLint plugin for import/export linting",
"author": {
"name": "Vida Xie",
"url": "https://github.com/9romise",
"email": "vida_2020@163.com"
},
"license": "MIT",
"homepage": "https://github.com/9romise/eslint-plugin-import-lite#readme",
"repository": {
"type": "git",
"url": "git+https://github.com/9romise/eslint-plugin-import-lite.git"
},
"bugs": {
"url": "https://github.com/9romise/eslint-plugin-import-lite/issues"
},
"keywords": [
"eslint",
"plugin",
"eslintplugin",
"eslint-plugin",
"import",
"eslint-plugin-import"
],
"sideEffects": false,
"exports": {
".": {
"types": "./dist/dts/index.d.ts",
"default": "./dist/index.mjs"
},
"./package.json": "./package.json",
"./rule-options": "./dist/dts/rule-options.d.ts"
},
"types": "./dist/dts/index.d.ts",
"files": [
"dist"
],
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"peerDependencies": {
"eslint": "^9.0.0 || ^10.0.0"
},
"devDependencies": {
"@arethetypeswrong/core": "^0.18.2",
"@types/node": "^25.5.0",
"@typescript-eslint/parser": "^8.57.1",
"@typescript-eslint/utils": "^8.57.1",
"@vida0905/eslint-config": "^2.11.0",
"@vitest/coverage-v8": "^4.1.0",
"change-case": "^5.4.4",
"es-toolkit": "^1.45.1",
"eslint": "^10.0.3",
"eslint-typegen": "^2.3.1",
"eslint-vitest-rule-tester": "^3.1.0",
"json-schema-to-typescript-lite": "^15.0.0",
"nano-staged": "^0.9.0",
"publint": "^0.3.18",
"simple-git-hooks": "^2.13.1",
"tinyglobby": "^0.2.15",
"tsdown": "^0.21.4",
"tsx": "^4.21.0",
"typescript": "^5.9.3",
"vitest": "^4.1.0",
"eslint-plugin-import-lite": "0.6.0"
},
"simple-git-hooks": {
"pre-commit": "npx nano-staged"
},
"nano-staged": {
"*": "eslint --fix"
},
"scripts": {
"dev": "tsdown --watch",
"build": "tsdown",
"test": "vitest",
"lint": "eslint .",
"update": "tsx scripts/update && eslint . --fix",
"typecheck": "tsc --noEmit",
"check": "npm run lint && npm run typecheck",
"release": "npx bumpp"
}
}