Skip to content

feat(plugin-js): oxc-based import-graph resolver, phantom-dep detection - #362

Draft
raphaelvigee wants to merge 1 commit into
raphaelvigee/feat-plugin-js-m0-m1-installfrom
raphaelvigee/feat-plugin-js-m2-import-graph
Draft

feat(plugin-js): oxc-based import-graph resolver, phantom-dep detection#362
raphaelvigee wants to merge 1 commit into
raphaelvigee/feat-plugin-js-m0-m1-installfrom
raphaelvigee/feat-plugin-js-m2-import-graph

Conversation

@raphaelvigee

@raphaelvigee raphaelvigee commented Aug 5, 2026

Copy link
Copy Markdown
Member

M2 of the JS/TS heph plugin plan (part 2/6 of the stack).

Fixes the deferred optionalDependencies hard-fail (platform-restricted optional deps now silently skip instead of failing Provider::get), then adds a real import-graph resolver on top of M1's package.json-declaration wiring.

  • oxc_parser + oxc_resolver extract and resolve import/require/dynamic-import specifiers per real Node condition-set semantics (separate ESM/CJS/types resolvers), building two distinct graphs (runtime vs type-only edges).
  • Phantom-dependency detection: an import resolving into a package not in the declared-dependency closure (deps + devDeps + peerDeps) is a hard error naming the file/specifier/package, hermetic against a fresh checkout (no ambient node_modules required) via a bare-specifier name check.
  • Conformance corpus covering exports-map condition ordering, wildcard specificity, array fallbacks, null-blocked subpaths, self-referencing imports, and the "imports" field — cross-checked live against a real Node binary when present, self-gated otherwise.

Reviewed by feature-quality/code-quality/hermeticity. Four BLOCKERs found and fixed: the phantom-dep check being a no-op without ambient node_modules, peerDependencies never counted as declared, an oxc_resolver default reading the ambient NODE_PATH env var, and a resolved-but-unclassifiable edge silently passing instead of failing closed. Each has a regression test.

Explicitly deferred: import-equals require() and require.resolve() extraction, per-specifier type modifier detection, provider-lifetime resolver caching (fixed later in the stack), pinning a hermetic Node toolchain for the conformance corpus's live cross-check.

Test plan

  • cargo build -p plugin-js -p plugin-js-cdylib
  • cargo test -p plugin-js
  • cargo clippy -p plugin-js -p plugin-js-cdylib --all-targets -- -D warnings
  • cargo fmt --check -p plugin-js -p plugin-js-cdylib

Stack created with GitHub Stacks CLIGive Feedback 💬

M2 of the JS/TS plugin: fixes the deferred optionalDependencies hard-fail
(platform-restricted optional deps now silently skip instead of failing
Provider::get), then adds a real import-graph resolver on top of M1's
package.json-declaration wiring.

- oxc_parser + oxc_resolver extract and resolve import/require/dynamic-import
  specifiers per real Node condition-set semantics (separate ESM/CJS/types
  resolvers), building two distinct graphs (runtime vs type-only edges).
- Phantom-dependency detection: an import resolving into a package not in the
  declared-dependency closure (deps + devDeps + peerDeps) is a hard error
  naming the file/specifier/package, hermetic against a fresh checkout (no
  ambient node_modules required) via a bare-specifier name check.
- Conformance corpus (crates/plugin-js/src/pluginjs/conformance.rs) covering
  exports-map condition ordering, wildcard specificity, array fallbacks,
  null-blocked subpaths, self-referencing imports, and the "imports" field —
  cross-checked live against a real Node binary when present, self-gated
  otherwise.

Reviewed by feature-quality/code-quality/hermeticity. Four BLOCKERs found and
fixed: the phantom-dep check being a no-op without ambient node_modules,
peerDependencies never counted as declared, an oxc_resolver default reading
the ambient NODE_PATH env var, and a resolved-but-unclassifiable edge
silently passing instead of failing closed. Each has a regression test.

Explicitly deferred (named, not silent): import-equals require() and
require.resolve() extraction, per-specifier `type` modifier detection,
provider-lifetime resolver caching (currently rebuilt per Provider::get
call — a stated perf follow-up), and pinning a hermetic Node toolchain so
the conformance corpus's live cross-check is guaranteed to run in CI rather
than opportunistically.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M3wZfyPsG8stfRQuybLRjN
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant