Skip to content

fix(meta): define build banner globals as writable - #78

Merged
joshunrau merged 3 commits into
mainfrom
fix/writable-dirname-banner
Aug 5, 2026
Merged

fix(meta): define build banner globals as writable#78
joshunrau merged 3 commits into
mainfrom
fix/writable-dirname-banner

Conversation

@joshunrau

Copy link
Copy Markdown
Collaborator

Summary

Fixes the crash reported in #77. The production build banner in src/meta/build.ts defined __dirname, __filename, and require on globalThis as non-writable. Some dependencies ship an esbuild-style banner of their own that assigns to globalThis['__dirname']. Once esbuild inlines such a runtime into our bundle, that assignment executes in module scope — where a non-writable property throws instead of failing silently:

TypeError: Cannot assign to read only property '__dirname' of object '#<Object>'

The process dies before the app bootstraps. Flipping the three properties to writable: true makes those later assignments harmless overwrites — in Prisma's case with an identical value, since both resolve to the dirname of the executing bundle.

Why we hadn't seen this

@prisma/client changed its ESM runtime banner from a module-local const __dirname to globalThis['__dirname'] = …. libnest pins 6.9.0, which still uses the harmless module-local form, so no build in this repo or in our apps has ever inlined the assigning banner. The bug is latent in every libnest release until an app's import graph pulls in a newer Prisma runtime — which is exactly how it surfaced downstream, on OpenDataCapture main.

One correction to the issue: the assignment landed in 6.19.0, not 6.19.3. Verified against the published tarballs — 6.19.0, 6.19.1, 6.19.2, 6.19.3 and current 7.x all carry it; 6.9.0 does not. 6.19.3 is just where the reporter happened to hit it.

Nothing shielded us: src/meta/plugins/prisma.ts only appends a PRISMA_QUERY_ENGINE_LIBRARY define and copies the engine binary — the runtime .mjs is not in the external list, so it does get inlined.

Scope

Only src/meta/build.ts is affected. The writable: false at src/meta/dev.ts:24 that the issue also points at is unrelated — it applies to user-supplied config.globals, not __dirname/__filename, and dev mode doesn't bundle, so Prisma's runtime loads as its own module where the assignment succeeds. Left as-is.

In practice only __dirname is implicated: Prisma's __filename and require are module-scoped consts that esbuild renames on inline. All three are flipped anyway, since Prisma is the instance at hand and not the only possible one.

Testing

Adds src/meta/__tests__/globals-banner.test.ts, which bundles a dependency carrying the upstream banner and boots the result in a subprocess. Confirmed non-vacuous — reverting the fix makes it fail with the exact TypeError from the issue.

npm run lint clean; full suite passes except example/app.test.ts, which fails identically on a clean tree here (local mongod port conflict, exit code 48) and is unrelated to this change.

Closes #77

joshunrau and others added 3 commits August 4, 2026 19:36
The production build banner defined __dirname, __filename, and require on
globalThis with writable: false. Some dependencies ship an esbuild-style
banner of their own that assigns to globalThis['__dirname'] — @prisma/client
does as of 6.19.3, having previously used a module-local const. Once esbuild
inlines such a runtime into the bundle, that assignment runs in module scope,
where a non-writable property throws rather than failing silently:

    TypeError: Cannot assign to read only property '__dirname' of object '#<Object>'

The process dies before the app bootstraps. Flipping the three properties to
writable: true makes those later assignments harmless overwrites; in Prisma's
case with an identical value, since both resolve to the dirname of the
executing bundle.

Adds a regression test that bundles a dependency carrying the upstream banner
and boots the result in a subprocess.

Fixes #77

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The globalThis['__dirname'] assignment in @prisma/client's ESM runtime banner
landed in 6.19.0, not 6.19.3 as the linked issue reports; 6.19.3 is just where
it was first hit downstream. Verified across 6.19.0 through 6.19.3 and 7.x,
all of which carry the assignment, against 6.9.0 which uses a module-local
const.

Also renames a local in the regression test to satisfy cSpell.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@codecov

codecov Bot commented Aug 5, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (080794b) to head (8abe453).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff            @@
##              main       #78   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           62        62           
  Lines          738       739    +1     
  Branches       125       125           
=========================================
+ Hits           738       739    +1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@joshunrau
joshunrau merged commit 724620d into main Aug 5, 2026
3 checks passed
@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown

🎉 This PR is included in version 8.4.1 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

build banner defines non-writable globalThis.__dirname, crashing bundles that inline @prisma/client's ESM runtime

1 participant