diff --git a/dev-packages/rollup-utils/npmHelpers.mjs b/dev-packages/rollup-utils/npmHelpers.mjs index 0014616f8071..43e11e36f19c 100644 --- a/dev-packages/rollup-utils/npmHelpers.mjs +++ b/dev-packages/rollup-utils/npmHelpers.mjs @@ -94,6 +94,11 @@ export function makeBaseNPMConfig(options = {}) { // (We don't need it, so why waste the bytes?) freeze: false, + // Assume externals are ESM-shaped (`__esModule` + `.default`), which our own `@sentry/*` + // packages satisfy via `esModule: 'if-default-prop'`. This keeps `import * as x` a live + // reference to the real module rather than an `_interopNamespace` copy — instrumentation code + // relies on that to monkey-patch modules like `fs` in place. Packages that pull in bare-CJS + // third-party deps (no `.default`) override this per-module (see server-utils). interop: 'esModule', }, diff --git a/packages/nextjs/src/config/webpack.ts b/packages/nextjs/src/config/webpack.ts index 94db1cca621d..30532b354360 100644 --- a/packages/nextjs/src/config/webpack.ts +++ b/packages/nextjs/src/config/webpack.ts @@ -433,7 +433,7 @@ export function constructWebpackConfigFunction({ // Orchestrion code-transform loader — Node server runtime only, never the edge compilation if (runtime === 'server' && userSentryOptions._experimental?.useDiagnosticsChannelInjection) { - newConfig.plugins.push(sentryOrchestrionWebpackPlugin() as WebpackPluginInstance); + newConfig.plugins.push(sentryOrchestrionWebpackPlugin() as unknown as WebpackPluginInstance); } return newConfig; diff --git a/packages/server-utils/rollup.npm.config.mjs b/packages/server-utils/rollup.npm.config.mjs index 5f02614f716d..0ff1a23ba534 100644 --- a/packages/server-utils/rollup.npm.config.mjs +++ b/packages/server-utils/rollup.npm.config.mjs @@ -42,6 +42,14 @@ export default [ exports: 'named', // set preserveModules to true because we don't want to bundle everything into one file. preserveModules: true, + // `@apm-js-collab/code-transformer-bundler-plugins` ships CJS entries as bare + // `module.exports = fn` with no `__esModule`/`.default`. The repo default + // `interop: 'esModule'` assumes ESM-shaped externals and would dereference a nonexistent + // `.default`, so a default import compiles to `codeTransformer.default(...)` → "not a + // function". Use 'auto' for just these so Rollup emits its interop helper. Scoped here (not + // repo-wide) because 'auto' also turns `import * as x` into a copy, which breaks in-place + // monkey-patching that other packages (e.g. the OTel fs instrumentation) depend on. + interop: id => (id?.startsWith('@apm-js-collab/code-transformer-bundler-plugins') ? 'auto' : 'esModule'), }, }, }), diff --git a/packages/server-utils/src/orchestrion/bundler/webpack.ts b/packages/server-utils/src/orchestrion/bundler/webpack.ts index 04eb56a2c6b9..860a1cc046bc 100644 --- a/packages/server-utils/src/orchestrion/bundler/webpack.ts +++ b/packages/server-utils/src/orchestrion/bundler/webpack.ts @@ -5,6 +5,7 @@ import { createRequire } from 'node:module'; import { dirname } from 'node:path'; import type { InstrumentationConfig } from '@apm-js-collab/code-transformer'; import { SENTRY_INSTRUMENTATIONS } from '../config'; +import codeTransformer from '@apm-js-collab/code-transformer-bundler-plugins/webpack'; // Both branches use `createRequire` (never alias the CJS `require`) so bundlers consuming this // module don't emit a "Critical dependency" warning. @@ -46,10 +47,6 @@ export function getSentryInstrumentations(): InstrumentationConfig[] { * does NOT inject the `__SENTRY_ORCHESTRION__.bundler` marker — that would disable the runtime * module hook, which externalized packages still need (hybrid setup). */ -export function sentryOrchestrionWebpackPlugin(): unknown { - const mod = getOrchestrionRequire()('@apm-js-collab/code-transformer-bundler-plugins/webpack') as { - default?: (options: { instrumentations: InstrumentationConfig[] }) => unknown; - }; - const codeTransformerWebpack = mod.default ?? (mod as unknown as NonNullable); - return codeTransformerWebpack({ instrumentations: SENTRY_INSTRUMENTATIONS }); +export function sentryOrchestrionWebpackPlugin(): ReturnType { + return codeTransformer({ instrumentations: SENTRY_INSTRUMENTATIONS }); }