Skip to content

fix: resolve require() in ES modules with the require condition - #626

Open
RClaudiuM wants to merge 1 commit into
vercel:mainfrom
RClaudiuM:fix/621-require-in-esm-cjs-resolve
Open

RClaudiuM wants to merge 1 commit into
vercel:mainfrom
RClaudiuM:fix/621-require-in-esm-cjs-resolve

Conversation

@RClaudiuM

Copy link
Copy Markdown

Fixes #621

What

A require() call inside an ES module (through module.createRequire, or the global require with mixedModules) was resolved with the import condition. emitDependency resolved every specifier in deps with cjsResolve = !isESM, and in an ES module deps held both the static import/export ... from declarations and the require() calls. For a dual package whose exports subpath has separate import and require targets, nft traced the ESM build while Node loads the CommonJS one, so the deployed function fails with MODULE_NOT_FOUND.

Fix

  • analyze now adds static import and export ... from sources to imports, next to dynamic import() (and module.register), which are already resolved with the import condition.
  • deps now only holds require-like references (require(), createRequire, require.resolve, special cases, wildcard requires), so emitDependency always resolves it with the require condition.

Relative and absolute paths resolve the same way under both conditions, so this only changes bare and # specifiers reached through require() in an ES module.

Tests

New unit fixture esm-require-exports-conditions: an ES module imports dual/imported and requires dual/required through createRequire. Each subpath has separate import and require targets.

  • Before: traces esm/imported.js and esm/required.js (the test fails).
  • After: traces esm/imported.js and cjs/required.js.

The repro from the issue (createRequire + require("dual/sub")), with mixedModules both true and false:

before: node_modules/dual/esm/sub.js node_modules/dual/package.json
after:  node_modules/dual/cjs/sub.js node_modules/dual/package.json

pnpm prettier-check, pnpm build and pnpm test pass locally. The exceptions are the bcrypt, ffmpeg-static and leveldown integration tests: they fail the same way on main here because I installed without running native build scripts.

A require() call in an ES module (through module.createRequire, or the
global require with mixedModules) was resolved with the "import" condition,
because every specifier in `deps` was resolved as an import when the
containing file was ESM. For a dual package whose exports subpath has
separate import and require targets, the ESM build was traced while Node
loads the CommonJS one.

Static import and export-from declarations now go into `imports`, next to
dynamic import(), and `deps` only holds require-like references, which are
always resolved with the "require" condition.

Fixes vercel#621
@RClaudiuM
RClaudiuM requested review from a team, icyJoseph, ijjk and styfle as code owners November 7, 2026 21:28
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.

require() inside an ES module is resolved with the import condition, tracing the wrong file of a dual package

1 participant