Skip to content

Commit a3903df

Browse files
committed
feat(sveltekit): Enable hidden source maps or keep settings
1 parent 7fb7632 commit a3903df

File tree

2 files changed

+156
-19
lines changed

2 files changed

+156
-19
lines changed

packages/sveltekit/src/vite/sourceMaps.ts

+90-18
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import * as child_process from 'child_process';
22
import * as fs from 'fs';
33
import * as path from 'path';
4-
import { escapeStringForRegex, uuid4 } from '@sentry/core';
4+
import { consoleSandbox, escapeStringForRegex, uuid4 } from '@sentry/core';
55
import { getSentryRelease } from '@sentry/node';
66
import type { SentryVitePluginOptions } from '@sentry/vite-plugin';
77
import { sentryVitePlugin } from '@sentry/vite-plugin';
8-
import type { Plugin } from 'vite';
8+
import type { Plugin, UserConfig } from 'vite';
99

1010
import MagicString from 'magic-string';
1111
import { WRAPPED_MODULE_SUFFIX } from './autoInstrument';
@@ -135,26 +135,18 @@ export async function makeCustomSentryVitePlugins(options?: CustomSentryVitePlug
135135
enforce: 'post', // this needs to be set to post, otherwise we don't pick up the output from the SvelteKit adapter
136136

137137
// Modify the config to generate source maps
138-
config: config => {
139-
const sourceMapsPreviouslyNotEnabled = !config.build?.sourcemap;
140-
if (debug && sourceMapsPreviouslyNotEnabled) {
141-
// eslint-disable-next-line no-console
142-
console.log('[Source Maps Plugin] Enabling source map generation');
143-
if (!mergedOptions.sourcemaps?.filesToDeleteAfterUpload) {
138+
config: (config: UserConfig) => {
139+
changeViteSourceMapSettings(config, options);
140+
141+
if (debug && !mergedOptions.sourcemaps?.filesToDeleteAfterUpload) {
142+
consoleSandbox(() => {
144143
// eslint-disable-next-line no-console
145144
console.warn(
146-
`[Source Maps Plugin] We recommend setting the \`sourceMapsUploadOptions.sourcemaps.filesToDeleteAfterUpload\` option to clean up source maps after uploading.
147-
[Source Maps Plugin] Otherwise, source maps might be deployed to production, depending on your configuration`,
145+
'[Source Maps Plugin] 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',
148146
);
149-
}
147+
});
150148
}
151-
return {
152-
...config,
153-
build: {
154-
...config.build,
155-
sourcemap: true,
156-
},
157-
};
149+
return config;
158150
},
159151

160152
resolveId: (id, _importer, _ref) => {
@@ -332,6 +324,86 @@ export async function makeCustomSentryVitePlugins(options?: CustomSentryVitePlug
332324
];
333325
}
334326

327+
/**
328+
* Whether the user enabled (true, 'hidden', 'inline') or disabled (false) source maps
329+
*/
330+
export type UserSourceMapSetting = 'enabled' | 'disabled' | 'unset' | undefined;
331+
332+
/** There are 3 ways to set up source maps (https://github.com/getsentry/sentry-javascript/issues/13993)
333+
*
334+
* 1. User explicitly disabled source maps
335+
* - keep this setting (emit a warning that errors won't be unminified in Sentry)
336+
* - We won't upload anything
337+
*
338+
* 2. Users enabled source map generation (true, 'hidden', 'inline').
339+
* - keep this setting (don't do anything - like deletion - besides uploading)
340+
*
341+
* 3. Users didn't set source maps generation
342+
* - we enable 'hidden' source maps generation
343+
* - configure `filesToDeleteAfterUpload` to delete all .map files (we emit a log about this)
344+
*
345+
* --> only exported for testing
346+
*/
347+
export function changeViteSourceMapSettings(
348+
viteConfig: {
349+
build?: {
350+
sourcemap?: boolean | 'inline' | 'hidden';
351+
};
352+
},
353+
sentryPluginOptions?: CustomSentryVitePluginOptions,
354+
): UserSourceMapSetting {
355+
let previousUserSourceMapSetting: UserSourceMapSetting = undefined;
356+
357+
viteConfig.build = viteConfig.build || {};
358+
359+
const viteSourceMap = viteConfig.build.sourcemap;
360+
361+
if (viteSourceMap === false) {
362+
warnExplicitlyDisabledSourceMap('vite.build.sourcemap');
363+
previousUserSourceMapSetting = 'disabled';
364+
} else if (viteSourceMap && ['hidden', 'inline', true].includes(viteSourceMap)) {
365+
logKeepSourceMapSetting('vite.build.sourcemap', viteSourceMap.toString(), sentryPluginOptions);
366+
previousUserSourceMapSetting = 'enabled';
367+
} else {
368+
viteConfig.build.sourcemap = 'hidden';
369+
logSentryEnablesSourceMap('vite.build.sourcemap', 'hidden');
370+
previousUserSourceMapSetting = 'unset';
371+
}
372+
373+
return previousUserSourceMapSetting;
374+
}
375+
376+
function logKeepSourceMapSetting(
377+
settingKey: string,
378+
settingValue: string,
379+
sentryPluginOptions?: CustomSentryVitePluginOptions,
380+
): void {
381+
if (sentryPluginOptions?.debug) {
382+
consoleSandbox(() => {
383+
// eslint-disable-next-line no-console
384+
console.log(
385+
`[Sentry] We discovered \`${settingKey}\` is set to \`${settingValue}\`. Sentry will keep this source map setting. This will un-minify the code snippet on the Sentry Issue page.`,
386+
);
387+
});
388+
}
389+
}
390+
391+
function warnExplicitlyDisabledSourceMap(settingKey: string): void {
392+
consoleSandbox(() => {
393+
// eslint-disable-next-line no-console
394+
console.warn(
395+
`[Sentry] Parts of source map generation are currently disabled in your Nuxt 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\`).`,
396+
);
397+
});
398+
}
399+
400+
function logSentryEnablesSourceMap(settingKey: string, settingValue: string): void {
401+
consoleSandbox(() => {
402+
// eslint-disable-next-line no-console
403+
console.log(`[Sentry] Enabled source map generation in the build options with \`${settingKey}: ${settingValue}\`.`);
404+
});
405+
}
406+
335407
function getFiles(dir: string): string[] {
336408
if (!fs.existsSync(dir)) {
337409
return [];

packages/sveltekit/test/vite/sourceMaps.test.ts

+66-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import { beforeEach, describe, expect, it, vi } from 'vitest';
2+
import { changeViteSourceMapSettings } from '../../src/vite/sourceMaps';
3+
import type { CustomSentryVitePluginOptions } from '../../src/vite/types';
24

35
import type { Plugin } from 'vite';
46
import { makeCustomSentryVitePlugins } from '../../src/vite/sourceMaps';
@@ -75,10 +77,26 @@ describe('makeCustomSentryVitePlugin()', () => {
7577
});
7678

7779
describe('Custom debug id source maps plugin plugin', () => {
78-
it('enables source map generation', async () => {
80+
it('enables source map generation when unset', async () => {
7981
const plugin = await getSentryViteSubPlugin('sentry-sveltekit-debug-id-upload-plugin');
8082
// @ts-expect-error this function exists!
8183
const sentrifiedConfig = plugin.config({ build: { foo: {} }, test: {} });
84+
expect(sentrifiedConfig).toEqual({
85+
build: {
86+
foo: {},
87+
sourcemap: 'hidden',
88+
},
89+
test: {},
90+
});
91+
});
92+
93+
it('keeps source map generation settings', async () => {
94+
const plugin = await getCustomSentryViteUploadSourcemapsPlugin();
95+
// @ts-expect-error this function exists!
96+
const sentrifiedConfig = plugin.config({
97+
build: { sourcemap: true, foo: {} },
98+
test: {},
99+
});
82100
expect(sentrifiedConfig).toEqual({
83101
build: {
84102
foo: {},
@@ -237,3 +255,50 @@ describe('makeCustomSentryVitePlugin()', () => {
237255
});
238256
});
239257
});
258+
259+
describe('changeViteSourceMapSettings()', () => {
260+
let viteConfig: { build?: { sourcemap?: boolean | 'inline' | 'hidden' } };
261+
let sentryModuleOptions: CustomSentryVitePluginOptions;
262+
263+
beforeEach(() => {
264+
viteConfig = {};
265+
sentryModuleOptions = {};
266+
});
267+
268+
it('handles vite source map settings', () => {
269+
const cases = [
270+
{ sourcemap: false, expectedSourcemap: false, expectedReturn: 'disabled' },
271+
{ sourcemap: 'hidden', expectedSourcemap: 'hidden', expectedReturn: 'enabled' },
272+
{ sourcemap: 'inline', expectedSourcemap: 'inline', expectedReturn: 'enabled' },
273+
{ sourcemap: true, expectedSourcemap: true, expectedReturn: 'enabled' },
274+
{ sourcemap: undefined, expectedSourcemap: 'hidden', expectedReturn: 'unset' },
275+
];
276+
277+
cases.forEach(({ sourcemap, expectedSourcemap, expectedReturn }) => {
278+
viteConfig.build = { sourcemap };
279+
const previousUserSourceMapSetting = changeViteSourceMapSettings(viteConfig, sentryModuleOptions);
280+
expect(viteConfig.build.sourcemap).toBe(expectedSourcemap);
281+
expect(previousUserSourceMapSetting).toBe(expectedReturn);
282+
});
283+
});
284+
285+
it('logs warnings and messages when debug is enabled', () => {
286+
const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
287+
const consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
288+
289+
sentryModuleOptions = { debug: true };
290+
291+
viteConfig.build = { sourcemap: false };
292+
changeViteSourceMapSettings(viteConfig, sentryModuleOptions);
293+
expect(consoleWarnSpy).toHaveBeenCalledWith(
294+
expect.stringContaining('Parts of source map generation are currently disabled'),
295+
);
296+
297+
viteConfig.build = { sourcemap: 'hidden' };
298+
changeViteSourceMapSettings(viteConfig, sentryModuleOptions);
299+
expect(consoleLogSpy).toHaveBeenCalledWith(expect.stringContaining('Sentry will keep this source map setting'));
300+
301+
consoleWarnSpy.mockRestore();
302+
consoleLogSpy.mockRestore();
303+
});
304+
});

0 commit comments

Comments
 (0)