fix(server-runtime-injection): Keep ES modules working on Deno - #24669
Conversation
size-limit report 📦
|
055e26b to
540f4fe
Compare
540f4fe to
c909f07
Compare
c909f07 to
29a2d60
Compare
29a2d60 to
12e0fce
Compare
3197ab3 to
65bccc8
Compare
65bccc8 to
0991af4
Compare
isaacs
left a comment
There was a problem hiding this comment.
This is straightforwardly correct. Closes the loop that we started with the JSON fix. TBH, I'm not sure why we didn't do that then. Oh well, probably thought we could get away without the added overhead.
Definitely add a comment to remove it in v12 (or 13 or whenever Deno ages out of needing it).
| * `'json'` or `'module'`. Without the format, Deno's CJS loader compiles JSON as JavaScript | ||
| * (`SyntaxError: Unexpected token ':'`), and the transform treats an ES module as CommonJS and | ||
| * injects a `require()` into it (`ReferenceError: require is not defined`). The format is restored | ||
| * on the `nextLoad` result, so the transform sees it too. Only Deno needs this. |
There was a problem hiding this comment.
Worth adding a comment here and in the PR description as well, that it's only needed as long as we support Deno versions that do not have the fix in denoland/deno#36849.
We're already gating on the presence/lack of a format, so we don't need any version sniffing, I don't think. But it'd be nice to know when we can cut the fix out entirely, certainly not before v12.
Actually, come to think of it, could also add a // todo(v12): evaluate if this is still needed for supported Deno versions so we know to circle back.
e9f557c to
92a3e17
Compare
| if (url.startsWith('file:') && url.endsWith('.js') && getPackageType(dirname(fileURLToPath(url))) === 'module') { | ||
| return 'module'; | ||
| } | ||
| return undefined; | ||
| } | ||
|
|
||
| /** | ||
| * Deno's `nextLoad` reports no `format` for a `.json` file, where Node reports `'json'`. With any | ||
| * load hook installed, Deno's CJS loader then compiles the JSON as JavaScript and `require()` of it | ||
| * throws `SyntaxError: Unexpected token ':'`. Restoring the format is enough, and only Deno needs | ||
| * it: on Node the format is never missing. | ||
| * Deno's `nextLoad` reports no `format` for a `.json` file or an ES module, where Node reports | ||
| * `'json'` or `'module'`. Without the format, Deno's CJS loader compiles JSON as JavaScript | ||
| * (`SyntaxError: Unexpected token ':'`), and the transform treats an ES module as CommonJS and |
There was a problem hiding this comment.
Bug: The getPackageType function incorrectly caches undefined and fails to check parent directories if reading an existing package.json file fails.
Severity: LOW
Suggested Fix
When readFileSync fails within the try...catch block, the function should not cache undefined. Instead, it should fall back to checking the parent directory, similar to the logic used when existsSync returns false.
Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.
Location: packages/server-runtime-injection/src/register.ts#L63-L72
Potential issue: The function `getPackageType` can incorrectly cache `undefined` for a
directory. This occurs if a `package.json` file exists but is unreadable (e.g., due to a
race condition or permissions issue). When `readFileSync` fails, the `catch` block sets
the module `type` to `undefined`. This `undefined` value is then cached for the
directory. The logic does not proceed to check parent directories for a `package.json`
file in this failure case, which can lead to incorrect module type resolution for
subsequent lookups in that directory.
Did we get this right? 👍 / 👎 to inform future reviews.
Deno's module hooks report no `format` for an ES module, so the orchestrion transform treated it as CommonJS and injected a `require()` that throws when the module loads. This broke `@sentry/node` on Deno for the ESM builds of libraries it instruments, such as the AI SDKs and `postgres`. The Deno load hook now restores the format of `.mjs` files and of `.js` files in a `"type": "module"` package, the same way it already did for JSON. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
92a3e17 to
6248b55
Compare
With
deno run --preload=npm:@sentry/deno/import, an instrumented library that ships an ES module build crashed on import withReferenceError: require is not defined. Deno's module hooks report noformatfor an ES module, so the orchestrion transform treated it as CommonJS and injected arequire()into it. This hit the ESM builds of the AI SDKs andpostgres, among others.The Deno load hook now restores the format of
.mjsfiles and of.jsfiles in a"type": "module"package, the same way it already did for JSON (#24412). It restores it on thenextLoadresult, because the transform reads the format there.Running the shared Node integration suites on Deno found this. The next PR in the stack runs them in CI; the Deno-only orchestrion suites did not catch it because they publish fake channel messages instead of loading the real libraries.
Part of #24052 and #23897.
🤖 Generated with Claude Code