Fix next.config.ts loading regression (exports is not defined in ES module scope) - #1051
Conversation
…ies from CJS plugin build
Next's TS config loader re-transpiles required .mjs files to CJS via SWC,
but Node evaluates .mjs files as ESM, so the transpiled code hits
'exports is not defined in ES module scope'. Bundle varlock into the
build-time entry points (like next-env-compat already does) so the CJS
output never requires an .mjs file. Also fixes require(esm) failures on
Node 22.0-22.11.
Convert { type X } imports to import type so rolldown drops the
side-effect require('varlock'), which was also inlining the entire
varlock index into next-env-compat (516KB -> 41KB).
Add a framework-test dev scenario covering next.config.ts (with new
deleteFiles harness support to drop the skeleton next.config.mjs).
|
The changes in this PR will be included in the next version bump.
|
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes covering the CommonJS build boundary, type-only import cleanup, and Next.js regression coverage.
- CJS build boundary: Bundles
varlockruntime imports into the build-time integration entries soplugin.cjsno longer loads ESM-only runtime files through Next's TypeScript config hook. - Type-only imports: Removes unintended runtime imports of the main
varlockentry and avoids bundling unrelated code intonext-env-compat.cjs. - Regression coverage: Adds
deleteFilesscenario isolation and exercises a realnext.config.tsthrough Next.js 15, 16, and canary dev startup and page rendering.
azure/gpt-5.6-sol | 𝕏
…hem into the nextjs plugin Bundling froze the plugin's copy of varlock runtime code at the integration's build time, so it could diverge from the installed varlock version. Instead, varlock now emits real .cjs builds of env, patch-console, patch-server-response, crypto, and exec-sync-varlock, exposed via the require condition. The nextjs plugin goes back to requiring the installed varlock, which is now hook-safe and works on Node without require(esm). This also covers the expo integration's CJS outputs, which had the same latent require(esm) exposure. next-env-compat stays bundled (required for Vercel serverless tracing, #584) but only carries runtime glue.
📦 Bundle size
dist/ only; native binaries are versioned separately and not counted here. |
There was a problem hiding this comment.
Important
The new CommonJS declaration build fails, so the varlock package cannot currently be built or released.
Reviewed changes since the prior Pullfrog review, covering the switch from integration-side bundling to package-level CommonJS runtime exports.
- Added CommonJS runtime builds: Added
.cjsbuilds and conditionalrequireexports for the five runtime entry points consumed by CommonJS integrations. - Restored external runtime loading: Removed
varlockbundling from the Next.js build-time entries so the plugin uses the installed package version. - Expanded release scope: Added a
varlockminor bump and updated the changeset to describe compatibility with Next's TypeScript config loader and older Node 22 releases.
azure/gpt-5.6-sol | 𝕏
varlock
@varlock/nextjs-integration
@varlock/native-helper-darwin
@varlock/native-helper-linux-arm64
@varlock/native-helper-linux-x64
@varlock/native-helper-win32-x64
commit: |
bun run --filter <pkg> build does not build workspace dependencies, and some builds need their dist output (varlock's d.ts bundling inlines @env-spec/utils's emitted declarations). This tripped up the automated PR review, which ran the varlock build in isolation and attributed the pre-existing dependency requirement to this PR's changes.




Fixes a regression from the tsdown migration (#1021): using
varlockNextConfigPluginin anext.config.tsfile fails withRoot cause
Next's
next.config.tsloader transpiles the config and everything it requires through arequire.extensionshook that converts ESM to CJS via SWC, then callsmod._compile. For.mjsfiles, Node'sModule._compileroutes toloadESMFromCJS, so the SWC-produced CJS code gets evaluated as ESM and the injectedexportsreferences blow up.Pre-tsdown this never triggered because varlock's dist was extensionless
.js. Post-tsdown,plugin.cjsdoesrequire('varlock/env')etc., and those entry points became ESM-only.mjsfiles. The trigger is varlock 1.17.1, independent of the integration version (verified all 4 published combos). The same requires also break outright on Node 22.0-22.11, whererequire(esm)is behind a flag.createRequirein the user's config does not work around it: the extension hooks are process-global.Fix
env,patch-console,patch-server-response,encrypt-env,exec-sync-varlock), exposed via therequirecondition. Requiring them from CJS stays CJS end to end, which is safe through Next's hook and on Node withoutrequire(esm). This also covers the expo integration's CJS outputs, which had the same latent exposure. Multiple instances coexisting with the ESM builds is already handled: env and redaction state live onglobalThisby design, and the patches guard via markers on the patched globals.next-env-compatstays bundled (required for Vercel serverless tracing, [BUG]: Cannot find module 'varlock', on Vercel #584) but only carries runtime glue; env loading always execs the installed CLI.import { type SerializedEnvGraph } from 'varlock'toimport type. The old form left a side-effectrequire("varlock")in the output that pulled the entire varlock index at runtime, and was inlining ~475KB of never-executed loader machinery intonext-env-compat.cjs(516KB -> 41KB).Tests
New framework-test dev scenario loads the app through a real
next.config.ts(runs on Next 15, 16, and canary), with newdeleteFilesharness support to drop the skeletonnext.config.mjsthat would otherwise shadow it. Verified the scenario fails against the pre-fix build and passes with the fix. Full Next 16 suite (both bundlers), expo suite, and varlock unit tests all pass.Also documents in AGENTS.md that single-package builds must go through turbo (
bunx turbo run buildfrom the package dir), sincebun run --filter <pkg> buildskips workspace dependency builds. That gap is what led the automated review to a false build-failure finding.