Releases: uhyo/import-lint
Release list
v0.1.9
New: imports of non-TS files are checked, with a nonTsFiles access-level option
Imports that resolve to an internal file ImportLint cannot parse as a TS/JS module — CSS modules, JSON, SVG, and so on — are now checked by the package-access rule instead of being silently skipped as unresolved (#6). When tsc-style resolution fails and the specifier itself names a non-TS extension (./Button.module.css), the resolver retries with the plain bundler-style algorithm and accepts the result only if it lands on a non-TS file. TS/JS semantics are untouched: a styles.d.css.ts declaration still wins over the raw .css beside it, and — like tsc — an extensionless specifier never resolves to a non-TS file. As a side effect, the old "unrecognized file extension, skipping" stderr noise for these imports is gone, and import-lint graph lists the non-TS files it found.
A non-TS file has no JSDoc tags, so its exports get the config's defaultImportability, exactly like an untagged TS export — unless the new nonTsFiles option assigns them a level explicitly:
Keys are globs matched against the exporting file's resolved project-relative path (not the import specifier); values map an export name to "public" | "package" | "private". "*" covers every export except default, following the ES spec's export * convention, so default must be assigned by name. Entries are tried in written order and the first match for the imported name wins — put specific patterns first. See the new Non-TS files section in the concepts guide for the full semantics — also covered by the built-in import-lint docs concepts / import-lint docs config.
Note for existing users: this can surface new errors. Under defaultImportability: "package", a cross-package CSS-module or JSON import that was previously skipped now errors. If you want the old don't-check behavior (which is also what eslint-plugin-import-access does — this is now the second deliberate divergence documented in the README's migration section), add:
"nonTsFiles": { "**": { "default": "public", "*": "public" } }Lint behavior is unchanged for imports that resolved before — TS/JS checking and output are identical to v0.1.8, and the resolver's hot paths are unaffected (the fallback only runs when tsc-style resolution fails on a specifier naming a non-TS extension).
Full Changelog: v0.1.8...v0.1.9
v0.1.8
New: tsconfig customConditions are honored during resolution
If your tsconfig sets compilerOptions.customConditions, ImportLint now applies those conditions when matching conditional package.json exports and #-subpath imports entries — so resolution agrees with what tsc and a bundler configured with the same conditions see. Previously the resolver only matched the fixed set types/import/require/node (plus default), so an imports map gated on a custom condition — like Vite lib-mode's #src/* mapping sources under a project-specific condition (#4) — fell through to its default branch or failed to resolve entirely, and those imports were silently skipped (or flagged by --report-unresolved) instead of being checked.
The conditions are read from the same tsconfig that already feeds the resolver (config tsconfig, --tsconfig, or the auto-discovered <project root>/tsconfig.json), with TypeScript's own semantics: extends chains are followed (including TS 5.0 array extends and bare-package targets resolved through node_modules), and customConditions is a non-merged option — the nearest definition wins. Watch mode picks up tsconfig edits automatically, as before.
Lint behavior is unchanged for projects whose tsconfig sets no customConditions — checking and output are identical to v0.1.7.
Full Changelog: v0.1.7...v0.1.8
v0.1.7
New: suppress a violation with a directive comment
You can now acknowledge a violation at the import site without fixing it (yet) — the ImportLint equivalent of ESLint's eslint-disable-next-line:
// import-lint-disable-next-line -- migrating gradually, see #123
import { issueToken } from "../auth/token";
import { issueToken } from "../auth/token"; // import-lint-disable-lineimport-lint-disable-next-line suppresses diagnostics on the following line; import-lint-disable-line suppresses them on its own line. Both work as // line comments or /* ... */ block comments. A bare directive suppresses everything on the target line; an optional rule-name list restricts it (package-access for access violations, unresolved for --report-unresolved warnings). Anything after a -- separator is a free-form justification, ignored by the parser. Directives match lines, so in a multiline import statement, place the directive inside the braces, directly above the violating specifier.
Migrating from eslint-plugin-import-access: replace // eslint-disable-next-line import-access/jsdoc with // import-lint-disable-next-line. See Suppressing a violation with a comment in the README; the built-in docs (import-lint docs fixing, import-lint explain) and the agent skill cover the new directives too.
Lint behavior is unchanged for code that contains no directives — checking and output are identical to v0.1.6 otherwise.
Full Changelog: v0.1.6...v0.1.7
v0.1.6
New: built-in docs — the docs and explain subcommands
import-lint docs [topic] prints a condensed built-in guide: concepts (the mental model), config (every option with its default), or fixing (how to fix a violation, in order of preference). import-lint explain <message-id> explains one diagnostic — what it means and the recommended fixes — for package, package:reexport, private, private:reexport, and unresolved (the ids reported in --format json's messageId field). Run either with no argument for an index.
The text ships inside the binary, so it always matches the installed version. It's written for humans and equally aimed at AI coding agents working in a repository that uses ImportLint.
New: a copy-pastable agent skill
skills/import-lint/ is a ready-made Agent Skill that teaches AI coding agents ImportLint's mental model, CLI usage, and the fix procedure for violations — including steering them away from tag-everything-@public "fixes". Copy it into your project (e.g. .claude/skills/import-lint/ for Claude Code, or wherever your agent tool looks for skills); it defers to the docs/explain subcommands above for version-matched depth.
import-lint init now ends with tips pointing at the built-in docs and the skill.
Docs
The Adoption guide was reworked, with smaller adjustments to the Concepts guide and Tutorial.
No lint-behavior changes — checking and output are identical to v0.1.5.
Full Changelog: v0.1.5...v0.1.6
v0.1.5
Fixed: spurious errors on Windows
The Windows build could report package-access violations for imports that are perfectly legal — including imports from a file's own directory. Two root causes, both fixed:
- Path canonicalization used
std::fs::canonicalize, which returns\\?\-verbatim paths on Windows; inside those,/isn't treated as a path separator, breaking the rule engine's directory comparisons against resolver-produced paths. Canonicalization now goes throughdunceand yields plainC:\paths. - The internal
node_relativehelper split paths on/only, sopackageDirectoryglob matching and the nested-subpackage check misbehaved on backslash paths. It's now built onPath::components().
All platforms (Linux, macOS, Windows) are now covered by CI on every push — the Windows and macOS suites run for the first time in this release's history, and are green.
No behavior changes on Linux or macOS. Windows users of any earlier version should upgrade.
Full Changelog: v0.1.4...v0.1.5
v0.1.4
Breaking change: init --preset removed
import-lint init now always scaffolds the one recommended config (formerly the standard preset) and is fully non-interactive — the --preset flag, the interactive picker, and the gradual/monorepo presets are gone. v0.1.3 is the last release that accepts (and, in scripts/CI, requires) --preset; a script passing --preset <name> now exits 2. Just drop the flag: import-lint init.
Why: the scaffolded config's packageDirectory fallback (files outside every *.package directory share one project-root package) makes it suit gradual adoption too — rename one directory at a time. The former gradual and monorepo presets live on as small config edits, documented in the Adoption guide.
Full Changelog: v0.1.3...v0.1.4
v0.1.3
v0.1.2
vscode-v0.1.0
ImportLint VS Code extension 0.1.0. See editors/vscode/CHANGELOG.md.