Skip to content

Adopt eslint-plugin-import-x to fix resolver loading (/typescript + /legacy)#49

Merged
kpal81xd merged 1 commit into
mainfrom
fix/import-resolver-v3
Jun 3, 2026
Merged

Adopt eslint-plugin-import-x to fix resolver loading (/typescript + /legacy)#49
kpal81xd merged 1 commit into
mainfrom
fix/import-resolver-v3

Conversation

@kpal81xd

@kpal81xd kpal81xd commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Wave 1 of the v3 rollout (#48) surfaced two resolver bugs that hit consumers on a normal install.

Bug A — /typescript: "invalid interface loaded as resolver"

eslint-plugin-import loads resolvers by name, relative to each linted file. The bundled eslint-import-resolver-typescript isn't always hoisted to the consumer's top level, so the lookup fails and falls back to require('typescript') (the compiler) → "invalid interface". A pilot (observer) saw 55 false import/* errors.

Bug B — /legacy: valid package subpaths flagged

/legacy enabled import/no-unresolved with no resolver, so the default node resolver (no exports-map support) flagged valid subpath imports like @playcanvas/eslint-config/legacy — the migration's own import line.

Fix — switch to eslint-plugin-import-x

Vanilla eslint-plugin-import (stale since mid-2025) only resolves resolvers by name from the linted file, which is the root of both bugs. eslint-plugin-import-x (the maintained fork) supports import-x/resolver-next, which lets the config pass the resolver object directly (import { createTypeScriptImportResolver }) — so resolution never depends on install hoisting. This is the setup eslint-import-resolver-typescript's own README blesses for flat config.

Per-tier namespace, by design:

  • /typescript — native import-x: rules are import-x/*, import-x/resolver-next: [createTypeScriptImportResolver({ alwaysTryTypes: true })]. A fresh strict tier (not a drop-in), so the new namespace is the clean choice; consumers that override import rules use import-x/*.
  • /legacy — import-x under the import namespace so rule names stay import/* and existing v2 consumer overrides keep working (preserves the drop-in promise for JS consumers like the engine). import-x/resolver-next: [createNodeResolver(), createTypeScriptImportResolver()] — node stays primary, exports-aware resolver only catches subpaths node misses.
  • Bumps typescript-eslint^8.60.1 (import-x's @typescript-eslint/utils ^8.56 peer); drops eslint-plugin-import.

TypeScript import-rule tuning (/typescript only)

  • Disable the TS-redundant, resolver-heavy rules: import-x/named, import-x/namespace, import-x/default, import-x/no-named-as-default-memberoff. The type checker enforces these more accurately, and they're the slowest import rules — a real win on the large TS repos in later waves. import-x/no-unresolved stays on (catches path typos / asset imports).
  • Type-import hygiene (pairs with @typescript-eslint/consistent-type-imports): import-x/consistent-type-specifier-style: ['error', 'prefer-top-level'], import-x/no-duplicates: ['warn', { prefer-inline: false }] — keep type-only imports as separate top-level import type statements.
  • alwaysTryTypes: true on the resolver so @types-only packages resolve (matches /legacy).

(/legacy keeps named/namespace/default on — it lints JS+JSDoc, where the type checker isn't doing that work.)

Validation

  • Build (tsc), prettier --check, self-lint (dogfoods /typescript): green — resolves an exports subpath (eslint-config-prettier/flat) + the typescript-eslint exports package, zero import-x/no-unresolved, zero "invalid interface". print-config confirms the per-tier namespaces and rule states.
  • All entry points load; publint: all good.
  • Pilot re-lint (observer/pcui-graph/canvas-mock) lands when they bump to beta.4. observer's import/no-unresolved: off override becomes import-x/no-unresolved: off (I'll handle it).

Target a 3.0.0-beta.4 cut from this so the held Wave 1 pilots can move off the buggy beta.3.

@kpal81xd kpal81xd force-pushed the fix/import-resolver-v3 branch from 786d0de to 1ed443a Compare June 3, 2026 13:08
@kpal81xd kpal81xd changed the title Fix import-resolver loading for /typescript and /legacy Adopt eslint-plugin-import-x to fix resolver loading (/typescript + /legacy) Jun 3, 2026
@kpal81xd kpal81xd force-pushed the fix/import-resolver-v3 branch from 1ed443a to 4ec52d7 Compare June 3, 2026 13:14
Vanilla eslint-plugin-import loads resolvers by name relative to each linted
file, so the bundled eslint-import-resolver-typescript wasn't found when npm
didn't hoist it to the consumer top level: /typescript fell back to the
`typescript` compiler ("invalid interface" — 55 false errors in a pilot), and
/legacy's import/no-unresolved flagged valid package subpaths (e.g. the
package's own /legacy entry) the default node resolver can't read.

Switch to eslint-plugin-import-x, which supports `import-x/resolver-next` and
lets the config pass the resolver object directly (import it, don't resolve it
by name) — so resolution never depends on install layout.

- /typescript: native import-x — rules are `import-x/*`, resolver-next holds the
  imported createTypeScriptImportResolver(). A fresh strict tier (not a drop-in),
  so the new namespace is fine.
- /legacy: import-x registered under the `import` namespace so rule names stay
  `import/*` and existing v2 consumer overrides keep working (preserves the
  drop-in promise). node resolver stays primary with the exports-aware resolver
  as a fallback, so resolution is unchanged for JS consumers.

Also tune /typescript's import rules for TS: turn off named/namespace/default/
no-named-as-default-member (the type checker enforces these more accurately and
they're the slow resolver-heavy rules; no-unresolved stays on), enforce separate
top-level `import type` (consistent-type-specifier-style), set no-duplicates
prefer-inline:false, and pass `alwaysTryTypes` so @types-only packages resolve.

Bump typescript-eslint to ^8.60.1 for import-x's @typescript-eslint/utils peer;
drop eslint-plugin-import.
@kpal81xd kpal81xd force-pushed the fix/import-resolver-v3 branch from 4ec52d7 to 547dee2 Compare June 3, 2026 13:34
@kpal81xd kpal81xd self-assigned this Jun 3, 2026
@kpal81xd kpal81xd added the bug Something isn't working label Jun 3, 2026
@kpal81xd kpal81xd merged commit eea4b20 into main Jun 3, 2026
3 checks passed
@kpal81xd kpal81xd deleted the fix/import-resolver-v3 branch June 3, 2026 14:07
kpal81xd added a commit that referenced this pull request Jun 3, 2026
Vanilla eslint-plugin-import loads resolvers by name relative to each linted
file, so the bundled eslint-import-resolver-typescript wasn't found when npm
didn't hoist it to the consumer top level: /typescript fell back to the
`typescript` compiler ("invalid interface" — 55 false errors in a pilot), and
/legacy's import/no-unresolved flagged valid package subpaths (e.g. the
package's own /legacy entry) the default node resolver can't read.

Switch to eslint-plugin-import-x, which supports `import-x/resolver-next` and
lets the config pass the resolver object directly (import it, don't resolve it
by name) — so resolution never depends on install layout.

- /typescript: native import-x — rules are `import-x/*`, resolver-next holds the
  imported createTypeScriptImportResolver(). A fresh strict tier (not a drop-in),
  so the new namespace is fine.
- /legacy: import-x registered under the `import` namespace so rule names stay
  `import/*` and existing v2 consumer overrides keep working (preserves the
  drop-in promise). node resolver stays primary with the exports-aware resolver
  as a fallback, so resolution is unchanged for JS consumers.

Also tune /typescript's import rules for TS: turn off named/namespace/default/
no-named-as-default-member (the type checker enforces these more accurately and
they're the slow resolver-heavy rules; no-unresolved stays on), enforce separate
top-level `import type` (consistent-type-specifier-style), set no-duplicates
prefer-inline:false, and pass `alwaysTryTypes` so @types-only packages resolve.

Bump typescript-eslint to ^8.60.1 for import-x's @typescript-eslint/utils peer;
drop eslint-plugin-import.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant