Skip to content
Open
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
33 changes: 18 additions & 15 deletions packages/bundler-plugin-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { createDebugIdUploadFunction } from "./debug-id-upload";
import { Logger } from "./logger";
import { Options, SentrySDKBuildFlags } from "./types";
import {
CodeInjection,
containsOnlyImports,
generateGlobalInjectorCode,
generateModuleMetadataInjectorCode,
Expand All @@ -21,7 +22,7 @@ import {
} from "./utils";

type InjectionPlugin = (
injectionCode: string,
injectionCode: CodeInjection,
debugIds: boolean,
logger: Logger
) => UnpluginOptions;
Expand Down Expand Up @@ -101,7 +102,7 @@ export function sentryUnpluginFactory({
plugins.push(bundleSizeOptimizationsPlugin(bundleSizeOptimizationReplacementValues));
}

let injectionCode = "";
const injectionCode = new CodeInjection();

if (!options.release.inject) {
logger.debug(
Expand All @@ -117,24 +118,24 @@ export function sentryUnpluginFactory({
injectBuildInformation: options._experiments.injectBuildInformation || false,
});
if (typeof injectionPlugin !== "function") {
plugins.push(injectionPlugin.releaseInjectionPlugin(code));
plugins.push(injectionPlugin.releaseInjectionPlugin(code.code()));
} else {
injectionCode += code;
injectionCode.append(code);
}
}

if (Object.keys(sentryBuildPluginManager.bundleMetadata).length > 0) {
const code = generateModuleMetadataInjectorCode(sentryBuildPluginManager.bundleMetadata);
if (typeof injectionPlugin !== "function") {
plugins.push(injectionPlugin.moduleMetadataInjectionPlugin(code));
plugins.push(injectionPlugin.moduleMetadataInjectionPlugin(code.code()));
} else {
injectionCode += code;
injectionCode.append(code);
}
}

if (
typeof injectionPlugin === "function" &&
(injectionCode !== "" || options.sourcemaps?.disable !== true)
(!injectionCode.isEmpty() || options.sourcemaps?.disable !== true)
) {
plugins.push(injectionPlugin(injectionCode, options.sourcemaps?.disable !== true, logger));
}
Expand Down Expand Up @@ -286,7 +287,7 @@ export function createRollupBundleSizeOptimizationHooks(replacementValues: Sentr
}

export function createRollupInjectionHooks(
injectionCode: string,
injectionCode: CodeInjection,
debugIds: boolean
): {
renderChunk: RenderChunkHook;
Expand All @@ -302,7 +303,7 @@ export function createRollupInjectionHooks(
return null;
}

let codeToInject = injectionCode;
const codeToInject = injectionCode.clone();

if (debugIds) {
// Check if a debug ID has already been injected to avoid duplicate injection (e.g. by another plugin or Sentry CLI)
Expand All @@ -316,7 +317,7 @@ export function createRollupInjectionHooks(
)
) {
const debugId = stringToUUID(code); // generate a deterministic debug ID
codeToInject += getDebugIdSnippet(debugId);
codeToInject.append(getDebugIdSnippet(debugId));
}
}

Expand All @@ -325,12 +326,12 @@ export function createRollupInjectionHooks(

if (match) {
// Add injected code after any comments or "use strict" at the beginning of the bundle.
ms.appendLeft(match.length, codeToInject);
ms.appendLeft(match.length, codeToInject.code());
} else {
// ms.replace() doesn't work when there is an empty string match (which happens if
// there is neither, a comment, nor a "use strict" at the top of the chunk) so we
// need this special case here.
ms.prepend(codeToInject);
ms.prepend(codeToInject.code());
}

return {
Expand Down Expand Up @@ -447,11 +448,13 @@ export function createComponentNameAnnotateHooks(ignoredComponents?: string[]):
};
}

export function getDebugIdSnippet(debugId: string): string {
return `;{try{(function(){var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="${debugId}",e._sentryDebugIdIdentifier="sentry-dbid-${debugId}");})();}catch(e){}};`;
export function getDebugIdSnippet(debugId: string): CodeInjection {
return new CodeInjection(
`var n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="${debugId}",e._sentryDebugIdIdentifier="sentry-dbid-${debugId}");`
);
}

export type { Logger } from "./logger";
export type { Options, SentrySDKBuildFlags } from "./types";
export { replaceBooleanFlagsInCode, stringToUUID } from "./utils";
export { CodeInjection, replaceBooleanFlagsInCode, stringToUUID } from "./utils";
export { createSentryBuildPluginManager } from "./build-plugin-manager";
61 changes: 46 additions & 15 deletions packages/bundler-plugin-core/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -311,33 +311,27 @@ export function generateGlobalInjectorCode({
}: {
release: string;
injectBuildInformation: boolean;
}): string {
// 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.)
let code = `!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{};`;

code += `e.SENTRY_RELEASE={id:${JSON.stringify(release)}};`;
}): CodeInjection {
let code = `e.SENTRY_RELEASE={id:${JSON.stringify(release)}};`;

if (injectBuildInformation) {
const buildInfo = getBuildInformation();

code += `e.SENTRY_BUILD_INFO=${JSON.stringify(buildInfo)};`;
}

code += "}catch(e){}}();";

return code;
return new CodeInjection(code);
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function generateModuleMetadataInjectorCode(metadata: any): string {
// 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.)
export function generateModuleMetadataInjectorCode(metadata: any): CodeInjection {
// We are merging the metadata objects in case modules are bundled twice with the plugin
// Use try-catch to avoid issues when bundlers rename global variables like 'window' to 'k'
return `!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{};e._sentryModuleMetadata=e._sentryModuleMetadata||{},e._sentryModuleMetadata[(new e.Error).stack]=function(e){for(var n=1;n<arguments.length;n++){var a=arguments[n];if(null!=a)for(var t in a)a.hasOwnProperty(t)&&(e[t]=a[t])}return e}({},e._sentryModuleMetadata[(new e.Error).stack],${JSON.stringify(
metadata
)})}catch(e){}}();`;
return new CodeInjection(
`e._sentryModuleMetadata=e._sentryModuleMetadata||{},e._sentryModuleMetadata[(new e.Error).stack]=function(e){for(var n=1;n<arguments.length;n++){var a=arguments[n];if(null!=a)for(var t in a)a.hasOwnProperty(t)&&(e[t]=a[t])}return e}({},e._sentryModuleMetadata[(new e.Error).stack],${JSON.stringify(
metadata
)});`
);
}

export function getBuildInformation(): {
Expand Down Expand Up @@ -459,3 +453,40 @@ export function containsOnlyImports(code: string): boolean {

return codeWithoutImports.length === 0;
}

export class CodeInjection {
// 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.)
private readonly header = `!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{};`;
private readonly footer = "}catch(e){}}();";

constructor(private body: string = "") {}

public code(): string {
if (this.isEmpty()) {
return "";
}

return this.header + this.body + this.footer;
}

public isEmpty(): boolean {
return this.body.length === 0;
}

public append(code: CodeInjection | string): void {
if (code instanceof CodeInjection) {
this.body += code.body;
} else {
this.body += code;
}
}

public clear(): void {
this.body = "";
}

public clone(): CodeInjection {
return new CodeInjection(this.body);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ exports[`generateGlobalInjectorCode generates code with release 1`] = `"!functio

exports[`generateGlobalInjectorCode generates code with release and build information 1`] = `"!function(){try{var e=\\"undefined\\"!=typeof window?window:\\"undefined\\"!=typeof global?global:\\"undefined\\"!=typeof globalThis?globalThis:\\"undefined\\"!=typeof self?self:{};e.SENTRY_RELEASE={id:\\"1.2.3\\"};e.SENTRY_BUILD_INFO={\\"deps\\":[\\"myDep\\",\\"rollup\\"],\\"depsVersions\\":{\\"rollup\\":3},\\"nodeVersion\\":18};}catch(e){}}();"`;

exports[`generateModuleMetadataInjectorCode generates code with empty metadata object 1`] = `"!function(){try{var e=\\"undefined\\"!=typeof window?window:\\"undefined\\"!=typeof global?global:\\"undefined\\"!=typeof globalThis?globalThis:\\"undefined\\"!=typeof self?self:{};e._sentryModuleMetadata=e._sentryModuleMetadata||{},e._sentryModuleMetadata[(new e.Error).stack]=function(e){for(var n=1;n<arguments.length;n++){var a=arguments[n];if(null!=a)for(var t in a)a.hasOwnProperty(t)&&(e[t]=a[t])}return e}({},e._sentryModuleMetadata[(new e.Error).stack],{})}catch(e){}}();"`;
exports[`generateModuleMetadataInjectorCode generates code with empty metadata object 1`] = `"!function(){try{var e=\\"undefined\\"!=typeof window?window:\\"undefined\\"!=typeof global?global:\\"undefined\\"!=typeof globalThis?globalThis:\\"undefined\\"!=typeof self?self:{};e._sentryModuleMetadata=e._sentryModuleMetadata||{},e._sentryModuleMetadata[(new e.Error).stack]=function(e){for(var n=1;n<arguments.length;n++){var a=arguments[n];if(null!=a)for(var t in a)a.hasOwnProperty(t)&&(e[t]=a[t])}return e}({},e._sentryModuleMetadata[(new e.Error).stack],{});}catch(e){}}();"`;

exports[`generateModuleMetadataInjectorCode generates code with metadata object 1`] = `"!function(){try{var e=\\"undefined\\"!=typeof window?window:\\"undefined\\"!=typeof global?global:\\"undefined\\"!=typeof globalThis?globalThis:\\"undefined\\"!=typeof self?self:{};e._sentryModuleMetadata=e._sentryModuleMetadata||{},e._sentryModuleMetadata[(new e.Error).stack]=function(e){for(var n=1;n<arguments.length;n++){var a=arguments[n];if(null!=a)for(var t in a)a.hasOwnProperty(t)&&(e[t]=a[t])}return e}({},e._sentryModuleMetadata[(new e.Error).stack],{\\"file1.js\\":{\\"foo\\":\\"bar\\"},\\"file2.js\\":{\\"bar\\":\\"baz\\"}})}catch(e){}}();"`;
exports[`generateModuleMetadataInjectorCode generates code with metadata object 1`] = `"!function(){try{var e=\\"undefined\\"!=typeof window?window:\\"undefined\\"!=typeof global?global:\\"undefined\\"!=typeof globalThis?globalThis:\\"undefined\\"!=typeof self?self:{};e._sentryModuleMetadata=e._sentryModuleMetadata||{},e._sentryModuleMetadata[(new e.Error).stack]=function(e){for(var n=1;n<arguments.length;n++){var a=arguments[n];if(null!=a)for(var t in a)a.hasOwnProperty(t)&&(e[t]=a[t])}return e}({},e._sentryModuleMetadata[(new e.Error).stack],{\\"file1.js\\":{\\"foo\\":\\"bar\\"},\\"file2.js\\":{\\"bar\\":\\"baz\\"}});}catch(e){}}();"`;
Loading
Loading