Skip to content

Commit 6ca81b8

Browse files
author
Luca Forstner
authored
ref(core): Only inject release into entrypoints per default (#166)
1 parent 11dc761 commit 6ca81b8

File tree

4 files changed

+55
-81
lines changed

4 files changed

+55
-81
lines changed

MIGRATION.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,13 @@ import type { Options } from "@sentry/rollup-plugin";
8282
import type { SentryRollupPluginOptions } from "@sentry/rollup-plugin";
8383
```
8484

85+
### Behavioral change of `releaseInjectionTargets`
86+
87+
Previously the plugins injected a Sentry release value into every module that was processed.
88+
This approach caused problems in some cases so moving forward, they will only inject the release value into entrypoints by default.
89+
90+
In case you need more fine grained control over which modules should have a release value, you can use the `releaseInjectionTargets` option.
91+
8592
### Removal of `customHeader` option
8693

8794
We removed the `customHeader` option in favor of the `headers` option.

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

Lines changed: 27 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,6 @@ import { getSentryCli } from "./sentry/cli";
2323
import { makeMain } from "@sentry/node";
2424
import 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-
3126
const 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;

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,8 @@ export type Options = Omit<IncludeEntry, "paths"> & {
7171
* if the release should be injected into the module and `false` otherwise. String
7272
* values of this option require a full match with the absolute path of the module.
7373
*
74-
* By default, the release will be injected into all modules - however, bundlers
75-
* will include the injected release code only once per entrypoint.
76-
* If release injection should be disabled, provide an empty array here.
74+
* By default, the release will be injected into all entrypoints. If release
75+
* injection should be disabled, provide an empty array here.
7776
*/
7877
releaseInjectionTargets?: (string | RegExp)[] | RegExp | string | ((filePath: string) => boolean);
7978

0 commit comments

Comments
 (0)