Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions packages/core/src/build-time-plugins/buildTimeOptionsBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,15 @@ export interface BuildTimeOptionsBase {
*/
bundleSizeOptimizations?: BundleSizeOptimizationsOptions;

/**
* Automatic instrumentation of server-side dependencies at build time.
*
* Set to `false` to turn it off.
*
* @default true
*/
buildTimeInstrumentation?: boolean;

/**
* A key that is used to identify the application in the Sentry bundler plugins.
* This key is used by the `thirdPartyErrorFilterIntegration` to filter out errors
Expand Down
9 changes: 9 additions & 0 deletions packages/node/src/bundler-plugin/esbuild.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@ export type SentryEsbuildPluginOptions = SentryEsbuildPluginOptionsBase & {
* @ignore This is for internal use only when this plugin is consumed by a framework SDK
*/
instrumentations?: NonNullable<Parameters<typeof sentryOrchestrionPlugin>[0]>['instrumentations'];

/**
* Automatic instrumentation of server-side dependencies at build time.
*
* Set to `false` to turn it off.
*
* @default true
*/
buildTimeInstrumentation?: boolean;
};

type EsbuildPlugin = ReturnType<typeof sentryOrchestrionPlugin>;
Expand Down
12 changes: 11 additions & 1 deletion packages/node/src/bundler-plugin/rollup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@ export type SentryRollupPluginOptions = SentryRollupPluginOptionsBase & {
* @ignore This is for internal use only when this plugin is consumed by a framework SDK
*/
instrumentations?: NonNullable<Parameters<typeof sentryOrchestrionPlugin>[0]>['instrumentations'];

/**
* Automatic instrumentation of server-side dependencies at build time.
*
* Set to `false` to turn it off.
*
* @default true
*/
buildTimeInstrumentation?: boolean;
};

type RollupPlugin = ReturnType<typeof sentryOrchestrionPlugin>;
Expand All @@ -27,5 +36,6 @@ type RollupPlugin = ReturnType<typeof sentryOrchestrionPlugin>;
* ```
*/
export function sentryRollupPlugin(options?: SentryRollupPluginOptions): RollupPlugin[] {
return [...sentryRollupBundlerPlugin(options), sentryOrchestrionPlugin(options)];
const bundlerPlugins = sentryRollupBundlerPlugin(options);
return [...bundlerPlugins, sentryOrchestrionPlugin(options)];
}
12 changes: 11 additions & 1 deletion packages/node/src/bundler-plugin/vite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@ export type SentryVitePluginOptions = SentryVitePluginOptionsBase & {
* @ignore This is for internal use only when this plugin is consumed by a framework SDK
*/
instrumentations?: NonNullable<Parameters<typeof sentryOrchestrionPlugin>[0]>['instrumentations'];

/**
* Automatic instrumentation of server-side dependencies at build time.
*
* Set to `false` to turn it off.
*
* @default true
*/
buildTimeInstrumentation?: boolean;
};

type VitePlugin = ReturnType<typeof sentryOrchestrionPlugin>;
Expand All @@ -28,5 +37,6 @@ type VitePlugin = ReturnType<typeof sentryOrchestrionPlugin>;
* ```
*/
export function sentryVitePlugin(options?: SentryVitePluginOptions): VitePlugin[] {
return [...sentryViteBundlerPlugin(options), sentryOrchestrionPlugin(options)];
const bundlerPlugins = sentryViteBundlerPlugin(options);
return [...bundlerPlugins, sentryOrchestrionPlugin(options)];
}
9 changes: 9 additions & 0 deletions packages/node/src/bundler-plugin/webpack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@ export type SentryWebpackPluginOptions = SentryWebpackPluginOptionsBase & {
* @ignore This is for internal use only when this plugin is consumed by a framework SDK
*/
instrumentations?: NonNullable<Parameters<typeof sentryOrchestrionWebpackPlugin>[0]>['instrumentations'];

/**
* Automatic instrumentation of server-side dependencies at build time.
*
* Set to `false` to turn it off.
*
* @default true
*/
buildTimeInstrumentation?: boolean;
};

type WebpackCompiler = Parameters<ReturnType<typeof sentryWebpackBundlerPlugin>['apply']>[0];
Expand Down
55 changes: 55 additions & 0 deletions packages/node/test/bundler-plugin/bundler-plugin.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { sentryEsbuildPlugin } from '../../src/bundler-plugin/esbuild';
import { sentryRollupPlugin } from '../../src/bundler-plugin/rollup';
import { sentryVitePlugin } from '../../src/bundler-plugin/vite';
import { sentryWebpackPlugin } from '../../src/bundler-plugin/webpack';

const orchestrionVite = vi.fn(() => ({ name: 'sentry-orchestrion-vite' }));
const orchestrionRollup = vi.fn(() => ({ name: 'sentry-orchestrion-rollup' }));
const orchestrionEsbuildSetup = vi.fn();
const orchestrionWebpackApply = vi.fn();

vi.mock('@sentry/bundler-plugins/vite', () => ({ sentryVitePlugin: () => [{ name: 'sentry-vite' }] }));
vi.mock('@sentry/bundler-plugins/rollup', () => ({ sentryRollupPlugin: () => [{ name: 'sentry-rollup' }] }));
vi.mock('@sentry/bundler-plugins/esbuild', () => ({
sentryEsbuildPlugin: () => ({ name: 'sentry-esbuild', setup: vi.fn() }),
}));
vi.mock('@sentry/bundler-plugins/webpack', () => ({ sentryWebpackPlugin: () => ({ apply: vi.fn() }) }));

vi.mock('@sentry/server-utils/orchestrion/vite', () => ({ sentryOrchestrionPlugin: () => orchestrionVite() }));
vi.mock('@sentry/server-utils/orchestrion/rollup', () => ({ sentryOrchestrionPlugin: () => orchestrionRollup() }));
vi.mock('@sentry/server-utils/orchestrion/esbuild', () => ({
sentryOrchestrionPlugin: () => ({ name: 'sentry-orchestrion-esbuild', setup: orchestrionEsbuildSetup }),
}));
vi.mock('@sentry/server-utils/orchestrion/webpack', () => ({
sentryOrchestrionWebpackPlugin: () => ({ apply: orchestrionWebpackApply }),
}));

describe('@sentry/node bundler plugins', () => {
beforeEach(() => {
vi.clearAllMocks();
});

// The wrappers always wire in the orchestrion plugin. Opting out via
// `buildTimeInstrumentation: false` is handled inside the orchestrion plugin
// itself (covered in @sentry/server-utils), which returns an inert plugin.
it('vite includes the orchestrion plugin', () => {
const plugins = sentryVitePlugin();
expect(plugins.map(p => p.name)).toContain('sentry-orchestrion-vite');
});

it('rollup includes the orchestrion plugin', () => {
const plugins = sentryRollupPlugin();
expect(plugins.map(p => p.name)).toContain('sentry-orchestrion-rollup');
});

it('esbuild runs the orchestrion setup', async () => {
await sentryEsbuildPlugin().setup({} as never);
expect(orchestrionEsbuildSetup).toHaveBeenCalledTimes(1);
});

it('webpack applies the orchestrion plugin', () => {
sentryWebpackPlugin().apply({} as never);
expect(orchestrionWebpackApply).toHaveBeenCalledTimes(1);
});
});
5 changes: 5 additions & 0 deletions packages/server-utils/src/orchestrion/bundler/esbuild.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ function matchesEsbuildExternal(entry: string, moduleName: string): boolean {
* ```
*/
export function sentryOrchestrionPlugin(options: PluginOptions = {}): Plugin {
if (options.buildTimeInstrumentation === false) {
// Inert plugin — no code transform, so no instrumentation lands in the bundle.
return { name: 'sentry-orchestrion-disabled', setup: () => undefined };
}

const plugin = codeTransformer(orchestrionTransformOptions(options));
const moduleNames = instrumentedModuleNames(options.instrumentations);
const setup = plugin.setup;
Expand Down
8 changes: 8 additions & 0 deletions packages/server-utils/src/orchestrion/bundler/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ export type PluginOptions = {
* Additional instrumentations to include with the default instrumentation.
*/
instrumentations?: InstrumentationConfig[];
/**
* Automatic instrumentation of server-side dependencies at build time.
*
* Set to `false` to make the plugin inert, so no instrumentation code is injected.
*
* @default true
*/
buildTimeInstrumentation?: boolean;
/**
* Custom transforms that can be applied using the `transform` option in each `InstrumentationConfig`.
*/
Expand Down
5 changes: 5 additions & 0 deletions packages/server-utils/src/orchestrion/bundler/rollup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ import { externalizedModulesWarning, orchestrionTransformOptions } from './optio
* ```
*/
export function sentryOrchestrionPlugin(options: PluginOptions = {}): Plugin {
if (options.buildTimeInstrumentation === false) {
// Inert plugin — no code transform, so no instrumentation lands in the bundle.
return { name: 'sentry-orchestrion-disabled' };
}

const moduleNames = instrumentedModuleNames(options.instrumentations);

return {
Expand Down
7 changes: 7 additions & 0 deletions packages/server-utils/src/orchestrion/bundler/vite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ import { externalEntryMatchesModule, externalizedModulesWarning, orchestrionTran
* ```
*/
export function sentryOrchestrionPlugin(options: PluginOptions = {}): Plugin {
if (options.buildTimeInstrumentation === false) {
// Return an inert plugin so SDKs that unconditionally push it into their
// plugin array can still opt out without any code transform, `noExternal`
// force-bundling, or injected diagnostics landing in the build.
return { name: 'sentry-orchestrion-disabled' };
}

return {
...codeTransformer(orchestrionTransformOptions(options)),
applyToEnvironment(environment) {
Expand Down
5 changes: 5 additions & 0 deletions packages/server-utils/src/orchestrion/bundler/webpack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,11 @@ function fixupLoaderPath(compiler: Compiler): void {
* transform, so a compilation warning is emitted for them.
*/
export function sentryOrchestrionWebpackPlugin(options: PluginOptions = {}): { apply(compiler: Compiler): void } {
if (options.buildTimeInstrumentation === false) {
// Inert plugin — no code transform, so no instrumentation lands in the bundle.
return { apply: () => undefined };
}

const plugin = codeTransformerWebpack(orchestrionTransformOptions(options));
const moduleNames = instrumentedModuleNames(options.instrumentations);
// The upstream plugin is a class instance, so `apply` is overridden in place
Expand Down
41 changes: 41 additions & 0 deletions packages/server-utils/test/orchestrion/bundler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,47 @@ describe('sentryOrchestrionPlugin (vite)', () => {
});
});

describe('buildTimeInstrumentation: false', () => {
const disabled = { buildTimeInstrumentation: false };

it('returns an inert vite plugin without the transform hooks', () => {
const plugin = vitePlugin(disabled);

expect(plugin.name).toBe('sentry-orchestrion-disabled');
expect(plugin.config).toBeUndefined();
expect(plugin.configResolved).toBeUndefined();
expect(plugin.applyToEnvironment).toBeUndefined();
});

it('returns an inert rollup plugin without the transform hooks', () => {
const plugin = rollupPlugin(disabled);

expect(plugin.name).toBe('sentry-orchestrion-disabled');
expect(plugin.buildStart).toBeUndefined();
expect((plugin as { transform?: unknown }).transform).toBeUndefined();
});

it('returns an inert esbuild plugin whose setup is a no-op', () => {
const build = { initialOptions: {}, onStart: vi.fn() } as unknown as PluginBuild;
const plugin = esbuildPlugin(disabled);

expect(plugin.name).toBe('sentry-orchestrion-disabled');
expect(plugin.setup(build)).toBeUndefined();
expect(build.onStart).not.toHaveBeenCalled();
});

it('returns an inert webpack plugin whose apply is a no-op', () => {
const tap = vi.fn();
const compiler = {
options: { externals: ['mysql'] },
hooks: { thisCompilation: { tap } },
} as unknown as Compiler;

expect(() => sentryOrchestrionWebpackPlugin(disabled).apply(compiler)).not.toThrow();
expect(tap).not.toHaveBeenCalled();
});
});

describe('resolveOrchestrionRuntimeRequest', () => {
it.each([
// Self-references — resolve through this package's own exports map to the CJS build.
Expand Down
Loading