You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
NFT does not walk ESM conditional exports branches for @swc/helpers #580
@swc/helpers has conditional exports (CJS default, ESM import)
NFT follows only the CJS branch
In Node ESM context ("type": "module"), the runtime resolves the import condition → missing ESM file → crash
Minimal repro: create a Next.js project with outputFileTracing: true, build locally, inspect .nft.json for any file that imports from @swc/helpers/_/_interop_require_default
Expected: both cjs/_interop_require_default.cjs and esm/_interop_require_default.js are traced
Actual: only cjs is traced
Workaround: outputFileTracingIncludes: { "*": ["./node_modules/@swc/helpers/esm/**"] }
We're also hitting this in a Next.js 15 app on Vercel using Node 22.x. In our case it's @reduxjs/toolkit (transitive dependency via recharts) rather than @swc/helpers, but seems to be the same root cause where nft traces the CJS path but Node resolves to the ESM entry at runtime.
Runtime resolves to redux-toolkit.modern.mjs, which then imports from redux/dist/redux.mjs, redux-thunk/dist/redux-thunk.mjs, etc. — all of which have the same CJS/ESM split in their exports maps. None of the ESM files are included in the SSR bundle.
This started after a deploy today, which I'm guessing was our first deploy with Node 22.22.3 rolled out on Vercel. We confirmed by rolling back to a deploy from last week (which continued to work) vs reverting the change we deployed today and redeploying, resulting in the same error. The relevant Node changes appear to be nodejs/node#59679 (separate cache for require(esm) in imported CJS) and nodejs/node#62080 (path normalization in finalizeResolution).
We worked around it two ways:
outputFileTracingIncludes covering the full dist/** for each affected package
Dynamically importing the affected components to keep them out of the SSR bundle entirely
The differentiator is the importer's module system. CJS require() makes NFT walk only the default (CJS) condition of the subpath's exports map. Under Node 22.22.3+ (nodejs/node#59679, nodejs/node#62080), the same require() at runtime resolves via the import condition because @swc/helpers/package.json declares "type": "module" — so the ESM file is what actually gets loaded. NFT and Node's resolver disagree, and the ESM sibling is missing from the lambda bundle.
This matches @chanson's @reduxjs/toolkit observation above — same pattern, different package.
Activity
styfle commentedon May 18, 2026
Please create a minimal repo without Next.js so we can verify if the bug is in Next.js or not
chanson commentedon May 18, 2026
We're also hitting this in a Next.js 15 app on Vercel using Node 22.x. In our case it's @reduxjs/toolkit (transitive dependency via recharts) rather than @swc/helpers, but seems to be the same root cause where nft traces the CJS path but Node resolves to the ESM entry at runtime.
@reduxjs/toolkit exports map:
Runtime resolves to redux-toolkit.modern.mjs, which then imports from redux/dist/redux.mjs, redux-thunk/dist/redux-thunk.mjs, etc. — all of which have the same CJS/ESM split in their exports maps. None of the ESM files are included in the SSR bundle.
This started after a deploy today, which I'm guessing was our first deploy with Node 22.22.3 rolled out on Vercel. We confirmed by rolling back to a deploy from last week (which continued to work) vs reverting the change we deployed today and redeploying, resulting in the same error. The relevant Node changes appear to be nodejs/node#59679 (separate cache for require(esm) in imported CJS) and nodejs/node#62080 (path normalization in finalizeResolution).
We worked around it two ways:
Diego4523BJJ commentedon May 18, 2026
Minimal repro without Next.js, as requested.
package.json{ "name": "nft-swc-helpers-esm-bug-repro", "version": "1.0.0", "private": true, "scripts": { "trace:cjs": "node trace.mjs entry.cjs", "trace:esm": "node trace.mjs entry.mjs" }, "dependencies": { "@swc/helpers": "0.5.21", "@vercel/nft": "^0.27.0" } }entry.cjs(mimics Next.js / SWC compiled output for Node Lambda)entry.mjs(control — Node ESM context)trace.mjsRun:
npm run trace:cjsoutput (current@vercel/nftbehaviour):npm run trace:esmoutput (no bug):The differentiator is the importer's module system. CJS
require()makes NFT walk only thedefault(CJS) condition of the subpath's exports map. Under Node 22.22.3+ (nodejs/node#59679,nodejs/node#62080), the samerequire()at runtime resolves via theimportcondition because@swc/helpers/package.jsondeclares"type": "module"— so the ESM file is what actually gets loaded. NFT and Node's resolver disagree, and the ESM sibling is missing from the lambda bundle.This matches @chanson's
@reduxjs/toolkitobservation above — same pattern, different package.styfle commentedon May 21, 2026
You're using nft@0.27.0 which is quite old. Try upgrading to nft@1.5.0 and it works as expected.