import { ESLint, Rule } from 'eslint'; /** * The default-configured `prefer-alias-imports` rule (uses `@/` -> `src`). * Exported on its own for consumers who register rules manually. */ declare const preferAliasImportsRule: Rule.RuleModule; /** * The unirend ESLint plugin. Register it in a flat config and enable the rule: * * ```js * import unirend from 'unirend/eslint-plugin'; * * export default [ * { * plugins: { unirend }, * rules: { 'unirend/prefer-alias-imports': 'error' }, * }, * ]; * ``` */ declare const plugin: ESLint.Plugin; /** * Per-invocation options for the `prefer-alias-imports` rule. Defaults match the * `@/*` -> `./src/*` alias in the generated tsconfig. */ interface PreferAliasImportsOptions { /** Directory the alias maps to, as a single path segment (default `"src"`). */ rootDir?: string; /** Alias prefix that stands in for `rootDir` (default `"@/"`). */ prefix?: string; } /** * Dependencies for {@link createPreferAliasImportsRule}, injectable so the rule * can be tested without walking the real filesystem. */ interface PreferAliasImportsDeps { /** Locate the boundary directory for a given importing-file directory. */ findBoundaryDir?: (fromDir: string) => string | null; } /** * Build the `prefer-alias-imports` ESLint rule. * * Flags relative imports that escape the importing file's project boundary (its * nearest tsconfig directory) and offers an autofix to the `@/` alias. Relative * imports that stay within the boundary are left alone, so it complements — not * fights — VSCode's `importModuleSpecifier: "project-relative"`: the editor * generates the right specifier, this rule guards hand-written/pasted ones. * * Unlike off-the-shelf rules, the boundary is derived from the actual tsconfig * layout rather than a single static root, so intra-app deep relative imports * (`../../components/Foo`) are permitted while cross-boundary ones are not. */ declare function createPreferAliasImportsRule(deps?: PreferAliasImportsDeps): Rule.RuleModule; /** * Inputs for {@link analyzeRelativeImport}. */ interface AnalyzeImportOptions { /** Absolute path of the file that contains the import. */ importerFile: string; /** The import specifier exactly as written in the source. */ importSource: string; /** * Absolute path of the directory that defines the import boundary — the * nearest `tsconfig.json` directory to {@link importerFile}. Relative imports * that resolve inside this directory are left alone; ones that escape it are * candidates for the alias. This mirrors how VSCode's * `importModuleSpecifier: "project-relative"` chooses between a relative path * and the alias. */ boundaryDir: string; /** * Directory the alias maps to, as a single path segment (default `"src"`). * Matches the `@/*` -> `./src/*` mapping in the generated tsconfig. */ rootDir?: string; /** Alias prefix that stands in for {@link rootDir} (default `"@/"`). */ prefix?: string; } /** * Result of {@link analyzeRelativeImport}. */ interface AnalyzeImportResult { /** * True when the import is relative, escapes the boundary, and a matching * alias path could be constructed. False for non-relative imports, imports * that stay within the boundary, or targets that fall outside `rootDir` * (which have no alias form and so are left untouched). */ shouldUseAlias: boolean; /** The aliased specifier to swap in, present only when `shouldUseAlias`. */ aliasedSource?: string; } /** * Decide whether a relative import should be rewritten to the `@/` alias. * * Pure and filesystem-free: callers supply the boundary directory (the nearest * tsconfig dir) so this can be unit-tested without touching disk. A relative * import is flagged only when it resolves *outside* the boundary directory and * the target lives under `rootDir` (so an alias path actually exists). Imports * that stay within the boundary — any depth of `./` or `../` inside the same * app — are intentionally left relative, matching the editor's * `project-relative` behavior. */ declare function analyzeRelativeImport(options: AnalyzeImportOptions): AnalyzeImportResult; /** * Clear the tsconfig-directory cache. * * The cache is process-global, which is ideal for a one-shot `eslint` CLI run * but means a long-lived language server won't notice a newly added/removed * `tsconfig.json` until it's cleared (editors typically restart the ESLint * worker on config changes). Exposed mainly for that case and for tests. */ declare function clearTsconfigDirCache(): void; /** * Walk up from `fromDir` and return the first directory that contains a * `tsconfig.json`, or `null` if none is found before the filesystem root. * * This locates the project boundary the same way VSCode's `project-relative` * import preference does — the nearest tsconfig to the importing file. Each * generated app ships its own `tsconfig.json`, so the boundary lands on the app * folder rather than collapsing to the repo root. * * Results are memoized (see {@link clearTsconfigDirCache}). `existsSync` is * injectable so the walk can be unit-tested without touching the real * filesystem. */ declare function findNearestTsconfigDir(fromDir: string, existsSync?: (path: string) => boolean): string | null; export { type AnalyzeImportOptions, type AnalyzeImportResult, type PreferAliasImportsDeps, type PreferAliasImportsOptions, analyzeRelativeImport, clearTsconfigDirCache, createPreferAliasImportsRule, plugin as default, findNearestTsconfigDir, plugin, preferAliasImportsRule };