@@ -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 */
3948export 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-
287275export 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 {
0 commit comments