Skip to content

Commit 96ebda5

Browse files
committed
feat: Combine injection plugins
1 parent 8e53019 commit 96ebda5

5 files changed

Lines changed: 82 additions & 212 deletions

File tree

packages/bundler-plugin-core/src/index.ts

Lines changed: 55 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,20 @@ import {
1919
stripQueryAndHashFromPath,
2020
} from "./utils";
2121

22-
interface SentryUnpluginFactoryOptions {
22+
type InjectionPlugin = (
23+
injectionCode: string,
24+
debugIds: boolean,
25+
logger: Logger
26+
) => UnpluginOptions;
27+
type LegacyPlugins = {
2328
releaseInjectionPlugin: (injectionCode: string) => UnpluginOptions;
24-
componentNameAnnotatePlugin?: (ignoredComponents?: string[]) => UnpluginOptions;
2529
moduleMetadataInjectionPlugin: (injectionCode: string) => UnpluginOptions;
2630
debugIdInjectionPlugin: (logger: Logger) => UnpluginOptions;
31+
};
32+
33+
interface SentryUnpluginFactoryOptions {
34+
injectionPlugin: InjectionPlugin | LegacyPlugins;
35+
componentNameAnnotatePlugin?: (ignoredComponents?: string[]) => UnpluginOptions;
2736
debugIdUploadPlugin: (
2837
upload: (buildArtifacts: string[]) => Promise<void>,
2938
logger: Logger,
@@ -37,10 +46,8 @@ interface SentryUnpluginFactoryOptions {
3746
* Creates an unplugin instance used to create Sentry plugins for Vite, Rollup, esbuild, and Webpack.
3847
*/
3948
export function sentryUnpluginFactory({
40-
releaseInjectionPlugin,
49+
injectionPlugin,
4150
componentNameAnnotatePlugin,
42-
moduleMetadataInjectionPlugin,
43-
debugIdInjectionPlugin,
4451
debugIdUploadPlugin,
4552
bundleSizeOptimizationsPlugin,
4653
}: SentryUnpluginFactoryOptions): UnpluginInstance<Options | undefined, true> {
@@ -93,6 +100,8 @@ export function sentryUnpluginFactory({
93100
plugins.push(bundleSizeOptimizationsPlugin(bundleSizeOptimizationReplacementValues));
94101
}
95102

103+
let injectionCode = "";
104+
96105
if (!options.release.inject) {
97106
logger.debug(
98107
"Release injection disabled via `release.inject` option. Will not inject release."
@@ -102,18 +111,31 @@ export function sentryUnpluginFactory({
102111
"No release name provided. Will not inject release. Please set the `release.name` option to identify your release."
103112
);
104113
} else {
105-
const injectionCode = generateGlobalInjectorCode({
114+
const code = generateGlobalInjectorCode({
106115
release: options.release.name,
107116
injectBuildInformation: options._experiments.injectBuildInformation || false,
108117
});
109-
plugins.push(releaseInjectionPlugin(injectionCode));
118+
if (typeof injectionPlugin !== "function") {
119+
plugins.push(injectionPlugin.releaseInjectionPlugin(code));
120+
} else {
121+
injectionCode += code;
122+
}
110123
}
111124

112125
if (Object.keys(sentryBuildPluginManager.bundleMetadata).length > 0) {
113-
const injectionCode = generateModuleMetadataInjectorCode(
114-
sentryBuildPluginManager.bundleMetadata
115-
);
116-
plugins.push(moduleMetadataInjectionPlugin(injectionCode));
126+
const code = generateModuleMetadataInjectorCode(sentryBuildPluginManager.bundleMetadata);
127+
if (typeof injectionPlugin !== "function") {
128+
plugins.push(injectionPlugin.moduleMetadataInjectionPlugin(code));
129+
} else {
130+
injectionCode += code;
131+
}
132+
}
133+
134+
if (
135+
typeof injectionPlugin === "function" &&
136+
(injectionCode !== "" || options.sourcemaps?.disable !== true)
137+
) {
138+
plugins.push(injectionPlugin(injectionCode, options.sourcemaps?.disable !== true, logger));
117139
}
118140

119141
// Add plugin to create and finalize releases, and also take care of adding commits and legacy sourcemaps
@@ -131,7 +153,9 @@ export function sentryUnpluginFactory({
131153
});
132154

133155
if (options.sourcemaps?.disable !== true) {
134-
plugins.push(debugIdInjectionPlugin(logger));
156+
if (typeof injectionPlugin !== "function") {
157+
plugins.push(injectionPlugin.debugIdInjectionPlugin(logger));
158+
}
135159

136160
if (options.sourcemaps?.disable !== "disable-upload") {
137161
// This option is only strongly typed for the webpack plugin, where it is used. It has no effect on other plugins
@@ -248,42 +272,6 @@ function shouldSkipCodeInjection(code: string, facadeModuleId: string | null | u
248272
return false;
249273
}
250274

251-
export function createRollupReleaseInjectionHooks(injectionCode: string): {
252-
renderChunk: RenderChunkHook;
253-
} {
254-
return {
255-
renderChunk(code: string, chunk: { fileName: string; facadeModuleId?: string | null }) {
256-
if (!isJsFile(chunk.fileName)) {
257-
return null; // returning null means not modifying the chunk at all
258-
}
259-
260-
// Skip empty chunks and HTML facade chunks (Vite MPA)
261-
if (shouldSkipCodeInjection(code, chunk.facadeModuleId)) {
262-
return null;
263-
}
264-
265-
const ms = new MagicString(code, { filename: chunk.fileName });
266-
267-
const match = code.match(COMMENT_USE_STRICT_REGEX)?.[0];
268-
269-
if (match) {
270-
// Add injected code after any comments or "use strict" at the beginning of the bundle.
271-
ms.appendLeft(match.length, injectionCode);
272-
} else {
273-
// ms.replace() doesn't work when there is an empty string match (which happens if
274-
// there is neither, a comment, nor a "use strict" at the top of the chunk) so we
275-
// need this special case here.
276-
ms.prepend(injectionCode);
277-
}
278-
279-
return {
280-
code: ms.toString(),
281-
map: ms.generateMap({ file: chunk.fileName, hires: "boundary" }),
282-
};
283-
},
284-
};
285-
}
286-
287275
export function createRollupBundleSizeOptimizationHooks(replacementValues: SentrySDKBuildFlags): {
288276
transform: UnpluginOptions["transform"];
289277
} {
@@ -294,7 +282,10 @@ export function createRollupBundleSizeOptimizationHooks(replacementValues: Sentr
294282
};
295283
}
296284

297-
export function createRollupDebugIdInjectionHooks(): {
285+
export function createRollupInjectionHooks(
286+
injectionCode: string,
287+
debugIds: boolean
288+
): {
298289
renderChunk: RenderChunkHook;
299290
} {
300291
return {
@@ -308,68 +299,35 @@ export function createRollupDebugIdInjectionHooks(): {
308299
return null;
309300
}
310301

311-
// Check if a debug ID has already been injected to avoid duplicate injection (e.g. by another plugin or Sentry CLI)
312-
const chunkStartSnippet = code.slice(0, 6000);
313-
const chunkEndSnippet = code.slice(-500);
314-
315-
if (
316-
chunkStartSnippet.includes("_sentryDebugIdIdentifier") ||
317-
chunkEndSnippet.includes("//# debugId=")
318-
) {
319-
return null; // Debug ID already present, skip injection
320-
}
321-
322-
const debugId = stringToUUID(code); // generate a deterministic debug ID
323-
const codeToInject = getDebugIdSnippet(debugId);
324-
325-
const ms = new MagicString(code, { filename: chunk.fileName });
326-
327-
const match = code.match(COMMENT_USE_STRICT_REGEX)?.[0];
302+
let codeToInject = injectionCode;
328303

329-
if (match) {
330-
// Add injected code after any comments or "use strict" at the beginning of the bundle.
331-
ms.appendLeft(match.length, codeToInject);
332-
} else {
333-
// ms.replace() doesn't work when there is an empty string match (which happens if
334-
// there is neither, a comment, nor a "use strict" at the top of the chunk) so we
335-
// need this special case here.
336-
ms.prepend(codeToInject);
337-
}
304+
if (debugIds) {
305+
// Check if a debug ID has already been injected to avoid duplicate injection (e.g. by another plugin or Sentry CLI)
306+
const chunkStartSnippet = code.slice(0, 6000);
307+
const chunkEndSnippet = code.slice(-500);
338308

339-
return {
340-
code: ms.toString(),
341-
map: ms.generateMap({ file: chunk.fileName, hires: "boundary" }),
342-
};
343-
},
344-
};
345-
}
346-
347-
export function createRollupModuleMetadataInjectionHooks(injectionCode: string): {
348-
renderChunk: RenderChunkHook;
349-
} {
350-
return {
351-
renderChunk(code: string, chunk: { fileName: string; facadeModuleId?: string | null }) {
352-
if (!isJsFile(chunk.fileName)) {
353-
return null; // returning null means not modifying the chunk at all
354-
}
309+
if (
310+
chunkStartSnippet.includes("_sentryDebugIdIdentifier") ||
311+
chunkEndSnippet.includes("//# debugId=")
312+
) {
313+
return null; // Debug ID already present, skip injection
314+
}
355315

356-
// Skip empty chunks and HTML facade chunks (Vite MPA)
357-
if (shouldSkipCodeInjection(code, chunk.facadeModuleId)) {
358-
return null;
316+
const debugId = stringToUUID(code); // generate a deterministic debug ID
317+
codeToInject += getDebugIdSnippet(debugId);
359318
}
360319

361320
const ms = new MagicString(code, { filename: chunk.fileName });
362-
363321
const match = code.match(COMMENT_USE_STRICT_REGEX)?.[0];
364322

365323
if (match) {
366324
// Add injected code after any comments or "use strict" at the beginning of the bundle.
367-
ms.appendLeft(match.length, injectionCode);
325+
ms.appendLeft(match.length, codeToInject);
368326
} else {
369327
// ms.replace() doesn't work when there is an empty string match (which happens if
370328
// there is neither, a comment, nor a "use strict" at the top of the chunk) so we
371329
// need this special case here.
372-
ms.prepend(injectionCode);
330+
ms.prepend(codeToInject);
373331
}
374332

375333
return {

packages/esbuild-plugin/src/index.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -317,9 +317,11 @@ function esbuildBundleSizeOptimizationsPlugin(
317317
}
318318

319319
const sentryUnplugin = sentryUnpluginFactory({
320-
releaseInjectionPlugin: esbuildReleaseInjectionPlugin,
321-
debugIdInjectionPlugin: esbuildDebugIdInjectionPlugin,
322-
moduleMetadataInjectionPlugin: esbuildModuleMetadataInjectionPlugin,
320+
injectionPlugin: {
321+
releaseInjectionPlugin: esbuildReleaseInjectionPlugin,
322+
debugIdInjectionPlugin: esbuildDebugIdInjectionPlugin,
323+
moduleMetadataInjectionPlugin: esbuildModuleMetadataInjectionPlugin,
324+
},
323325
debugIdUploadPlugin: esbuildDebugIdUploadPlugin,
324326
bundleSizeOptimizationsPlugin: esbuildBundleSizeOptimizationsPlugin,
325327
});

packages/rollup-plugin/src/index.ts

Lines changed: 5 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
import {
22
sentryUnpluginFactory,
33
Options,
4-
createRollupReleaseInjectionHooks,
5-
createRollupModuleMetadataInjectionHooks,
6-
createRollupDebugIdInjectionHooks,
4+
createRollupInjectionHooks,
75
createRollupDebugIdUploadHooks,
86
SentrySDKBuildFlags,
97
createRollupBundleSizeOptimizationHooks,
@@ -12,31 +10,17 @@ import {
1210
} from "@sentry/bundler-plugin-core";
1311
import type { UnpluginOptions } from "unplugin";
1412

15-
function rollupReleaseInjectionPlugin(injectionCode: string): UnpluginOptions {
16-
return {
17-
name: "sentry-rollup-release-injection-plugin",
18-
rollup: createRollupReleaseInjectionHooks(injectionCode),
19-
};
20-
}
21-
2213
function rollupComponentNameAnnotatePlugin(ignoredComponents?: string[]): UnpluginOptions {
2314
return {
2415
name: "sentry-rollup-component-name-annotate-plugin",
2516
rollup: createComponentNameAnnotateHooks(ignoredComponents),
2617
};
2718
}
2819

29-
function rollupDebugIdInjectionPlugin(): UnpluginOptions {
30-
return {
31-
name: "sentry-rollup-debug-id-injection-plugin",
32-
rollup: createRollupDebugIdInjectionHooks(),
33-
};
34-
}
35-
36-
function rollupModuleMetadataInjectionPlugin(injectionCode: string): UnpluginOptions {
20+
function rollupInjectionPlugin(injectionCode: string, debugIds: boolean): UnpluginOptions {
3721
return {
38-
name: "sentry-rollup-module-metadata-injection-plugin",
39-
rollup: createRollupModuleMetadataInjectionHooks(injectionCode),
22+
name: "sentry-rollup-injection-plugin",
23+
rollup: createRollupInjectionHooks(injectionCode, debugIds),
4024
};
4125
}
4226

@@ -61,10 +45,8 @@ function rollupBundleSizeOptimizationsPlugin(
6145
}
6246

6347
const sentryUnplugin = sentryUnpluginFactory({
64-
releaseInjectionPlugin: rollupReleaseInjectionPlugin,
48+
injectionPlugin: rollupInjectionPlugin,
6549
componentNameAnnotatePlugin: rollupComponentNameAnnotatePlugin,
66-
debugIdInjectionPlugin: rollupDebugIdInjectionPlugin,
67-
moduleMetadataInjectionPlugin: rollupModuleMetadataInjectionPlugin,
6850
debugIdUploadPlugin: rollupDebugIdUploadPlugin,
6951
bundleSizeOptimizationsPlugin: rollupBundleSizeOptimizationsPlugin,
7052
});

packages/vite-plugin/src/index.ts

Lines changed: 4 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
import {
22
sentryUnpluginFactory,
33
Options,
4-
createRollupReleaseInjectionHooks,
5-
createRollupModuleMetadataInjectionHooks,
6-
createRollupDebugIdInjectionHooks,
4+
createRollupInjectionHooks,
75
createRollupDebugIdUploadHooks,
86
SentrySDKBuildFlags,
97
createRollupBundleSizeOptimizationHooks,
@@ -12,13 +10,13 @@ import {
1210
} from "@sentry/bundler-plugin-core";
1311
import { UnpluginOptions, VitePlugin } from "unplugin";
1412

15-
function viteReleaseInjectionPlugin(injectionCode: string): UnpluginOptions {
13+
function viteInjectionPlugin(injectionCode: string, debugIds: boolean): UnpluginOptions {
1614
return {
1715
name: "sentry-vite-release-injection-plugin",
1816
// run `post` to avoid tripping up @rollup/plugin-commonjs when cjs is used
1917
// as we inject an `import` statement
2018
enforce: "post" as const, // need this so that vite runs the resolveId hook
21-
vite: createRollupReleaseInjectionHooks(injectionCode),
19+
vite: createRollupInjectionHooks(injectionCode, debugIds),
2220
};
2321
}
2422

@@ -30,20 +28,6 @@ function viteComponentNameAnnotatePlugin(ignoredComponents?: string[]): Unplugin
3028
};
3129
}
3230

33-
function viteDebugIdInjectionPlugin(): UnpluginOptions {
34-
return {
35-
name: "sentry-vite-debug-id-injection-plugin",
36-
vite: createRollupDebugIdInjectionHooks(),
37-
};
38-
}
39-
40-
function viteModuleMetadataInjectionPlugin(injectionCode: string): UnpluginOptions {
41-
return {
42-
name: "sentry-vite-module-metadata-injection-plugin",
43-
vite: createRollupModuleMetadataInjectionHooks(injectionCode),
44-
};
45-
}
46-
4731
function viteDebugIdUploadPlugin(
4832
upload: (buildArtifacts: string[]) => Promise<void>,
4933
logger: Logger,
@@ -65,10 +49,8 @@ function viteBundleSizeOptimizationsPlugin(
6549
}
6650

6751
const sentryUnplugin = sentryUnpluginFactory({
68-
releaseInjectionPlugin: viteReleaseInjectionPlugin,
52+
injectionPlugin: viteInjectionPlugin,
6953
componentNameAnnotatePlugin: viteComponentNameAnnotatePlugin,
70-
debugIdInjectionPlugin: viteDebugIdInjectionPlugin,
71-
moduleMetadataInjectionPlugin: viteModuleMetadataInjectionPlugin,
7254
debugIdUploadPlugin: viteDebugIdUploadPlugin,
7355
bundleSizeOptimizationsPlugin: viteBundleSizeOptimizationsPlugin,
7456
});

0 commit comments

Comments
 (0)