Skip to content

Commit 2c77a7d

Browse files
committed
Sort out the tests and Vite
1 parent 0ddbac0 commit 2c77a7d

File tree

12 files changed

+244
-460
lines changed

12 files changed

+244
-460
lines changed

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

Lines changed: 0 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ import SentryCli from "@sentry/cli";
66
import { logger } from "@sentry/utils";
77
import * as fs from "fs";
88
import { glob } from "glob";
9-
import MagicString, { SourceMap } from "magic-string";
10-
import * as path from "path";
119
import { createUnplugin, TransformResult, UnpluginInstance, UnpluginOptions } from "unplugin";
1210
import { createSentryBuildPluginManager } from "./build-plugin-manager";
1311
import { createDebugIdUploadFunction } from "./debug-id-upload";
@@ -18,8 +16,6 @@ import {
1816
containsOnlyImports,
1917
generateReleaseInjectorCode,
2018
generateModuleMetadataInjectorCode,
21-
replaceBooleanFlagsInCode,
22-
stringToUUID,
2319
stripQueryAndHashFromPath,
2420
} from "./utils";
2521

@@ -231,24 +227,6 @@ export const COMMENT_USE_STRICT_REGEX =
231227
// Note: CodeQL complains that this regex potentially has n^2 runtime. This likely won't affect realistic files.
232228
/^(?:\s*|\/\*(?:.|\r|\n)*?\*\/|\/\/.*[\n\r])*(?:"[^"]*";|'[^']*';)?/;
233229

234-
/**
235-
* Simplified `renderChunk` hook type from Rollup.
236-
* We can't reference the type directly because the Vite plugin complains
237-
* about type mismatches
238-
*/
239-
type RenderChunkHook = (
240-
code: string,
241-
chunk: {
242-
fileName: string;
243-
facadeModuleId?: string | null;
244-
},
245-
outputOptions?: unknown,
246-
meta?: { magicString?: MagicString }
247-
) => {
248-
code: string;
249-
readonly map?: SourceMap;
250-
} | null;
251-
252230
/**
253231
* Checks if a file is a JavaScript file based on its extension.
254232
* Handles query strings and hashes in the filename.
@@ -289,89 +267,6 @@ export function shouldSkipCodeInjection(
289267
return false;
290268
}
291269

292-
export function createRollupBundleSizeOptimizationHooks(replacementValues: SentrySDKBuildFlags): {
293-
transform: UnpluginOptions["transform"];
294-
} {
295-
return {
296-
transform(code: string) {
297-
return replaceBooleanFlagsInCode(code, replacementValues);
298-
},
299-
};
300-
}
301-
302-
export function createRollupInjectionHooks(
303-
injectionCode: CodeInjection,
304-
debugIds: boolean
305-
): {
306-
renderChunk: RenderChunkHook;
307-
} {
308-
return {
309-
renderChunk(
310-
code: string,
311-
chunk: { fileName: string; facadeModuleId?: string | null },
312-
_?: unknown,
313-
meta?: { magicString?: MagicString }
314-
) {
315-
if (!isJsFile(chunk.fileName)) {
316-
return null; // returning null means not modifying the chunk at all
317-
}
318-
319-
// Skip empty chunks and HTML facade chunks (Vite MPA)
320-
if (shouldSkipCodeInjection(code, chunk.facadeModuleId)) {
321-
return null;
322-
}
323-
324-
const codeToInject = injectionCode.clone();
325-
326-
if (debugIds) {
327-
// Check if a debug ID has already been injected to avoid duplicate injection (e.g. by another plugin or Sentry CLI)
328-
const chunkStartSnippet = code.slice(0, 6000);
329-
const chunkEndSnippet = code.slice(-500);
330-
331-
if (
332-
!(
333-
chunkStartSnippet.includes("_sentryDebugIdIdentifier") ||
334-
chunkEndSnippet.includes("//# debugId=")
335-
)
336-
) {
337-
const debugId = stringToUUID(code); // generate a deterministic debug ID
338-
codeToInject.append(getDebugIdSnippet(debugId));
339-
}
340-
}
341-
342-
const ms = meta?.magicString || new MagicString(code, { filename: chunk.fileName });
343-
const match = code.match(COMMENT_USE_STRICT_REGEX)?.[0];
344-
345-
if (match) {
346-
// Add injected code after any comments or "use strict" at the beginning of the bundle.
347-
ms.appendLeft(match.length, codeToInject.code());
348-
} else {
349-
// ms.replace() doesn't work when there is an empty string match (which happens if
350-
// there is neither, a comment, nor a "use strict" at the top of the chunk) so we
351-
// need this special case here.
352-
ms.prepend(codeToInject.code());
353-
}
354-
355-
// Rolldown can pass a native MagicString instance in meta.magicString
356-
// https://rolldown.rs/in-depth/native-magic-string#usage-examples
357-
if (ms?.constructor?.name === "BindingMagicString") {
358-
// Rolldown docs say to return the magic string instance directly in this case
359-
return { code: ms as unknown as string };
360-
}
361-
362-
return {
363-
code: ms.toString(),
364-
get map() {
365-
return ms.generateMap({
366-
file: chunk.fileName,
367-
hires: "boundary",
368-
});
369-
},
370-
};
371-
},
372-
};
373-
}
374-
375270
export function globFiles(outputDir: string): Promise<string[]> {
376271
return glob(
377272
["/**/*.js", "/**/*.mjs", "/**/*.cjs", "/**/*.js.map", "/**/*.mjs.map", "/**/*.cjs.map"].map(
@@ -385,42 +280,6 @@ export function globFiles(outputDir: string): Promise<string[]> {
385280
);
386281
}
387282

388-
export function createRollupDebugIdUploadHooks(
389-
upload: (buildArtifacts: string[]) => Promise<void>,
390-
_logger: Logger,
391-
createDependencyOnBuildArtifacts: () => () => void
392-
): {
393-
writeBundle: (
394-
outputOptions: { dir?: string; file?: string },
395-
bundle: { [fileName: string]: unknown }
396-
) => Promise<void>;
397-
} {
398-
const freeGlobalDependencyOnDebugIdSourcemapArtifacts = createDependencyOnBuildArtifacts();
399-
return {
400-
async writeBundle(
401-
outputOptions: { dir?: string; file?: string },
402-
bundle: { [fileName: string]: unknown }
403-
) {
404-
try {
405-
if (outputOptions.dir) {
406-
const outputDir = outputOptions.dir;
407-
const buildArtifacts = await globFiles(outputDir);
408-
await upload(buildArtifacts);
409-
} else if (outputOptions.file) {
410-
await upload([outputOptions.file]);
411-
} else {
412-
const buildArtifacts = Object.keys(bundle).map((asset) =>
413-
path.join(path.resolve(), asset)
414-
);
415-
await upload(buildArtifacts);
416-
}
417-
} finally {
418-
freeGlobalDependencyOnDebugIdSourcemapArtifacts();
419-
}
420-
},
421-
};
422-
}
423-
424283
export function createComponentNameAnnotateHooks(
425284
ignoredComponents: string[],
426285
injectIntoHtml: boolean

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

Lines changed: 2 additions & 196 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Compiler } from "webpack";
2-
import { getDebugIdSnippet, sentryUnpluginFactory, createRollupInjectionHooks } from "../src";
3-
import { CodeInjection, containsOnlyImports } from "../src/utils";
2+
import { getDebugIdSnippet, sentryUnpluginFactory } from "../src";
3+
import { containsOnlyImports } from "../src/utils";
44

55
describe("getDebugIdSnippet", () => {
66
it("returns the debugId injection snippet for a passed debugId", () => {
@@ -142,200 +142,6 @@ app.mount('#app');
142142
});
143143
});
144144

145-
describe("createRollupInjectionHooks", () => {
146-
const inject = new CodeInjection();
147-
const hooks = createRollupInjectionHooks(inject, true);
148-
149-
beforeEach(() => {
150-
inject.clear();
151-
});
152-
153-
describe("renderChunk", () => {
154-
it("should inject debug ID into clean JavaScript files", () => {
155-
const code = 'console.log("Hello world");';
156-
const result = hooks.renderChunk(code, { fileName: "bundle.js" });
157-
158-
expect(result).not.toBeNull();
159-
expect(result?.code).toMatchInlineSnapshot(
160-
`"!function(){try{var e=\\"undefined\\"!=typeof window?window:\\"undefined\\"!=typeof global?global:\\"undefined\\"!=typeof globalThis?globalThis:\\"undefined\\"!=typeof self?self:{};var n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]=\\"d4309f93-5358-4ae1-bcf0-3813aa590eb5\\",e._sentryDebugIdIdentifier=\\"sentry-dbid-d4309f93-5358-4ae1-bcf0-3813aa590eb5\\");}catch(e){}}();console.log(\\"Hello world\\");"`
161-
);
162-
});
163-
164-
it("should inject debug ID after 'use strict'", () => {
165-
const code = '"use strict";\nconsole.log("Hello world");';
166-
const result = hooks.renderChunk(code, { fileName: "bundle.js" });
167-
168-
expect(result).not.toBeNull();
169-
expect(result?.code).toMatchInlineSnapshot(`
170-
"\\"use strict\\";!function(){try{var e=\\"undefined\\"!=typeof window?window:\\"undefined\\"!=typeof global?global:\\"undefined\\"!=typeof globalThis?globalThis:\\"undefined\\"!=typeof self?self:{};var n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]=\\"79a86c07-8ecc-4367-82b0-88cf822f2d41\\",e._sentryDebugIdIdentifier=\\"sentry-dbid-79a86c07-8ecc-4367-82b0-88cf822f2d41\\");}catch(e){}}();
171-
console.log(\\"Hello world\\");"
172-
`);
173-
});
174-
175-
it.each([
176-
["bundle.js"],
177-
["bundle.mjs"],
178-
["bundle.cjs"],
179-
["bundle.js?foo=bar"],
180-
["bundle.js#hash"],
181-
])("should process file '%s': %s", (fileName) => {
182-
const code = 'console.log("test");';
183-
const result = hooks.renderChunk(code, { fileName });
184-
185-
expect(result).not.toBeNull();
186-
expect(result?.code).toMatchInlineSnapshot(
187-
`"!function(){try{var e=\\"undefined\\"!=typeof window?window:\\"undefined\\"!=typeof global?global:\\"undefined\\"!=typeof globalThis?globalThis:\\"undefined\\"!=typeof self?self:{};var n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]=\\"b80112c0-6818-486d-96f0-185c023439b4\\",e._sentryDebugIdIdentifier=\\"sentry-dbid-b80112c0-6818-486d-96f0-185c023439b4\\");}catch(e){}}();console.log(\\"test\\");"`
188-
);
189-
});
190-
191-
it.each([["index.html"], ["styles.css"]])("should NOT process file '%s': %s", (fileName) => {
192-
const code = 'console.log("test");';
193-
const result = hooks.renderChunk(code, { fileName });
194-
195-
expect(result).toBeNull();
196-
});
197-
198-
it.each([
199-
[
200-
"inline format at start",
201-
';{try{(function(){var e="undefined"!=typeof window?window:e._sentryDebugIdIdentifier="sentry-dbid-existing-id");})();}catch(e){}};console.log("test");',
202-
],
203-
[
204-
"comment format at end",
205-
'console.log("test");\n//# debugId=f6ccd6f4-7ea0-4854-8384-1c9f8340af81\n//# sourceMappingURL=bundle.js.map',
206-
],
207-
[
208-
"inline format with large file",
209-
'"use strict";\n' +
210-
"// comment\n".repeat(10) +
211-
';{try{(function(){var e="undefined"!=typeof window?window:e._sentryDebugIdIdentifier="sentry-dbid-existing-id");})();}catch(e){}};' +
212-
'\nconsole.log("line");\n'.repeat(100),
213-
],
214-
])("should NOT inject when debug ID already exists (%s)", (_description, code) => {
215-
const result = hooks.renderChunk(code, { fileName: "bundle.js" });
216-
expect(result?.code).not.toContain("_sentryDebugIds");
217-
});
218-
219-
it("should only check boundaries for performance (not entire file)", () => {
220-
// Inline format beyond first 6KB boundary
221-
const codeWithInlineBeyond6KB =
222-
"a".repeat(6100) +
223-
';{try{(function(){var e="undefined"!=typeof window?window:e._sentryDebugIdIdentifier="sentry-dbid-existing-id");})();}catch(e){}};';
224-
225-
expect(hooks.renderChunk(codeWithInlineBeyond6KB, { fileName: "bundle.js" })).not.toBeNull();
226-
227-
// Comment format beyond last 500 bytes boundary
228-
const codeWithCommentBeyond500B =
229-
"//# debugId=f6ccd6f4-7ea0-4854-8384-1c9f8340af81\n" + "a".repeat(600);
230-
231-
expect(
232-
hooks.renderChunk(codeWithCommentBeyond500B, { fileName: "bundle.js" })
233-
).not.toBeNull();
234-
});
235-
236-
describe("HTML facade chunks (MPA vs SPA)", () => {
237-
// Issue #829: MPA facades should be skipped
238-
// Regression fix: SPA main bundles with HTML facades should NOT be skipped
239-
240-
it.each([
241-
["empty", ""],
242-
["only side-effect imports", `import './shared-module.js';`],
243-
["only named imports", `import { foo, bar } from './shared-module.js';`],
244-
["only re-exports", `export * from './shared-module.js';`],
245-
[
246-
"multiple imports and comments",
247-
`// This is a facade module
248-
import './moduleA.js';
249-
import { x } from './moduleB.js';
250-
/* block comment */
251-
export * from './moduleC.js';`,
252-
],
253-
["'use strict' and imports only", `"use strict";\nimport './shared-module.js';`],
254-
["query string in facadeModuleId", `import './shared.js';`, "?query=param"],
255-
["hash in facadeModuleId", `import './shared.js';`, "#hash"],
256-
])("should SKIP HTML facade chunks: %s", (_, code, suffix = "") => {
257-
const result = hooks.renderChunk(code, {
258-
fileName: "page1.js",
259-
facadeModuleId: `/path/to/page1.html${suffix}`,
260-
});
261-
expect(result).toBeNull();
262-
});
263-
264-
it("should inject into HTML facade with function declarations", () => {
265-
const result = hooks.renderChunk(`function main() { console.log("hello"); }`, {
266-
fileName: "index.js",
267-
facadeModuleId: "/path/to/index.html",
268-
});
269-
expect(result).not.toBeNull();
270-
expect(result?.code).toMatchInlineSnapshot(
271-
`"!function(){try{var e=\\"undefined\\"!=typeof window?window:\\"undefined\\"!=typeof global?global:\\"undefined\\"!=typeof globalThis?globalThis:\\"undefined\\"!=typeof self?self:{};var n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]=\\"c4c89e04-3658-4874-b25b-07e638185091\\",e._sentryDebugIdIdentifier=\\"sentry-dbid-c4c89e04-3658-4874-b25b-07e638185091\\");}catch(e){}}();function main() { console.log(\\"hello\\"); }"`
272-
);
273-
});
274-
275-
it("should inject into HTML facade with variable declarations", () => {
276-
const result = hooks.renderChunk(`const x = 42;`, {
277-
fileName: "index.js",
278-
facadeModuleId: "/path/to/index.html",
279-
});
280-
expect(result).not.toBeNull();
281-
expect(result?.code).toMatchInlineSnapshot(
282-
`"!function(){try{var e=\\"undefined\\"!=typeof window?window:\\"undefined\\"!=typeof global?global:\\"undefined\\"!=typeof globalThis?globalThis:\\"undefined\\"!=typeof self?self:{};var n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]=\\"43e69766-1963-49f2-a291-ff8de60cc652\\",e._sentryDebugIdIdentifier=\\"sentry-dbid-43e69766-1963-49f2-a291-ff8de60cc652\\");}catch(e){}}();const x = 42;"`
283-
);
284-
});
285-
286-
it("should inject into HTML facade with substantial code (SPA main bundle)", () => {
287-
const code = `import { initApp } from './app.js';
288-
289-
const config = { debug: true };
290-
291-
function bootstrap() {
292-
initApp(config);
293-
}
294-
295-
bootstrap();`;
296-
const result = hooks.renderChunk(code, {
297-
fileName: "index.js",
298-
facadeModuleId: "/path/to/index.html",
299-
});
300-
expect(result).not.toBeNull();
301-
expect(result?.code).toMatchInlineSnapshot(`
302-
"!function(){try{var e=\\"undefined\\"!=typeof window?window:\\"undefined\\"!=typeof global?global:\\"undefined\\"!=typeof globalThis?globalThis:\\"undefined\\"!=typeof self?self:{};var n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]=\\"d0c4524b-496e-45a4-9852-7558d043ba3c\\",e._sentryDebugIdIdentifier=\\"sentry-dbid-d0c4524b-496e-45a4-9852-7558d043ba3c\\");}catch(e){}}();import { initApp } from './app.js';
303-
304-
const config = { debug: true };
305-
306-
function bootstrap() {
307-
initApp(config);
308-
}
309-
310-
bootstrap();"
311-
`);
312-
});
313-
314-
it("should inject into HTML facade with mixed imports and code", () => {
315-
const result = hooks.renderChunk(
316-
`import './polyfills.js';\nimport { init } from './app.js';\n\ninit();`,
317-
{ fileName: "index.js", facadeModuleId: "/path/to/index.html" }
318-
);
319-
expect(result).not.toBeNull();
320-
expect(result?.code).toMatchInlineSnapshot(`
321-
"!function(){try{var e=\\"undefined\\"!=typeof window?window:\\"undefined\\"!=typeof global?global:\\"undefined\\"!=typeof globalThis?globalThis:\\"undefined\\"!=typeof self?self:{};var n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]=\\"28f0bbaa-9aeb-40c4-98c9-4e44f1d4e175\\",e._sentryDebugIdIdentifier=\\"sentry-dbid-28f0bbaa-9aeb-40c4-98c9-4e44f1d4e175\\");}catch(e){}}();import './polyfills.js';
322-
import { init } from './app.js';
323-
324-
init();"
325-
`);
326-
});
327-
328-
it("should inject into regular JS chunks (no HTML facade)", () => {
329-
const result = hooks.renderChunk(`console.log("Hello");`, { fileName: "bundle.js" });
330-
expect(result).not.toBeNull();
331-
expect(result?.code).toMatchInlineSnapshot(
332-
`"!function(){try{var e=\\"undefined\\"!=typeof window?window:\\"undefined\\"!=typeof global?global:\\"undefined\\"!=typeof globalThis?globalThis:\\"undefined\\"!=typeof self?self:{};var n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]=\\"79f18a7f-ca16-4168-9797-906c82058367\\",e._sentryDebugIdIdentifier=\\"sentry-dbid-79f18a7f-ca16-4168-9797-906c82058367\\");}catch(e){}}();console.log(\\"Hello\\");"`
333-
);
334-
});
335-
});
336-
});
337-
});
338-
339145
describe("sentryUnpluginFactory sourcemaps.disable behavior", () => {
340146
const mockComponentNameAnnotatePlugin = jest.fn(() => ({
341147
name: "mock-component-name-annotate-plugin",

packages/e2e-tests/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
"glob": "8.0.3",
3535
"jest": "^28.1.3",
3636
"premove": "^4.0.0",
37-
"rollup": "2.77.0",
37+
"rollup": "3.2.0",
3838
"ts-node": "^10.9.1",
3939
"vite": "3.0.0",
4040
"webpack4": "npm:webpack@^4",

0 commit comments

Comments
 (0)