Address review feedback on no-self-package-imports rule

- `visitImports` now also reads `exportKind` so `export type { … } from`
  statements are classified as type-only, fixing a false positive in the
  self-import rule (and correctly skipping them in `no-undeclared-imports`
  too).
- The reachability-graph regex in `no-self-package-imports` skips
  `import type` / `export type` edges so files reachable only via
  type-only re-exports aren't pulled into a runtime bundle and no longer
  get false-positive same-entry errors.
- `SOURCE_EXTENSIONS` now includes `.mts` and `.cts` so entries and
  barrels using those extensions are followed correctly.
- The ESLint plugin changeset wording matches the `error` severity of
  the recommended config.
- Adds regression fixtures and RuleTester cases for `export type …` at
  both entries and for a file only reachable via a type-only edge.

Signed-off-by: Marat Dyatko <maratd@spotify.com>
Made-with: Cursor
This commit is contained in:
Marat Dyatko
2026-04-23 16:28:20 +02:00
parent ab1cdbb9db
commit f1e26b8ed7
6 changed files with 57 additions and 5 deletions
@@ -28,7 +28,16 @@ const getPackages = require('../lib/getPackages');
* @property {string} sourceFile - The source file for the entry, relative to the package dir.
*/
const SOURCE_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs'];
const SOURCE_EXTENSIONS = [
'.ts',
'.tsx',
'.mts',
'.cts',
'.js',
'.jsx',
'.mjs',
'.cjs',
];
// Cache the per-package analysis across files lint invocations. The key is the
// absolute package dir; the value is a Map from absolute file path to the set
@@ -84,9 +93,11 @@ function resolveSourcePath(fromFile, specifier) {
}
// Matches `from '...'` specifiers in `import`/`export ... from` statements.
// Uses a non-greedy body so multi-line imports match cleanly.
// Uses a non-greedy body so multi-line imports match cleanly. The negative
// lookahead skips `import type` / `export type` statements because type-only
// edges are erased at runtime and can't pull files into a runtime bundle.
const FROM_SPEC_RE =
/(?:^|[\s;}])(?:import|export)\b[^"';]*?\bfrom\s*["']([^"']+)["']/gm;
/(?:^|[\s;}])(?:import|export)\b(?!\s+type\b)[^"';]*?\bfrom\s*["']([^"']+)["']/gm;
// Matches side-effect imports: `import '...';`.
const SIDE_EFFECT_IMPORT_RE = /(?:^|[\s;}])import\s*["']([^"']+)["']/gm;