Context
Discovered while fixing #1927 (native engine had no workspace-awareness in resolve_import_path_inner).
Description
resolveViaExports() in src/domain/graph/resolve.ts resolves a bare specifier (e.g. import "some-pkg/sub") through a package's package.json exports field — conditional exports, subpath patterns ("./lib/*"), array fallbacks, etc. It's used for:
- Plain
node_modules bare specifiers in resolveImportPathJS().
- As the first attempt inside
resolveViaWorkspace() for monorepo workspace packages (before falling back to main/source/index-file resolution).
There is no equivalent function anywhere in crates/codegraph-core. The native engine's resolve_import_path_inner (crates/codegraph-core/src/domain/graph/resolve.rs) only implements resolve_via_alias (tsconfig/jsconfig path aliases) for non-relative specifiers — a bare specifier pointing at a package that only exposes its entry via the exports field (no matching main/index-file convention) resolves incorrectly or not at all under native, while WASM/JS resolves it correctly.
The #1927 fix (native workspace-awareness) explicitly does not attempt to close this gap — resolve_via_workspace() in Rust skips the exports-field lookup and goes straight to main/source/index-file resolution, so workspace packages that rely solely on a conditional exports map (no main field, or a main that differs from the real entry) may still resolve differently between engines.
Suggested next step
Port resolveViaExports()'s logic (parseBareSpecifier — already mirrored in Rust as of #1927 — findPackageDir, getPackageExports, resolveCondition, resolveSubpathMap, matchSubpathPattern) into crates/codegraph-core/src/domain/graph/resolve.rs, wire it into resolve_non_relative_import (and resolve_via_workspace's root/subpath branches) the same way the JS engine does, and add a crates/codegraph-core ↔ src/domain/graph/resolve.ts parity test fixture exercising a package with only an exports field (no main).
Source
Found during: fix for #1927 (native monorepo workspace package import resolution)
Context
Discovered while fixing #1927 (native engine had no workspace-awareness in
resolve_import_path_inner).Description
resolveViaExports()insrc/domain/graph/resolve.tsresolves a bare specifier (e.g.import "some-pkg/sub") through a package'spackage.jsonexportsfield — conditional exports, subpath patterns ("./lib/*"), array fallbacks, etc. It's used for:node_modulesbare specifiers inresolveImportPathJS().resolveViaWorkspace()for monorepo workspace packages (before falling back tomain/source/index-file resolution).There is no equivalent function anywhere in
crates/codegraph-core. The native engine'sresolve_import_path_inner(crates/codegraph-core/src/domain/graph/resolve.rs) only implementsresolve_via_alias(tsconfig/jsconfig path aliases) for non-relative specifiers — a bare specifier pointing at a package that only exposes its entry via theexportsfield (no matchingmain/index-file convention) resolves incorrectly or not at all under native, while WASM/JS resolves it correctly.The #1927 fix (native workspace-awareness) explicitly does not attempt to close this gap —
resolve_via_workspace()in Rust skips theexports-field lookup and goes straight tomain/source/index-file resolution, so workspace packages that rely solely on a conditionalexportsmap (nomainfield, or amainthat differs from the real entry) may still resolve differently between engines.Suggested next step
Port
resolveViaExports()'s logic (parseBareSpecifier— already mirrored in Rust as of #1927 —findPackageDir,getPackageExports,resolveCondition,resolveSubpathMap,matchSubpathPattern) intocrates/codegraph-core/src/domain/graph/resolve.rs, wire it intoresolve_non_relative_import(andresolve_via_workspace's root/subpath branches) the same way the JS engine does, and add acrates/codegraph-core↔src/domain/graph/resolve.tsparity test fixture exercising a package with only anexportsfield (nomain).Source
Found during: fix for #1927 (native monorepo workspace package import resolution)