Skip to content

Commit 5388844

Browse files
author
Luca Forstner
committed
ref(core): Extract release injection into separate plugins
1 parent 1838cd0 commit 5388844

File tree

8 files changed

+264
-201
lines changed

8 files changed

+264
-201
lines changed

MIGRATION.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
This document serves as a migration guide, documenting all breaking changes between major versions of the Sentry bundler plugins.
44

5+
## Unreleased
6+
7+
- The minimum compatible version of rollup is version `3.2.0`.
8+
- Removed functionality for the `releaseInjectionTargets` option.
9+
510
## [Unreleased] Upgrading from 1.x to 2.x (Webpack Plugin Only)
611

712
Version 2 of `@sentry/webpack-plugin` is a complete rewrite of version 1, relying on bundler-agnostic code (based on [unjs/unplugin](https://github.com/unjs/unplugin)). While we tried to keep changes to v1 of the webpack plugin minimal, a adjustments are nevertheless necessary:

packages/bundler-plugin-core/package.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,14 @@
5353
},
5454
"dependencies": {
5555
"@sentry/cli": "^2.17.0",
56-
"@sentry/node": "^7.19.0",
57-
"@sentry/tracing": "^7.19.0",
56+
"@sentry/node": "7.19.0",
57+
"@sentry/tracing": "7.19.0",
5858
"find-up": "5.0.0",
5959
"glob": "9.3.2",
6060
"magic-string": "0.27.0",
6161
"unplugin": "1.0.1",
62-
"webpack-sources": "3.2.3"
62+
"webpack-sources": "3.2.3",
63+
"webpack-4": "npm:webpack@^4"
6364
},
6465
"devDependencies": {
6566
"@babel/core": "7.18.5",

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

Lines changed: 17 additions & 174 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { createUnplugin, UnpluginOptions } from "unplugin";
2-
import MagicString from "magic-string";
32
import { Options, BuildContext } from "./types";
43
import {
54
createNewRelease,
@@ -33,16 +32,15 @@ import { glob } from "glob";
3332
import { injectDebugIdSnippetIntoChunk, prepareBundleForDebugIdUpload } from "./debug-id";
3433
import webpackSources from "webpack-sources";
3534
import type { sources } from "webpack";
36-
37-
const ALLOWED_TRANSFORMATION_FILE_ENDINGS = [".js", ".ts", ".jsx", ".tsx", ".mjs"];
35+
import {
36+
esbuildReleaseInjectionPlugin,
37+
rolluplikeReleaseInjectionPlugin,
38+
webpackReleaseInjectionPlugin,
39+
} from "./plugins/release-injection";
3840

3941
// Use createRequire because esm doesn't like built-in require.resolve
4042
const require = createRequire(import.meta.url);
4143

42-
const releaseInjectionFilePath = require.resolve(
43-
"@sentry/bundler-plugin-core/sentry-release-injection-file"
44-
);
45-
4644
const esbuildDebugIdInjectionFilePath = require.resolve(
4745
"@sentry/bundler-plugin-core/sentry-esbuild-debugid-injection-file"
4846
);
@@ -168,125 +166,6 @@ const unplugin = createUnplugin<Options, true>((userOptions, unpluginMetaContext
168166
op: "function.plugin",
169167
name: "Sentry Bundler Plugin execution",
170168
});
171-
172-
releaseInjectionSpan = addSpanToTransaction(
173-
{ hub: sentryHub, parentSpan: transaction, logger, cli },
174-
"function.plugin.inject_release",
175-
"Release injection"
176-
);
177-
},
178-
179-
/**
180-
* Responsible for returning the "sentry-release-injector" ID when we encounter it. We return the ID so load is
181-
* called and we can "virtually" load the module. See `load` hook for more info on why it's virtual.
182-
*
183-
* We also record the id (i.e. absolute path) of any non-entrypoint.
184-
*
185-
* @param id For imports: The absolute path of the module to be imported. For entrypoints: The path the user defined as entrypoint - may also be relative.
186-
* @param importer For imports: The absolute path of the module that imported this module. For entrypoints: `undefined`.
187-
* @param options Additional information to use for making a resolving decision.
188-
* @returns `"sentry-release-injector"` when the imported file is called `"sentry-release-injector"`. Otherwise returns `undefined`.
189-
*/
190-
resolveId(id, importer, { isEntry }) {
191-
logger.debug('Called "resolveId":', { id, importer, isEntry });
192-
return undefined;
193-
},
194-
195-
/**
196-
* This hook determines whether we want to transform a module. In the sentry bundler plugin we want to transform every entrypoint
197-
* unless configured otherwise with the `releaseInjectionTargets` option.
198-
*
199-
* @param id Always the absolute (fully resolved) path to the module.
200-
* @returns `true` or `false` depending on whether we want to transform the module. For the sentry bundler plugin we only
201-
* want to transform the release injector file.
202-
*/
203-
transformInclude(id) {
204-
logger.debug('Called "transformInclude":', { id });
205-
206-
if (id.includes("sentry-release-injection-file")) {
207-
return true;
208-
}
209-
210-
if (id.match(/\\node_modules\\|\/node_modules\//)) {
211-
return false; // never transform 3rd party modules
212-
}
213-
214-
// We normalize the id because vite always passes `id` as a unix style path which causes problems when a user passes
215-
// a windows style path to `releaseInjectionTargets`
216-
const normalizedId = path.normalize(id);
217-
218-
if (options.releaseInjectionTargets) {
219-
// If there's an `releaseInjectionTargets` option transform (ie. inject the release varible) when the file path matches the option.
220-
if (typeof options.releaseInjectionTargets === "function") {
221-
return options.releaseInjectionTargets(normalizedId);
222-
}
223-
224-
return options.releaseInjectionTargets.some((entry) => {
225-
if (entry instanceof RegExp) {
226-
return entry.test(normalizedId);
227-
} else {
228-
const normalizedEntry = path.normalize(entry);
229-
return normalizedId === normalizedEntry;
230-
}
231-
});
232-
} else {
233-
const pathIsOrdinary = !normalizedId.includes("?") && !normalizedId.includes("#");
234-
235-
const pathHasAllowedFileEnding = ALLOWED_TRANSFORMATION_FILE_ENDINGS.some(
236-
(allowedFileEnding) => normalizedId.endsWith(allowedFileEnding)
237-
);
238-
239-
return pathIsOrdinary && pathHasAllowedFileEnding;
240-
}
241-
},
242-
243-
/**
244-
* This hook is responsible for injecting the "sentry release injector" imoprt statement into each entrypoint unless
245-
* configured otherwise with the `releaseInjectionTargets` option (logic for that is in the `transformInclude` hook).
246-
*
247-
* @param code Code of the file to transform.
248-
* @param id Always the absolute (fully resolved) path to the module.
249-
* @returns transformed code + source map
250-
*/
251-
async transform(code, id) {
252-
logger.debug('Called "transform":', { id });
253-
254-
if (!options.injectRelease) {
255-
return;
256-
}
257-
258-
// The MagicString library allows us to generate sourcemaps for the changes we make to the user code.
259-
const ms = new MagicString(code);
260-
261-
if (code.includes("_sentry_release_injection_file")) {
262-
// Appending instead of prepending has less probability of mucking with user's source maps.
263-
ms.append(
264-
generateGlobalInjectorCode({
265-
release: await releaseNamePromise,
266-
injectReleasesMap: options.injectReleasesMap,
267-
injectBuildInformation: options._experiments.injectBuildInformation || false,
268-
org: options.org,
269-
project: options.project,
270-
})
271-
);
272-
} else {
273-
// Appending instead of prepending has less probability of mucking with user's source maps.
274-
// Luckily import statements get hoisted to the top anyways.
275-
// 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).
276-
ms.append(`;\nimport "${releaseInjectionFilePath.replace(/\\/g, "\\\\")}";`);
277-
}
278-
279-
if (unpluginMetaContext.framework === "esbuild") {
280-
// esbuild + unplugin is buggy at the moment when we return an object with a `map` (sourcemap) property.
281-
// Currently just returning a string here seems to work and even correctly sourcemaps the code we generate.
282-
// However, other bundlers need the `map` property
283-
return ms.toString();
284-
} else {
285-
return {
286-
code: ms.toString(),
287-
map: ms.generateMap(),
288-
};
289-
}
290169
},
291170

292171
/**
@@ -505,6 +384,18 @@ const unplugin = createUnplugin<Options, true>((userOptions, unpluginMetaContext
505384
}
506385
}
507386

387+
if (options.injectRelease) {
388+
if (unpluginMetaContext.framework === "esbuild") {
389+
plugins.push(esbuildReleaseInjectionPlugin(options));
390+
} else if (unpluginMetaContext.framework === "webpack") {
391+
plugins.push(webpackReleaseInjectionPlugin(options));
392+
} else if (unpluginMetaContext.framework === "rollup") {
393+
plugins.push(rolluplikeReleaseInjectionPlugin(options));
394+
} else if (unpluginMetaContext.framework === "vite") {
395+
plugins.push(rolluplikeReleaseInjectionPlugin(options));
396+
}
397+
}
398+
508399
return plugins;
509400
});
510401

@@ -530,54 +421,6 @@ function handleError(
530421
}
531422
}
532423

533-
/**
534-
* Generates code for the global injector which is responsible for setting the global
535-
* `SENTRY_RELEASE` & `SENTRY_BUILD_INFO` variables.
536-
*/
537-
function generateGlobalInjectorCode({
538-
release,
539-
injectReleasesMap,
540-
injectBuildInformation,
541-
org,
542-
project,
543-
}: {
544-
release: string;
545-
injectReleasesMap: boolean;
546-
injectBuildInformation: boolean;
547-
org?: string;
548-
project?: string;
549-
}) {
550-
// The code below is mostly ternary operators because it saves bundle size.
551-
// The checks are to support as many environments as possible. (Node.js, Browser, webworkers, etc.)
552-
let code = `
553-
var _global =
554-
typeof window !== 'undefined' ?
555-
window :
556-
typeof global !== 'undefined' ?
557-
global :
558-
typeof self !== 'undefined' ?
559-
self :
560-
{};
561-
562-
_global.SENTRY_RELEASE={id:"${release}"};`;
563-
564-
if (injectReleasesMap && project) {
565-
const key = org ? `${project}@${org}` : project;
566-
code += `
567-
_global.SENTRY_RELEASES=_global.SENTRY_RELEASES || {};
568-
_global.SENTRY_RELEASES["${key}"]={id:"${release}"};`;
569-
}
570-
571-
if (injectBuildInformation) {
572-
const buildInfo = getBuildInformation();
573-
574-
code += `
575-
_global.SENTRY_BUILD_INFO=${JSON.stringify(buildInfo)};`;
576-
}
577-
578-
return code;
579-
}
580-
581424
export function getBuildInformation() {
582425
const packageJson = getPackageJson();
583426

packages/bundler-plugin-core/src/options-mapping.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ type OptionalInternalOptions = Partial<
3333
| "configFile"
3434
| "headers"
3535
| "sourcemaps"
36+
| "release"
3637
>
3738
>;
3839

0 commit comments

Comments
 (0)