Skip to content

Commit 0607188

Browse files
author
Luca Forstner
authored
feat(core): Import release injection code from each module (#188)
1 parent f49cc63 commit 0607188

File tree

7 files changed

+67
-55
lines changed

7 files changed

+67
-55
lines changed

packages/bundler-plugin-core/.eslintrc.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ module.exports = {
1010
"jest.config.js",
1111
"rollup.config.js",
1212
"test/fixtures/**/*",
13+
"sentry-release-injection-file.js",
1314
],
1415
parserOptions: {
1516
tsconfigRootDir: __dirname,

packages/bundler-plugin-core/package.json

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,9 @@
1010
"access": "public"
1111
},
1212
"files": [
13-
"dist"
13+
"dist",
14+
"sentry-release-injection-file.js"
1415
],
15-
"exports": {
16-
".": {
17-
"import": "./dist/esm/index.mjs",
18-
"require": "./dist/cjs/index.js"
19-
}
20-
},
2116
"main": "dist/cjs/index.js",
2217
"module": "dist/esm/index.mjs",
2318
"types": "dist/types/index.d.ts",

packages/bundler-plugin-core/rollup.config.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@ export default {
1818
commonjs(),
1919
json(),
2020
replace({
21-
__PACKAGE_VERSION__: JSON.stringify(packageJson.version),
21+
preventAssignment: true,
22+
values: {
23+
__PACKAGE_VERSION__: JSON.stringify(packageJson.version),
24+
},
2225
}),
2326
babel({
2427
extensions,
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// This const is used for nothing except to make this file identifiable via its content.
2+
// We search for "_sentry_release_injection_file" in the plugin to determine for sure that the file we look at is the release injection file.
3+
4+
// _sentry_release_injection_file

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

Lines changed: 30 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,20 @@ import { getDependencies, getPackageJson, parseMajorVersion } from "./utils";
2828

2929
const ALLOWED_TRANSFORMATION_FILE_ENDINGS = [".js", ".ts", ".jsx", ".tsx", ".mjs"];
3030

31+
const releaseInjectionFilePath = require.resolve(
32+
"@sentry/bundler-plugin-core/sentry-release-injection-file"
33+
);
3134
/**
3235
* The sentry bundler plugin concerns itself with two things:
3336
* - Release injection
3437
* - Sourcemaps upload
3538
*
3639
* Release injection:
37-
* Per default the sentry bundler plugin will inject a global `SENTRY_RELEASE` into
38-
* each JavaScript/TypeScript entrypoint. On a technical level this is done by identifying
39-
* entrypoints in the `resolveId` hook and prepending user code in the `transform` hook.
40-
* If a user wants to inject the release into a particular set of modules instead,
41-
* they can use the `releaseInjectionTargets` option.
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.
4245
*
4346
* Source maps upload:
4447
*
@@ -102,8 +105,6 @@ const unplugin = createUnplugin<Options>((options, unpluginMetaContext) => {
102105
let transaction: Transaction | undefined;
103106
let releaseInjectionSpan: Span | undefined;
104107

105-
const absolueEntrypointPaths = new Set<string>();
106-
107108
return {
108109
name: "sentry-plugin",
109110
enforce: "pre", // needed for Vite to call resolveId hook
@@ -164,11 +165,6 @@ const unplugin = createUnplugin<Options>((options, unpluginMetaContext) => {
164165
*/
165166
resolveId(id, importer, { isEntry }) {
166167
logger.debug('Called "resolveId":', { id, importer, isEntry });
167-
168-
if (isEntry) {
169-
absolueEntrypointPaths.add(path.resolve(path.normalize(id)));
170-
}
171-
172168
return undefined;
173169
},
174170

@@ -187,6 +183,10 @@ const unplugin = createUnplugin<Options>((options, unpluginMetaContext) => {
187183
// a windows style path to `releaseInjectionTargets`
188184
const normalizedId = path.normalize(id);
189185

186+
if (id.includes("sentry-release-injection-file")) {
187+
return true;
188+
}
189+
190190
if (internalOptions.releaseInjectionTargets) {
191191
// If there's an `releaseInjectionTargets` option transform (ie. inject the release varible) when the file path matches the option.
192192
if (typeof internalOptions.releaseInjectionTargets === "function") {
@@ -201,16 +201,14 @@ const unplugin = createUnplugin<Options>((options, unpluginMetaContext) => {
201201
return normalizedId === normalizedEntry;
202202
}
203203
});
204-
} else if (absolueEntrypointPaths.has(normalizedId)) {
204+
} else {
205205
const pathIsOrdinary = !normalizedId.includes("?") && !normalizedId.includes("#");
206206

207207
const pathHasAllowedFileEnding = ALLOWED_TRANSFORMATION_FILE_ENDINGS.some(
208208
(allowedFileEnding) => normalizedId.endsWith(allowedFileEnding)
209209
);
210210

211211
return pathIsOrdinary && pathHasAllowedFileEnding;
212-
} else {
213-
return false;
214212
}
215213
},
216214

@@ -232,15 +230,23 @@ const unplugin = createUnplugin<Options>((options, unpluginMetaContext) => {
232230
// The MagicString library allows us to generate sourcemaps for the changes we make to the user code.
233231
const ms = new MagicString(code);
234232

235-
ms.prepend(
236-
generateGlobalInjectorCode({
237-
release: await releaseNamePromise,
238-
injectReleasesMap: internalOptions.injectReleasesMap,
239-
injectBuildInformation: internalOptions._experiments.injectBuildInformation || false,
240-
org: internalOptions.org,
241-
project: internalOptions.project,
242-
})
243-
);
233+
if (code.includes("_sentry_release_injection_file")) {
234+
// Appending instead of prepending has less probability of mucking with user's source maps.
235+
ms.append(
236+
generateGlobalInjectorCode({
237+
release: await releaseNamePromise,
238+
injectReleasesMap: internalOptions.injectReleasesMap,
239+
injectBuildInformation: internalOptions._experiments.injectBuildInformation || false,
240+
org: internalOptions.org,
241+
project: internalOptions.project,
242+
})
243+
);
244+
} else {
245+
// Appending instead of prepending has less probability of mucking with user's source maps.
246+
// Luckily import statements get hoisted to the top anyways.
247+
// The import needs to be an absolute path because Rollup doesn't bundle stuff in `node_modules` by default when bundling CJS (unless the import path is absolute or the node-resolve-plugin is used).
248+
ms.append(`;\nimport "${releaseInjectionFilePath.replace(/\\/g, "\\\\")}";`);
249+
}
244250

245251
if (unpluginMetaContext.framework === "esbuild") {
246252
// esbuild + unplugin is buggy at the moment when we return an object with a `map` (sourcemap) property.

packages/e2e-tests/scenarios/basic-upload/__snapshots__/basic-upload.test.ts.snap

Lines changed: 26 additions & 19 deletions
Large diffs are not rendered by default.

packages/playground/rollup.config.js

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
// @ts-check
2-
import commonjs from "@rollup/plugin-commonjs";
3-
import resolve from "@rollup/plugin-node-resolve";
42
import { sentryRollupPlugin } from "@sentry/bundler-plugin-core";
53
import placeHolderOptions from "./config.json";
64

@@ -11,8 +9,6 @@ const extensions = [".js"];
119
export default {
1210
input,
1311
plugins: [
14-
resolve({ extensions }),
15-
commonjs(),
1612
sentryRollupPlugin({
1713
...placeHolderOptions,
1814
}),

0 commit comments

Comments
 (0)