From 2a473399af9e0bbca10a60872fe5d9187183dba0 Mon Sep 17 00:00:00 2001 From: Tim Fish Date: Thu, 29 Jan 2026 18:30:52 +0000 Subject: [PATCH 1/8] PoC: Regular Rollup Plugin --- packages/bundler-plugin-core/src/index.ts | 44 +-- packages/bundler-plugin-core/src/utils.ts | 2 +- .../test/__snapshots__/utils.test.ts.snap | 8 +- .../bundler-plugin-core/test/utils.test.ts | 8 +- packages/rollup-plugin/src/index.ts | 250 ++++++++++++++---- .../rollup-plugin/test/public-api.test.ts | 21 +- 6 files changed, 241 insertions(+), 92 deletions(-) diff --git a/packages/bundler-plugin-core/src/index.ts b/packages/bundler-plugin-core/src/index.ts index a31e4215..e282f625 100644 --- a/packages/bundler-plugin-core/src/index.ts +++ b/packages/bundler-plugin-core/src/index.ts @@ -16,7 +16,7 @@ import { Options, SentrySDKBuildFlags } from "./types"; import { CodeInjection, containsOnlyImports, - generateGlobalInjectorCode, + generateReleaseInjectorCode, generateModuleMetadataInjectorCode, replaceBooleanFlagsInCode, stringToUUID, @@ -118,7 +118,7 @@ export function sentryUnpluginFactory({ "No release name provided. Will not inject release. Please set the `release.name` option to identify your release." ); } else { - const code = generateGlobalInjectorCode({ + const code = generateReleaseInjectorCode({ release: options.release.name, injectBuildInformation: options._experiments.injectBuildInformation || false, }); @@ -227,7 +227,7 @@ export function sentryCliBinaryExists(): boolean { // We need to be careful not to inject the snippet before any `"use strict";`s. // As an additional complication `"use strict";`s may come after any number of comments. -const COMMENT_USE_STRICT_REGEX = +export const COMMENT_USE_STRICT_REGEX = // Note: CodeQL complains that this regex potentially has n^2 runtime. This likely won't affect realistic files. /^(?:\s*|\/\*(?:.|\r|\n)*?\*\/|\/\/.*[\n\r])*(?:"[^"]*";|'[^']*';)?/; @@ -253,7 +253,7 @@ type RenderChunkHook = ( * Checks if a file is a JavaScript file based on its extension. * Handles query strings and hashes in the filename. */ -function isJsFile(fileName: string): boolean { +export function isJsFile(fileName: string): boolean { const cleanFileName = stripQueryAndHashFromPath(fileName); return [".js", ".mjs", ".cjs"].some((ext) => cleanFileName.endsWith(ext)); } @@ -272,7 +272,10 @@ function isJsFile(fileName: string): boolean { * @param facadeModuleId - The facade module ID (if any) - HTML files create facade chunks * @returns true if the chunk should be skipped */ -function shouldSkipCodeInjection(code: string, facadeModuleId: string | null | undefined): boolean { +export function shouldSkipCodeInjection( + code: string, + facadeModuleId: string | null | undefined +): boolean { // Skip empty chunks - these are placeholder chunks that should be optimized away if (code.trim().length === 0) { return true; @@ -369,6 +372,19 @@ export function createRollupInjectionHooks( }; } +export function globFiles(outputDir: string): Promise { + return glob( + ["/**/*.js", "/**/*.mjs", "/**/*.cjs", "/**/*.js.map", "/**/*.mjs.map", "/**/*.cjs.map"].map( + (q) => `${q}?(\\?*)?(#*)` + ), // We want to allow query and hashes strings at the end of files + { + root: outputDir, + absolute: true, + nodir: true, + } + ); +} + export function createRollupDebugIdUploadHooks( upload: (buildArtifacts: string[]) => Promise, _logger: Logger, @@ -388,21 +404,7 @@ export function createRollupDebugIdUploadHooks( try { if (outputOptions.dir) { const outputDir = outputOptions.dir; - const buildArtifacts = await glob( - [ - "/**/*.js", - "/**/*.mjs", - "/**/*.cjs", - "/**/*.js.map", - "/**/*.mjs.map", - "/**/*.cjs.map", - ].map((q) => `${q}?(\\?*)?(#*)`), // We want to allow query and hashes strings at the end of files - { - root: outputDir, - absolute: true, - nodir: true, - } - ); + const buildArtifacts = await globFiles(outputDir); await upload(buildArtifacts); } else if (outputOptions.file) { await upload([outputOptions.file]); @@ -492,3 +494,5 @@ export type { Logger } from "./logger"; export type { Options, SentrySDKBuildFlags } from "./types"; export { CodeInjection, replaceBooleanFlagsInCode, stringToUUID } from "./utils"; export { createSentryBuildPluginManager } from "./build-plugin-manager"; +export { generateReleaseInjectorCode, generateModuleMetadataInjectorCode } from "./utils"; +export { createDebugIdUploadFunction } from "./debug-id-upload"; diff --git a/packages/bundler-plugin-core/src/utils.ts b/packages/bundler-plugin-core/src/utils.ts index 9726895e..45d98ab4 100644 --- a/packages/bundler-plugin-core/src/utils.ts +++ b/packages/bundler-plugin-core/src/utils.ts @@ -305,7 +305,7 @@ export function determineReleaseName(): string | undefined { * Generates code for the global injector which is responsible for setting the global * `SENTRY_RELEASE` & `SENTRY_BUILD_INFO` variables. */ -export function generateGlobalInjectorCode({ +export function generateReleaseInjectorCode({ release, injectBuildInformation, }: { diff --git a/packages/bundler-plugin-core/test/__snapshots__/utils.test.ts.snap b/packages/bundler-plugin-core/test/__snapshots__/utils.test.ts.snap index 1bacbe62..0597ebce 100644 --- a/packages/bundler-plugin-core/test/__snapshots__/utils.test.ts.snap +++ b/packages/bundler-plugin-core/test/__snapshots__/utils.test.ts.snap @@ -1,9 +1,9 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -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){}}();"`; - -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){}}();"`; - 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 { +describe("generateReleaseInjectorCode", () => { it("generates code with release", () => { - const generatedCode = generateGlobalInjectorCode({ + const generatedCode = generateReleaseInjectorCode({ release: "1.2.3", injectBuildInformation: false, }); @@ -244,7 +244,7 @@ describe("generateGlobalInjectorCode", () => { }) ); - const generatedCode = generateGlobalInjectorCode({ + const generatedCode = generateReleaseInjectorCode({ release: "1.2.3", injectBuildInformation: true, }); diff --git a/packages/rollup-plugin/src/index.ts b/packages/rollup-plugin/src/index.ts index f29ac5f0..21993770 100644 --- a/packages/rollup-plugin/src/index.ts +++ b/packages/rollup-plugin/src/index.ts @@ -1,62 +1,218 @@ import { - CodeInjection, - sentryUnpluginFactory, + createSentryBuildPluginManager, + generateReleaseInjectorCode, + generateModuleMetadataInjectorCode, + isJsFile, + shouldSkipCodeInjection, Options, - createRollupInjectionHooks, - createRollupDebugIdUploadHooks, - SentrySDKBuildFlags, - createRollupBundleSizeOptimizationHooks, + getDebugIdSnippet, + stringToUUID, + COMMENT_USE_STRICT_REGEX, + createDebugIdUploadFunction, + globFiles, createComponentNameAnnotateHooks, - Logger, + replaceBooleanFlagsInCode, + CodeInjection, } from "@sentry/bundler-plugin-core"; -import type { UnpluginOptions } from "unplugin"; +import MagicString, { SourceMap } from "magic-string"; +import type { TransformHook } from "rollup"; +import * as path from "path"; -function rollupComponentNameAnnotatePlugin( - ignoredComponents: string[], - injectIntoHtml: boolean -): UnpluginOptions { - return { - name: "sentry-rollup-component-name-annotate-plugin", - rollup: createComponentNameAnnotateHooks(ignoredComponents, injectIntoHtml), - }; -} +function hasExistingDebugID(code: string): boolean { + // Check if a debug ID has already been injected to avoid duplicate injection (e.g. by another plugin or Sentry CLI) + const chunkStartSnippet = code.slice(0, 6000); + const chunkEndSnippet = code.slice(-500); -function rollupInjectionPlugin(injectionCode: CodeInjection, debugIds: boolean): UnpluginOptions { - return { - name: "sentry-rollup-injection-plugin", - rollup: createRollupInjectionHooks(injectionCode, debugIds), - }; -} + if ( + chunkStartSnippet.includes("_sentryDebugIdIdentifier") || + chunkEndSnippet.includes("//# debugId=") + ) { + return true; // Debug ID already present, skip injection + } -function rollupDebugIdUploadPlugin( - upload: (buildArtifacts: string[]) => Promise, - logger: Logger, - createDependencyOnBuildArtifacts: () => () => void -): UnpluginOptions { - return { - name: "sentry-rollup-debug-id-upload-plugin", - rollup: createRollupDebugIdUploadHooks(upload, logger, createDependencyOnBuildArtifacts), - }; + return false; } -function rollupBundleSizeOptimizationsPlugin( - replacementValues: SentrySDKBuildFlags -): UnpluginOptions { +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type +export function sentryRollupPlugin(userOptions: Options = {}) { + const sentryBuildPluginManager = createSentryBuildPluginManager(userOptions, { + loggerPrefix: userOptions._metaOptions?.loggerPrefixOverride ?? "[sentry-rollup-plugin]", + buildTool: "rollup", + }); + + const { + logger, + normalizedOptions: options, + bundleSizeOptimizationReplacementValues: replacementValues, + bundleMetadata, + createDependencyOnBuildArtifacts, + } = sentryBuildPluginManager; + + if (options.disable) { + return { + name: "sentry-noop-plugin", + }; + } + + if (process.cwd().match(/\\node_modules\\|\/node_modules\//)) { + logger.warn( + "Running Sentry plugin from within a `node_modules` folder. Some features may not work." + ); + } + + const freeGlobalDependencyOnBuildArtifacts = createDependencyOnBuildArtifacts(); + const upload = createDebugIdUploadFunction({ sentryBuildPluginManager }); + const sourcemapsEnabled = options.sourcemaps?.disable !== true; + const staticInjectionCode = new CodeInjection(); + + if (!options.release.inject) { + logger.debug( + "Release injection disabled via `release.inject` option. Will not inject release." + ); + } else if (!options.release.name) { + logger.debug( + "No release name provided. Will not inject release. Please set the `release.name` option to identify your release." + ); + } else { + staticInjectionCode.append( + generateReleaseInjectorCode({ + release: options.release.name, + injectBuildInformation: options._experiments.injectBuildInformation || false, + }) + ); + } + + if (Object.keys(bundleMetadata).length > 0) { + staticInjectionCode.append(generateModuleMetadataInjectorCode(bundleMetadata)); + } + + const transformAnnotations = options.reactComponentAnnotation?.enabled + ? createComponentNameAnnotateHooks( + options.reactComponentAnnotation?.ignoredComponents || [], + !!options.reactComponentAnnotation?._experimentalInjectIntoHtml + ) + : undefined; + + const transformReplace = Object.keys(replacementValues).length > 0; + const shouldTransform = transformAnnotations || transformReplace; + + function buildStart(): void { + void sentryBuildPluginManager.telemetry.emitBundlerPluginExecutionSignal().catch(() => { + // Telemetry failures are acceptable + }); + } + + function transform(code: string, id: string): ReturnType { + // Component annotations are only in user code and boolean flag replacements are + // only in Sentry code. If we successfully add annotations, we can return early. + + if (transformAnnotations?.transform) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore TS complains about 'this' + const result = transformAnnotations.transform(code, id); + if (result) { + return result; + } + } + + if (transformReplace) { + return replaceBooleanFlagsInCode(code, replacementValues); + } + + return null; + } + + function renderChunk( + code: string, + chunk: { fileName: string; facadeModuleId?: string | null } + ): { + code: string; + map: SourceMap; + } | null { + if (!isJsFile(chunk.fileName)) { + return null; // returning null means not modifying the chunk at all + } + + // Skip empty chunks and HTML facade chunks (Vite MPA) + if (shouldSkipCodeInjection(code, chunk.facadeModuleId)) { + return null; + } + + const injectCode = staticInjectionCode.clone(); + + if (sourcemapsEnabled && !hasExistingDebugID(code)) { + const debugId = stringToUUID(code); // generate a deterministic debug ID + injectCode.append(getDebugIdSnippet(debugId)); + } + + if (injectCode.isEmpty()) { + return null; + } + + const ms = new MagicString(code, { filename: chunk.fileName }); + + const match = code.match(COMMENT_USE_STRICT_REGEX)?.[0]; + + if (match) { + // Add injected code after any comments or "use strict" at the beginning of the bundle. + ms.appendLeft(match.length, injectCode.code()); + } else { + // ms.replace() doesn't work when there is an empty string match (which happens if + // there is neither, a comment, nor a "use strict" at the top of the chunk) so we + // need this special case here. + ms.prepend(injectCode.code()); + } + + return { + code: ms.toString(), + map: ms.generateMap({ file: chunk.fileName, hires: "boundary" as unknown as undefined }), + }; + } + + async function writeBundle( + outputOptions: { dir?: string; file?: string }, + bundle: { [fileName: string]: unknown } + ): Promise { + if (!sourcemapsEnabled) { + return; + } + + try { + await sentryBuildPluginManager.createRelease(); + + if (outputOptions.dir) { + const outputDir = outputOptions.dir; + const buildArtifacts = await globFiles(outputDir); + await upload(buildArtifacts); + } else if (outputOptions.file) { + await upload([outputOptions.file]); + } else { + const buildArtifacts = Object.keys(bundle).map((asset) => path.join(path.resolve(), asset)); + await upload(buildArtifacts); + } + } finally { + freeGlobalDependencyOnBuildArtifacts(); + await sentryBuildPluginManager.deleteArtifacts(); + } + } + + if (shouldTransform) { + return { + name: "sentry-rollup-plugin", + buildStart, + transform, + renderChunk, + writeBundle, + }; + } + return { - name: "sentry-rollup-bundle-size-optimizations-plugin", - rollup: createRollupBundleSizeOptimizationHooks(replacementValues), + name: "sentry-rollup-plugin", + buildStart, + renderChunk, + writeBundle, }; } -const sentryUnplugin = sentryUnpluginFactory({ - injectionPlugin: rollupInjectionPlugin, - componentNameAnnotatePlugin: rollupComponentNameAnnotatePlugin, - debugIdUploadPlugin: rollupDebugIdUploadPlugin, - bundleSizeOptimizationsPlugin: rollupBundleSizeOptimizationsPlugin, -}); - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export const sentryRollupPlugin: (options?: Options) => any = sentryUnplugin.rollup; - export type { Options as SentryRollupPluginOptions } from "@sentry/bundler-plugin-core"; export { sentryCliBinaryExists } from "@sentry/bundler-plugin-core"; diff --git a/packages/rollup-plugin/test/public-api.test.ts b/packages/rollup-plugin/test/public-api.test.ts index ced22fbc..b4e72418 100644 --- a/packages/rollup-plugin/test/public-api.test.ts +++ b/packages/rollup-plugin/test/public-api.test.ts @@ -1,4 +1,3 @@ -import { Plugin } from "rollup"; import { sentryRollupPlugin } from "../src"; test("Rollup plugin should exist", () => { @@ -11,25 +10,15 @@ describe("sentryRollupPlugin", () => { jest.clearAllMocks(); }); - it("returns an array of rollup plugins", () => { - const plugins = sentryRollupPlugin({ + it("returns a single rollup plugin", () => { + const plugin = sentryRollupPlugin({ authToken: "test-token", org: "test-org", project: "test-project", - }) as Plugin[]; + }); - expect(Array.isArray(plugins)).toBe(true); + expect(Array.isArray(plugin)).not.toBe(true); - const pluginNames = plugins.map((plugin) => plugin.name); - - expect(pluginNames).toEqual( - expect.arrayContaining([ - "sentry-telemetry-plugin", - "sentry-release-management-plugin", - "sentry-rollup-injection-plugin", - "sentry-rollup-debug-id-upload-plugin", - "sentry-file-deletion-plugin", - ]) - ); + expect(plugin.name).toBe("sentry-rollup-plugin"); }); }); From 2c77a7d2eed4cd6ebd4dc9a1e47b79b61fccb80b Mon Sep 17 00:00:00 2001 From: Tim Fish Date: Mon, 9 Feb 2026 13:21:37 +0100 Subject: [PATCH 2/8] Sort out the tests and Vite --- packages/bundler-plugin-core/src/index.ts | 141 ------------ .../bundler-plugin-core/test/index.test.ts | 198 +---------------- packages/e2e-tests/package.json | 2 +- .../disabled-sourcemaps-upload.test.ts | 26 --- .../input/bundle.js | 2 - packages/rollup-plugin/package.json | 2 +- packages/rollup-plugin/src/index.ts | 38 +++- .../rollup-plugin/test/public-api.test.ts | 200 +++++++++++++++++- packages/vite-plugin/package.json | 2 +- packages/vite-plugin/src/index.ts | 76 +------ packages/vite-plugin/test/public-api.test.ts | 10 +- yarn.lock | 7 - 12 files changed, 244 insertions(+), 460 deletions(-) delete mode 100644 packages/integration-tests/fixtures/disabled-sourcemaps-upload/disabled-sourcemaps-upload.test.ts delete mode 100644 packages/integration-tests/fixtures/disabled-sourcemaps-upload/input/bundle.js diff --git a/packages/bundler-plugin-core/src/index.ts b/packages/bundler-plugin-core/src/index.ts index e282f625..345f4b9a 100644 --- a/packages/bundler-plugin-core/src/index.ts +++ b/packages/bundler-plugin-core/src/index.ts @@ -6,8 +6,6 @@ import SentryCli from "@sentry/cli"; import { logger } from "@sentry/utils"; import * as fs from "fs"; import { glob } from "glob"; -import MagicString, { SourceMap } from "magic-string"; -import * as path from "path"; import { createUnplugin, TransformResult, UnpluginInstance, UnpluginOptions } from "unplugin"; import { createSentryBuildPluginManager } from "./build-plugin-manager"; import { createDebugIdUploadFunction } from "./debug-id-upload"; @@ -18,8 +16,6 @@ import { containsOnlyImports, generateReleaseInjectorCode, generateModuleMetadataInjectorCode, - replaceBooleanFlagsInCode, - stringToUUID, stripQueryAndHashFromPath, } from "./utils"; @@ -231,24 +227,6 @@ export const COMMENT_USE_STRICT_REGEX = // Note: CodeQL complains that this regex potentially has n^2 runtime. This likely won't affect realistic files. /^(?:\s*|\/\*(?:.|\r|\n)*?\*\/|\/\/.*[\n\r])*(?:"[^"]*";|'[^']*';)?/; -/** - * Simplified `renderChunk` hook type from Rollup. - * We can't reference the type directly because the Vite plugin complains - * about type mismatches - */ -type RenderChunkHook = ( - code: string, - chunk: { - fileName: string; - facadeModuleId?: string | null; - }, - outputOptions?: unknown, - meta?: { magicString?: MagicString } -) => { - code: string; - readonly map?: SourceMap; -} | null; - /** * Checks if a file is a JavaScript file based on its extension. * Handles query strings and hashes in the filename. @@ -289,89 +267,6 @@ export function shouldSkipCodeInjection( return false; } -export function createRollupBundleSizeOptimizationHooks(replacementValues: SentrySDKBuildFlags): { - transform: UnpluginOptions["transform"]; -} { - return { - transform(code: string) { - return replaceBooleanFlagsInCode(code, replacementValues); - }, - }; -} - -export function createRollupInjectionHooks( - injectionCode: CodeInjection, - debugIds: boolean -): { - renderChunk: RenderChunkHook; -} { - return { - renderChunk( - code: string, - chunk: { fileName: string; facadeModuleId?: string | null }, - _?: unknown, - meta?: { magicString?: MagicString } - ) { - if (!isJsFile(chunk.fileName)) { - return null; // returning null means not modifying the chunk at all - } - - // Skip empty chunks and HTML facade chunks (Vite MPA) - if (shouldSkipCodeInjection(code, chunk.facadeModuleId)) { - return null; - } - - const codeToInject = injectionCode.clone(); - - if (debugIds) { - // Check if a debug ID has already been injected to avoid duplicate injection (e.g. by another plugin or Sentry CLI) - const chunkStartSnippet = code.slice(0, 6000); - const chunkEndSnippet = code.slice(-500); - - if ( - !( - chunkStartSnippet.includes("_sentryDebugIdIdentifier") || - chunkEndSnippet.includes("//# debugId=") - ) - ) { - const debugId = stringToUUID(code); // generate a deterministic debug ID - codeToInject.append(getDebugIdSnippet(debugId)); - } - } - - const ms = meta?.magicString || new MagicString(code, { filename: chunk.fileName }); - const match = code.match(COMMENT_USE_STRICT_REGEX)?.[0]; - - if (match) { - // Add injected code after any comments or "use strict" at the beginning of the bundle. - ms.appendLeft(match.length, codeToInject.code()); - } else { - // ms.replace() doesn't work when there is an empty string match (which happens if - // there is neither, a comment, nor a "use strict" at the top of the chunk) so we - // need this special case here. - ms.prepend(codeToInject.code()); - } - - // Rolldown can pass a native MagicString instance in meta.magicString - // https://rolldown.rs/in-depth/native-magic-string#usage-examples - if (ms?.constructor?.name === "BindingMagicString") { - // Rolldown docs say to return the magic string instance directly in this case - return { code: ms as unknown as string }; - } - - return { - code: ms.toString(), - get map() { - return ms.generateMap({ - file: chunk.fileName, - hires: "boundary", - }); - }, - }; - }, - }; -} - export function globFiles(outputDir: string): Promise { return glob( ["/**/*.js", "/**/*.mjs", "/**/*.cjs", "/**/*.js.map", "/**/*.mjs.map", "/**/*.cjs.map"].map( @@ -385,42 +280,6 @@ export function globFiles(outputDir: string): Promise { ); } -export function createRollupDebugIdUploadHooks( - upload: (buildArtifacts: string[]) => Promise, - _logger: Logger, - createDependencyOnBuildArtifacts: () => () => void -): { - writeBundle: ( - outputOptions: { dir?: string; file?: string }, - bundle: { [fileName: string]: unknown } - ) => Promise; -} { - const freeGlobalDependencyOnDebugIdSourcemapArtifacts = createDependencyOnBuildArtifacts(); - return { - async writeBundle( - outputOptions: { dir?: string; file?: string }, - bundle: { [fileName: string]: unknown } - ) { - try { - if (outputOptions.dir) { - const outputDir = outputOptions.dir; - const buildArtifacts = await globFiles(outputDir); - await upload(buildArtifacts); - } else if (outputOptions.file) { - await upload([outputOptions.file]); - } else { - const buildArtifacts = Object.keys(bundle).map((asset) => - path.join(path.resolve(), asset) - ); - await upload(buildArtifacts); - } - } finally { - freeGlobalDependencyOnDebugIdSourcemapArtifacts(); - } - }, - }; -} - export function createComponentNameAnnotateHooks( ignoredComponents: string[], injectIntoHtml: boolean diff --git a/packages/bundler-plugin-core/test/index.test.ts b/packages/bundler-plugin-core/test/index.test.ts index 863d3666..2b56d984 100644 --- a/packages/bundler-plugin-core/test/index.test.ts +++ b/packages/bundler-plugin-core/test/index.test.ts @@ -1,6 +1,6 @@ import { Compiler } from "webpack"; -import { getDebugIdSnippet, sentryUnpluginFactory, createRollupInjectionHooks } from "../src"; -import { CodeInjection, containsOnlyImports } from "../src/utils"; +import { getDebugIdSnippet, sentryUnpluginFactory } from "../src"; +import { containsOnlyImports } from "../src/utils"; describe("getDebugIdSnippet", () => { it("returns the debugId injection snippet for a passed debugId", () => { @@ -142,200 +142,6 @@ app.mount('#app'); }); }); -describe("createRollupInjectionHooks", () => { - const inject = new CodeInjection(); - const hooks = createRollupInjectionHooks(inject, true); - - beforeEach(() => { - inject.clear(); - }); - - describe("renderChunk", () => { - it("should inject debug ID into clean JavaScript files", () => { - const code = 'console.log("Hello world");'; - const result = hooks.renderChunk(code, { fileName: "bundle.js" }); - - expect(result).not.toBeNull(); - expect(result?.code).toMatchInlineSnapshot( - `"!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\\");"` - ); - }); - - it("should inject debug ID after 'use strict'", () => { - const code = '"use strict";\nconsole.log("Hello world");'; - const result = hooks.renderChunk(code, { fileName: "bundle.js" }); - - expect(result).not.toBeNull(); - expect(result?.code).toMatchInlineSnapshot(` - "\\"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){}}(); - console.log(\\"Hello world\\");" - `); - }); - - it.each([ - ["bundle.js"], - ["bundle.mjs"], - ["bundle.cjs"], - ["bundle.js?foo=bar"], - ["bundle.js#hash"], - ])("should process file '%s': %s", (fileName) => { - const code = 'console.log("test");'; - const result = hooks.renderChunk(code, { fileName }); - - expect(result).not.toBeNull(); - expect(result?.code).toMatchInlineSnapshot( - `"!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\\");"` - ); - }); - - it.each([["index.html"], ["styles.css"]])("should NOT process file '%s': %s", (fileName) => { - const code = 'console.log("test");'; - const result = hooks.renderChunk(code, { fileName }); - - expect(result).toBeNull(); - }); - - it.each([ - [ - "inline format at start", - ';{try{(function(){var e="undefined"!=typeof window?window:e._sentryDebugIdIdentifier="sentry-dbid-existing-id");})();}catch(e){}};console.log("test");', - ], - [ - "comment format at end", - 'console.log("test");\n//# debugId=f6ccd6f4-7ea0-4854-8384-1c9f8340af81\n//# sourceMappingURL=bundle.js.map', - ], - [ - "inline format with large file", - '"use strict";\n' + - "// comment\n".repeat(10) + - ';{try{(function(){var e="undefined"!=typeof window?window:e._sentryDebugIdIdentifier="sentry-dbid-existing-id");})();}catch(e){}};' + - '\nconsole.log("line");\n'.repeat(100), - ], - ])("should NOT inject when debug ID already exists (%s)", (_description, code) => { - const result = hooks.renderChunk(code, { fileName: "bundle.js" }); - expect(result?.code).not.toContain("_sentryDebugIds"); - }); - - it("should only check boundaries for performance (not entire file)", () => { - // Inline format beyond first 6KB boundary - const codeWithInlineBeyond6KB = - "a".repeat(6100) + - ';{try{(function(){var e="undefined"!=typeof window?window:e._sentryDebugIdIdentifier="sentry-dbid-existing-id");})();}catch(e){}};'; - - expect(hooks.renderChunk(codeWithInlineBeyond6KB, { fileName: "bundle.js" })).not.toBeNull(); - - // Comment format beyond last 500 bytes boundary - const codeWithCommentBeyond500B = - "//# debugId=f6ccd6f4-7ea0-4854-8384-1c9f8340af81\n" + "a".repeat(600); - - expect( - hooks.renderChunk(codeWithCommentBeyond500B, { fileName: "bundle.js" }) - ).not.toBeNull(); - }); - - describe("HTML facade chunks (MPA vs SPA)", () => { - // Issue #829: MPA facades should be skipped - // Regression fix: SPA main bundles with HTML facades should NOT be skipped - - it.each([ - ["empty", ""], - ["only side-effect imports", `import './shared-module.js';`], - ["only named imports", `import { foo, bar } from './shared-module.js';`], - ["only re-exports", `export * from './shared-module.js';`], - [ - "multiple imports and comments", - `// This is a facade module -import './moduleA.js'; -import { x } from './moduleB.js'; -/* block comment */ -export * from './moduleC.js';`, - ], - ["'use strict' and imports only", `"use strict";\nimport './shared-module.js';`], - ["query string in facadeModuleId", `import './shared.js';`, "?query=param"], - ["hash in facadeModuleId", `import './shared.js';`, "#hash"], - ])("should SKIP HTML facade chunks: %s", (_, code, suffix = "") => { - const result = hooks.renderChunk(code, { - fileName: "page1.js", - facadeModuleId: `/path/to/page1.html${suffix}`, - }); - expect(result).toBeNull(); - }); - - it("should inject into HTML facade with function declarations", () => { - const result = hooks.renderChunk(`function main() { console.log("hello"); }`, { - fileName: "index.js", - facadeModuleId: "/path/to/index.html", - }); - expect(result).not.toBeNull(); - expect(result?.code).toMatchInlineSnapshot( - `"!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\\"); }"` - ); - }); - - it("should inject into HTML facade with variable declarations", () => { - const result = hooks.renderChunk(`const x = 42;`, { - fileName: "index.js", - facadeModuleId: "/path/to/index.html", - }); - expect(result).not.toBeNull(); - expect(result?.code).toMatchInlineSnapshot( - `"!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;"` - ); - }); - - it("should inject into HTML facade with substantial code (SPA main bundle)", () => { - const code = `import { initApp } from './app.js'; - -const config = { debug: true }; - -function bootstrap() { - initApp(config); -} - -bootstrap();`; - const result = hooks.renderChunk(code, { - fileName: "index.js", - facadeModuleId: "/path/to/index.html", - }); - expect(result).not.toBeNull(); - expect(result?.code).toMatchInlineSnapshot(` - "!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'; - - const config = { debug: true }; - - function bootstrap() { - initApp(config); - } - - bootstrap();" - `); - }); - - it("should inject into HTML facade with mixed imports and code", () => { - const result = hooks.renderChunk( - `import './polyfills.js';\nimport { init } from './app.js';\n\ninit();`, - { fileName: "index.js", facadeModuleId: "/path/to/index.html" } - ); - expect(result).not.toBeNull(); - expect(result?.code).toMatchInlineSnapshot(` - "!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'; - import { init } from './app.js'; - - init();" - `); - }); - - it("should inject into regular JS chunks (no HTML facade)", () => { - const result = hooks.renderChunk(`console.log("Hello");`, { fileName: "bundle.js" }); - expect(result).not.toBeNull(); - expect(result?.code).toMatchInlineSnapshot( - `"!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\\");"` - ); - }); - }); - }); -}); - describe("sentryUnpluginFactory sourcemaps.disable behavior", () => { const mockComponentNameAnnotatePlugin = jest.fn(() => ({ name: "mock-component-name-annotate-plugin", diff --git a/packages/e2e-tests/package.json b/packages/e2e-tests/package.json index 5684e3c7..51f713d7 100644 --- a/packages/e2e-tests/package.json +++ b/packages/e2e-tests/package.json @@ -34,7 +34,7 @@ "glob": "8.0.3", "jest": "^28.1.3", "premove": "^4.0.0", - "rollup": "2.77.0", + "rollup": "3.2.0", "ts-node": "^10.9.1", "vite": "3.0.0", "webpack4": "npm:webpack@^4", diff --git a/packages/integration-tests/fixtures/disabled-sourcemaps-upload/disabled-sourcemaps-upload.test.ts b/packages/integration-tests/fixtures/disabled-sourcemaps-upload/disabled-sourcemaps-upload.test.ts deleted file mode 100644 index dd22ef31..00000000 --- a/packages/integration-tests/fixtures/disabled-sourcemaps-upload/disabled-sourcemaps-upload.test.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { sentryRollupPlugin } from "@sentry/rollup-plugin"; - -const debugIdUploadPluginName = "sentry-rollup-debug-id-upload-plugin"; - -test("should not call upload plugin when sourcemaps are disabled", () => { - const plugins = sentryRollupPlugin({ - telemetry: false, - sourcemaps: { - disable: true, - }, - }) as Array<{ name: string }>; - - const debugIdUploadPlugin = plugins.find((plugin) => plugin.name === debugIdUploadPluginName); - - expect(debugIdUploadPlugin).toBeUndefined(); -}); - -test("should call upload plugin when sourcemaps are enabled", () => { - const plugins = sentryRollupPlugin({ - telemetry: false, - }) as Array<{ name: string }>; - - const debugIdUploadPlugin = plugins.find((plugin) => plugin.name === debugIdUploadPluginName); - - expect(debugIdUploadPlugin).toBeDefined(); -}); diff --git a/packages/integration-tests/fixtures/disabled-sourcemaps-upload/input/bundle.js b/packages/integration-tests/fixtures/disabled-sourcemaps-upload/input/bundle.js deleted file mode 100644 index 85b0fd2b..00000000 --- a/packages/integration-tests/fixtures/disabled-sourcemaps-upload/input/bundle.js +++ /dev/null @@ -1,2 +0,0 @@ -// eslint-disable-next-line no-console -console.log("Beep!"); diff --git a/packages/rollup-plugin/package.json b/packages/rollup-plugin/package.json index bffcd031..28911e43 100644 --- a/packages/rollup-plugin/package.json +++ b/packages/rollup-plugin/package.json @@ -50,7 +50,7 @@ }, "dependencies": { "@sentry/bundler-plugin-core": "4.9.0", - "unplugin": "1.0.1" + "magic-string": "0.30.8" }, "peerDependencies": { "rollup": ">=3.2.0" diff --git a/packages/rollup-plugin/src/index.ts b/packages/rollup-plugin/src/index.ts index 21993770..2ab2e736 100644 --- a/packages/rollup-plugin/src/index.ts +++ b/packages/rollup-plugin/src/index.ts @@ -33,11 +33,14 @@ function hasExistingDebugID(code: string): boolean { return false; } +/** + * @ignore - this is the internal plugin factory function + */ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type -export function sentryRollupPlugin(userOptions: Options = {}) { +export function _rollupPluginInternal(userOptions: Options = {}, buildTool: string) { const sentryBuildPluginManager = createSentryBuildPluginManager(userOptions, { - loggerPrefix: userOptions._metaOptions?.loggerPrefixOverride ?? "[sentry-rollup-plugin]", - buildTool: "rollup", + loggerPrefix: userOptions._metaOptions?.loggerPrefixOverride ?? `[sentry-${buildTool}-plugin]`, + buildTool, }); const { @@ -124,10 +127,12 @@ export function sentryRollupPlugin(userOptions: Options = {}) { function renderChunk( code: string, - chunk: { fileName: string; facadeModuleId?: string | null } + chunk: { fileName: string; facadeModuleId?: string | null }, + _?: unknown, + meta?: { magicString?: MagicString } ): { code: string; - map: SourceMap; + map?: SourceMap; } | null { if (!isJsFile(chunk.fileName)) { return null; // returning null means not modifying the chunk at all @@ -149,8 +154,7 @@ export function sentryRollupPlugin(userOptions: Options = {}) { return null; } - const ms = new MagicString(code, { filename: chunk.fileName }); - + const ms = meta?.magicString || new MagicString(code, { filename: chunk.fileName }); const match = code.match(COMMENT_USE_STRICT_REGEX)?.[0]; if (match) { @@ -163,6 +167,13 @@ export function sentryRollupPlugin(userOptions: Options = {}) { ms.prepend(injectCode.code()); } + // Rolldown can pass a native MagicString instance in meta.magicString + // https://rolldown.rs/in-depth/native-magic-string#usage-examples + if (ms?.constructor?.name === "BindingMagicString") { + // Rolldown docs say to return the magic string instance directly in this case + return { code: ms as unknown as string }; + } + return { code: ms.toString(), map: ms.generateMap({ file: chunk.fileName, hires: "boundary" as unknown as undefined }), @@ -196,9 +207,11 @@ export function sentryRollupPlugin(userOptions: Options = {}) { } } + const name = `sentry-${buildTool}-plugin`; + if (shouldTransform) { return { - name: "sentry-rollup-plugin", + name, buildStart, transform, renderChunk, @@ -207,12 +220,19 @@ export function sentryRollupPlugin(userOptions: Options = {}) { } return { - name: "sentry-rollup-plugin", + name, buildStart, renderChunk, writeBundle, }; } +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type, @typescript-eslint/no-explicit-any +export function sentryRollupPlugin(userOptions: Options = {}): any { + // We return an array here so we don't break backwards compatibility with what + // unplugin used to return + return [_rollupPluginInternal(userOptions, "rollup")]; +} + export type { Options as SentryRollupPluginOptions } from "@sentry/bundler-plugin-core"; export { sentryCliBinaryExists } from "@sentry/bundler-plugin-core"; diff --git a/packages/rollup-plugin/test/public-api.test.ts b/packages/rollup-plugin/test/public-api.test.ts index b4e72418..22190364 100644 --- a/packages/rollup-plugin/test/public-api.test.ts +++ b/packages/rollup-plugin/test/public-api.test.ts @@ -1,4 +1,5 @@ import { sentryRollupPlugin } from "../src"; +import { Plugin, SourceMap } from "rollup"; test("Rollup plugin should exist", () => { expect(sentryRollupPlugin).toBeDefined(); @@ -11,7 +12,7 @@ describe("sentryRollupPlugin", () => { }); it("returns a single rollup plugin", () => { - const plugin = sentryRollupPlugin({ + const [plugin] = sentryRollupPlugin({ authToken: "test-token", org: "test-org", project: "test-project", @@ -19,6 +20,201 @@ describe("sentryRollupPlugin", () => { expect(Array.isArray(plugin)).not.toBe(true); - expect(plugin.name).toBe("sentry-rollup-plugin"); + expect(plugin?.name).toBe("sentry-rollup-plugin"); + }); +}); + +describe("Hooks", () => { + const [plugin] = sentryRollupPlugin({ release: { inject: false } }) as [Plugin]; + + const renderChunk = plugin.renderChunk as ( + code: string, + chunkInfo: { fileName: string; facadeModuleId?: string } + ) => { + code: string; + map: SourceMap; + } | null; + + describe("renderChunk", () => { + it("should inject debug ID into clean JavaScript files", () => { + const code = 'console.log("Hello world");'; + const result = renderChunk(code, { fileName: "bundle.js" }); + + expect(result).not.toBeNull(); + expect(result?.code).toMatchInlineSnapshot( + `"!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\\");"` + ); + }); + + it("should inject debug ID after 'use strict'", () => { + const code = '"use strict";\nconsole.log("Hello world");'; + const result = renderChunk(code, { fileName: "bundle.js" }); + + expect(result).not.toBeNull(); + expect(result?.code).toMatchInlineSnapshot(` + "\\"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){}}(); + console.log(\\"Hello world\\");" + `); + }); + + it.each([ + ["bundle.js"], + ["bundle.mjs"], + ["bundle.cjs"], + ["bundle.js?foo=bar"], + ["bundle.js#hash"], + ])("should process file '%s': %s", (fileName) => { + const code = 'console.log("test");'; + const result = renderChunk(code, { fileName }); + + expect(result).not.toBeNull(); + expect(result?.code).toMatchInlineSnapshot( + `"!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\\");"` + ); + }); + + it.each([["index.html"], ["styles.css"]])("should NOT process file '%s': %s", (fileName) => { + const code = 'console.log("test");'; + const result = renderChunk(code, { fileName }); + + expect(result).toBeNull(); + }); + + it.each([ + [ + "inline format at start", + ';{try{(function(){var e="undefined"!=typeof window?window:e._sentryDebugIdIdentifier="sentry-dbid-existing-id");})();}catch(e){}};console.log("test");', + ], + [ + "comment format at end", + 'console.log("test");\n//# debugId=f6ccd6f4-7ea0-4854-8384-1c9f8340af81\n//# sourceMappingURL=bundle.js.map', + ], + [ + "inline format with large file", + '"use strict";\n' + + "// comment\n".repeat(10) + + ';{try{(function(){var e="undefined"!=typeof window?window:e._sentryDebugIdIdentifier="sentry-dbid-existing-id");})();}catch(e){}};' + + '\nconsole.log("line");\n'.repeat(100), + ], + ])("should NOT inject when debug ID already exists (%s)", (_description, code) => { + const result = renderChunk(code, { fileName: "bundle.js" }); + expect(result).toBeNull(); + }); + + it("should only check boundaries for performance (not entire file)", () => { + // Inline format beyond first 6KB boundary + const codeWithInlineBeyond6KB = + "a".repeat(6100) + + ';{try{(function(){var e="undefined"!=typeof window?window:e._sentryDebugIdIdentifier="sentry-dbid-existing-id");})();}catch(e){}};'; + + expect(renderChunk(codeWithInlineBeyond6KB, { fileName: "bundle.js" })).not.toBeNull(); + + // Comment format beyond last 500 bytes boundary + const codeWithCommentBeyond500B = + "//# debugId=f6ccd6f4-7ea0-4854-8384-1c9f8340af81\n" + "a".repeat(600); + + expect(renderChunk(codeWithCommentBeyond500B, { fileName: "bundle.js" })).not.toBeNull(); + }); + + describe("HTML facade chunks (MPA vs SPA)", () => { + // Issue #829: MPA facades should be skipped + // Regression fix: SPA main bundles with HTML facades should NOT be skipped + + it.each([ + ["empty", ""], + ["only side-effect imports", `import './shared-module.js';`], + ["only named imports", `import { foo, bar } from './shared-module.js';`], + ["only re-exports", `export * from './shared-module.js';`], + [ + "multiple imports and comments", + `// This is a facade module +import './moduleA.js'; +import { x } from './moduleB.js'; +/* block comment */ +export * from './moduleC.js';`, + ], + ["'use strict' and imports only", `"use strict";\nimport './shared-module.js';`], + ["query string in facadeModuleId", `import './shared.js';`, "?query=param"], + ["hash in facadeModuleId", `import './shared.js';`, "#hash"], + ])("should SKIP HTML facade chunks: %s", (_, code, suffix = "") => { + const result = renderChunk(code, { + fileName: "page1.js", + facadeModuleId: `/path/to/page1.html${suffix}`, + }); + expect(result).toBeNull(); + }); + + it("should inject into HTML facade with function declarations", () => { + const result = renderChunk(`function main() { console.log("hello"); }`, { + fileName: "index.js", + facadeModuleId: "/path/to/index.html", + }); + expect(result).not.toBeNull(); + expect(result?.code).toMatchInlineSnapshot( + `"!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\\"); }"` + ); + }); + + it("should inject into HTML facade with variable declarations", () => { + const result = renderChunk(`const x = 42;`, { + fileName: "index.js", + facadeModuleId: "/path/to/index.html", + }); + expect(result).not.toBeNull(); + expect(result?.code).toMatchInlineSnapshot( + `"!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;"` + ); + }); + + it("should inject into HTML facade with substantial code (SPA main bundle)", () => { + const code = `import { initApp } from './app.js'; + +const config = { debug: true }; + +function bootstrap() { + initApp(config); +} + +bootstrap();`; + const result = renderChunk(code, { + fileName: "index.js", + facadeModuleId: "/path/to/index.html", + }); + expect(result).not.toBeNull(); + expect(result?.code).toMatchInlineSnapshot(` + "!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'; + + const config = { debug: true }; + + function bootstrap() { + initApp(config); + } + + bootstrap();" + `); + }); + + it("should inject into HTML facade with mixed imports and code", () => { + const result = renderChunk( + `import './polyfills.js';\nimport { init } from './app.js';\n\ninit();`, + { fileName: "index.js", facadeModuleId: "/path/to/index.html" } + ); + expect(result).not.toBeNull(); + expect(result?.code).toMatchInlineSnapshot(` + "!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'; + import { init } from './app.js'; + + init();" + `); + }); + + it("should inject into regular JS chunks (no HTML facade)", () => { + const result = renderChunk(`console.log("Hello");`, { fileName: "bundle.js" }); + expect(result).not.toBeNull(); + expect(result?.code).toMatchInlineSnapshot( + `"!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\\");"` + ); + }); + }); }); }); diff --git a/packages/vite-plugin/package.json b/packages/vite-plugin/package.json index e4e6dc2e..69c70f5a 100644 --- a/packages/vite-plugin/package.json +++ b/packages/vite-plugin/package.json @@ -49,7 +49,7 @@ }, "dependencies": { "@sentry/bundler-plugin-core": "4.9.0", - "unplugin": "1.0.1" + "@sentry/rollup-plugin": "4.9.0" }, "devDependencies": { "@babel/core": "7.18.5", diff --git a/packages/vite-plugin/src/index.ts b/packages/vite-plugin/src/index.ts index 51a79602..e3326386 100644 --- a/packages/vite-plugin/src/index.ts +++ b/packages/vite-plugin/src/index.ts @@ -1,68 +1,14 @@ -import { - CodeInjection, - sentryUnpluginFactory, - Options, - createRollupInjectionHooks, - createRollupDebugIdUploadHooks, - SentrySDKBuildFlags, - createRollupBundleSizeOptimizationHooks, - createComponentNameAnnotateHooks, - Logger, -} from "@sentry/bundler-plugin-core"; -import { UnpluginOptions, VitePlugin } from "unplugin"; - -function viteInjectionPlugin(injectionCode: CodeInjection, debugIds: boolean): UnpluginOptions { - return { - name: "sentry-vite-injection-plugin", - // run `post` to avoid tripping up @rollup/plugin-commonjs when cjs is used - // as we inject an `import` statement - enforce: "post" as const, // need this so that vite runs the resolveId hook - vite: createRollupInjectionHooks(injectionCode, debugIds), - }; -} - -function viteComponentNameAnnotatePlugin( - ignoredComponents: string[], - injectIntoHtml: boolean -): UnpluginOptions { - return { - name: "sentry-vite-component-name-annotate-plugin", - enforce: "pre" as const, - vite: createComponentNameAnnotateHooks(ignoredComponents, injectIntoHtml), - }; -} - -function viteDebugIdUploadPlugin( - upload: (buildArtifacts: string[]) => Promise, - logger: Logger, - createDependencyOnBuildArtifacts: () => () => void -): UnpluginOptions { - return { - name: "sentry-vite-debug-id-upload-plugin", - vite: createRollupDebugIdUploadHooks(upload, logger, createDependencyOnBuildArtifacts), - }; -} - -function viteBundleSizeOptimizationsPlugin( - replacementValues: SentrySDKBuildFlags -): UnpluginOptions { - return { - name: "sentry-vite-bundle-size-optimizations-plugin", - vite: createRollupBundleSizeOptimizationHooks(replacementValues), - }; -} - -const sentryUnplugin = sentryUnpluginFactory({ - injectionPlugin: viteInjectionPlugin, - componentNameAnnotatePlugin: viteComponentNameAnnotatePlugin, - debugIdUploadPlugin: viteDebugIdUploadPlugin, - bundleSizeOptimizationsPlugin: viteBundleSizeOptimizationsPlugin, -}); - -export const sentryVitePlugin = (options?: Options): VitePlugin[] => { - const result = sentryUnplugin.vite(options); - // unplugin returns a single plugin instead of an array when only one plugin is created, so we normalize this here. - return Array.isArray(result) ? result : [result]; +import { SentryRollupPluginOptions } from "@sentry/rollup-plugin"; +import { _rollupPluginInternal } from "@sentry/rollup-plugin"; +import { Plugin } from "vite"; + +export const sentryVitePlugin = (options?: SentryRollupPluginOptions): Plugin[] => { + return [ + { + enforce: "pre", + ..._rollupPluginInternal(options, "vite"), + }, + ]; }; export type { Options as SentryVitePluginOptions } from "@sentry/bundler-plugin-core"; diff --git a/packages/vite-plugin/test/public-api.test.ts b/packages/vite-plugin/test/public-api.test.ts index bd0d0827..cc709292 100644 --- a/packages/vite-plugin/test/public-api.test.ts +++ b/packages/vite-plugin/test/public-api.test.ts @@ -21,15 +21,7 @@ describe("sentryVitePlugin", () => { const pluginNames = plugins.map((plugin) => plugin.name); - expect(pluginNames).toEqual( - expect.arrayContaining([ - "sentry-telemetry-plugin", - "sentry-release-management-plugin", - "sentry-vite-injection-plugin", - "sentry-vite-debug-id-upload-plugin", - "sentry-file-deletion-plugin", - ]) - ); + expect(pluginNames).toEqual(expect.arrayContaining(["sentry-vite-plugin"])); }); it("returns an array of Vite pluginswhen unplugin returns a single plugin", () => { diff --git a/yarn.lock b/yarn.lock index 5f7ab0c9..4c0e8a6e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11535,13 +11535,6 @@ rollup@2.75.7: optionalDependencies: fsevents "~2.3.2" -rollup@2.77.0: - version "2.77.0" - resolved "https://registry.npmjs.org/rollup/-/rollup-2.77.0.tgz#749eaa5ac09b6baa52acc076bc46613eddfd53f4" - integrity sha512-vL8xjY4yOQEw79DvyXLijhnhh+R/O9zpF/LEgkCebZFtb6ELeN9H3/2T0r8+mp+fFTBHZ5qGpOpW2ela2zRt3g== - optionalDependencies: - fsevents "~2.3.2" - rollup@2.79.2: version "2.79.2" resolved "https://registry.npmjs.org/rollup/-/rollup-2.79.2.tgz#f150e4a5db4b121a21a747d762f701e5e9f49090" From 0ab8602d59e34e888ded70be31fe332915b1582e Mon Sep 17 00:00:00 2001 From: Tim Fish Date: Mon, 9 Feb 2026 21:35:19 +0100 Subject: [PATCH 3/8] More fixes --- packages/rollup-plugin/src/index.ts | 2 +- packages/rollup-plugin/test/public-api.test.ts | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/rollup-plugin/src/index.ts b/packages/rollup-plugin/src/index.ts index 2ab2e736..bded11f1 100644 --- a/packages/rollup-plugin/src/index.ts +++ b/packages/rollup-plugin/src/index.ts @@ -34,7 +34,7 @@ function hasExistingDebugID(code: string): boolean { } /** - * @ignore - this is the internal plugin factory function + * @ignore - this is the internal plugin factory function only used for the Vite plugin! */ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type export function _rollupPluginInternal(userOptions: Options = {}, buildTool: string) { diff --git a/packages/rollup-plugin/test/public-api.test.ts b/packages/rollup-plugin/test/public-api.test.ts index 22190364..e470d965 100644 --- a/packages/rollup-plugin/test/public-api.test.ts +++ b/packages/rollup-plugin/test/public-api.test.ts @@ -11,16 +11,17 @@ describe("sentryRollupPlugin", () => { jest.clearAllMocks(); }); - it("returns a single rollup plugin", () => { - const [plugin] = sentryRollupPlugin({ + it("returns an array of rollup plugins (although only one)", () => { + const plugins = sentryRollupPlugin({ authToken: "test-token", org: "test-org", project: "test-project", - }); + }) as Plugin[]; - expect(Array.isArray(plugin)).not.toBe(true); + expect(Array.isArray(plugins)).toBe(true); + expect(plugins).toHaveLength(1); - expect(plugin?.name).toBe("sentry-rollup-plugin"); + expect(plugins[0]?.name).toBe("sentry-rollup-plugin"); }); }); From c7f027adb9353498f5bfdde73199dd5bd0d73923 Mon Sep 17 00:00:00 2001 From: Tim Fish Date: Wed, 11 Feb 2026 14:01:27 +0100 Subject: [PATCH 4/8] always create a release even if sourcemap upload is disabled --- packages/rollup-plugin/src/index.ts | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/packages/rollup-plugin/src/index.ts b/packages/rollup-plugin/src/index.ts index 4f66b94d..0f2b36f0 100644 --- a/packages/rollup-plugin/src/index.ts +++ b/packages/rollup-plugin/src/index.ts @@ -204,22 +204,22 @@ export function _rollupPluginInternal( outputOptions: { dir?: string; file?: string }, bundle: { [fileName: string]: unknown } ): Promise { - if (!sourcemapsEnabled) { - return; - } - try { await sentryBuildPluginManager.createRelease(); - if (outputOptions.dir) { - const outputDir = outputOptions.dir; - const buildArtifacts = await globFiles(outputDir); - await upload(buildArtifacts); - } else if (outputOptions.file) { - await upload([outputOptions.file]); - } else { - const buildArtifacts = Object.keys(bundle).map((asset) => path.join(path.resolve(), asset)); - await upload(buildArtifacts); + if (sourcemapsEnabled && options.sourcemaps?.disable !== "disable-upload") { + if (outputOptions.dir) { + const outputDir = outputOptions.dir; + const buildArtifacts = await globFiles(outputDir); + await upload(buildArtifacts); + } else if (outputOptions.file) { + await upload([outputOptions.file]); + } else { + const buildArtifacts = Object.keys(bundle).map((asset) => + path.join(path.resolve(), asset) + ); + await upload(buildArtifacts); + } } } finally { freeGlobalDependencyOnBuildArtifacts(); From abac461eb81d5c8764dfcdc47f3eb4ae4857cfef Mon Sep 17 00:00:00 2001 From: Tim Fish Date: Sat, 14 Feb 2026 09:54:20 +0000 Subject: [PATCH 5/8] PR review --- packages/rollup-plugin/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/rollup-plugin/src/index.ts b/packages/rollup-plugin/src/index.ts index 0f2b36f0..902051cd 100644 --- a/packages/rollup-plugin/src/index.ts +++ b/packages/rollup-plugin/src/index.ts @@ -54,7 +54,7 @@ function getRollupMajorVersion(): string | undefined { // eslint-disable-next-line @typescript-eslint/explicit-function-return-type export function _rollupPluginInternal( userOptions: Options = {}, - buildTool: string, + buildTool: "rollup" | "vite", buildToolMajorVersion?: string ) { const sentryBuildPluginManager = createSentryBuildPluginManager(userOptions, { From 136e95f3164a68b1bea3edf9132a6ef18835d5d3 Mon Sep 17 00:00:00 2001 From: Tim Fish Date: Sat, 14 Feb 2026 11:04:08 +0000 Subject: [PATCH 6/8] feat: Remove unplugin --- packages/bundler-plugin-core/package.json | 3 +- packages/bundler-plugin-core/src/index.ts | 216 +----------------- .../bundler-plugin-core/test/index.test.ts | 174 +------------- .../e2e-tests/utils/create-cjs-bundles.ts | 34 +-- .../esbuild-plugin/test/public-api.test.ts | 8 +- .../utils/create-cjs-bundles-for-react.ts | 12 +- .../utils/create-cjs-bundles-with-query.ts | 10 +- .../utils/create-cjs-bundles.ts | 12 +- yarn.lock | 95 ++++++-- 9 files changed, 117 insertions(+), 447 deletions(-) diff --git a/packages/bundler-plugin-core/package.json b/packages/bundler-plugin-core/package.json index 46255342..90c1c6e9 100644 --- a/packages/bundler-plugin-core/package.json +++ b/packages/bundler-plugin-core/package.json @@ -58,8 +58,7 @@ "dotenv": "^16.3.1", "find-up": "^5.0.0", "glob": "^10.5.0", - "magic-string": "0.30.8", - "unplugin": "1.0.1" + "magic-string": "0.30.8" }, "devDependencies": { "@babel/preset-env": "7.18.2", diff --git a/packages/bundler-plugin-core/src/index.ts b/packages/bundler-plugin-core/src/index.ts index 4a3758c7..4009360d 100644 --- a/packages/bundler-plugin-core/src/index.ts +++ b/packages/bundler-plugin-core/src/index.ts @@ -6,214 +6,7 @@ import SentryCli from "@sentry/cli"; import { logger } from "@sentry/utils"; import * as fs from "fs"; import { glob } from "glob"; -import { createUnplugin, TransformResult, UnpluginInstance, UnpluginOptions } from "unplugin"; -import { createSentryBuildPluginManager } from "./build-plugin-manager"; -import { createDebugIdUploadFunction } from "./debug-id-upload"; -import { Logger } from "./logger"; -import { Options, SentrySDKBuildFlags } from "./types"; -import { - CodeInjection, - containsOnlyImports, - generateReleaseInjectorCode, - generateModuleMetadataInjectorCode, - stripQueryAndHashFromPath, -} from "./utils"; - -type InjectionPlugin = ( - injectionCode: CodeInjection, - debugIds: boolean, - logger: Logger -) => UnpluginOptions; -type LegacyPlugins = { - releaseInjectionPlugin: (injectionCode: string) => UnpluginOptions; - moduleMetadataInjectionPlugin: (injectionCode: string) => UnpluginOptions; - debugIdInjectionPlugin: (logger: Logger) => UnpluginOptions; -}; - -interface SentryUnpluginFactoryOptions { - injectionPlugin: InjectionPlugin | LegacyPlugins; - componentNameAnnotatePlugin?: ( - ignoredComponents: string[], - injectIntoHtml: boolean - ) => UnpluginOptions; - debugIdUploadPlugin: ( - upload: (buildArtifacts: string[]) => Promise, - logger: Logger, - createDependencyOnBuildArtifacts: () => () => void, - webpack_forceExitOnBuildComplete?: boolean - ) => UnpluginOptions; - bundleSizeOptimizationsPlugin: (buildFlags: SentrySDKBuildFlags) => UnpluginOptions; - getBundlerMajorVersion?: () => string | undefined; -} - -/** - * Creates an unplugin instance used to create Sentry plugins for Vite, Rollup, esbuild, and Webpack. - */ -export function sentryUnpluginFactory({ - injectionPlugin, - componentNameAnnotatePlugin, - debugIdUploadPlugin, - bundleSizeOptimizationsPlugin, - getBundlerMajorVersion, -}: SentryUnpluginFactoryOptions): UnpluginInstance { - return createUnplugin((userOptions = {}, unpluginMetaContext) => { - const sentryBuildPluginManager = createSentryBuildPluginManager(userOptions, { - loggerPrefix: - userOptions._metaOptions?.loggerPrefixOverride ?? - `[sentry-${unpluginMetaContext.framework}-plugin]`, - buildTool: unpluginMetaContext.framework, - buildToolMajorVersion: getBundlerMajorVersion?.(), - }); - - const { - logger, - normalizedOptions: options, - bundleSizeOptimizationReplacementValues, - } = sentryBuildPluginManager; - - if (options.disable) { - return [ - { - name: "sentry-noop-plugin", - }, - ]; - } - - if (process.cwd().match(/\\node_modules\\|\/node_modules\//)) { - logger.warn( - "Running Sentry plugin from within a `node_modules` folder. Some features may not work." - ); - } - - const plugins: UnpluginOptions[] = []; - - // Add plugin to emit a telemetry signal when the build starts - plugins.push({ - name: "sentry-telemetry-plugin", - buildStart() { - // Technically, for very fast builds we might miss the telemetry signal - // but it's okay because telemetry is not critical for us. - // We cannot await the flush here because it would block the build start - // which in turn would break module federation builds, see - // https://github.com/getsentry/sentry-javascript-bundler-plugins/issues/816 - void sentryBuildPluginManager.telemetry.emitBundlerPluginExecutionSignal().catch(() => { - // Nothing for the users to do here. If telemetry fails it's acceptable. - }); - }, - }); - - if (Object.keys(bundleSizeOptimizationReplacementValues).length > 0) { - plugins.push(bundleSizeOptimizationsPlugin(bundleSizeOptimizationReplacementValues)); - } - - const injectionCode = new CodeInjection(); - - if (!options.release.inject) { - logger.debug( - "Release injection disabled via `release.inject` option. Will not inject release." - ); - } else if (!options.release.name) { - logger.debug( - "No release name provided. Will not inject release. Please set the `release.name` option to identify your release." - ); - } else { - const code = generateReleaseInjectorCode({ - release: options.release.name, - injectBuildInformation: options._experiments.injectBuildInformation || false, - }); - if (typeof injectionPlugin !== "function") { - plugins.push(injectionPlugin.releaseInjectionPlugin(code.code())); - } else { - injectionCode.append(code); - } - } - - if (Object.keys(sentryBuildPluginManager.bundleMetadata).length > 0) { - const code = generateModuleMetadataInjectorCode(sentryBuildPluginManager.bundleMetadata); - if (typeof injectionPlugin !== "function") { - plugins.push(injectionPlugin.moduleMetadataInjectionPlugin(code.code())); - } else { - injectionCode.append(code); - } - } - - if ( - typeof injectionPlugin === "function" && - (!injectionCode.isEmpty() || options.sourcemaps?.disable !== true) - ) { - plugins.push(injectionPlugin(injectionCode, options.sourcemaps?.disable !== true, logger)); - } - - // Add plugin to create and finalize releases, and also take care of adding commits and legacy sourcemaps - const freeGlobalDependencyOnBuildArtifacts = - sentryBuildPluginManager.createDependencyOnBuildArtifacts(); - plugins.push({ - name: "sentry-release-management-plugin", - async writeBundle() { - try { - await sentryBuildPluginManager.createRelease(); - } finally { - freeGlobalDependencyOnBuildArtifacts(); - } - }, - }); - - if (options.sourcemaps?.disable !== true) { - if (typeof injectionPlugin !== "function") { - plugins.push(injectionPlugin.debugIdInjectionPlugin(logger)); - } - - if (options.sourcemaps?.disable !== "disable-upload") { - // This option is only strongly typed for the webpack plugin, where it is used. It has no effect on other plugins - const webpack_forceExitOnBuildComplete = - typeof options._experiments["forceExitOnBuildCompletion"] === "boolean" - ? options._experiments["forceExitOnBuildCompletion"] - : undefined; - - plugins.push( - debugIdUploadPlugin( - createDebugIdUploadFunction({ - sentryBuildPluginManager, - }), - logger, - sentryBuildPluginManager.createDependencyOnBuildArtifacts, - webpack_forceExitOnBuildComplete - ) - ); - } - } - - if (options.reactComponentAnnotation) { - if (!options.reactComponentAnnotation.enabled) { - logger.debug( - "The component name annotate plugin is currently disabled. Skipping component name annotations." - ); - } else if (options.reactComponentAnnotation.enabled && !componentNameAnnotatePlugin) { - logger.warn( - "The component name annotate plugin is currently not supported by '@sentry/esbuild-plugin'" - ); - } else { - componentNameAnnotatePlugin && - plugins.push( - componentNameAnnotatePlugin( - options.reactComponentAnnotation.ignoredComponents || [], - !!options.reactComponentAnnotation._experimentalInjectIntoHtml - ) - ); - } - } - - // Add plugin to delete unwanted artifacts like source maps after the uploads have completed - plugins.push({ - name: "sentry-file-deletion-plugin", - async writeBundle() { - await sentryBuildPluginManager.deleteArtifacts(); - }, - }); - - return plugins; - }); -} +import { CodeInjection, containsOnlyImports, stripQueryAndHashFromPath } from "./utils"; /** * Determines whether the Sentry CLI binary is in its expected location. @@ -283,18 +76,17 @@ export function globFiles(outputDir: string): Promise { ); } +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type export function createComponentNameAnnotateHooks( ignoredComponents: string[], injectIntoHtml: boolean -): { - transform: UnpluginOptions["transform"]; -} { +) { type ParserPlugins = NonNullable< NonNullable[1]>["parserOpts"] >["plugins"]; return { - async transform(this: void, code: string, id: string): Promise { + async transform(this: void, code: string, id: string) { // id may contain query and hash which will trip up our file extension logic below const idWithoutQueryAndHash = stripQueryAndHashFromPath(id); diff --git a/packages/bundler-plugin-core/test/index.test.ts b/packages/bundler-plugin-core/test/index.test.ts index 2b56d984..e9bdcf1d 100644 --- a/packages/bundler-plugin-core/test/index.test.ts +++ b/packages/bundler-plugin-core/test/index.test.ts @@ -1,5 +1,5 @@ import { Compiler } from "webpack"; -import { getDebugIdSnippet, sentryUnpluginFactory } from "../src"; +import { getDebugIdSnippet } from "../src"; import { containsOnlyImports } from "../src/utils"; describe("getDebugIdSnippet", () => { @@ -141,175 +141,3 @@ app.mount('#app'); }); }); }); - -describe("sentryUnpluginFactory sourcemaps.disable behavior", () => { - const mockComponentNameAnnotatePlugin = jest.fn(() => ({ - name: "mock-component-name-annotate-plugin", - })); - - const mockInjectionPlugin = jest.fn(() => ({ - name: "mock-injection-plugin", - })); - - const mockDebugIdUploadPlugin = jest.fn(() => ({ - name: "mock-debug-id-upload-plugin", - })); - - const mockBundleSizeOptimizationsPlugin = jest.fn(() => ({ - name: "mock-bundle-size-optimizations-plugin", - })); - - const createUnpluginInstance = (): ReturnType => { - return sentryUnpluginFactory({ - injectionPlugin: mockInjectionPlugin, - componentNameAnnotatePlugin: mockComponentNameAnnotatePlugin, - debugIdUploadPlugin: mockDebugIdUploadPlugin, - bundleSizeOptimizationsPlugin: mockBundleSizeOptimizationsPlugin, - }); - }; - - beforeEach(() => { - jest.clearAllMocks(); - }); - - describe("when sourcemaps.disable is true", () => { - it("should not include debug ID injection or upload plugins", () => { - const unpluginInstance = createUnpluginInstance(); - - const plugins = unpluginInstance.raw( - { - authToken: "test-token", - org: "test-org", - project: "test-project", - sourcemaps: { - disable: true, - }, - }, - { framework: "webpack", webpack: { compiler: {} as Compiler } } - ); - - const pluginNames = plugins.map((plugin) => plugin.name); - - // Should not include debug ID related plugins - expect(pluginNames).not.toContain("mock-debug-id-injection-plugin"); - expect(pluginNames).not.toContain("mock-debug-id-upload-plugin"); - - // Should still include other core plugins - expect(pluginNames).toContain("sentry-telemetry-plugin"); - expect(pluginNames).toContain("sentry-release-management-plugin"); - expect(pluginNames).toContain("sentry-file-deletion-plugin"); - }); - }); - - describe('when sourcemaps.disable is "disable-upload"', () => { - it("should include debug ID injection plugin but not upload plugin", () => { - const unpluginInstance = createUnpluginInstance(); - - const plugins = unpluginInstance.raw( - { - authToken: "test-token", - org: "test-org", - project: "test-project", - sourcemaps: { - disable: "disable-upload", - }, - }, - { framework: "webpack", webpack: { compiler: {} as Compiler } } - ); - - const pluginNames = plugins.map((plugin) => plugin.name); - - // Should include debug ID injection but not upload - expect(pluginNames).toContain("mock-injection-plugin"); - expect(pluginNames).not.toContain("mock-debug-id-upload-plugin"); - - // Should still include other core plugins - expect(pluginNames).toContain("sentry-telemetry-plugin"); - expect(pluginNames).toContain("sentry-release-management-plugin"); - expect(pluginNames).toContain("sentry-file-deletion-plugin"); - }); - }); - - describe("when sourcemaps.disable is false", () => { - it("should include both debug ID injection and upload plugins", () => { - const unpluginInstance = createUnpluginInstance(); - - const plugins = unpluginInstance.raw( - { - authToken: "test-token", - org: "test-org", - project: "test-project", - sourcemaps: { - disable: false, - }, - }, - { framework: "webpack", webpack: { compiler: {} as Compiler } } - ); - - const pluginNames = plugins.map((plugin) => plugin.name); - - // Should include both debug ID related plugins - expect(pluginNames).toContain("mock-injection-plugin"); - expect(pluginNames).toContain("mock-debug-id-upload-plugin"); - - // Should include other core plugins - expect(pluginNames).toContain("sentry-telemetry-plugin"); - expect(pluginNames).toContain("sentry-release-management-plugin"); - expect(pluginNames).toContain("sentry-file-deletion-plugin"); - }); - }); - - describe("when sourcemaps.disable is undefined (default)", () => { - it("should include both debug ID injection and upload plugins", () => { - const unpluginInstance = createUnpluginInstance(); - - const plugins = unpluginInstance.raw( - { - authToken: "test-token", - org: "test-org", - project: "test-project", - // sourcemaps.disable not specified (undefined) - }, - { framework: "webpack", webpack: { compiler: {} as Compiler } } - ); - - const pluginNames = plugins.map((plugin) => plugin.name); - - // Should include both debug ID related plugins by default - expect(pluginNames).toContain("mock-injection-plugin"); - expect(pluginNames).toContain("mock-debug-id-upload-plugin"); - - // Should include other core plugins - expect(pluginNames).toContain("sentry-telemetry-plugin"); - expect(pluginNames).toContain("sentry-release-management-plugin"); - expect(pluginNames).toContain("sentry-file-deletion-plugin"); - }); - }); - - describe("when entire sourcemaps option is undefined", () => { - it("should include both debug ID injection and upload plugins", () => { - const unpluginInstance = createUnpluginInstance(); - - const plugins = unpluginInstance.raw( - { - authToken: "test-token", - org: "test-org", - project: "test-project", - // sourcemaps option not specified at all - }, - { framework: "webpack", webpack: { compiler: {} as Compiler } } - ); - - const pluginNames = plugins.map((plugin) => plugin.name); - - // Should include both debug ID related plugins by default - expect(pluginNames).toContain("mock-injection-plugin"); - expect(pluginNames).toContain("mock-debug-id-upload-plugin"); - - // Should include other core plugins - expect(pluginNames).toContain("sentry-telemetry-plugin"); - expect(pluginNames).toContain("sentry-release-management-plugin"); - expect(pluginNames).toContain("sentry-file-deletion-plugin"); - }); - }); -}); diff --git a/packages/e2e-tests/utils/create-cjs-bundles.ts b/packages/e2e-tests/utils/create-cjs-bundles.ts index ebd55151..c66cdd45 100644 --- a/packages/e2e-tests/utils/create-cjs-bundles.ts +++ b/packages/e2e-tests/utils/create-cjs-bundles.ts @@ -14,9 +14,9 @@ import { sentryRollupPlugin } from "@sentry/rollup-plugin"; export function createCjsBundles( entrypoints: { [name: string]: string }, outFolder: string, - sentryUnpluginOptions: Options + sentryPluginOptions: Options ): void { - if (!sentryUnpluginOptions.release) { + if (!sentryPluginOptions.release) { console.error("Config has no release set, aborting"); return; } @@ -36,11 +36,11 @@ export function createCjsBundles( }, plugins: [ sentryVitePlugin({ - ...sentryUnpluginOptions, + ...sentryPluginOptions, release: { - name: `${sentryUnpluginOptions.release.name!}-vite`, + name: `${sentryPluginOptions.release.name!}-vite`, uploadLegacySourcemaps: `${ - sentryUnpluginOptions.release.uploadLegacySourcemaps as string + sentryPluginOptions.release.uploadLegacySourcemaps as string }/vite`, }, }), @@ -52,11 +52,11 @@ export function createCjsBundles( input: entrypoints, plugins: [ sentryRollupPlugin({ - ...sentryUnpluginOptions, + ...sentryPluginOptions, release: { - name: `${sentryUnpluginOptions.release.name!}-rollup`, + name: `${sentryPluginOptions.release.name!}-rollup`, uploadLegacySourcemaps: `${ - sentryUnpluginOptions.release.uploadLegacySourcemaps as string + sentryPluginOptions.release.uploadLegacySourcemaps as string }/rollup`, }, }), @@ -77,11 +77,11 @@ export function createCjsBundles( sourcemap: true, plugins: [ sentryEsbuildPlugin({ - ...sentryUnpluginOptions, + ...sentryPluginOptions, release: { - name: `${sentryUnpluginOptions.release.name!}-esbuild`, + name: `${sentryPluginOptions.release.name!}-esbuild`, uploadLegacySourcemaps: `${ - sentryUnpluginOptions.release.uploadLegacySourcemaps as string + sentryPluginOptions.release.uploadLegacySourcemaps as string }/esbuild`, }, }), @@ -104,11 +104,11 @@ export function createCjsBundles( target: "node", // needed for webpack 4 so we can access node api plugins: [ sentryWebpackPlugin({ - ...sentryUnpluginOptions, + ...sentryPluginOptions, release: { - name: `${sentryUnpluginOptions.release.name!}-webpack4`, + name: `${sentryPluginOptions.release.name!}-webpack4`, uploadLegacySourcemaps: `${ - sentryUnpluginOptions.release.uploadLegacySourcemaps as string + sentryPluginOptions.release.uploadLegacySourcemaps as string }/webpack4`, }, }), @@ -135,11 +135,11 @@ export function createCjsBundles( mode: "production", plugins: [ sentryWebpackPlugin({ - ...sentryUnpluginOptions, + ...sentryPluginOptions, release: { - name: `${sentryUnpluginOptions.release.name!}-webpack5`, + name: `${sentryPluginOptions.release.name!}-webpack5`, uploadLegacySourcemaps: `${ - sentryUnpluginOptions.release.uploadLegacySourcemaps as string + sentryPluginOptions.release.uploadLegacySourcemaps as string }/webpack5`, }, }), diff --git a/packages/esbuild-plugin/test/public-api.test.ts b/packages/esbuild-plugin/test/public-api.test.ts index a428c8be..c9408006 100644 --- a/packages/esbuild-plugin/test/public-api.test.ts +++ b/packages/esbuild-plugin/test/public-api.test.ts @@ -1,5 +1,5 @@ -import { EsbuildPlugin } from "unplugin"; import { sentryEsbuildPlugin } from "../src"; +import { Plugin } from "esbuild"; test("Esbuild plugin should exist", () => { expect(sentryEsbuildPlugin).toBeDefined(); @@ -8,13 +8,13 @@ test("Esbuild plugin should exist", () => { describe("sentryEsbuildPlugin", () => { it("returns an esbuild plugin", () => { - const plugins = sentryEsbuildPlugin({ + const plugin = sentryEsbuildPlugin({ authToken: "test-token", org: "test-org", project: "test-project", - }) as EsbuildPlugin; + }) as Plugin; // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - expect(plugins).toEqual({ name: "sentry-esbuild-plugin", setup: expect.any(Function) }); + expect(plugin).toEqual({ name: "sentry-esbuild-plugin", setup: expect.any(Function) }); }); }); diff --git a/packages/integration-tests/utils/create-cjs-bundles-for-react.ts b/packages/integration-tests/utils/create-cjs-bundles-for-react.ts index 757708e9..095784e8 100644 --- a/packages/integration-tests/utils/create-cjs-bundles-for-react.ts +++ b/packages/integration-tests/utils/create-cjs-bundles-for-react.ts @@ -22,7 +22,7 @@ const nodejsMajorversion = process.version.split(".")[0]!.slice(1); export function createCjsBundles( entrypoints: { [name: string]: string }, outFolder: string, - sentryUnpluginOptions: Options, + sentryPluginOptions: Options, plugins: string[] = [] ): void { if (plugins.length === 0 || plugins.includes("vite")) { @@ -38,7 +38,7 @@ export function createCjsBundles( }, }, }, - plugins: [react({ jsxRuntime: "automatic" }), sentryVitePlugin(sentryUnpluginOptions)], + plugins: [react({ jsxRuntime: "automatic" }), sentryVitePlugin(sentryPluginOptions)], }); } if (plugins.length === 0 || plugins.includes("rollup")) { @@ -50,7 +50,7 @@ export function createCjsBundles( extensions: RESOLVABLE_EXTENSIONS, }), commonjs(), - sentryRollupPlugin(sentryUnpluginOptions), + sentryRollupPlugin(sentryPluginOptions), babelPlugin({ babelHelpers: "bundled", presets: [["@babel/preset-react", { runtime: "automatic" }]], @@ -71,7 +71,7 @@ export function createCjsBundles( void esbuild.build({ entryPoints: entrypoints, outdir: path.join(outFolder, "esbuild"), - plugins: [sentryEsbuildPlugin(sentryUnpluginOptions)], + plugins: [sentryEsbuildPlugin(sentryPluginOptions)], minify: true, bundle: true, jsx: "automatic", @@ -111,7 +111,7 @@ export function createCjsBundles( ], }, target: "node", // needed for webpack 4 so we can access node api - plugins: [sentryWebpackPlugin(sentryUnpluginOptions)], + plugins: [sentryWebpackPlugin(sentryPluginOptions)], }, handleWebpack ); @@ -149,7 +149,7 @@ export function createCjsBundles( }, ], }, - plugins: [sentryWebpackPlugin(sentryUnpluginOptions)], + plugins: [sentryWebpackPlugin(sentryPluginOptions)], }, handleWebpack ); diff --git a/packages/integration-tests/utils/create-cjs-bundles-with-query.ts b/packages/integration-tests/utils/create-cjs-bundles-with-query.ts index b3c065ed..59b54994 100644 --- a/packages/integration-tests/utils/create-cjs-bundles-with-query.ts +++ b/packages/integration-tests/utils/create-cjs-bundles-with-query.ts @@ -14,7 +14,7 @@ const nodejsMajorVersion = process.version.split(".")[0]!.slice(1); export function createCjsBundlesWithQueryParam( entrypoints: { [name: string]: string }, outFolder: string, - sentryUnpluginOptions: Options, + sentryPluginOptions: Options, plugins: string[] = [] ): void { if (plugins.length === 0 || plugins.includes("vite")) { @@ -31,14 +31,14 @@ export function createCjsBundlesWithQueryParam( }, }, }, - plugins: [sentryVitePlugin(sentryUnpluginOptions)], + plugins: [sentryVitePlugin(sentryPluginOptions)], }); } if (plugins.length === 0 || plugins.includes("rollup")) { void rollup .rollup({ input: entrypoints, - plugins: [sentryRollupPlugin(sentryUnpluginOptions)], + plugins: [sentryRollupPlugin(sentryPluginOptions)], }) .then((bundle) => bundle.write({ @@ -69,7 +69,7 @@ export function createCjsBundlesWithQueryParam( libraryTarget: "commonjs", }, target: "node", // needed for webpack 4 so we can access node api - plugins: [sentryWebpackPlugin(sentryUnpluginOptions)], + plugins: [sentryWebpackPlugin(sentryPluginOptions)], }, (err) => { if (err) { @@ -93,7 +93,7 @@ export function createCjsBundlesWithQueryParam( }, }, mode: "production", - plugins: [sentryWebpackPlugin(sentryUnpluginOptions)], + plugins: [sentryWebpackPlugin(sentryPluginOptions)], }, (err) => { if (err) { diff --git a/packages/integration-tests/utils/create-cjs-bundles.ts b/packages/integration-tests/utils/create-cjs-bundles.ts index bc60ce2b..631fa288 100644 --- a/packages/integration-tests/utils/create-cjs-bundles.ts +++ b/packages/integration-tests/utils/create-cjs-bundles.ts @@ -17,7 +17,7 @@ type Bundlers = "webpack4" | "webpack5" | "esbuild" | "rollup" | "vite" | string export function createCjsBundles( entrypoints: { [name: string]: string }, outFolder: string, - sentryUnpluginOptions: Options, + sentryPluginOptions: Options, plugins: Bundlers[] = [] ): void { if (plugins.length === 0 || plugins.includes("vite")) { @@ -34,7 +34,7 @@ export function createCjsBundles( }, }, }, - plugins: [sentryVitePlugin(sentryUnpluginOptions)], + plugins: [sentryVitePlugin(sentryPluginOptions)], }); } @@ -42,7 +42,7 @@ export function createCjsBundles( void rollup .rollup({ input: entrypoints, - plugins: [sentryRollupPlugin(sentryUnpluginOptions)], + plugins: [sentryRollupPlugin(sentryPluginOptions)], }) .then((bundle) => bundle.write({ @@ -59,7 +59,7 @@ export function createCjsBundles( sourcemap: true, entryPoints: entrypoints, outdir: path.join(outFolder, "esbuild"), - plugins: [sentryEsbuildPlugin(sentryUnpluginOptions)], + plugins: [sentryEsbuildPlugin(sentryPluginOptions)], minify: true, bundle: true, format: "cjs", @@ -80,7 +80,7 @@ export function createCjsBundles( filename: "[name].js?[contenthash]", }, target: "node", // needed for webpack 4 so we can access node api - plugins: [sentryWebpackPlugin(sentryUnpluginOptions)], + plugins: [sentryWebpackPlugin(sentryPluginOptions)], }, (err) => { if (err) { @@ -103,7 +103,7 @@ export function createCjsBundles( }, }, mode: "production", - plugins: [sentryWebpackPlugin(sentryUnpluginOptions)], + plugins: [sentryWebpackPlugin(sentryPluginOptions)], }, (err) => { if (err) { diff --git a/yarn.lock b/yarn.lock index 4c0e8a6e..cb4861b7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3334,7 +3334,19 @@ "@types/source-list-map" "*" source-map "^0.7.3" -"@types/webpack4@npm:@types/webpack@^4", "@types/webpack@npm:@types/webpack@^4": +"@types/webpack4@npm:@types/webpack@^4": + version "4.41.33" + resolved "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.33.tgz#16164845a5be6a306bcbe554a8e67f9cac215ffc" + integrity sha512-PPajH64Ft2vWevkerISMtnZ8rTs4YmRbs+23c402J0INmxDKCrhZNvwZYtzx96gY2wAtXdrK1BS2fiC8MlLr3g== + dependencies: + "@types/node" "*" + "@types/tapable" "^1" + "@types/uglify-js" "*" + "@types/webpack-sources" "*" + anymatch "^3.0.0" + source-map "^0.6.0" + +"@types/webpack@npm:@types/webpack@^4": version "4.41.33" resolved "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.33.tgz#16164845a5be6a306bcbe554a8e67f9cac215ffc" integrity sha512-PPajH64Ft2vWevkerISMtnZ8rTs4YmRbs+23c402J0INmxDKCrhZNvwZYtzx96gY2wAtXdrK1BS2fiC8MlLr3g== @@ -3960,7 +3972,7 @@ acorn@^7.1.1: resolved "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== -acorn@^8.2.4, acorn@^8.4.1, acorn@^8.5.0, acorn@^8.7.1, acorn@^8.8.0, acorn@^8.8.1: +acorn@^8.2.4, acorn@^8.4.1, acorn@^8.5.0, acorn@^8.7.1, acorn@^8.8.0: version "8.8.2" resolved "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== @@ -4884,7 +4896,7 @@ chokidar@^2.1.8: optionalDependencies: fsevents "^1.2.7" -chokidar@^3.4.1, chokidar@^3.5.1, chokidar@^3.5.3: +chokidar@^3.4.1, chokidar@^3.5.1: version "3.5.3" resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== @@ -12101,7 +12113,16 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" -"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -12183,7 +12204,14 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": + version "6.0.1" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -12833,16 +12861,6 @@ unpipe@1.0.0, unpipe@~1.0.0: resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== -unplugin@1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/unplugin/-/unplugin-1.0.1.tgz#83b528b981cdcea1cad422a12cd02e695195ef3f" - integrity sha512-aqrHaVBWW1JVKBHmGo33T5TxeL0qWzfvjWokObHA9bYmN7eNDkwOxmLjhioHl9878qDFMAaT51XNroRyuz7WxA== - dependencies: - acorn "^8.8.1" - chokidar "^3.5.3" - webpack-sources "^3.2.3" - webpack-virtual-modules "^0.5.0" - unset-value@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" @@ -13110,12 +13128,7 @@ webpack-sources@^3.2.3: resolved "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== -webpack-virtual-modules@^0.5.0: - version "0.5.0" - resolved "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.5.0.tgz#362f14738a56dae107937ab98ea7062e8bdd3b6c" - integrity sha512-kyDivFZ7ZM0BVOUteVbDFhlRt7Ah/CSPwJdi8hBpkK7QLumUqdLtVfm/PX/hkcnrvr0i77fO5+TjZ94Pe+C9iw== - -"webpack4@npm:webpack@4.46.0", "webpack4@npm:webpack@^4", "webpack@npm:webpack@^4": +"webpack4@npm:webpack@4.46.0", "webpack4@npm:webpack@^4": version "4.46.0" resolved "https://registry.npmjs.org/webpack/-/webpack-4.46.0.tgz#bf9b4404ea20a073605e0a011d188d77cb6ad542" integrity sha512-6jJuJjg8znb/xRItk7bkT0+Q7AHCYjjFnvKIWQPkNIOyRqoCGvkOs0ipeQzrqz4l5FtN5ZI/ukEHroeX/o1/5Q== @@ -13204,6 +13217,35 @@ webpack-virtual-modules@^0.5.0: watchpack "^2.4.0" webpack-sources "^3.2.3" +"webpack@npm:webpack@^4": + version "4.46.0" + resolved "https://registry.npmjs.org/webpack/-/webpack-4.46.0.tgz#bf9b4404ea20a073605e0a011d188d77cb6ad542" + integrity sha512-6jJuJjg8znb/xRItk7bkT0+Q7AHCYjjFnvKIWQPkNIOyRqoCGvkOs0ipeQzrqz4l5FtN5ZI/ukEHroeX/o1/5Q== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-module-context" "1.9.0" + "@webassemblyjs/wasm-edit" "1.9.0" + "@webassemblyjs/wasm-parser" "1.9.0" + acorn "^6.4.1" + ajv "^6.10.2" + ajv-keywords "^3.4.1" + chrome-trace-event "^1.0.2" + enhanced-resolve "^4.5.0" + eslint-scope "^4.0.3" + json-parse-better-errors "^1.0.2" + loader-runner "^2.4.0" + loader-utils "^1.2.3" + memory-fs "^0.4.1" + micromatch "^3.1.10" + mkdirp "^0.5.3" + neo-async "^2.6.1" + node-libs-browser "^2.2.1" + schema-utils "^1.0.0" + tapable "^1.1.3" + terser-webpack-plugin "^1.4.3" + watchpack "^1.7.4" + webpack-sources "^1.4.1" + whatwg-encoding@^1.0.5: version "1.0.5" resolved "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" @@ -13301,7 +13343,16 @@ worker-farm@^1.7.0: dependencies: errno "~0.1.7" -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": + version "7.0.0" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== From 48b6aba9f88dd2d32bc335f5fdebe106600eab87 Mon Sep 17 00:00:00 2001 From: Tim Fish Date: Mon, 16 Feb 2026 17:37:11 +0000 Subject: [PATCH 7/8] Fix async --- packages/rollup-plugin/src/index.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/rollup-plugin/src/index.ts b/packages/rollup-plugin/src/index.ts index 902051cd..3ae57bc7 100644 --- a/packages/rollup-plugin/src/index.ts +++ b/packages/rollup-plugin/src/index.ts @@ -15,7 +15,7 @@ import { CodeInjection, } from "@sentry/bundler-plugin-core"; import MagicString, { SourceMap } from "magic-string"; -import type { TransformHook } from "rollup"; +import type { TransformResult } from "rollup"; import * as path from "node:path"; import { createRequire } from "node:module"; @@ -125,14 +125,12 @@ export function _rollupPluginInternal( }); } - function transform(code: string, id: string): ReturnType { + async function transform(code: string, id: string): Promise { // Component annotations are only in user code and boolean flag replacements are // only in Sentry code. If we successfully add annotations, we can return early. if (transformAnnotations?.transform) { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore TS complains about 'this' - const result = transformAnnotations.transform(code, id); + const result = await transformAnnotations.transform(code, id); if (result) { return result; } From 94d6938dc24cef3311198694cb014229ce9ec92c Mon Sep 17 00:00:00 2001 From: Tim Fish Date: Mon, 16 Feb 2026 17:57:06 +0000 Subject: [PATCH 8/8] Fix lint --- packages/bundler-plugin-core/test/index.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/bundler-plugin-core/test/index.test.ts b/packages/bundler-plugin-core/test/index.test.ts index e9bdcf1d..45cf107d 100644 --- a/packages/bundler-plugin-core/test/index.test.ts +++ b/packages/bundler-plugin-core/test/index.test.ts @@ -1,4 +1,3 @@ -import { Compiler } from "webpack"; import { getDebugIdSnippet } from "../src"; import { containsOnlyImports } from "../src/utils";