Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add applicationKey option to identify application code from within the SDK #540

Merged
merged 2 commits into from
Jun 3, 2024
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
19 changes: 15 additions & 4 deletions packages/bundler-plugin-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -224,19 +224,30 @@ export function sentryUnpluginFactory({
plugins.push(releaseInjectionPlugin(injectionCode));
}

if (options.moduleMetadata) {
let metadata: Record<string, unknown>;
if (options.moduleMetadata || options.applicationKey) {
let metadata: Record<string, unknown> = {};

if (options.applicationKey) {
// We use different keys so that if user-code receives multiple bundling passes, we will store the application keys of all the passes.
// It is a bit unfortunate that we have to inject the metadata snippet at the top, because after multiple
// injections, the first injection will always "win" because it comes last in the code. We would generally be
// fine with making the last bundling pass win. But because it cannot win, we have to use a workaround of storing
// the app keys in different object keys.
// We can simply use the `_sentryBundlerPluginAppKey:` to filter for app keys in the SDK.
metadata[`_sentryBundlerPluginAppKey:${options.applicationKey}`] = true;
}

if (typeof options.moduleMetadata === "function") {
const args = {
org: options.org,
project: options.project,
release: options.release.name,
};
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
metadata = options.moduleMetadata(args);
metadata = { ...metadata, ...options.moduleMetadata(args) };
} else {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
metadata = options.moduleMetadata;
metadata = { ...metadata, ...options.moduleMetadata };
}

const injectionCode = generateModuleMetadataInjectorCode(metadata);
Expand Down
1 change: 1 addition & 0 deletions packages/bundler-plugin-core/src/options-mapping.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export function normalizeUserOptions(userOptions: UserOptions) {
metaFramework: userOptions._metaOptions?.telemetry?.metaFramework,
},
},
applicationKey: userOptions.applicationKey,
moduleMetadata: userOptions.moduleMetadata || userOptions._experiments?.moduleMetadata,
_experiments: userOptions._experiments ?? {},
};
Expand Down
6 changes: 6 additions & 0 deletions packages/bundler-plugin-core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,12 @@ export interface Options {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
moduleMetadata?: ModuleMetadata | ModuleMetadataCallback;

/**
* A key which will embedded in all the bundled files. The SDK will be able to use the key to apply filtering
* rules, for example using the `thirdPartyErrorFilterIntegration`.
*/
applicationKey?: string;

/**
* Options that are considered experimental and subject to change.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// Simply output the metadata to the console so it can be checked in a test
// eslint-disable-next-line no-console
console.log(JSON.stringify(global._sentryModuleMetadata));
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/* eslint-disable jest/no-standalone-expect */
/* eslint-disable jest/expect-expect */
import { execSync } from "child_process";
import path from "path";
import { testIfNodeMajorVersionIsLessThan18 } from "../../utils/testIf";

function checkBundle(bundlePath: string): void {
const output = execSync(`node ${bundlePath}`, { encoding: "utf-8" });

const map = JSON.parse(output) as Record<string, string>;

// There should be only one key in the map
expect(Object.values(map)).toHaveLength(1);
// The value should be the expected metadata
expect(Object.values(map)).toEqual([{ ["_sentryBundlerPluginAppKey:my-app"]: true }]);
}

describe("appKey injection", () => {
testIfNodeMajorVersionIsLessThan18("webpack 4 bundle", () => {
checkBundle(path.join(__dirname, "out", "webpack4", "bundle.js"));
});

test("webpack 5 bundle", () => {
checkBundle(path.join(__dirname, "out", "webpack5", "bundle.js"));
});

test("esbuild bundle", () => {
checkBundle(path.join(__dirname, "out", "esbuild", "bundle.js"));
});

test("rollup bundle", () => {
checkBundle(path.join(__dirname, "out", "rollup", "bundle.js"));
});

test("vite bundle", () => {
checkBundle(path.join(__dirname, "out", "vite", "bundle.js"));
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import * as path from "path";
import { createCjsBundles } from "../../utils/create-cjs-bundles";

const outputDir = path.resolve(__dirname, "out");

createCjsBundles(
{
bundle: path.resolve(__dirname, "input", "bundle.js"),
},
outputDir,
{
applicationKey: "my-app",
},
["webpack4", "webpack5", "esbuild", "rollup", "vite"]
);
Loading