@@ -23,11 +23,6 @@ import { getSentryCli } from "./sentry/cli";
2323import { makeMain } from "@sentry/node" ;
2424import path from "path" ;
2525
26- // We prefix the polyfill id with \0 to tell other plugins not to try to load or transform it.
27- // This hack is taken straight from https://rollupjs.org/guide/en/#resolveid.
28- // This probably doesn't work for all bundlers but for rollup it does.
29- const RELEASE_INJECTOR_ID = "\0sentry-release-injector" ;
30-
3126const ALLOWED_TRANSFORMATION_FILE_ENDINGS = [ ".js" , ".ts" , ".jsx" , ".tsx" , ".mjs" ] ;
3227
3328/**
@@ -36,17 +31,17 @@ const ALLOWED_TRANSFORMATION_FILE_ENDINGS = [".js", ".ts", ".jsx", ".tsx", ".mjs
3631 * - Sourcemaps upload
3732 *
3833 * Release injection:
39- *
40- * Per default the sentry bundler plugin will inject a global `SENTRY_RELEASE` into each JavaScript/TypeScript module
41- * that is part of the bundle. On a technical level this is done by appending an import (`import "sentry-release-injector;"`)
42- * to all entrypoint files of the user code (see `transformInclude` and `transform` hooks). This import is then resolved
43- * by the sentry plugin to a virtual module that sets the global variable (see `resolveId` and `load` hooks).
44- * If a user wants to inject the release into a particular set of modules they can use the `releaseInjectionTargets` option.
34+ * Per default the sentry bundler plugin will inject a global `SENTRY_RELEASE` into
35+ * each JavaScript/TypeScript entrypoint. On a technical level this is done by identifying
36+ * entrypoints in the `resolveId` hook and prepending user code in the `transform` hook.
37+ * If a user wants to inject the release into a particular set of modules instead,
38+ * they can use the `releaseInjectionTargets` option.
4539 *
4640 * Source maps upload:
4741 *
48- * The sentry bundler plugin will also take care of uploading source maps to Sentry. This is all done in the
49- * `writeBundle` hook. In this hook the sentry plugin will execute the release creation pipeline:
42+ * The sentry bundler plugin will also take care of uploading source maps to Sentry. This
43+ * is all done in the `writeBundle` hook. In this hook the sentry plugin will execute the
44+ * release creation pipeline:
5045 *
5146 * 1. Create a new release
5247 * 2. Delete already uploaded artifacts for this release (if `cleanArtifacts` is enabled)
@@ -104,6 +99,8 @@ const unplugin = createUnplugin<Options>((options, unpluginMetaContext) => {
10499 let transaction : Transaction | undefined ;
105100 let releaseInjectionSpan : Span | undefined ;
106101
102+ const absolueEntrypointPaths = new Set < string > ( ) ;
103+
107104 return {
108105 name : "sentry-plugin" ,
109106 enforce : "pre" , // needed for Vite to call resolveId hook
@@ -165,38 +162,11 @@ const unplugin = createUnplugin<Options>((options, unpluginMetaContext) => {
165162 resolveId ( id , importer , { isEntry } ) {
166163 logger . debug ( 'Called "resolveId":' , { id, importer, isEntry } ) ;
167164
168- if ( id === RELEASE_INJECTOR_ID ) {
169- return RELEASE_INJECTOR_ID ;
170- } else {
171- return undefined ;
165+ if ( isEntry ) {
166+ absolueEntrypointPaths . add ( path . resolve ( path . normalize ( id ) ) ) ;
172167 }
173- } ,
174-
175- loadInclude ( id ) {
176- logger . debug ( 'Called "loadInclude":' , { id } ) ;
177- return id === RELEASE_INJECTOR_ID ;
178- } ,
179168
180- /**
181- * Responsible for "virtually" loading the "sentry-release-injector" module. "Virtual" means that the module is not
182- * read from somewhere on disk but rather just returned via a string.
183- *
184- * @param id Always the absolute (fully resolved) path to the module.
185- * @returns The global injector code when we load the "sentry-release-injector" module. Otherwise returns `undefined`.
186- */
187- async load ( id ) {
188- logger . debug ( 'Called "load":' , { id } ) ;
189-
190- if ( id === RELEASE_INJECTOR_ID ) {
191- return generateGlobalInjectorCode ( {
192- release : await releaseNamePromise ,
193- injectReleasesMap : internalOptions . injectReleasesMap ,
194- org : internalOptions . org ,
195- project : internalOptions . project ,
196- } ) ;
197- } else {
198- return undefined ;
199- }
169+ return undefined ;
200170 } ,
201171
202172 /**
@@ -214,11 +184,6 @@ const unplugin = createUnplugin<Options>((options, unpluginMetaContext) => {
214184 // a windows style path to `releaseInjectionTargets`
215185 const normalizedId = path . normalize ( id ) ;
216186
217- // We don't want to transform our injected code.
218- if ( normalizedId === RELEASE_INJECTOR_ID ) {
219- return false ;
220- }
221-
222187 if ( internalOptions . releaseInjectionTargets ) {
223188 // If there's an `releaseInjectionTargets` option transform (ie. inject the release varible) when the file path matches the option.
224189 if ( typeof internalOptions . releaseInjectionTargets === "function" ) {
@@ -233,15 +198,16 @@ const unplugin = createUnplugin<Options>((options, unpluginMetaContext) => {
233198 return normalizedId === normalizedEntry ;
234199 }
235200 } ) ;
236- } else {
237- const isInNodeModules = normalizedId . split ( path . sep ) . includes ( "node_modules" ) ;
201+ } else if ( absolueEntrypointPaths . has ( normalizedId ) ) {
238202 const pathIsOrdinary = ! normalizedId . includes ( "?" ) && ! normalizedId . includes ( "#" ) ;
239203
240204 const pathHasAllowedFileEnding = ALLOWED_TRANSFORMATION_FILE_ENDINGS . some (
241205 ( allowedFileEnding ) => normalizedId . endsWith ( allowedFileEnding )
242206 ) ;
243207
244- return ! isInNodeModules && pathIsOrdinary && pathHasAllowedFileEnding ;
208+ return pathIsOrdinary && pathHasAllowedFileEnding ;
209+ } else {
210+ return false ;
245211 }
246212 } ,
247213
@@ -253,15 +219,20 @@ const unplugin = createUnplugin<Options>((options, unpluginMetaContext) => {
253219 * @param id Always the absolute (fully resolved) path to the module.
254220 * @returns transformed code + source map
255221 */
256- transform ( code , id ) {
222+ async transform ( code , id ) {
257223 logger . debug ( 'Called "transform":' , { id } ) ;
258224
259225 // The MagicString library allows us to generate sourcemaps for the changes we make to the user code.
260226 const ms = new MagicString ( code ) ;
261227
262- // Appending instead of prepending has less probability of mucking with user's source maps.
263- // Luckily import statements get hoisted to the top anyways.
264- ms . append ( `;\nimport "${ RELEASE_INJECTOR_ID } ";` ) ;
228+ ms . prepend (
229+ generateGlobalInjectorCode ( {
230+ release : await releaseNamePromise ,
231+ injectReleasesMap : internalOptions . injectReleasesMap ,
232+ org : internalOptions . org ,
233+ project : internalOptions . project ,
234+ } )
235+ ) ;
265236
266237 if ( unpluginMetaContext . framework === "esbuild" ) {
267238 // esbuild + unplugin is buggy at the moment when we return an object with a `map` (sourcemap) property.
@@ -387,8 +358,7 @@ function generateGlobalInjectorCode({
387358 const key = org ? `${ project } @${ org } ` : project ;
388359 code += `
389360 _global.SENTRY_RELEASES=_global.SENTRY_RELEASES || {};
390- _global.SENTRY_RELEASES["${ key } "]={id:"${ release } "};
391- ` ;
361+ _global.SENTRY_RELEASES["${ key } "]={id:"${ release } "};` ;
392362 }
393363
394364 return code ;
0 commit comments