Skip to content

fix(core): Avoid console output and telemetry init when plugins are disabled #741

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 20, 2025
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
91 changes: 87 additions & 4 deletions packages/bundler-plugin-core/src/build-plugin-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import * as dotenv from "dotenv";
import * as fs from "fs";
import * as os from "os";
import * as path from "path";
import { normalizeUserOptions, validateOptions } from "./options-mapping";
import { createLogger } from "./logger";
import { NormalizedOptions, normalizeUserOptions, validateOptions } from "./options-mapping";
import { createLogger, Logger } from "./logger";
import {
allowedToSendTelemetry,
createSentryInstance,
Expand All @@ -25,7 +25,60 @@ import { glob } from "glob";
import { defaultRewriteSourcesHook, prepareBundleForDebugIdUpload } from "./debug-id-upload";
import { dynamicSamplingContextToSentryBaggageHeader } from "@sentry/utils";

export type SentryBuildPluginManager = ReturnType<typeof createSentryBuildPluginManager>;
export type SentryBuildPluginManager = {
/**
* A logger instance that takes the options passed to the build plugin manager into account. (for silencing and log level etc.)
*/
logger: Logger;

/**
* Options after normalization. Includes things like the inferred release name.
*/
normalizedOptions: NormalizedOptions;
/**
* Magic strings and their replacement values that can be used for bundle size optimizations. This already takes
* into account the options passed to the build plugin manager.
*/
bundleSizeOptimizationReplacementValues: SentrySDKBuildFlags;
/**
* Metadata that should be injected into bundles if possible. Takes into account options passed to the build plugin manager.
*/
// See `generateModuleMetadataInjectorCode` for how this should be used exactly
bundleMetadata: Record<string, unknown>;

/**
* Contains utility functions for emitting telemetry via the build plugin manager.
*/
telemetry: {
/**
* Emits a `Sentry Bundler Plugin execution` signal.
*/
emitBundlerPluginExecutionSignal(): Promise<void>;
};

/**
* Will potentially create a release based on the build plugin manager options.
*
* Also
* - finalizes the release
* - sets commits
* - uploads legacy sourcemaps
* - adds deploy information
*/
createRelease(): Promise<void>;

/**
* Uploads sourcemaps using the "Debug ID" method. This function takes a list of build artifact paths that will be uploaded
*/
uploadSourcemaps(buildArtifactPaths: string[]): Promise<void>;

/**
* Will delete artifacts based on the passed `sourcemaps.filesToDeleteAfterUpload` option.
*/
deleteArtifacts(): Promise<void>;

createDependencyOnBuildArtifacts: () => () => void;
};

/**
* Creates a build plugin manager that exposes primitives for everything that a Sentry JavaScript SDK or build tooling may do during a build.
Expand All @@ -44,7 +97,7 @@ export function createSentryBuildPluginManager(
*/
loggerPrefix: string;
}
) {
): SentryBuildPluginManager {
const logger = createLogger({
prefix: bundlerPluginMetaContext.loggerPrefix,
silent: userOptions.silent ?? false,
Expand Down Expand Up @@ -73,6 +126,36 @@ export function createSentryBuildPluginManager(

const options = normalizeUserOptions(userOptions);

if (options.disable) {
// Early-return a noop build plugin manager instance so that we
// don't continue validating options, setting up Sentry, etc.
// Otherwise we might create side-effects or log messages that
// users don't expect from a disabled plugin.
return {
normalizedOptions: options,
logger,
bundleSizeOptimizationReplacementValues: {},
telemetry: {
emitBundlerPluginExecutionSignal: async () => {
/* noop */
},
},
bundleMetadata: {},
createRelease: async () => {
/* noop */
},
uploadSourcemaps: async () => {
/* noop */
},
deleteArtifacts: async () => {
/* noop */
},
createDependencyOnBuildArtifacts: () => () => {
/* noop */
},
};
}

const shouldSendTelemetry = allowedToSendTelemetry(options);
const { sentryScope, sentryClient } = createSentryInstance(
options,
Expand Down
84 changes: 81 additions & 3 deletions packages/bundler-plugin-core/src/options-mapping.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,90 @@
import { Logger } from "./logger";
import { Options as UserOptions, SetCommitsOptions } from "./types";
import {
Options as UserOptions,
SetCommitsOptions,
RewriteSourcesHook,
ResolveSourceMapHook,
IncludeEntry,
ModuleMetadata,
ModuleMetadataCallback,
} from "./types";
import { determineReleaseName } from "./utils";

export type NormalizedOptions = ReturnType<typeof normalizeUserOptions>;
export type NormalizedOptions = {
org: string | undefined;
project: string | undefined;
authToken: string | undefined;
url: string;
headers: Record<string, string> | undefined;
debug: boolean;
silent: boolean;
errorHandler: ((err: Error) => void) | undefined;
telemetry: boolean;
disable: boolean;
sourcemaps:
| {
disable?: boolean;
assets?: string | string[];
ignore?: string | string[];
rewriteSources?: RewriteSourcesHook;
resolveSourceMap?: ResolveSourceMapHook;
filesToDeleteAfterUpload?: string | string[] | Promise<string | string[] | undefined>;
}
| undefined;
release: {
name: string | undefined;
inject: boolean;
create: boolean;
finalize: boolean;
vcsRemote: string;
setCommits:
| (SetCommitsOptions & {
shouldNotThrowOnFailure?: boolean;
})
| false
| undefined;
dist?: string;
deploy?: {
env: string;
started?: number | string;
finished?: number | string;
time?: number;
name?: string;
url?: string;
};
uploadLegacySourcemaps?: string | IncludeEntry | Array<string | IncludeEntry>;
};
bundleSizeOptimizations:
| {
excludeDebugStatements?: boolean;
excludeTracing?: boolean;
excludeReplayCanvas?: boolean;
excludeReplayShadowDom?: boolean;
excludeReplayIframe?: boolean;
excludeReplayWorker?: boolean;
}
| undefined;
reactComponentAnnotation:
| {
enabled?: boolean;
ignoredComponents?: string[];
}
| undefined;
_metaOptions: {
telemetry: {
metaFramework: string | undefined;
};
};
applicationKey: string | undefined;
moduleMetadata: ModuleMetadata | ModuleMetadataCallback | undefined;
_experiments: {
injectBuildInformation?: boolean;
} & Record<string, unknown>;
};

export const SENTRY_SAAS_URL = "https://sentry.io";

export function normalizeUserOptions(userOptions: UserOptions) {
export function normalizeUserOptions(userOptions: UserOptions): NormalizedOptions {
const options = {
org: userOptions.org ?? process.env["SENTRY_ORG"],
project: userOptions.project ?? process.env["SENTRY_PROJECT"],
Expand Down
51 changes: 51 additions & 0 deletions packages/bundler-plugin-core/test/build-plugin-manager.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { createSentryBuildPluginManager } from "../src/build-plugin-manager";

describe("createSentryBuildPluginManager", () => {
describe("when disabled", () => {
it("initializes a no-op build plugin manager", () => {
const buildPluginManager = createSentryBuildPluginManager(
{
disable: true,
},
{
buildTool: "webpack",
loggerPrefix: "[sentry-webpack-plugin]",
}
);

expect(buildPluginManager).toBeDefined();
expect(buildPluginManager.logger).toBeDefined();
expect(buildPluginManager.normalizedOptions.disable).toBe(true);
});

it("does not log anything to the console", () => {
const logSpy = jest.spyOn(console, "log");
const infoSpy = jest.spyOn(console, "info");
const debugSpy = jest.spyOn(console, "debug");
const warnSpy = jest.spyOn(console, "warn");
const errorSpy = jest.spyOn(console, "error");

createSentryBuildPluginManager(
{
disable: true,
release: {
deploy: {
// An empty string triggers a validation error (but satisfies the type checker)
env: "",
},
},
},
{
buildTool: "webpack",
loggerPrefix: "[sentry-webpack-plugin]",
}
);

expect(logSpy).not.toHaveBeenCalled();
expect(infoSpy).not.toHaveBeenCalled();
expect(debugSpy).not.toHaveBeenCalled();
expect(warnSpy).not.toHaveBeenCalled();
expect(errorSpy).not.toHaveBeenCalled();
});
});
});
Loading