-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
feat(nitro): Handle sourcemap preparation and upload #19304
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: awad/js-1128-nitro-use-tracing-channels-for-srvx-and-h3
Are you sure you want to change the base?
Changes from all commits
7bb0926
c8aa72e
0cbc323
07659b6
00bd835
973bf37
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,34 +1,49 @@ | ||
| import type { Options as SentryBundlerPluginOptions } from '@sentry/bundler-plugin-core'; | ||
| import type { NitroConfig } from 'nitro/types'; | ||
| import { createNitroModule } from './module'; | ||
| import { configureSourcemapSettings } from './sourceMaps'; | ||
|
|
||
| type SentryNitroOptions = { | ||
| // TODO: Add options | ||
| }; | ||
| export type SentryNitroOptions = Pick< | ||
| SentryBundlerPluginOptions, | ||
| | 'org' | ||
| | 'project' | ||
| | 'authToken' | ||
| | 'url' | ||
| | 'headers' | ||
| | 'debug' | ||
| | 'silent' | ||
| | 'errorHandler' | ||
| | 'telemetry' | ||
| | 'disable' | ||
| | 'sourcemaps' | ||
| | 'release' | ||
| | 'bundleSizeOptimizations' | ||
| | '_metaOptions' | ||
| >; | ||
|
|
||
| /** | ||
| * Modifies the passed in Nitro configuration with automatic build-time instrumentation. | ||
| * | ||
| * @param config A Nitro configuration object, as usually exported in `nitro.config.ts` or `nitro.config.mjs`. | ||
| * @returns The modified config to be exported | ||
| */ | ||
| export function withSentryConfig(config: NitroConfig, moduleOptions?: SentryNitroOptions): NitroConfig { | ||
| return setupSentryNitroModule(config, moduleOptions); | ||
| export function withSentryConfig(config: NitroConfig, sentryOptions?: SentryNitroOptions): NitroConfig { | ||
| return setupSentryNitroModule(config, sentryOptions); | ||
| } | ||
|
|
||
| /** | ||
| * Sets up the Sentry Nitro module, useful for meta framework integrations. | ||
| */ | ||
| export function setupSentryNitroModule( | ||
| config: NitroConfig, | ||
| _moduleOptions?: SentryNitroOptions, | ||
| moduleOptions?: SentryNitroOptions, | ||
| _serverConfigFile?: string, | ||
| ): NitroConfig { | ||
| if (!config.tracingChannel) { | ||
| config.tracingChannel = true; | ||
| } | ||
|
|
||
| configureSourcemapSettings(config, moduleOptions); | ||
|
|
||
| config.modules = config.modules || []; | ||
| config.modules.push(createNitroModule()); | ||
| config.modules.push(createNitroModule(moduleOptions)); | ||
|
|
||
| return config; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,14 +1,17 @@ | ||
| import type { NitroModule } from 'nitro/types'; | ||
| import type { SentryNitroOptions } from './config'; | ||
| import { instrumentServer } from './instruments/instrumentServer'; | ||
| import { setupSourceMaps } from './sourceMaps'; | ||
|
|
||
| /** | ||
| * Creates a Nitro module to setup the Sentry SDK. | ||
| */ | ||
| export function createNitroModule(): NitroModule { | ||
| export function createNitroModule(sentryOptions?: SentryNitroOptions): NitroModule { | ||
| return { | ||
| name: 'sentry', | ||
| setup: nitro => { | ||
| instrumentServer(nitro); | ||
| setupSourceMaps(nitro, sentryOptions); | ||
| }, | ||
| }; | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,121 @@ | ||||||||||||||||||||||||||
| import type { Options } from '@sentry/bundler-plugin-core'; | ||||||||||||||||||||||||||
| import { createSentryBuildPluginManager } from '@sentry/bundler-plugin-core'; | ||||||||||||||||||||||||||
| import { debug } from '@sentry/core'; | ||||||||||||||||||||||||||
| import type { Nitro, NitroConfig } from 'nitro/types'; | ||||||||||||||||||||||||||
| import type { SentryNitroOptions } from './config'; | ||||||||||||||||||||||||||
|
Comment on lines
+1
to
+5
|
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||
| * Registers a `compiled` hook to upload source maps after the build completes. | ||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||
| export function setupSourceMaps(nitro: Nitro, options?: SentryNitroOptions): void { | ||||||||||||||||||||||||||
| // The `compiled` hook fires on EVERY rebuild during `nitro dev` watch mode. | ||||||||||||||||||||||||||
| // nitro.options.dev is reliably set by the time module setup runs. | ||||||||||||||||||||||||||
| if (nitro.options.dev) { | ||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // Respect user's explicit disable | ||||||||||||||||||||||||||
| if (options?.sourcemaps?.disable === true || options?.disable === true) { | ||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| nitro.hooks.hook('compiled', async (_nitro: Nitro) => { | ||||||||||||||||||||||||||
| await handleSourceMapUpload(_nitro, options); | ||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||
| * Handles the actual source map upload after the build completes. | ||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||
| async function handleSourceMapUpload(nitro: Nitro, options?: SentryNitroOptions): Promise<void> { | ||||||||||||||||||||||||||
| const outputDir = nitro.options.output.serverDir; | ||||||||||||||||||||||||||
| const pluginOptions = getPluginOptions(options); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| const sentryBuildPluginManager = createSentryBuildPluginManager(pluginOptions, { | ||||||||||||||||||||||||||
| buildTool: 'nitro', | ||||||||||||||||||||||||||
| loggerPrefix: '[@sentry/nitro]', | ||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| await sentryBuildPluginManager.telemetry.emitBundlerPluginExecutionSignal(); | ||||||||||||||||||||||||||
| await sentryBuildPluginManager.createRelease(); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| if (options?.sourcemaps?.disable !== 'disable-upload') { | ||||||||||||||||||||||||||
| await sentryBuildPluginManager.injectDebugIds([outputDir]); | ||||||||||||||||||||||||||
| await sentryBuildPluginManager.uploadSourcemaps([outputDir], { | ||||||||||||||||||||||||||
| // We don't prepare the artifacts because we injected debug IDs manually before | ||||||||||||||||||||||||||
| prepareArtifacts: false, | ||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
Comment on lines
+42
to
+48
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bug: When Suggested FixMove the Prompt for AI AgentDid we get this right? 👍 / 👎 to inform future reviews.
Comment on lines
+42
to
+48
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| await sentryBuildPluginManager.deleteArtifacts(); | ||||||||||||||||||||||||||
|
Comment on lines
+48
to
+50
|
||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||
| * Normalizes the beginning of a path from e.g. ../../../ to ./ | ||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||
| function normalizePath(path: string): string { | ||||||||||||||||||||||||||
| return path.replace(/^(\.\.\/)+/, './'); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||
| * Builds the plugin options for `createSentryBuildPluginManager` from the Sentry Nitro options. | ||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||
| * Only exported for testing purposes. | ||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||
| export function getPluginOptions(options?: SentryNitroOptions): Options { | ||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||
| org: options?.org ?? process.env.SENTRY_ORG, | ||||||||||||||||||||||||||
| project: options?.project ?? process.env.SENTRY_PROJECT, | ||||||||||||||||||||||||||
| authToken: options?.authToken ?? process.env.SENTRY_AUTH_TOKEN, | ||||||||||||||||||||||||||
| url: options?.url ?? process.env.SENTRY_URL, | ||||||||||||||||||||||||||
| headers: options?.headers, | ||||||||||||||||||||||||||
| telemetry: options?.telemetry ?? true, | ||||||||||||||||||||||||||
| debug: options?.debug ?? false, | ||||||||||||||||||||||||||
| silent: options?.silent ?? false, | ||||||||||||||||||||||||||
| errorHandler: options?.errorHandler, | ||||||||||||||||||||||||||
| sourcemaps: { | ||||||||||||||||||||||||||
| disable: options?.sourcemaps?.disable, | ||||||||||||||||||||||||||
| assets: options?.sourcemaps?.assets, | ||||||||||||||||||||||||||
| ignore: options?.sourcemaps?.ignore, | ||||||||||||||||||||||||||
| filesToDeleteAfterUpload: options?.sourcemaps?.filesToDeleteAfterUpload ?? ['**/*.map'], | ||||||||||||||||||||||||||
| rewriteSources: (source: string) => normalizePath(source), | ||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. User-provided
|
||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||
| release: options?.release, | ||||||||||||||||||||||||||
| bundleSizeOptimizations: options?.bundleSizeOptimizations, | ||||||||||||||||||||||||||
| _metaOptions: { | ||||||||||||||||||||||||||
| telemetry: { | ||||||||||||||||||||||||||
| metaFramework: 'nitro', | ||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||
| ...options?._metaOptions, | ||||||||||||||||||||||||||
|
Comment on lines
+86
to
+89
|
||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||
| * Configures the Nitro config to enable source map generation. | ||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||
| export function configureSourcemapSettings(config: NitroConfig, moduleOptions?: SentryNitroOptions): void { | ||||||||||||||||||||||||||
| const sourcemapUploadDisabled = moduleOptions?.sourcemaps?.disable === true || moduleOptions?.disable === true; | ||||||||||||||||||||||||||
| if (sourcemapUploadDisabled) { | ||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| if (config.sourcemap === false) { | ||||||||||||||||||||||||||
| debug.warn( | ||||||||||||||||||||||||||
| '[Sentry] You have explicitly disabled source maps (`sourcemap: false`). Sentry is overriding this to `true` so that errors can be un-minified in Sentry. To disable Sentry source map uploads entirely, use `sourcemaps: { disable: true }` in your Sentry options instead.', | ||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| config.sourcemap = true; | ||||||||||||||||||||||||||
|
Comment on lines
+103
to
+109
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the source map is explicitly disabled, we should not mess with this option, but just log the warning. Only when it's The rules about how we handle source maps are e.g. described here: sentry-javascript/packages/nuxt/src/vite/sourceMaps.ts Lines 225 to 236 in 13dc7a1
|
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // Nitro v3 has a `sourcemapMinify` plugin that destructively deletes `sourcesContent`, | ||||||||||||||||||||||||||
| // `x_google_ignoreList`, and clears `mappings` for any chunk containing `node_modules`. | ||||||||||||||||||||||||||
| // This makes sourcemaps unusable for Sentry. | ||||||||||||||||||||||||||
| // FIXME: Not sure about this one, it works either way? | ||||||||||||||||||||||||||
|
Comment on lines
+113
to
+114
|
||||||||||||||||||||||||||
| config.experimental = config.experimental || {}; | ||||||||||||||||||||||||||
| config.experimental.sourcemapMinify = false; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| if (moduleOptions?.debug) { | ||||||||||||||||||||||||||
| debug.log('[Sentry] Enabled source map generation and configured build settings for Sentry source map uploads.'); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||


There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can/should use
BuildTimeOptionsBasehere. That way we have a second layer of type versioning. The underlying build-time options from the bundler plugins might have breaking changes (that's whyunstable_sentryBundlerPluginOptionsuses the directly exported type of the bundler plugins).The
BuildTimeOptionsBasefollows the semantic versioning of this repository.