Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,9 @@ In version 2 we removed this functionality because it lead to intransparent nami

Going forward, if you need similar functionality, we recommend providing folder paths in the `include` and `include.paths` options and narrowing down the matched files with the `ignore`, `ignoreFile` or `ext` options.
The `ignore` and `ignoreFile` options will still allow globbing patterns.

### Injecting `SENTRY_RELEASES` Map

Previously, the webpack plugin always injected a `SENTRY_RELEASES` variable into the global object which would map from `project@org` to the `release` value. In version 2, we made this behaviour opt-in by setting the `injectReleasesMap` option in the plugin options to `true`.

The purpose of this option is to support module-federated projects or micro frontend setups where multiple projects would want to access the global release variable. However, Sentry SDKs by default never accessed this variable so it would require manual user-intervention to make use of it. Making this behaviour opt-in decreases the bundle size impact of our plugin for the majority of users.
31 changes: 28 additions & 3 deletions packages/bundler-plugin-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,12 @@ const unplugin = createUnplugin<Options>((options, unpluginMetaContext) => {
});

if (id === RELEASE_INJECTOR_ID) {
return generateGlobalInjectorCode({ release: internalOptions.release });
return generateGlobalInjectorCode({
release: internalOptions.release,
injectReleasesMap: internalOptions.injectReleasesMap,
org: internalOptions.org,
project: internalOptions.project,
});
} else {
return undefined;
}
Expand Down Expand Up @@ -320,10 +325,20 @@ const unplugin = createUnplugin<Options>((options, unpluginMetaContext) => {
* Generates code for the "sentry-release-injector" which is responsible for setting the global `SENTRY_RELEASE`
* variable.
*/
function generateGlobalInjectorCode({ release }: { release: string }) {
function generateGlobalInjectorCode({
release,
injectReleasesMap,
org,
project,
}: {
release: string;
injectReleasesMap: boolean;
org?: string;
project?: string;
}) {
// The code below is mostly ternary operators because it saves bundle size.
// The checks are to support as many environments as possible. (Node.js, Browser, webworkers, etc.)
return `
let code = `
var _global =
typeof window !== 'undefined' ?
window :
Expand All @@ -334,6 +349,16 @@ function generateGlobalInjectorCode({ release }: { release: string }) {
{};

_global.SENTRY_RELEASE={id:"${release}"};`;

if (injectReleasesMap && project) {
const key = org ? `${project}@${org}` : project;
code += `
_global.SENTRY_RELEASES=_global.SENTRY_RELEASES || {};
_global.SENTRY_RELEASES["${key}"]={id:"${release}"};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

l: can we make this hidden behind a flag? I'd rather not inject things unnecessarily. I recognize this is a breaking change, but given this is undocumented I'm fine making the change.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's fair and it would not bloat the bundle unnecessarily for all 99% of users. Will do.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, is this something that people could have to opt-in to? If that's easy to do, I would say let people opt in (then, in a hypothetical world where we have a better understanding of which options are set by users, we can take better decisions in the future about this functionality). Seems like it is something pretty specific to want.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

`;
}

return code;
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
Expand Down
2 changes: 2 additions & 0 deletions packages/bundler-plugin-core/src/options-mapping.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ type RequiredInternalOptions = Required<
| "silent"
| "cleanArtifacts"
| "telemetry"
| "injectReleasesMap"
>
>;

Expand Down Expand Up @@ -100,6 +101,7 @@ export function normalizeUserOptions(userOptions: UserOptions): InternalOptions
entries,
include,
configFile: userOptions.configFile,
injectReleasesMap: userOptions.injectReleasesMap ?? false,
};
}

Expand Down
9 changes: 9 additions & 0 deletions packages/bundler-plugin-core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,15 @@ export type Options = Omit<IncludeEntry, "paths"> & {
* defaults from ~/.sentryclirc are always loaded
*/
configFile?: string;

/**
* If set to true, the plugin will inject an additional `SENTRY_RELEASES` variable that
* maps from `{org}@{project}` to the `release` value. This might be helpful for webpack
* module federation or micro frontend setups.
*
* Defaults to `false`
*/
injectReleasesMap?: boolean;
};

export type IncludeEntry = {
Expand Down
2 changes: 2 additions & 0 deletions packages/bundler-plugin-core/test/option-mappings.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ describe("normalizeUserOptions()", () => {
telemetry: true,
url: "https://sentry.io/",
vcsRemote: "origin",
injectReleasesMap: false,
});
});

Expand Down Expand Up @@ -78,6 +79,7 @@ describe("normalizeUserOptions()", () => {
telemetry: true,
url: "https://sentry.io/",
vcsRemote: "origin",
injectReleasesMap: false,
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
process.stdout.write(global.SENTRY_RELEASES["releasesProject@releasesOrg"].id.toString());
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import childProcess from "child_process";
import path from "path";

/**
* Runs a node file in a seprate process.
*
* @param bundlePath Path of node file to run
* @returns Stdout of the process
*/
function checkBundle(bundlePath: string): void {
const processOutput = childProcess.execSync(`node ${bundlePath}`, { encoding: "utf-8" });
expect(processOutput).toBe("I AM A RELEASE!");
}

test("esbuild bundle", () => {
expect.assertions(1);
checkBundle(path.join(__dirname, "./out/esbuild/index.js"));
});

test("rollup bundle", () => {
expect.assertions(1);
checkBundle(path.join(__dirname, "./out/rollup/index.js"));
});

test("vite bundle", () => {
expect.assertions(1);
checkBundle(path.join(__dirname, "./out/vite/index.js"));
});

test("webpack 4 bundle", () => {
expect.assertions(1);
checkBundle(path.join(__dirname, "./out/webpack4/index.js"));
});

test("webpack 5 bundle", () => {
expect.assertions(1);
checkBundle(path.join(__dirname, "./out/webpack5/index.js"));
});
15 changes: 15 additions & 0 deletions packages/integration-tests/fixtures/releases-injection/setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Options } from "@sentry/bundler-plugin-core";
import * as path from "path";
import { createCjsBundles } from "../../utils/create-cjs-bundles";

const entryPointPath = path.resolve(__dirname, "./input/entrypoint.js");
const outputDir = path.resolve(__dirname, "./out");

createCjsBundles({ index: entryPointPath }, outputDir, {
release: "I AM A RELEASE!",
project: "releasesProject",
org: "releasesOrg",
include: outputDir,
dryRun: true,
injectReleasesMap: true,
} as Options);