Skip to content

Commit 1271ced

Browse files
authored
feat: Rollup/Vite no longer uses unplugin (#858)
1 parent 79d3f9e commit 1271ced

File tree

17 files changed

+478
-543
lines changed

17 files changed

+478
-543
lines changed

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

Lines changed: 20 additions & 159 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";
@@ -16,10 +14,8 @@ import { Options, SentrySDKBuildFlags } from "./types";
1614
import {
1715
CodeInjection,
1816
containsOnlyImports,
19-
generateGlobalInjectorCode,
17+
generateReleaseInjectorCode,
2018
generateModuleMetadataInjectorCode,
21-
replaceBooleanFlagsInCode,
22-
stringToUUID,
2319
stripQueryAndHashFromPath,
2420
} from "./utils";
2521

@@ -121,7 +117,7 @@ export function sentryUnpluginFactory({
121117
"No release name provided. Will not inject release. Please set the `release.name` option to identify your release."
122118
);
123119
} else {
124-
const code = generateGlobalInjectorCode({
120+
const code = generateReleaseInjectorCode({
125121
release: options.release.name,
126122
injectBuildInformation: options._experiments.injectBuildInformation || false,
127123
});
@@ -230,33 +226,15 @@ export function sentryCliBinaryExists(): boolean {
230226

231227
// We need to be careful not to inject the snippet before any `"use strict";`s.
232228
// As an additional complication `"use strict";`s may come after any number of comments.
233-
const COMMENT_USE_STRICT_REGEX =
229+
export const COMMENT_USE_STRICT_REGEX =
234230
// Note: CodeQL complains that this regex potentially has n^2 runtime. This likely won't affect realistic files.
235231
/^(?:\s*|\/\*(?:.|\r|\n)*?\*\/|\/\/.*[\n\r])*(?:"[^"]*";|'[^']*';)?/;
236232

237-
/**
238-
* Simplified `renderChunk` hook type from Rollup.
239-
* We can't reference the type directly because the Vite plugin complains
240-
* about type mismatches
241-
*/
242-
type RenderChunkHook = (
243-
code: string,
244-
chunk: {
245-
fileName: string;
246-
facadeModuleId?: string | null;
247-
},
248-
outputOptions?: unknown,
249-
meta?: { magicString?: MagicString }
250-
) => {
251-
code: string;
252-
readonly map?: SourceMap;
253-
} | null;
254-
255233
/**
256234
* Checks if a file is a JavaScript file based on its extension.
257235
* Handles query strings and hashes in the filename.
258236
*/
259-
function isJsFile(fileName: string): boolean {
237+
export function isJsFile(fileName: string): boolean {
260238
const cleanFileName = stripQueryAndHashFromPath(fileName);
261239
return [".js", ".mjs", ".cjs"].some((ext) => cleanFileName.endsWith(ext));
262240
}
@@ -275,7 +253,10 @@ function isJsFile(fileName: string): boolean {
275253
* @param facadeModuleId - The facade module ID (if any) - HTML files create facade chunks
276254
* @returns true if the chunk should be skipped
277255
*/
278-
function shouldSkipCodeInjection(code: string, facadeModuleId: string | null | undefined): boolean {
256+
export function shouldSkipCodeInjection(
257+
code: string,
258+
facadeModuleId: string | null | undefined
259+
): boolean {
279260
// Skip empty chunks - these are placeholder chunks that should be optimized away
280261
if (code.trim().length === 0) {
281262
return true;
@@ -289,137 +270,17 @@ function shouldSkipCodeInjection(code: string, facadeModuleId: string | null | u
289270
return false;
290271
}
291272

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-
375-
export function createRollupDebugIdUploadHooks(
376-
upload: (buildArtifacts: string[]) => Promise<void>,
377-
_logger: Logger,
378-
createDependencyOnBuildArtifacts: () => () => void
379-
): {
380-
writeBundle: (
381-
outputOptions: { dir?: string; file?: string },
382-
bundle: { [fileName: string]: unknown }
383-
) => Promise<void>;
384-
} {
385-
const freeGlobalDependencyOnDebugIdSourcemapArtifacts = createDependencyOnBuildArtifacts();
386-
return {
387-
async writeBundle(
388-
outputOptions: { dir?: string; file?: string },
389-
bundle: { [fileName: string]: unknown }
390-
) {
391-
try {
392-
if (outputOptions.dir) {
393-
const outputDir = outputOptions.dir;
394-
const buildArtifacts = await glob(
395-
[
396-
"/**/*.js",
397-
"/**/*.mjs",
398-
"/**/*.cjs",
399-
"/**/*.js.map",
400-
"/**/*.mjs.map",
401-
"/**/*.cjs.map",
402-
].map((q) => `${q}?(\\?*)?(#*)`), // We want to allow query and hashes strings at the end of files
403-
{
404-
root: outputDir,
405-
absolute: true,
406-
nodir: true,
407-
}
408-
);
409-
await upload(buildArtifacts);
410-
} else if (outputOptions.file) {
411-
await upload([outputOptions.file]);
412-
} else {
413-
const buildArtifacts = Object.keys(bundle).map((asset) =>
414-
path.join(path.resolve(), asset)
415-
);
416-
await upload(buildArtifacts);
417-
}
418-
} finally {
419-
freeGlobalDependencyOnDebugIdSourcemapArtifacts();
420-
}
421-
},
422-
};
273+
export function globFiles(outputDir: string): Promise<string[]> {
274+
return glob(
275+
["/**/*.js", "/**/*.mjs", "/**/*.cjs", "/**/*.js.map", "/**/*.mjs.map", "/**/*.cjs.map"].map(
276+
(q) => `${q}?(\\?*)?(#*)`
277+
), // We want to allow query and hashes strings at the end of files
278+
{
279+
root: outputDir,
280+
absolute: true,
281+
nodir: true,
282+
}
283+
);
423284
}
424285

425286
export function createComponentNameAnnotateHooks(
@@ -497,7 +358,7 @@ export {
497358
CodeInjection,
498359
replaceBooleanFlagsInCode,
499360
stringToUUID,
500-
generateGlobalInjectorCode,
361+
generateReleaseInjectorCode,
501362
generateModuleMetadataInjectorCode,
502363
} from "./utils";
503364
export { createSentryBuildPluginManager } from "./build-plugin-manager";

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,7 @@ export function determineReleaseName(): string | undefined {
305305
* Generates code for the global injector which is responsible for setting the global
306306
* `SENTRY_RELEASE` & `SENTRY_BUILD_INFO` variables.
307307
*/
308-
export function generateGlobalInjectorCode({
308+
export function generateReleaseInjectorCode({
309309
release,
310310
injectBuildInformation,
311311
}: {
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3-
exports[`generateGlobalInjectorCode generates code with release 1`] = `"!function(){try{var e=\\"undefined\\"!=typeof window?window:\\"undefined\\"!=typeof global?global:\\"undefined\\"!=typeof globalThis?globalThis:\\"undefined\\"!=typeof self?self:{};e.SENTRY_RELEASE={id:\\"1.2.3\\"};}catch(e){}}();"`;
4-
5-
exports[`generateGlobalInjectorCode generates code with release and build information 1`] = `"!function(){try{var e=\\"undefined\\"!=typeof window?window:\\"undefined\\"!=typeof global?global:\\"undefined\\"!=typeof globalThis?globalThis:\\"undefined\\"!=typeof self?self:{};e.SENTRY_RELEASE={id:\\"1.2.3\\"};e.SENTRY_BUILD_INFO={\\"deps\\":[\\"myDep\\",\\"rollup\\"],\\"depsVersions\\":{\\"rollup\\":3},\\"nodeVersion\\":18};}catch(e){}}();"`;
6-
73
exports[`generateModuleMetadataInjectorCode generates code with empty metadata object 1`] = `"!function(){try{var e=\\"undefined\\"!=typeof window?window:\\"undefined\\"!=typeof global?global:\\"undefined\\"!=typeof globalThis?globalThis:\\"undefined\\"!=typeof self?self:{};e._sentryModuleMetadata=e._sentryModuleMetadata||{},e._sentryModuleMetadata[(new e.Error).stack]=function(e){for(var n=1;n<arguments.length;n++){var a=arguments[n];if(null!=a)for(var t in a)a.hasOwnProperty(t)&&(e[t]=a[t])}return e}({},e._sentryModuleMetadata[(new e.Error).stack],{});}catch(e){}}();"`;
84
95
exports[`generateModuleMetadataInjectorCode generates code with metadata object 1`] = `"!function(){try{var e=\\"undefined\\"!=typeof window?window:\\"undefined\\"!=typeof global?global:\\"undefined\\"!=typeof globalThis?globalThis:\\"undefined\\"!=typeof self?self:{};e._sentryModuleMetadata=e._sentryModuleMetadata||{},e._sentryModuleMetadata[(new e.Error).stack]=function(e){for(var n=1;n<arguments.length;n++){var a=arguments[n];if(null!=a)for(var t in a)a.hasOwnProperty(t)&&(e[t]=a[t])}return e}({},e._sentryModuleMetadata[(new e.Error).stack],{\\"file1.js\\":{\\"foo\\":\\"bar\\"},\\"file2.js\\":{\\"bar\\":\\"baz\\"}});}catch(e){}}();"`;
6+
7+
exports[`generateReleaseInjectorCode generates code with release 1`] = `"!function(){try{var e=\\"undefined\\"!=typeof window?window:\\"undefined\\"!=typeof global?global:\\"undefined\\"!=typeof globalThis?globalThis:\\"undefined\\"!=typeof self?self:{};e.SENTRY_RELEASE={id:\\"1.2.3\\"};}catch(e){}}();"`;
8+
9+
exports[`generateReleaseInjectorCode generates code with release and build information 1`] = `"!function(){try{var e=\\"undefined\\"!=typeof window?window:\\"undefined\\"!=typeof global?global:\\"undefined\\"!=typeof globalThis?globalThis:\\"undefined\\"!=typeof self?self:{};e.SENTRY_RELEASE={id:\\"1.2.3\\"};e.SENTRY_BUILD_INFO={\\"deps\\":[\\"myDep\\",\\"rollup\\"],\\"depsVersions\\":{\\"rollup\\":3},\\"nodeVersion\\":18};}catch(e){}}();"`;

0 commit comments

Comments
 (0)