[GH-ISSUE #1761] [Bug] A sibling node_modules file overrides package exports during JavaScript analysis #593

Closed
opened 2026-10-15 18:26:28 +00:00 by bsakaguchi · 0 comments
Owner

Originally created by @shoyann on GitHub (Oct 11, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/1761

Before submitting

  • I searched open and closed issues and pull requests for related reports.
  • This reproduction contains no credentials or private target data.

Area

Evidence, artifacts, or reconstruction — JavaScript application analysis.

Observed behavior

When an inventoried package has an exports field and there is also a sibling node_modules/fixture.js, analyze-javascript-application resolves require("fixture") to the sibling file before consulting the package exports.

On current main, the public CLI reports:

{
  "module_link_kind": "require",
  "specifier": "fixture",
  "resolved_path": "node_modules/fixture.js",
  "resolution_status": "resolved"
}

Node 24.18.0 selects node_modules/fixture/actual.cjs from the same files. The same wrong edge appears for an ESM import. Replacing the exports map with { ".": null } still produces a resolved sibling edge in REA, while Node rejects the request with ERR_PACKAGE_PATH_NOT_EXPORTED.

Reproduction

From a built source checkout (npm ci && npm run build:cached):

mkdir -p exports-repro/node_modules/fixture
printf '%s\n' '{"name":"repro","main":"main.cjs"}' > exports-repro/package.json
printf '%s\n' 'const value = require("fixture");' > exports-repro/main.cjs
printf '%s\n' 'module.exports = "sibling";' > exports-repro/node_modules/fixture.js
printf '%s\n' '{"name":"fixture","exports":"./actual.cjs"}' > exports-repro/node_modules/fixture/package.json
printf '%s\n' 'module.exports = "export";' > exports-repro/node_modules/fixture/actual.cjs

node -p 'require.resolve("fixture", {paths: [require("node:path").resolve("exports-repro")]})'
node scripts/rea.mjs analyze-javascript-application "$PWD/exports-repro" --format json

Compare the Node result with the application graph edge whose properties.specifier is fixture.

Also reproduced with an explicitly blocked root, a missing selected export, and an invalid export target. Those cases return Node errors but REA resolves the sibling file.

Expected behavior

Apply package exports before sibling-file fallback. A selected export must resolve to its exact target; blocked, missing, and invalid exports must retain the corresponding unresolved outcome. Legacy CommonJS sibling-file priority should remain when exports is absent or null.

The Node 24.18.0 resolution algorithm runs LOAD_PACKAGE_EXPORTS before LOAD_AS_FILE in each node_modules directory.

Impact and scope

Static module edges can name a file the actual runtime never loads, including a successful-looking edge for a request the runtime rejects. This affects the shared JavaScript reconstruction path. The reproduction analyzes inert local source; no native provider is involved.

REA version

6.4.0, source main 6ff4d4a.

Runtime and platform

Node.js 24.18.0, npm 11.16.0, Ubuntu on WSL, x64.

Interface and earliest failure stage

Executed through the public CLI and the shared reconstruction workflow. Earliest incorrect stage: JavaScript artifact path resolution before graph construction. MCP uses this same workflow; transport regression coverage will accompany the fix.

Analysis

bareModuleCandidate in src/application/javascript/JavaScriptArtifactPathResolution.ts returns the root direct-file candidate before resolvePackageSpecifier reads the package metadata.

I have reproduced the issue and am preparing a focused fix with real Node resolver comparisons and CLI/MCP regression coverage.

Originally created by @shoyann on GitHub (Oct 11, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/1761 ### Before submitting - [x] I searched open and closed issues and pull requests for related reports. - [x] This reproduction contains no credentials or private target data. ### Area Evidence, artifacts, or reconstruction — JavaScript application analysis. ### Observed behavior When an inventoried package has an `exports` field and there is also a sibling `node_modules/fixture.js`, `analyze-javascript-application` resolves `require("fixture")` to the sibling file before consulting the package exports. On current main, the public CLI reports: ```json { "module_link_kind": "require", "specifier": "fixture", "resolved_path": "node_modules/fixture.js", "resolution_status": "resolved" } ``` Node 24.18.0 selects `node_modules/fixture/actual.cjs` from the same files. The same wrong edge appears for an ESM import. Replacing the exports map with `{ ".": null }` still produces a resolved sibling edge in REA, while Node rejects the request with `ERR_PACKAGE_PATH_NOT_EXPORTED`. ### Reproduction From a built source checkout (`npm ci && npm run build:cached`): ```sh mkdir -p exports-repro/node_modules/fixture printf '%s\n' '{"name":"repro","main":"main.cjs"}' > exports-repro/package.json printf '%s\n' 'const value = require("fixture");' > exports-repro/main.cjs printf '%s\n' 'module.exports = "sibling";' > exports-repro/node_modules/fixture.js printf '%s\n' '{"name":"fixture","exports":"./actual.cjs"}' > exports-repro/node_modules/fixture/package.json printf '%s\n' 'module.exports = "export";' > exports-repro/node_modules/fixture/actual.cjs node -p 'require.resolve("fixture", {paths: [require("node:path").resolve("exports-repro")]})' node scripts/rea.mjs analyze-javascript-application "$PWD/exports-repro" --format json ``` Compare the Node result with the application graph edge whose `properties.specifier` is `fixture`. Also reproduced with an explicitly blocked root, a missing selected export, and an invalid export target. Those cases return Node errors but REA resolves the sibling file. ### Expected behavior Apply package exports before sibling-file fallback. A selected export must resolve to its exact target; blocked, missing, and invalid exports must retain the corresponding unresolved outcome. Legacy CommonJS sibling-file priority should remain when exports is absent or null. The [Node 24.18.0 resolution algorithm](https://nodejs.org/download/release/v24.18.0/docs/api/modules.html#all-together) runs `LOAD_PACKAGE_EXPORTS` before `LOAD_AS_FILE` in each `node_modules` directory. ### Impact and scope Static module edges can name a file the actual runtime never loads, including a successful-looking edge for a request the runtime rejects. This affects the shared JavaScript reconstruction path. The reproduction analyzes inert local source; no native provider is involved. ### REA version 6.4.0, source main `6ff4d4a`. ### Runtime and platform Node.js 24.18.0, npm 11.16.0, Ubuntu on WSL, x64. ### Interface and earliest failure stage Executed through the public CLI and the shared reconstruction workflow. Earliest incorrect stage: JavaScript artifact path resolution before graph construction. MCP uses this same workflow; transport regression coverage will accompany the fix. ### Analysis `bareModuleCandidate` in `src/application/javascript/JavaScriptArtifactPathResolution.ts` returns the root direct-file candidate before `resolvePackageSpecifier` reads the package metadata. I have reproduced the issue and am preparing a focused fix with real Node resolver comparisons and CLI/MCP regression coverage.
bsakaguchi 2026-10-15 18:26:28 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#593
No description provided.