diff --git a/packages/bundler-plugin-core/src/index.ts b/packages/bundler-plugin-core/src/index.ts index 3831fa8c..8799f1d8 100644 --- a/packages/bundler-plugin-core/src/index.ts +++ b/packages/bundler-plugin-core/src/index.ts @@ -29,7 +29,7 @@ import { logger } from "@sentry/utils"; interface SentryUnpluginFactoryOptions { releaseInjectionPlugin: (injectionCode: string) => UnpluginOptions; componentNameAnnotatePlugin?: () => UnpluginOptions; - moduleMetadataInjectionPlugin?: (injectionCode: string) => UnpluginOptions; + moduleMetadataInjectionPlugin: (injectionCode: string) => UnpluginOptions; debugIdInjectionPlugin: (logger: Logger) => UnpluginOptions; debugIdUploadPlugin: (upload: (buildArtifacts: string[]) => Promise) => UnpluginOptions; bundleSizeOptimizationsPlugin: (buildFlags: SentrySDKBuildFlags) => UnpluginOptions; @@ -96,6 +96,13 @@ export function sentryUnpluginFactory({ const options = normalizeUserOptions(userOptions); + // TODO(v3): Remove this warning + if (userOptions._experiments?.moduleMetadata) { + logger.warn( + "The `_experiments.moduleMetadata` option has been promoted to being stable. You can safely move the option out of the `_experiments` object scope." + ); + } + if (unpluginMetaContext.watchMode || options.disable) { return [ { @@ -217,25 +224,23 @@ export function sentryUnpluginFactory({ plugins.push(releaseInjectionPlugin(injectionCode)); } - if (moduleMetadataInjectionPlugin && options._experiments.moduleMetadata) { - let metadata: object; - if (typeof options._experiments.moduleMetadata === "function") { + if (options.moduleMetadata) { + let metadata: Record; + if (typeof options.moduleMetadata === "function") { const args = { org: options.org, project: options.project, release: options.release.name, }; - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call - metadata = options._experiments.moduleMetadata(args); + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + metadata = options.moduleMetadata(args); } else { // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - metadata = options._experiments.moduleMetadata; + metadata = options.moduleMetadata; } const injectionCode = generateModuleMetadataInjectorCode(metadata); plugins.push(moduleMetadataInjectionPlugin(injectionCode)); - } else if (options._experiments.moduleMetadata) { - logger.warn("'moduleMetadata' is currently only supported by '@sentry/webpack-plugin'"); } if (!options.release.name) { diff --git a/packages/bundler-plugin-core/src/options-mapping.ts b/packages/bundler-plugin-core/src/options-mapping.ts index 32306c8d..ca25a3d8 100644 --- a/packages/bundler-plugin-core/src/options-mapping.ts +++ b/packages/bundler-plugin-core/src/options-mapping.ts @@ -29,6 +29,7 @@ export function normalizeUserOptions(userOptions: UserOptions) { }, bundleSizeOptimizations: userOptions.bundleSizeOptimizations, reactComponentAnnotation: userOptions.reactComponentAnnotation, + moduleMetadata: userOptions.moduleMetadata || userOptions._experiments?.moduleMetadata, _experiments: userOptions._experiments ?? {}, }; diff --git a/packages/bundler-plugin-core/src/sentry/telemetry.ts b/packages/bundler-plugin-core/src/sentry/telemetry.ts index d011b13c..61620e9b 100644 --- a/packages/bundler-plugin-core/src/sentry/telemetry.ts +++ b/packages/bundler-plugin-core/src/sentry/telemetry.ts @@ -70,7 +70,7 @@ export function setTelemetryDataOnHub(options: NormalizedOptions, hub: Hub, bund ); } - hub.setTag("module-metadata", !!options._experiments.moduleMetadata); + hub.setTag("module-metadata", !!options.moduleMetadata); hub.setTag("inject-build-information", !!options._experiments.injectBuildInformation); // Optional release pipeline steps diff --git a/packages/bundler-plugin-core/src/types.ts b/packages/bundler-plugin-core/src/types.ts index 6419c074..6aae6771 100644 --- a/packages/bundler-plugin-core/src/types.ts +++ b/packages/bundler-plugin-core/src/types.ts @@ -297,11 +297,27 @@ export interface Options { enabled?: boolean; }; + /** + * Metadata that should be associated with the built application. + * + * The metadata is serialized and can be looked up at runtime from within the SDK (for example in the `beforeSend`, + * event processors, or the transport), allowing for custom event filtering logic or routing of events. + * + * Metadata can either be passed directly or alternatively a callback can be provided that will be + * called with the following parameters: + * - `org`: The organization slug. + * - `project`: The project slug. + * - `release`: The release name. + */ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + moduleMetadata?: ModuleMetadata | ModuleMetadataCallback; + /** * Options that are considered experimental and subject to change. * * @experimental API that does not follow semantic versioning and may change in any release */ + // TODO(v3): Remove these _experiments?: { /** * If set to true, the plugin will inject an additional `SENTRY_BUILD_INFO` variable. @@ -312,18 +328,23 @@ export interface Options { injectBuildInformation?: boolean; /** - * Metadata associated with this module. + * NOTE: This option has been promoted to stable. * - * The metadata is serialized and can be looked up at runtime by filename. + * Metadata that should be associated with the built application. + * + * The metadata is serialized and can be looked up at runtime from within the SDK (for example in the `beforeSend`, + * event processors, or the transport), allowing for custom event filtering logic or routing of events. * * Metadata can either be passed directly or alternatively a callback can be provided that will be - * called with the following arguments: + * called with the following parameters: * - `org`: The organization slug. * - `project`: The project slug. * - `release`: The release name. + * + * @deprecated Use the non-experimental `moduleMetadata` option instead. (Basically just move this option out of `_experiments`) */ // eslint-disable-next-line @typescript-eslint/no-explicit-any - moduleMetadata?: any | ModuleMetadataCallback; + moduleMetadata?: ModuleMetadata | ModuleMetadataCallback; }; /** @@ -340,13 +361,18 @@ export interface Options { }; } +export interface ModuleMetadata { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + [key: string]: any; +} + export interface ModuleMetadataCallbackArgs { org?: string; project?: string; release?: string; } -export type ModuleMetadataCallback = (args: ModuleMetadataCallbackArgs) => object; +export type ModuleMetadataCallback = (args: ModuleMetadataCallbackArgs) => ModuleMetadata; export type IncludeEntry = { /** diff --git a/packages/bundler-plugin-core/src/utils.ts b/packages/bundler-plugin-core/src/utils.ts index 1fbe1083..97f6f97f 100644 --- a/packages/bundler-plugin-core/src/utils.ts +++ b/packages/bundler-plugin-core/src/utils.ts @@ -280,18 +280,27 @@ export function generateGlobalInjectorCode({ export function generateModuleMetadataInjectorCode(metadata: any) { // The code below is mostly ternary operators because it saves bundle size. // The checks are to support as many environments as possible. (Node.js, Browser, webworkers, etc.) - return ` - var _global2 = - typeof window !== 'undefined' ? - window : - typeof global !== 'undefined' ? - global : - typeof self !== 'undefined' ? - self : - {}; - - _global2._sentryModuleMetadata = _global2._sentryModuleMetadata || {}; - _global2._sentryModuleMetadata[new Error().stack] = ${JSON.stringify(metadata)};`; + // We are merging the metadata objects in case modules are bundled twice with the plugin + return `{ + var _sentryModuleMetadataGlobal = + typeof window !== "undefined" + ? window + : typeof global !== "undefined" + ? global + : typeof self !== "undefined" + ? self + : {}; + + _sentryModuleMetadataGlobal._sentryModuleMetadata = + _sentryModuleMetadataGlobal._sentryModuleMetadata || {}; + + _sentryModuleMetadataGlobal._sentryModuleMetadata[new Error().stack] = + Object.assign( + {}, + _sentryModuleMetadataGlobal._sentryModuleMetadata[new Error().stack], + ${JSON.stringify(metadata)} + ); +}`; } function getBuildInformation() { diff --git a/packages/integration-tests/fixtures/metadata-injection/setup.ts b/packages/integration-tests/fixtures/metadata-injection/setup.ts index 23e3822c..6f7c31d1 100644 --- a/packages/integration-tests/fixtures/metadata-injection/setup.ts +++ b/packages/integration-tests/fixtures/metadata-injection/setup.ts @@ -9,9 +9,7 @@ createCjsBundles( }, outputDir, { - _experiments: { - moduleMetadata: { team: "frontend" }, - }, + moduleMetadata: { team: "frontend" }, }, ["webpack4", "webpack5", "esbuild", "rollup", "vite"] );