fix(payload): use named import for loadEnvConfig to fix CJS interop under raw tsx#16630
fix(payload): use named import for loadEnvConfig to fix CJS interop under raw tsx#16630chae4chae wants to merge 1 commit into
Conversation
…nder raw tsx
`@next/env` is pure CJS (no `"type"`, no `"exports"`, just
`main: "dist/index.js"`) and emits its API as named exports
(see `@next/env`'s `dist/index.d.ts`: `export declare function
loadEnvConfig(...)`). There is no real default export.
The current code:
import nextEnvImport from '@next/env'
const { loadEnvConfig } = nextEnvImport
only typechecks because of `esModuleInterop`, and only works at
runtime under bundlers (Next, esbuild with synthetic-default
enabled) that synthesize a default for CJS modules. Under raw
`tsx` invocation, `nextEnvImport` is undefined and the top-level
destructure throws:
TypeError: Cannot destructure property 'loadEnvConfig'
of 'import_env.default' as it is undefined.
at .../payload/dist/bin/loadEnv.js:3:9
This bricks every script that imports `payload` from outside
Next.js — including projects' own seed/migration scripts that
follow Payload v3's documented `getPayload({ config })` pattern,
because `loadEnv.js` is loaded transitively via
`payload/node` → `@payloadcms/drizzle/dist/utilities/blocksToJsonMigrator.js`
(or equivalent in the Mongo adapter).
Switching to a named import — `import { loadEnvConfig } from '@next/env'` —
matches the package's actual export shape, removes the unnecessary
destructure indirection, and works under both Next's bundler and
raw `tsx`. No behavior change otherwise.
Tested downstream by patching this file via `pnpm patch` in a
project; `getPayload()` then succeeds end-to-end under
`tsx scripts/<name>.ts`.
|
Pull Request titles must follow the Conventional Commits specification and have valid scopes. Unknown scope "payload" found in pull request title "fix(payload): use named import for loadEnvConfig to fix CJS interop under raw tsx". Scope must match one of: cpa, claude, codemod, db-*, db-d1-sqlite, db-mongodb, db-postgres, db-vercel-postgres, db-sqlite, db-d1-sqlite, drizzle, email-*, email-nodemailer, email-resend, eslint, evals, graphql, kv, kv-redis, live-preview, live-preview-react, live-preview-vue, next, payload-cloud, plugin-cloud, plugin-cloud-storage, plugin-ecommerce, plugin-form-builder, plugin-import-export, plugin-mcp, plugin-multi-tenant, plugin-nested-docs, plugin-redirects, plugin-search, plugin-sentry, plugin-seo, plugin-stripe, richtext-*, richtext-lexical, sdk, skills, storage-*, storage-azure, storage-gcs, storage-r2, storage-uploadthing, storage-vercel-blob, storage-s3, translations, ui, templates, examples(/(\w|-)+)?, deps. |
Summary
@next/envis pure CJS (no"type", no"exports", justmain: "dist/index.js") and emits its API as named exports —loadEnvConfigis not available on a default export. Today's code inloadEnv.ts:only typechecks because of
esModuleInterop, and only works at runtime under bundlers (Next, esbuild with synthetic-default enabled) that synthesize a default for CJS modules. Under rawtsx,nextEnvImportisundefinedand the top-level destructure throws:Switching to a named import —
import { loadEnvConfig } from '@next/env'— matches the package's actual.d.tsexport shape (export declare function loadEnvConfig(...)), removes the now-unnecessary destructure indirection, and works under both Next's bundler and rawtsx. No behavior change otherwise.Reproduction
Any script that imports
payloadfrom outside Next.js — e.g. a seed/migration script following the documentedgetPayload({ config })pattern — fails when invoked viatsx scripts/<name>.ts. The crash is reached transitively: the Postgres adapter pulls in@payloadcms/drizzle/dist/utilities/blocksToJsonMigrator.js, which top-level importspayload/node, which re-exportsloadEnvfrombin/loadEnv.js. SoloadEnv.jsevaluates eagerly and the top-level destructure throws before any user code runs.Verification
Tested downstream by applying this exact change via
pnpm patchon Payload v3.78.0 in a separate project. After patching,getPayload({ config })initializes cleanly undertsx scripts/<name>.tsand the script proceeds through schema pull, DB connection, and seed logic without further errors. Same source still works inside Next.js (no behavior change for the normal path).Diff
Net: 1 file, +1/-2 lines.