Skip to content

fix: Keep the rewrites when next.config.js is an ES module - #81

Merged
razor-x merged 1 commit into
mainfrom
claude/nextlove-v4-parser-upgrade-xzn95k
Aug 12, 2026
Merged

fix: Keep the rewrites when next.config.js is an ES module#81
razor-x merged 1 commit into
mainfrom
claude/nextlove-v4-parser-upgrade-xzn95k

Conversation

@razor-x

@razor-x razor-x commented Aug 12, 2026

Copy link
Copy Markdown
Member

The bug

copyNextjsConfig loads next.config.js with a require and only falls back to import when that throws. For an ES module config the require no longer throws, and returns a module namespace rather than the config, in two cases:

  • Since Node.js 22.12, require of an ES module succeeds and returns a namespace.
  • The nsm bin registers esbuild-runner, which hooks .js outside node_modules and transpiles an ES module to CommonJS, marking the result with __esModule.

typeof nextConfig.rewrites === "function" is then false on the namespace, so rewrites is dropped from the generated .nsm/next.config.ts. There is no error — the build succeeds and the config comes out as:

export default { default: { /* ... */ } }

A project that relies on a rewrite to reach its routes then 404s on every request with nothing to explain it. This is what seamapi/fake-seam-connect hit when moving to Node.js 22.

The fix

Unwrap the default export whenever a namespace is loaded, whichever way it was loaded — Symbol.toStringTag === "Module" covers Node's namespace, __esModule covers esbuild's CommonJS output, and the import fallback goes through the same unwrap.

Two smaller correctness fixes in the same function:

  • Resolve rewrites into a copy of the config. A module namespace is frozen, so assigning onto it fails.
  • Pass a file URL to the import fallback, so it also resolves on Windows.

Tests

The loading is extracted into an exported loadNextjsConfig so it can be tested directly, with fixtures for a CommonJS object config, a CommonJS function config and an ES module config. The sample project used by the existing tests has a CommonJS config, which is why this path was never covered.

The new ES module test fails on main and passes with this change. All 12 tests pass, and prettier --check is clean.

Verified end to end against seamapi/fake-seam-connect on Node.js 22.22: with this fix and no workaround in the consumer, nsm build emits the rewrites correctly and the routes resolve.

🤖 Generated with Claude Code

https://claude.ai/code/session_01V6bQ6zQqwT1vq9NkrG1UTm


Generated by Claude Code

copyNextjsConfig loads next.config.js with a require and only falls back to
import when that throws. The require returns a module namespace rather than
the config for an ES module in two cases: since Node.js 22.12 require of an
ES module succeeds and returns a namespace, and the nsm bin registers
esbuild-runner, which transpiles an ES module to CommonJS and marks the result
with __esModule.

Reading the config off the namespace left rewrites undefined, so it was
dropped from the generated .nsm/next.config.ts. A project that relies on a
rewrite to reach its routes then 404s on every request, with no error to
explain it.

Unwrap the default export whenever a namespace is loaded, whichever way it was
loaded. Resolve rewrites into a copy of the config, since a module namespace is
frozen, and pass a file URL to the import fallback so it also works on Windows.

Extract the loading into loadNextjsConfig and cover the CommonJS object,
CommonJS function and ES module configs with tests. The sample project used by
the existing tests has a CommonJS config, so the ES module path was untested.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V6bQ6zQqwT1vq9NkrG1UTm
razor-x pushed a commit to seamapi/fake-seam-connect that referenced this pull request Aug 12, 2026
The workaround regenerated .nsm/next.config.ts after nsm build to restore the
rewrites nsm drops. Apply the upstream fix with patch-package instead, so nsm
writes the config correctly in the first place.

Run patch-package from prebuild:nsm rather than a lifecycle script. postinstall
runs when the published package is installed as a dependency, where
patch-package is absent, and prepare runs in the production install stage of
the Dockerfile, which omits devDependencies. Only nsm build reads the patched
file, so applying it there covers every caller.

Remove the patch once a nextjs-server-modules release includes the fix.
seamapi/nextjs-server-modules#81

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V6bQ6zQqwT1vq9NkrG1UTm
@razor-x
razor-x merged commit 8c02fb9 into main Aug 12, 2026
2 checks passed
@razor-x
razor-x deleted the claude/nextlove-v4-parser-upgrade-xzn95k branch August 12, 2026 17:54
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.

2 participants