Skip to content

Commit 6d2bfb4

Browse files
authored
feat(astro): Respect user-specified source map setting (#14941)
Closes #14934
1 parent e886671 commit 6d2bfb4

File tree

4 files changed

+211
-12
lines changed

4 files changed

+211
-12
lines changed

docs/migration/v8-to-v9.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,16 @@ In v9, an `undefined` value will be treated the same as if the value is not defi
9898

9999
This behavior was changed because the Next.js Build ID is non-deterministic and the release name is injected into client bundles, causing build artifacts to be non-deterministic. This caused issues for some users. Additionally, because it is uncertain whether it will be possible to rely on a Build ID when Turbopack becomes stable, we decided to pull the plug now instead of introducing confusing behavior in the future.
100100

101+
### All Meta-Framework SDKs (`@sentry/astro`, `@sentry/nuxt`)
102+
103+
- Updated source map generation to respect the user-provided value of your build config, such as `vite.build.sourcemap`:
104+
105+
- Explicitly disabled (false): Emit warning, no source map upload.
106+
- Explicitly enabled (true, 'hidden', 'inline'): No changes, source maps are uploaded and not automatically deleted.
107+
- Unset: Enable 'hidden', delete `.map` files after uploading them to Sentry.
108+
109+
To customize which files are deleted after upload, define the `filesToDeleteAfterUpload` array with globs.
110+
101111
### Uncategorized (TODO)
102112

103113
TODO

packages/astro/src/integration/index.ts

Lines changed: 93 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import * as path from 'path';
33
import { sentryVitePlugin } from '@sentry/vite-plugin';
44
import type { AstroConfig, AstroIntegration } from 'astro';
55

6-
import { dropUndefinedKeys } from '@sentry/core';
6+
import { consoleSandbox, dropUndefinedKeys } from '@sentry/core';
77
import { buildClientSnippet, buildSdkInitFileImportSnippet, buildServerSnippet } from './snippets';
88
import type { SentryOptions } from './types';
99

@@ -35,19 +35,31 @@ export const sentryAstro = (options: SentryOptions = {}): AstroIntegration => {
3535

3636
// We don't need to check for AUTH_TOKEN here, because the plugin will pick it up from the env
3737
if (shouldUploadSourcemaps && command !== 'dev') {
38-
// TODO(v9): Remove this warning
39-
if (config?.vite?.build?.sourcemap === false) {
40-
logger.warn(
41-
"You disabled sourcemaps with the `vite.build.sourcemap` option. Currently, the Sentry SDK will override this option to generate sourcemaps. In future versions, the Sentry SDK will not override the `vite.build.sourcemap` option if you explicitly disable it. If you want to generate and upload sourcemaps please set the `vite.build.sourcemap` option to 'hidden' or undefined.",
42-
);
43-
}
38+
const computedSourceMapSettings = getUpdatedSourceMapSettings(config, options);
39+
40+
let updatedFilesToDeleteAfterUpload: string[] | undefined = undefined;
41+
42+
if (
43+
typeof uploadOptions?.filesToDeleteAfterUpload === 'undefined' &&
44+
computedSourceMapSettings.previousUserSourceMapSetting === 'unset'
45+
) {
46+
// This also works for adapters, as the source maps are also copied to e.g. the .vercel folder
47+
updatedFilesToDeleteAfterUpload = ['./dist/**/client/**/*.map', './dist/**/server/**/*.map'];
4448

45-
// TODO: Add deleteSourcemapsAfterUpload option and warn if it isn't set.
49+
consoleSandbox(() => {
50+
// eslint-disable-next-line no-console
51+
console.log(
52+
`[Sentry] Setting \`sourceMapsUploadOptions.filesToDeleteAfterUpload: ${JSON.stringify(
53+
updatedFilesToDeleteAfterUpload,
54+
)}\` to delete generated source maps after they were uploaded to Sentry.`,
55+
);
56+
});
57+
}
4658

4759
updateConfig({
4860
vite: {
4961
build: {
50-
sourcemap: true,
62+
sourcemap: computedSourceMapSettings.updatedSourceMapSetting,
5163
},
5264
plugins: [
5365
sentryVitePlugin(
@@ -58,6 +70,8 @@ export const sentryAstro = (options: SentryOptions = {}): AstroIntegration => {
5870
telemetry: uploadOptions.telemetry ?? true,
5971
sourcemaps: {
6072
assets: uploadOptions.assets ?? [getSourcemapsAssetsGlob(config)],
73+
filesToDeleteAfterUpload:
74+
uploadOptions?.filesToDeleteAfterUpload ?? updatedFilesToDeleteAfterUpload,
6175
},
6276
bundleSizeOptimizations: {
6377
...options.bundleSizeOptimizations,
@@ -171,3 +185,73 @@ function getSourcemapsAssetsGlob(config: AstroConfig): string {
171185
// fallback to default output dir
172186
return 'dist/**/*';
173187
}
188+
189+
/**
190+
* Whether the user enabled (true, 'hidden', 'inline') or disabled (false) source maps
191+
*/
192+
export type UserSourceMapSetting = 'enabled' | 'disabled' | 'unset' | undefined;
193+
194+
/** There are 3 ways to set up source map generation (https://github.com/getsentry/sentry-javascript/issues/13993)
195+
*
196+
* 1. User explicitly disabled source maps
197+
* - keep this setting (emit a warning that errors won't be unminified in Sentry)
198+
* - We won't upload anything
199+
*
200+
* 2. Users enabled source map generation (true, 'hidden', 'inline').
201+
* - keep this setting (don't do anything - like deletion - besides uploading)
202+
*
203+
* 3. Users didn't set source maps generation
204+
* - we enable 'hidden' source maps generation
205+
* - configure `filesToDeleteAfterUpload` to delete all .map files (we emit a log about this)
206+
*
207+
* --> only exported for testing
208+
*/
209+
export function getUpdatedSourceMapSettings(
210+
astroConfig: AstroConfig,
211+
sentryOptions?: SentryOptions,
212+
): { previousUserSourceMapSetting: UserSourceMapSetting; updatedSourceMapSetting: boolean | 'inline' | 'hidden' } {
213+
let previousUserSourceMapSetting: UserSourceMapSetting = undefined;
214+
215+
astroConfig.build = astroConfig.build || {};
216+
217+
const viteSourceMap = astroConfig?.vite?.build?.sourcemap;
218+
let updatedSourceMapSetting = viteSourceMap;
219+
220+
const settingKey = 'vite.build.sourcemap';
221+
222+
if (viteSourceMap === false) {
223+
previousUserSourceMapSetting = 'disabled';
224+
updatedSourceMapSetting = viteSourceMap;
225+
226+
consoleSandbox(() => {
227+
// eslint-disable-next-line no-console
228+
console.warn(
229+
`[Sentry] Source map generation are currently disabled in your Astro configuration (\`${settingKey}: false\`). This setting is either a default setting or was explicitly set in your configuration. Sentry won't override this setting. Without source maps, code snippets on the Sentry Issues page will remain minified. To show unminified code, enable source maps in \`${settingKey}\` (e.g. by setting them to \`hidden\`).`,
230+
);
231+
});
232+
} else if (viteSourceMap && ['hidden', 'inline', true].includes(viteSourceMap)) {
233+
previousUserSourceMapSetting = 'enabled';
234+
updatedSourceMapSetting = viteSourceMap;
235+
236+
if (sentryOptions?.debug) {
237+
consoleSandbox(() => {
238+
// eslint-disable-next-line no-console
239+
console.log(
240+
`[Sentry] We discovered \`${settingKey}\` is set to \`${viteSourceMap.toString()}\`. Sentry will keep this source map setting. This will un-minify the code snippet on the Sentry Issue page.`,
241+
);
242+
});
243+
}
244+
} else {
245+
previousUserSourceMapSetting = 'unset';
246+
updatedSourceMapSetting = 'hidden';
247+
248+
consoleSandbox(() => {
249+
// eslint-disable-next-line no-console
250+
console.log(
251+
`[Sentry] Enabled source map generation in the build options with \`${settingKey}: 'hidden'\`. The source maps will be deleted after they were uploaded to Sentry.`,
252+
);
253+
});
254+
}
255+
256+
return { previousUserSourceMapSetting, updatedSourceMapSetting };
257+
}

packages/astro/src/integration/types.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,16 @@ type SourceMapsOptions = {
7373
* @see https://www.npmjs.com/package/glob#glob-primer
7474
*/
7575
assets?: string | Array<string>;
76+
77+
/**
78+
* A glob or an array of globs that specifies the build artifacts that should be deleted after the artifact
79+
* upload to Sentry has been completed.
80+
*
81+
* @default [] - By default no files are deleted.
82+
*
83+
* The globbing patterns follow the implementation of the glob package. (https://www.npmjs.com/package/glob)
84+
*/
85+
filesToDeleteAfterUpload?: string | Array<string>;
7686
};
7787

7888
type BundleSizeOptimizationOptions = {

packages/astro/test/integration/index.test.ts

Lines changed: 98 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
import { afterEach, describe, expect, it, vi } from 'vitest';
1+
import type { AstroConfig } from 'astro';
2+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
3+
import { getUpdatedSourceMapSettings } from '../../src/integration/index';
4+
import type { SentryOptions } from '../../src/integration/types';
25

36
import { sentryAstro } from '../../src/integration';
47

@@ -31,7 +34,7 @@ describe('sentryAstro integration', () => {
3134
expect(integration.name).toBe('@sentry/astro');
3235
});
3336

34-
it('enables source maps and adds the sentry vite plugin if an auth token is detected', async () => {
37+
it('enables "hidden" source maps, adds filesToDeleteAfterUpload and adds the sentry vite plugin if an auth token is detected', async () => {
3538
const integration = sentryAstro({
3639
sourceMapsUploadOptions: { enabled: true, org: 'my-org', project: 'my-project', telemetry: false },
3740
});
@@ -44,7 +47,7 @@ describe('sentryAstro integration', () => {
4447
expect(updateConfig).toHaveBeenCalledWith({
4548
vite: {
4649
build: {
47-
sourcemap: true,
50+
sourcemap: 'hidden',
4851
},
4952
plugins: ['sentryVitePlugin'],
5053
},
@@ -60,6 +63,7 @@ describe('sentryAstro integration', () => {
6063
bundleSizeOptimizations: {},
6164
sourcemaps: {
6265
assets: ['out/**/*'],
66+
filesToDeleteAfterUpload: ['./dist/**/client/**/*.map', './dist/**/server/**/*.map'],
6367
},
6468
_metaOptions: {
6569
telemetry: {
@@ -86,6 +90,7 @@ describe('sentryAstro integration', () => {
8690
bundleSizeOptimizations: {},
8791
sourcemaps: {
8892
assets: ['dist/**/*'],
93+
filesToDeleteAfterUpload: ['./dist/**/client/**/*.map', './dist/**/server/**/*.map'],
8994
},
9095
_metaOptions: {
9196
telemetry: {
@@ -119,6 +124,7 @@ describe('sentryAstro integration', () => {
119124
bundleSizeOptimizations: {},
120125
sourcemaps: {
121126
assets: ['{.vercel,dist}/**/*'],
127+
filesToDeleteAfterUpload: ['./dist/**/client/**/*.map', './dist/**/server/**/*.map'],
122128
},
123129
_metaOptions: {
124130
telemetry: {
@@ -157,6 +163,7 @@ describe('sentryAstro integration', () => {
157163
bundleSizeOptimizations: {},
158164
sourcemaps: {
159165
assets: ['dist/server/**/*, dist/client/**/*'],
166+
filesToDeleteAfterUpload: ['./dist/**/client/**/*.map', './dist/**/server/**/*.map'],
160167
},
161168
_metaOptions: {
162169
telemetry: {
@@ -166,6 +173,35 @@ describe('sentryAstro integration', () => {
166173
});
167174
});
168175

176+
it('prefers user-specified filesToDeleteAfterUpload over the default values', async () => {
177+
const integration = sentryAstro({
178+
sourceMapsUploadOptions: {
179+
enabled: true,
180+
org: 'my-org',
181+
project: 'my-project',
182+
filesToDeleteAfterUpload: ['./custom/path/**/*'],
183+
},
184+
});
185+
// @ts-expect-error - the hook exists, and we only need to pass what we actually use
186+
await integration.hooks['astro:config:setup']({
187+
updateConfig,
188+
injectScript,
189+
// @ts-expect-error - only passing in partial config
190+
config: {
191+
outDir: new URL('file://path/to/project/build'),
192+
},
193+
});
194+
195+
expect(sentryVitePluginSpy).toHaveBeenCalledTimes(1);
196+
expect(sentryVitePluginSpy).toHaveBeenCalledWith(
197+
expect.objectContaining({
198+
sourcemaps: expect.objectContaining({
199+
filesToDeleteAfterUpload: ['./custom/path/**/*'],
200+
}),
201+
}),
202+
);
203+
});
204+
169205
it("doesn't enable source maps if `sourceMapsUploadOptions.enabled` is `false`", async () => {
170206
const integration = sentryAstro({
171207
sourceMapsUploadOptions: { enabled: false },
@@ -373,3 +409,62 @@ describe('sentryAstro integration', () => {
373409
expect(addMiddleware).toHaveBeenCalledTimes(0);
374410
});
375411
});
412+
413+
describe('getUpdatedSourceMapSettings', () => {
414+
let astroConfig: Omit<AstroConfig, 'vite'> & { vite: { build: { sourcemap?: any } } };
415+
let sentryOptions: SentryOptions;
416+
417+
beforeEach(() => {
418+
astroConfig = { vite: { build: {} } } as Omit<AstroConfig, 'vite'> & { vite: { build: { sourcemap?: any } } };
419+
sentryOptions = {};
420+
});
421+
422+
it('should keep explicitly disabled source maps disabled', () => {
423+
astroConfig.vite.build.sourcemap = false;
424+
const result = getUpdatedSourceMapSettings(astroConfig, sentryOptions);
425+
expect(result.previousUserSourceMapSetting).toBe('disabled');
426+
expect(result.updatedSourceMapSetting).toBe(false);
427+
});
428+
429+
it('should keep explicitly enabled source maps enabled', () => {
430+
const cases = [
431+
{ sourcemap: true, expected: true },
432+
{ sourcemap: 'hidden', expected: 'hidden' },
433+
{ sourcemap: 'inline', expected: 'inline' },
434+
];
435+
436+
cases.forEach(({ sourcemap, expected }) => {
437+
astroConfig.vite.build.sourcemap = sourcemap;
438+
const result = getUpdatedSourceMapSettings(astroConfig, sentryOptions);
439+
expect(result.previousUserSourceMapSetting).toBe('enabled');
440+
expect(result.updatedSourceMapSetting).toBe(expected);
441+
});
442+
});
443+
444+
it('should enable "hidden" source maps when unset', () => {
445+
astroConfig.vite.build.sourcemap = undefined;
446+
const result = getUpdatedSourceMapSettings(astroConfig, sentryOptions);
447+
expect(result.previousUserSourceMapSetting).toBe('unset');
448+
expect(result.updatedSourceMapSetting).toBe('hidden');
449+
});
450+
451+
it('should log warnings and messages when debug is enabled', () => {
452+
const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
453+
const consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
454+
455+
sentryOptions = { debug: true };
456+
457+
astroConfig.vite.build.sourcemap = false;
458+
getUpdatedSourceMapSettings(astroConfig, sentryOptions);
459+
expect(consoleWarnSpy).toHaveBeenCalledWith(
460+
expect.stringContaining('Source map generation are currently disabled'),
461+
);
462+
463+
astroConfig.vite.build.sourcemap = 'hidden';
464+
getUpdatedSourceMapSettings(astroConfig, sentryOptions);
465+
expect(consoleLogSpy).toHaveBeenCalledWith(expect.stringContaining('Sentry will keep this source map setting'));
466+
467+
consoleWarnSpy.mockRestore();
468+
consoleLogSpy.mockRestore();
469+
});
470+
});

0 commit comments

Comments
 (0)