Skip to content

Commit db8e347

Browse files
author
Luca Forstner
authored
fix: Fix esbuild debug ID generation (#325)
1 parent dee55bb commit db8e347

File tree

7 files changed

+101
-4
lines changed

7 files changed

+101
-4
lines changed

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,14 +175,20 @@ export function stringToUUID(str: string): string {
175175
const md5sum = crypto.createHash("md5");
176176
md5sum.update(str);
177177
const md5Hash = md5sum.digest("hex");
178+
179+
// Position 16 is fixed to either 8, 9, a, or b in the uuid v4 spec (10xx in binary)
180+
// RFC 4122 section 4.4
181+
const v4variant = ["8", "9", "a", "b"][md5Hash.substring(16, 17).charCodeAt(0) % 4] as string;
182+
178183
return (
179184
md5Hash.substring(0, 8) +
180185
"-" +
181186
md5Hash.substring(8, 12) +
182187
"-4" +
183188
md5Hash.substring(13, 16) +
184189
"-" +
185-
md5Hash.substring(16, 20) +
190+
v4variant +
191+
md5Hash.substring(17, 20) +
186192
"-" +
187193
md5Hash.substring(20)
188194
).toLowerCase();

packages/bundler-plugin-core/test/utils.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,6 @@ describe("getDependencies", () => {
172172

173173
describe("stringToUUID", () => {
174174
test("should return a deterministic UUID", () => {
175-
expect(stringToUUID("Nothing personnel kid")).toBe("52c7a762-5ddf-49a7-6f16-6874a8cb2512");
175+
expect(stringToUUID("Nothing personnel kid")).toBe("52c7a762-5ddf-49a7-af16-6874a8cb2512");
176176
});
177177
});

packages/esbuild-plugin/src/index.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ function esbuildDebugIdInjectionPlugin(): UnpluginOptions {
5151
} else {
5252
return {
5353
pluginName,
54+
// needs to be an abs path, otherwise esbuild will complain
55+
path: path.isAbsolute(args.path) ? args.path : path.join(args.resolveDir, args.path),
5456
pluginData: {
5557
isProxyResolver: true,
5658
originalPath: args.path,
@@ -81,11 +83,12 @@ function esbuildDebugIdInjectionPlugin(): UnpluginOptions {
8183
return {
8284
loader: "js",
8385
pluginName,
86+
// We need to use JSON.stringify below so that any escape backslashes stay escape backslashes, in order not to break paths on windows
8487
contents: `
8588
import "_sentry-debug-id-injection-stub";
86-
import * as OriginalModule from "${originalPath}";
89+
import * as OriginalModule from ${JSON.stringify(originalPath)};
8790
export default OriginalModule.default;
88-
export * from "${originalPath}";`,
91+
export * from ${JSON.stringify(originalPath)};`,
8992
resolveDir: originalResolveDir,
9093
};
9194
});
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/* eslint-disable jest/no-standalone-expect */
2+
/* eslint-disable jest/expect-expect */
3+
import childProcess from "child_process";
4+
import path from "path";
5+
import { testIfNodeMajorVersionIsLessThan18 } from "../../utils/testIf";
6+
7+
function checkBundle(bundlePath1: string, bundlePath2: string): string[] {
8+
const process1Output = childProcess.execSync(`node ${bundlePath1}`, { encoding: "utf-8" });
9+
const debugIdMap1 = JSON.parse(process1Output) as Record<string, string>;
10+
const debugIds1 = Object.values(debugIdMap1);
11+
expect(debugIds1.length).toBeGreaterThan(0);
12+
expect(debugIds1).toContainEqual(
13+
expect.stringMatching(/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/)
14+
);
15+
16+
const process2Output = childProcess.execSync(`node ${bundlePath2}`, { encoding: "utf-8" });
17+
const debugIdMap2 = JSON.parse(process2Output) as Record<string, string>;
18+
const debugIds2 = Object.values(debugIdMap2);
19+
expect(debugIds2.length).toBeGreaterThan(0);
20+
expect(debugIds2).toContainEqual(
21+
expect.stringMatching(/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/)
22+
);
23+
24+
expect(debugIds1).not.toEqual(debugIds2);
25+
26+
return [...debugIds1, ...debugIds2];
27+
}
28+
29+
test("esbuild bundle", () => {
30+
checkBundle(
31+
path.join(__dirname, "out", "esbuild", "bundle1.js"),
32+
path.join(__dirname, "out", "esbuild", "bundle2.js")
33+
);
34+
});
35+
36+
test("rollup bundle", () => {
37+
checkBundle(
38+
path.join(__dirname, "out", "rollup", "bundle1.js"),
39+
path.join(__dirname, "out", "rollup", "bundle2.js")
40+
);
41+
});
42+
43+
test("vite bundle", () => {
44+
checkBundle(
45+
path.join(__dirname, "out", "vite", "bundle1.js"),
46+
path.join(__dirname, "out", "vite", "bundle2.js")
47+
);
48+
});
49+
50+
testIfNodeMajorVersionIsLessThan18("webpack 4 bundle", () => {
51+
checkBundle(
52+
path.join(__dirname, "out", "webpack4", "bundle1.js"),
53+
path.join(__dirname, "out", "webpack4", "bundle2.js")
54+
);
55+
});
56+
57+
test("webpack 5 bundle", () => {
58+
checkBundle(
59+
path.join(__dirname, "out", "webpack5", "bundle1.js"),
60+
path.join(__dirname, "out", "webpack5", "bundle2.js")
61+
);
62+
});
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// eslint-disable-next-line no-console
2+
console.log(JSON.stringify(global._sentryDebugIds));
3+
4+
// Just so the two bundles generate different hashes:
5+
global.iAmBundle1 = 1;
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// eslint-disable-next-line no-console
2+
console.log(JSON.stringify(global._sentryDebugIds));
3+
4+
// Just so the two bundles generate different hashes:
5+
global.iAmBundle2 = 2;
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import * as path from "path";
2+
import { createCjsBundles } from "../../utils/create-cjs-bundles";
3+
4+
const outputDir = path.resolve(__dirname, "out");
5+
6+
createCjsBundles(
7+
{
8+
bundle1: path.resolve(__dirname, "input", "bundle1.js"),
9+
bundle2: path.resolve(__dirname, "input", "bundle2.js"),
10+
},
11+
outputDir,
12+
{
13+
telemetry: false,
14+
release: { name: "I AM A RELEASE!", create: false },
15+
}
16+
);

0 commit comments

Comments
 (0)