From abb7bb7f5fe94e4b5c587f4e44586cb1386f6c37 Mon Sep 17 00:00:00 2001 From: JPeer264 Date: Tue, 27 Oct 2026 10:34:22 -0600 Subject: [PATCH] fix(server-utils): Fix orchestrion source maps of instrumented modules `@apm-js-collab/code-transformer-bundler-plugins` 0.7.4 gave the combined input source map to the code transformer once `outputOptions` had run, and Rolldown calls `outputOptions` before the transforms. The transformer then added a second source `/` (for example `ioredis/built/Redis.js`) without `sourcesContent`. That file does not exist, so source map flattening (for example in `@sentry/sveltekit`) failed for the whole chunk. 0.7.5 gives the real file path to the transformer (code-transformer 0.19.0) and no longer gives it the input map, so the map of an instrumented module only names its file in `node_modules`. This also fixes the source name of instrumented modules in webpack. Co-Authored-By: Claude Opus 5.5 --- packages/server-utils/package.json | 2 +- .../src/orchestrion/bundler/webpack.ts | 7 ++- .../test/orchestrion/config.test.ts | 2 +- .../test/orchestrion/sourcemap.test.ts | 59 +++++++++++++++++++ pnpm-lock.yaml | 25 ++++++-- 5 files changed, 85 insertions(+), 10 deletions(-) create mode 100644 packages/server-utils/test/orchestrion/sourcemap.test.ts diff --git a/packages/server-utils/package.json b/packages/server-utils/package.json index c4fcc740ec4f..042c4701da7e 100644 --- a/packages/server-utils/package.json +++ b/packages/server-utils/package.json @@ -101,7 +101,7 @@ "@sentry/core": "11.9.0" }, "devDependencies": { - "@apm-js-collab/code-transformer-bundler-plugins": "^0.7.4", + "@apm-js-collab/code-transformer-bundler-plugins": "^0.7.5", "@neondatabase/serverless": "1.2.0", "@sentry-internal/rollup-utils": "11.9.0", "@types/node": "^20.19.0", diff --git a/packages/server-utils/src/orchestrion/bundler/webpack.ts b/packages/server-utils/src/orchestrion/bundler/webpack.ts index 4aed6df60460..dec13a661026 100644 --- a/packages/server-utils/src/orchestrion/bundler/webpack.ts +++ b/packages/server-utils/src/orchestrion/bundler/webpack.ts @@ -16,8 +16,11 @@ import { externalEntryMatchesModule, externalizedModulesWarning, orchestrionTran // Explicitly annotated with the vendored types so the emitted declaration doesn't reference // `@apm-js-collab/code-transformer-bundler-plugins` — a bundled devDependency consumers don't have. -export const serializeInstrumentations: (configs: AnyInstrumentationConfig[]) => SerializableInstrumentationConfig[] = - serializeInstrumentationsImpl; +// Upstream also allows a function in `transform`. The vendored configs only allow a string there, +// and the serializer copies `transform` unchanged, so its output only contains strings too. +export const serializeInstrumentations = serializeInstrumentationsImpl as ( + configs: AnyInstrumentationConfig[], +) => SerializableInstrumentationConfig[]; export type { SerializableInstrumentationConfig } from '../apmTypes'; export { getOrchestrionLoaderPath, resolveOrchestrionRuntimeRequest }; diff --git a/packages/server-utils/test/orchestrion/config.test.ts b/packages/server-utils/test/orchestrion/config.test.ts index 3d9a76415b9c..0385405c1573 100644 --- a/packages/server-utils/test/orchestrion/config.test.ts +++ b/packages/server-utils/test/orchestrion/config.test.ts @@ -1,4 +1,4 @@ -import type { InstrumentationConfig } from '@apm-js-collab/code-transformer-bundler-plugins/core'; +import type { InstrumentationConfig } from '../../src/orchestrion/apmTypes'; import { describe, expect, it } from 'vitest'; import { getInstrumentedModuleNames, diff --git a/packages/server-utils/test/orchestrion/sourcemap.test.ts b/packages/server-utils/test/orchestrion/sourcemap.test.ts new file mode 100644 index 000000000000..6b9a2d738994 --- /dev/null +++ b/packages/server-utils/test/orchestrion/sourcemap.test.ts @@ -0,0 +1,59 @@ +import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'; +import { tmpdir } from 'node:os'; +import { join } from 'node:path'; +import { build, type Plugin, type Rollup } from 'vite'; +import { afterAll, beforeAll, describe, expect, it } from 'vitest'; +import type { InstrumentationConfig } from '../../src/orchestrion/apmTypes'; +import { sentryOrchestrionPlugin as sentryOrchestrionRollupPlugin } from '../../src/orchestrion/bundler/rollup'; +import { sentryOrchestrionPlugin as sentryOrchestrionVitePlugin } from '../../src/orchestrion/bundler/vite'; + +const instrumentations: InstrumentationConfig[] = [ + { + channelName: 'query', + module: { name: 'fake-db', versionRange: '>=1.0.0', filePath: 'index.js' }, + functionQuery: { expressionName: 'query', kind: 'Sync' }, + }, +]; + +describe('orchestrion bundler plugins source maps', () => { + let root: string; + + beforeAll(() => { + root = mkdtempSync(join(tmpdir(), 'orchestrion-sourcemap-')); + const packageDir = join(root, 'node_modules', 'fake-db'); + mkdirSync(packageDir, { recursive: true }); + writeFileSync(join(packageDir, 'package.json'), JSON.stringify({ name: 'fake-db', version: '1.0.0' })); + writeFileSync(join(packageDir, 'index.js'), 'exports.query = function query(sql) {\n return sql;\n};\n'); + writeFileSync( + join(root, 'server.js'), + "import { query } from 'fake-db';\nexport const run = () => query('SELECT 1');\n", + ); + }); + + afterAll(() => rmSync(root, { recursive: true, force: true })); + + // https://github.com/getsentry/sentry-javascript/issues/25329 + it.each([ + ['vite', () => sentryOrchestrionVitePlugin({ instrumentations })], + ['rollup', () => sentryOrchestrionRollupPlugin({ instrumentations }) as Plugin], + ])('maps the transformed module only to its file in node_modules (%s plugin)', async (_name, createPlugin) => { + const output = (await build({ + root, + configFile: false, + logLevel: 'silent', + plugins: [createPlugin()], + ssr: { noExternal: ['fake-db'] }, + build: { + ssr: 'server.js', + sourcemap: true, + write: false, + rollupOptions: { external: ['@sentry/server-utils'] }, + }, + })) as Rollup.RollupOutput; + + const chunk = output.output[0]; + + expect(chunk.code).toContain('orchestrion:fake-db:query'); + expect(chunk.map?.sources).toEqual(['../node_modules/fake-db/index.js', '../server.js']); + }); +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 534a75fb921d..333b96a66511 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2204,8 +2204,8 @@ importers: version: link:../core devDependencies: '@apm-js-collab/code-transformer-bundler-plugins': - specifier: ^0.7.4 - version: 0.7.4(esbuild@0.28.1)(rollup@4.62.3)(vite@8.3.1(@types/node@20.19.43)(esbuild@0.28.1)(jiti@2.7.0)(less@4.2.0(supports-color@10.0.0))(sass@1.72.0)(stylus@0.59.0(supports-color@10.0.0))(terser@5.43.1)(tsx@4.23.0)(yaml@2.9.0))(webpack@5.104.1(clean-css@5.2.2)(cssnano@7.1.9(postcss@8.5.31))(csso@5.0.5)(esbuild@0.28.1)(html-minifier-terser@6.1.0)(lightningcss@1.33.0)(postcss@8.5.31)(uglify-js@3.13.3)) + specifier: ^0.7.5 + version: 0.7.5(esbuild@0.28.1)(rollup@4.62.3)(vite@8.3.1(@types/node@20.19.43)(esbuild@0.28.1)(jiti@2.7.0)(less@4.2.0(supports-color@10.0.0))(sass@1.72.0)(stylus@0.59.0(supports-color@10.0.0))(terser@5.43.1)(tsx@4.23.0)(yaml@2.9.0))(webpack@5.104.1(clean-css@5.2.2)(cssnano@7.1.9(postcss@8.5.31))(csso@5.0.5)(esbuild@0.28.1)(html-minifier-terser@6.1.0)(lightningcss@1.33.0)(postcss@8.5.31)(uglify-js@3.13.3)) '@neondatabase/serverless': specifier: 1.2.0 version: 1.2.0 @@ -2776,8 +2776,8 @@ packages: zod: optional: true - '@apm-js-collab/code-transformer-bundler-plugins@0.7.4': - resolution: {integrity: sha512-nAfOeZPSUAQvJa1iFT/5oCrTm5YQhMMrfCNthNnaXHZiOQhu1KGuLoIx7HtbAi3wfwaBYLaICPIeenIaEwcXIg==} + '@apm-js-collab/code-transformer-bundler-plugins@0.7.5': + resolution: {integrity: sha512-/nmTAee5mBly9QiIQSGIKITv9TcEyLTQJFbYjfyWCEfYofxQgurMrT5fLDIRneSPqICucRTxgPMHmc6/q7cgfw==} engines: {node: '>=18.0.0'} peerDependencies: esbuild: '*' @@ -2798,6 +2798,10 @@ packages: resolution: {integrity: sha512-u1Hb6bHjWtkSpiprwVP6YaHC1DTN4RAU3zYkUDUe7WMnJwdyU1pwTL9dFKiSJB9IiLue/EQovmyx6xhU7FFtAQ==} hasBin: true + '@apm-js-collab/code-transformer@0.19.0': + resolution: {integrity: sha512-KGR8dyF4L5dIbnfW2a7n3WQiUxIvR+Zs5IPVXJKNL6XVR9oWR9lm6N/AQXtnimiUDiURlwrZOIlpbGgQ3hU6yQ==} + hasBin: true + '@apm-js-collab/tracing-hooks@0.13.3': resolution: {integrity: sha512-l/ScKzA7sg1hZKc3QMtbnA3blVkTteN3Yq97AejfFrEiR0S0lay3KmyJJ44p45EEVIGP85fLfki4a6OD/7Jb8w==} @@ -19881,9 +19885,9 @@ snapshots: optionalDependencies: zod: 3.25.76 - '@apm-js-collab/code-transformer-bundler-plugins@0.7.4(esbuild@0.28.1)(rollup@4.62.3)(vite@8.3.1(@types/node@20.19.43)(esbuild@0.28.1)(jiti@2.7.0)(less@4.2.0(supports-color@10.0.0))(sass@1.72.0)(stylus@0.59.0(supports-color@10.0.0))(terser@5.43.1)(tsx@4.23.0)(yaml@2.9.0))(webpack@5.104.1(clean-css@5.2.2)(cssnano@7.1.9(postcss@8.5.31))(csso@5.0.5)(esbuild@0.28.1)(html-minifier-terser@6.1.0)(lightningcss@1.33.0)(postcss@8.5.31)(uglify-js@3.13.3))': + '@apm-js-collab/code-transformer-bundler-plugins@0.7.5(esbuild@0.28.1)(rollup@4.62.3)(vite@8.3.1(@types/node@20.19.43)(esbuild@0.28.1)(jiti@2.7.0)(less@4.2.0(supports-color@10.0.0))(sass@1.72.0)(stylus@0.59.0(supports-color@10.0.0))(terser@5.43.1)(tsx@4.23.0)(yaml@2.9.0))(webpack@5.104.1(clean-css@5.2.2)(cssnano@7.1.9(postcss@8.5.31))(csso@5.0.5)(esbuild@0.28.1)(html-minifier-terser@6.1.0)(lightningcss@1.33.0)(postcss@8.5.31)(uglify-js@3.13.3))': dependencies: - '@apm-js-collab/code-transformer': 0.18.1 + '@apm-js-collab/code-transformer': 0.19.0 es-module-lexer: 2.1.0 magic-string: 0.30.21 module-details-from-path: 1.0.4 @@ -19902,6 +19906,15 @@ snapshots: semifies: 1.0.0 source-map: 0.6.1 + '@apm-js-collab/code-transformer@0.19.0': + dependencies: + '@types/estree': 1.0.9 + astring: 1.9.0 + esquery: 1.7.0 + meriyah: 6.1.4 + semifies: 1.0.0 + source-map: 0.6.1 + '@apm-js-collab/tracing-hooks@0.13.3(supports-color@7.2.0)': dependencies: '@apm-js-collab/code-transformer': 0.18.1