From 80ec41a1f9b7501b2c6a3ff3ee338c45cf9c4bf1 Mon Sep 17 00:00:00 2001 From: Sigrid Huemer <32902192+s1gr1d@users.noreply.github.com> Date: Wed, 4 Sep 2024 17:15:30 +0200 Subject: [PATCH] feat(nuxt): Upload sourcemaps generated by Nitro (#13382) Adding another source maps generation step with rollup to include Nitro-generated source maps as well. closes https://github.com/getsentry/sentry-javascript/issues/13383 --- packages/nuxt/package.json | 1 + packages/nuxt/src/vite/sourceMaps.ts | 126 +++++++++++++++++++-------- yarn.lock | 8 ++ 3 files changed, 97 insertions(+), 38 deletions(-) diff --git a/packages/nuxt/package.json b/packages/nuxt/package.json index 2aaa9f06dd78..688cac5489af 100644 --- a/packages/nuxt/package.json +++ b/packages/nuxt/package.json @@ -47,6 +47,7 @@ "@sentry/core": "8.28.0", "@sentry/node": "8.28.0", "@sentry/opentelemetry": "8.28.0", + "@sentry/rollup-plugin": "2.22.3", "@sentry/types": "8.28.0", "@sentry/utils": "8.28.0", "@sentry/vite-plugin": "2.22.3", diff --git a/packages/nuxt/src/vite/sourceMaps.ts b/packages/nuxt/src/vite/sourceMaps.ts index 3518c45409e0..18eed2cbcfd8 100644 --- a/packages/nuxt/src/vite/sourceMaps.ts +++ b/packages/nuxt/src/vite/sourceMaps.ts @@ -1,52 +1,102 @@ import type { Nuxt } from '@nuxt/schema'; -import { sentryVitePlugin } from '@sentry/vite-plugin'; +import { type SentryRollupPluginOptions, sentryRollupPlugin } from '@sentry/rollup-plugin'; +import { type SentryVitePluginOptions, sentryVitePlugin } from '@sentry/vite-plugin'; +import type { NitroConfig } from 'nitropack'; import type { SentryNuxtModuleOptions } from '../common/types'; /** - * Setup source maps for Sentry inside the Nuxt module during build time. + * Setup source maps for Sentry inside the Nuxt module during build time (in Vite for Nuxt and Rollup for Nitro). */ export function setupSourceMaps(moduleOptions: SentryNuxtModuleOptions, nuxt: Nuxt): void { - nuxt.hook('vite:extendConfig', async (viteInlineConfig, _env) => { - const sourceMapsUploadOptions = moduleOptions.sourceMapsUploadOptions || {}; - - if ((sourceMapsUploadOptions.enabled ?? true) && viteInlineConfig.mode !== 'development') { - const sentryPlugin = sentryVitePlugin({ - org: sourceMapsUploadOptions.org ?? process.env.SENTRY_ORG, - project: sourceMapsUploadOptions.project ?? process.env.SENTRY_PROJECT, - authToken: sourceMapsUploadOptions.authToken ?? process.env.SENTRY_AUTH_TOKEN, - telemetry: sourceMapsUploadOptions.telemetry ?? true, - sourcemaps: { - assets: sourceMapsUploadOptions.sourcemaps?.assets ?? undefined, - ignore: sourceMapsUploadOptions.sourcemaps?.ignore ?? undefined, - filesToDeleteAfterUpload: sourceMapsUploadOptions.sourcemaps?.filesToDeleteAfterUpload ?? undefined, - }, - _metaOptions: { - telemetry: { - metaFramework: 'nuxt', - }, - }, - debug: moduleOptions.debug ?? false, - }); + const sourceMapsUploadOptions = moduleOptions.sourceMapsUploadOptions || {}; + const sourceMapsEnabled = sourceMapsUploadOptions.enabled ?? true; + nuxt.hook('vite:extendConfig', async (viteInlineConfig, _env) => { + if (sourceMapsEnabled && viteInlineConfig.mode !== 'development') { + // Add Sentry plugin viteInlineConfig.plugins = viteInlineConfig.plugins || []; - viteInlineConfig.plugins.push(sentryPlugin); - - const sourceMapsPreviouslyEnabled = viteInlineConfig.build?.sourcemap; - - if (moduleOptions.debug && !sourceMapsPreviouslyEnabled) { - // eslint-disable-next-line no-console - console.log('[Sentry]: Enabled source maps generation in the Vite build options.'); - if (!moduleOptions.sourceMapsUploadOptions?.sourcemaps?.filesToDeleteAfterUpload) { - // eslint-disable-next-line no-console - console.warn( - `[Sentry] We recommend setting the \`sourceMapsUploadOptions.sourcemaps.filesToDeleteAfterUpload\` option to clean up source maps after uploading. -[Sentry] Otherwise, source maps might be deployed to production, depending on your configuration`, - ); - } - } + viteInlineConfig.plugins.push(sentryVitePlugin(getPluginOptions(moduleOptions))); + // Enable source maps viteInlineConfig.build = viteInlineConfig.build || {}; viteInlineConfig.build.sourcemap = true; + + logDebugInfo(moduleOptions, viteInlineConfig.build?.sourcemap); + } + }); + + nuxt.hook('nitro:config', (nitroConfig: NitroConfig) => { + if (sourceMapsEnabled && !nitroConfig.dev) { + if (!nitroConfig.rollupConfig) { + nitroConfig.rollupConfig = {}; + } + + if (nitroConfig.rollupConfig.plugins === null || nitroConfig.rollupConfig.plugins === undefined) { + nitroConfig.rollupConfig.plugins = []; + } else if (!Array.isArray(nitroConfig.rollupConfig.plugins)) { + // `rollupConfig.plugins` can be a single plugin, so we want to put it into an array so that we can push our own plugin + nitroConfig.rollupConfig.plugins = [nitroConfig.rollupConfig.plugins]; + } + + // Add Sentry plugin + nitroConfig.rollupConfig.plugins.push(sentryRollupPlugin(getPluginOptions(moduleOptions, true))); + + // Enable source maps + nitroConfig.rollupConfig.output = nitroConfig?.rollupConfig?.output || {}; + nitroConfig.rollupConfig.output.sourcemap = true; + nitroConfig.rollupConfig.output.sourcemapExcludeSources = false; // Adding "sourcesContent" to the source map (Nitro sets this eto `true`) + + logDebugInfo(moduleOptions, nitroConfig.rollupConfig.output?.sourcemap); } }); } + +/** + * Normalizes the beginning of a path from e.g. ../../../ to ./ + */ +function normalizePath(path: string): string { + return path.replace(/^(\.\.\/)+/, './'); +} + +function getPluginOptions( + moduleOptions: SentryNuxtModuleOptions, + isNitro = false, +): SentryVitePluginOptions | SentryRollupPluginOptions { + const sourceMapsUploadOptions = moduleOptions.sourceMapsUploadOptions || {}; + + return { + org: sourceMapsUploadOptions.org ?? process.env.SENTRY_ORG, + project: sourceMapsUploadOptions.project ?? process.env.SENTRY_PROJECT, + authToken: sourceMapsUploadOptions.authToken ?? process.env.SENTRY_AUTH_TOKEN, + telemetry: sourceMapsUploadOptions.telemetry ?? true, + sourcemaps: { + assets: + sourceMapsUploadOptions.sourcemaps?.assets ?? isNitro ? ['./.output/server/**/*'] : ['./.output/public/**/*'], + ignore: sourceMapsUploadOptions.sourcemaps?.ignore ?? undefined, + filesToDeleteAfterUpload: sourceMapsUploadOptions.sourcemaps?.filesToDeleteAfterUpload ?? undefined, + rewriteSources: (source: string) => normalizePath(source), + }, + _metaOptions: { + telemetry: { + metaFramework: 'nuxt', + }, + }, + debug: moduleOptions.debug ?? false, + }; +} + +function logDebugInfo(moduleOptions: SentryNuxtModuleOptions, sourceMapsPreviouslyEnabled: boolean): void { + if (moduleOptions.debug && !sourceMapsPreviouslyEnabled) { + // eslint-disable-next-line no-console + console.log('[Sentry]: Enabled source maps generation in the Vite build options.'); + + const sourceMapsUploadOptions = moduleOptions.sourceMapsUploadOptions || {}; + + if (!sourceMapsUploadOptions.sourcemaps?.filesToDeleteAfterUpload) { + // eslint-disable-next-line no-console + console.warn( + '[Sentry] We recommend setting the `sourceMapsUploadOptions.sourcemaps.filesToDeleteAfterUpload` option to clean up source maps after uploading. Otherwise, source maps might be deployed to production, depending on your configuration', + ); + } + } +} diff --git a/yarn.lock b/yarn.lock index b0a8c712e374..a79245f2672c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8309,6 +8309,14 @@ "@sentry/cli-win32-i686" "2.33.1" "@sentry/cli-win32-x64" "2.33.1" +"@sentry/rollup-plugin@2.22.3": + version "2.22.3" + resolved "https://registry.yarnpkg.com/@sentry/rollup-plugin/-/rollup-plugin-2.22.3.tgz#18ab4b7903ee723bee4cf789b38bb3febb05faae" + integrity sha512-I1UsnYzZm5W7/Pyah2yxuMRxmzgf5iDKoptFfMaerpRO5oBhFO3tMnKSLAlYMvuXKRoYkInNv6ckkUcSOF6jig== + dependencies: + "@sentry/bundler-plugin-core" "2.22.3" + unplugin "1.0.1" + "@sentry/vite-plugin@2.22.3", "@sentry/vite-plugin@^2.22.3": version "2.22.3" resolved "https://registry.yarnpkg.com/@sentry/vite-plugin/-/vite-plugin-2.22.3.tgz#b52802412b6f3d8e3e56742afc9624d9babae5b6"