From 62b922d76375dc2a4d33d9348fb3059f116efc8c Mon Sep 17 00:00:00 2001 From: Abdelrahman Awad Date: Thu, 5 Nov 2026 14:00:30 -0500 Subject: [PATCH] fix(node): Read syncBuiltinESMExports off the module namespace so Deno can load @sentry/node Deno before 2.8.2 has syncBuiltinESMExports only on the default export of node:module, so the named import from #26086 failed to link and every ESM import of @sentry/node threw. --- .../integrations/fs/vendored/instrumentation.ts | 9 +++++++-- packages/node/test/integrations/fs.test.ts | 17 +++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 packages/node/test/integrations/fs.test.ts diff --git a/packages/node/src/integrations/fs/vendored/instrumentation.ts b/packages/node/src/integrations/fs/vendored/instrumentation.ts index a6cbf2aef79c..b878fff7f79d 100644 --- a/packages/node/src/integrations/fs/vendored/instrumentation.ts +++ b/packages/node/src/integrations/fs/vendored/instrumentation.ts @@ -26,7 +26,7 @@ import { withActiveSpan, } from '@sentry/core'; import * as fsNamespace from 'fs'; -import { syncBuiltinESMExports } from 'module'; +import * as moduleNamespace from 'module'; import { promisify } from 'util'; import { CALLBACK_FUNCTIONS, PROMISE_FUNCTIONS, SYNC_FUNCTIONS } from './constants'; import type { FMember, FPMember, FsInstrumentationConfig, GenericFunction } from './types'; @@ -36,6 +36,11 @@ import { indexFs } from './utils'; // behind its default export instead. The CJS build has no `.default` and already gets the mutable object. const fs = (fsNamespace as { default?: typeof fsNamespace }).default ?? fsNamespace; +// Deno before 2.8.2 has `syncBuiltinESMExports` only on the default export of `node:module`, so a named import fails to link. +type SyncBuiltinESMExports = { syncBuiltinESMExports?: () => void }; +const moduleExports = moduleNamespace as unknown as SyncBuiltinESMExports & { default?: SyncBuiltinESMExports }; +const syncBuiltinESMExports = moduleExports.default?.syncBuiltinESMExports ?? moduleExports.syncBuiltinESMExports; + const SPAN_ORIGIN = 'auto.file.fs'; const SPAN_OP = FILE; @@ -312,7 +317,7 @@ export function enableFsInstrumentation(config: FsInstrumentationConfig = {}): v // Named ESM imports (`import { readFile } from 'fs'`) are snapshots of the CJS exports until synced. try { - syncBuiltinESMExports(); + syncBuiltinESMExports?.(); } catch { // Cloudflare workerd exports `syncBuiltinESMExports` but throws `ERR_METHOD_NOT_IMPLEMENTED` when it is called. } diff --git a/packages/node/test/integrations/fs.test.ts b/packages/node/test/integrations/fs.test.ts new file mode 100644 index 000000000000..dfe107feac42 --- /dev/null +++ b/packages/node/test/integrations/fs.test.ts @@ -0,0 +1,17 @@ +import { describe, expect, it, vi } from 'vitest'; + +const { syncBuiltinESMExports } = vi.hoisted(() => ({ syncBuiltinESMExports: vi.fn() })); + +// Deno before 2.8.2 has `syncBuiltinESMExports` only on the default export of `node:module`, so a named import of it fails to link. +vi.mock('module', () => ({ default: { syncBuiltinESMExports } })); +vi.mock('fs', () => ({ default: { promises: {}, realpath: {}, realpathSync: {} } })); + +describe('enableFsInstrumentation', () => { + it('syncs the builtin ESM exports when `module` has no named `syncBuiltinESMExports` export', async () => { + const { enableFsInstrumentation } = await import('../../src/integrations/fs/vendored/instrumentation'); + + enableFsInstrumentation(); + + expect(syncBuiltinESMExports).toHaveBeenCalledTimes(1); + }); +});