Skip to content

Commit 7bb0926

Browse files
committed
feat: sourcemaps upload and debug ID injection
1 parent 214e8f5 commit 7bb0926

File tree

7 files changed

+510
-43
lines changed

7 files changed

+510
-43
lines changed

packages/nitro/package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,14 @@
3838
"nitro": ">=3.0.260415-beta"
3939
},
4040
"dependencies": {
41+
"@sentry/bundler-plugin-core": "^5.2.0",
4142
"@sentry/core": "10.48.0",
4243
"@sentry/node": "10.48.0",
4344
"otel-tracing-channel": "^0.2.0"
4445
},
4546
"devDependencies": {
46-
"nitro": "^3.0.260415-beta",
47-
"h3": "^2.0.1-rc.13"
47+
"h3": "^2.0.1-rc.13",
48+
"nitro": "^3.0.260415-beta"
4849
},
4950
"scripts": {
5051
"build": "run-p build:transpile build:types",

packages/nitro/rollup.npm.config.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ export default [
55
makeBaseNPMConfig({
66
entrypoints: ['src/index.ts', 'src/runtime/plugins/server.ts'],
77
packageSpecificConfig: {
8-
external: [/^nitro/, 'otel-tracing-channel', /^h3/, /^srvx/],
8+
external: [/^nitro/, 'otel-tracing-channel', /^h3/, /^srvx/, '@sentry/bundler-plugin-core'],
99
},
1010
}),
1111
),

packages/nitro/src/config.ts

Lines changed: 48 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,72 @@
1+
import type { Options as SentryBundlerPluginOptions } from '@sentry/bundler-plugin-core';
2+
import { debug } from '@sentry/core';
13
import type { NitroConfig } from 'nitro/types';
24
import { createNitroModule } from './module';
35

4-
type SentryNitroOptions = {
5-
// TODO: Add options
6-
};
6+
export type SentryNitroOptions = Pick<
7+
SentryBundlerPluginOptions,
8+
| 'org'
9+
| 'project'
10+
| 'authToken'
11+
| 'url'
12+
| 'headers'
13+
| 'debug'
14+
| 'silent'
15+
| 'errorHandler'
16+
| 'telemetry'
17+
| 'disable'
18+
| 'sourcemaps'
19+
| 'release'
20+
| 'bundleSizeOptimizations'
21+
| '_metaOptions'
22+
>;
723

824
/**
925
* Modifies the passed in Nitro configuration with automatic build-time instrumentation.
10-
*
11-
* @param config A Nitro configuration object, as usually exported in `nitro.config.ts` or `nitro.config.mjs`.
12-
* @returns The modified config to be exported
1326
*/
14-
export function withSentryConfig(config: NitroConfig, moduleOptions?: SentryNitroOptions): NitroConfig {
15-
return setupSentryNitroModule(config, moduleOptions);
27+
export function withSentryConfig(config: NitroConfig, sentryOptions?: SentryNitroOptions): NitroConfig {
28+
return setupSentryNitroModule(config, sentryOptions);
1629
}
1730

1831
/**
1932
* Sets up the Sentry Nitro module, useful for meta framework integrations.
2033
*/
2134
export function setupSentryNitroModule(
2235
config: NitroConfig,
23-
_moduleOptions?: SentryNitroOptions,
36+
moduleOptions?: SentryNitroOptions,
2437
_serverConfigFile?: string,
2538
): NitroConfig {
2639
if (!config.tracingChannel) {
2740
config.tracingChannel = true;
2841
}
2942

43+
const sourcemapUploadDisabled = moduleOptions?.sourcemaps?.disable === true || moduleOptions?.disable === true;
44+
45+
if (!sourcemapUploadDisabled) {
46+
configureSourcemapSettings(config, moduleOptions);
47+
}
48+
3049
config.modules = config.modules || [];
31-
config.modules.push(createNitroModule());
50+
config.modules.push(createNitroModule(moduleOptions));
3251

3352
return config;
3453
}
54+
55+
function configureSourcemapSettings(config: NitroConfig, moduleOptions?: SentryNitroOptions): void {
56+
if (config.sourcemap === false) {
57+
debug.warn(
58+
'[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.',
59+
);
60+
}
61+
config.sourcemap = true;
62+
63+
// Nitro v3 has a `sourcemapMinify` plugin that destructively deletes `sourcesContent`,
64+
// `x_google_ignoreList`, and clears `mappings` for any chunk containing `node_modules`.
65+
// This makes sourcemaps unusable for Sentry.
66+
config.experimental = config.experimental || {};
67+
config.experimental.sourcemapMinify = false;
68+
69+
if (moduleOptions?.debug) {
70+
debug.log('[Sentry] Enabled source map generation and configured build settings for Sentry source map uploads.');
71+
}
72+
}

packages/nitro/src/module.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
import type { NitroModule } from 'nitro/types';
2+
import type { SentryNitroOptions } from './config';
23
import { instrumentServer } from './instruments/instrumentServer';
4+
import { setupSourceMaps } from './sourceMaps';
35

46
/**
57
* Creates a Nitro module to setup the Sentry SDK.
68
*/
7-
export function createNitroModule(): NitroModule {
9+
export function createNitroModule(sentryOptions?: SentryNitroOptions): NitroModule {
810
return {
911
name: 'sentry',
1012
setup: nitro => {
1113
instrumentServer(nitro);
14+
setupSourceMaps(nitro, sentryOptions);
1215
},
1316
};
1417
}

packages/nitro/src/sourceMaps.ts

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import type { Options } from '@sentry/bundler-plugin-core';
2+
import { createSentryBuildPluginManager } from '@sentry/bundler-plugin-core';
3+
import type { Nitro } from 'nitro/types';
4+
import type { SentryNitroOptions } from './config';
5+
6+
/**
7+
* Registers a `compiled` hook to upload source maps after the build completes.
8+
*/
9+
export function setupSourceMaps(nitro: Nitro, options?: SentryNitroOptions): void {
10+
// The `compiled` hook fires on EVERY rebuild during `nitro dev` watch mode.
11+
// nitro.options.dev is reliably set by the time module setup runs.
12+
if (nitro.options.dev) {
13+
return;
14+
}
15+
16+
// Respect user's explicit disable
17+
if (options?.sourcemaps?.disable === true || options?.disable === true) {
18+
return;
19+
}
20+
21+
nitro.hooks.hook('compiled', async (_nitro: Nitro) => {
22+
await handleSourceMapUpload(_nitro, options);
23+
});
24+
}
25+
26+
/**
27+
* Handles the actual source map upload after the build completes.
28+
*/
29+
async function handleSourceMapUpload(nitro: Nitro, options?: SentryNitroOptions): Promise<void> {
30+
const outputDir = nitro.options.output.serverDir;
31+
const pluginOptions = getPluginOptions(options);
32+
33+
const sentryBuildPluginManager = createSentryBuildPluginManager(pluginOptions, {
34+
buildTool: 'nitro',
35+
loggerPrefix: '[@sentry/nitro]',
36+
});
37+
38+
await sentryBuildPluginManager.telemetry.emitBundlerPluginExecutionSignal();
39+
await sentryBuildPluginManager.createRelease();
40+
41+
if (options?.sourcemaps?.disable !== 'disable-upload') {
42+
await sentryBuildPluginManager.injectDebugIds([outputDir]);
43+
await sentryBuildPluginManager.uploadSourcemaps([outputDir], {
44+
// We don't prepare the artifacts because we injected debug IDs manually before
45+
prepareArtifacts: false,
46+
});
47+
}
48+
49+
await sentryBuildPluginManager.deleteArtifacts();
50+
}
51+
52+
/**
53+
* Normalizes the beginning of a path from e.g. ../../../ to ./
54+
*/
55+
function normalizePath(path: string): string {
56+
return path.replace(/^(\.\.\/)+/, './');
57+
}
58+
59+
/**
60+
* Builds the plugin options for `createSentryBuildPluginManager` from the Sentry Nitro options.
61+
*
62+
* Only exported for testing purposes.
63+
*/
64+
export function getPluginOptions(options?: SentryNitroOptions): Options {
65+
return {
66+
org: options?.org ?? process.env.SENTRY_ORG,
67+
project: options?.project ?? process.env.SENTRY_PROJECT,
68+
authToken: options?.authToken ?? process.env.SENTRY_AUTH_TOKEN,
69+
url: options?.url ?? process.env.SENTRY_URL,
70+
headers: options?.headers,
71+
telemetry: options?.telemetry ?? true,
72+
debug: options?.debug ?? false,
73+
silent: options?.silent ?? false,
74+
errorHandler: options?.errorHandler,
75+
sourcemaps: {
76+
disable: options?.sourcemaps?.disable,
77+
assets: options?.sourcemaps?.assets,
78+
ignore: options?.sourcemaps?.ignore,
79+
filesToDeleteAfterUpload: options?.sourcemaps?.filesToDeleteAfterUpload ?? ['**/*.map'],
80+
rewriteSources: (source: string) => normalizePath(source),
81+
},
82+
release: options?.release,
83+
bundleSizeOptimizations: options?.bundleSizeOptimizations,
84+
_metaOptions: {
85+
telemetry: {
86+
metaFramework: 'nitro',
87+
},
88+
...options?._metaOptions,
89+
},
90+
};
91+
}

0 commit comments

Comments
 (0)