diff --git a/packages/gatsby/gatsby-node.js b/packages/gatsby/gatsby-node.js index 911fcda7b437..3b40481cd89d 100644 --- a/packages/gatsby/gatsby-node.js +++ b/packages/gatsby/gatsby-node.js @@ -7,8 +7,24 @@ const SENTRY_USER_CONFIG = ['./sentry.config.js', './sentry.config.ts']; exports.onCreateWebpackConfig = ({ getConfig, actions }, options) => { const enableClientWebpackPlugin = options.enableClientWebpackPlugin !== false; if (process.env.NODE_ENV === 'production' && enableClientWebpackPlugin) { - const deleteSourcemapsAfterUpload = options.deleteSourcemapsAfterUpload === true; + const prevSourceMapSetting = getConfig() && 'devtool' in getConfig() ? getConfig().devtool : undefined; + const shouldAutomaticallyEnableSourceMaps = + prevSourceMapSetting !== 'source-map' && prevSourceMapSetting !== 'hidden-source-map'; + + if (shouldAutomaticallyEnableSourceMaps) { + // eslint-disable-next-line no-console + console.log( + '[Sentry] Automatically enabling source map generation by setting `devtool: "hidden-source-map"`. Those source maps will be deleted after they were uploaded to Sentry', + ); + } + + // Delete source maps per default or when this is explicitly set to `true` (`deleteSourceMapsAfterUpload: true` can override the default behavior) + const deleteSourcemapsAfterUpload = + options.deleteSourcemapsAfterUpload || + (options.deleteSourcemapsAfterUpload !== false && shouldAutomaticallyEnableSourceMaps); + actions.setWebpackConfig({ + devtool: shouldAutomaticallyEnableSourceMaps ? 'hidden-source-map' : prevSourceMapSetting, plugins: [ sentryWebpackPlugin({ sourcemaps: { diff --git a/packages/gatsby/test/gatsby-node.test.ts b/packages/gatsby/test/gatsby-node.test.ts index 006cb6f9e2c0..5ff15ecf034e 100644 --- a/packages/gatsby/test/gatsby-node.test.ts +++ b/packages/gatsby/test/gatsby-node.test.ts @@ -28,12 +28,12 @@ describe('onCreateWebpackConfig', () => { setWebpackConfig: jest.fn(), }; - const getConfig = jest.fn(); + const getConfig = jest.fn().mockReturnValue({ devtool: 'source-map' }); onCreateWebpackConfig({ actions, getConfig }, {}); expect(actions.setWebpackConfig).toHaveBeenCalledTimes(1); - expect(actions.setWebpackConfig).toHaveBeenLastCalledWith({ plugins: expect.any(Array) }); + expect(actions.setWebpackConfig).toHaveBeenLastCalledWith({ devtool: 'source-map', plugins: expect.any(Array) }); }); it('does not set a webpack config if enableClientWebpackPlugin is false', () => { @@ -41,30 +41,121 @@ describe('onCreateWebpackConfig', () => { setWebpackConfig: jest.fn(), }; - const getConfig = jest.fn(); + const getConfig = jest.fn().mockReturnValue({ devtool: 'source-map' }); onCreateWebpackConfig({ actions, getConfig }, { enableClientWebpackPlugin: false }); expect(actions.setWebpackConfig).toHaveBeenCalledTimes(0); }); - it('sets sourceMapFilesToDeleteAfterUpload when provided in options', () => { + describe('delete source maps after upload', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + const actions = { setWebpackConfig: jest.fn(), }; const getConfig = jest.fn(); - onCreateWebpackConfig({ actions, getConfig }, { deleteSourcemapsAfterUpload: true }); + it('sets sourceMapFilesToDeleteAfterUpload when provided in options', () => { + const actions = { + setWebpackConfig: jest.fn(), + }; - expect(actions.setWebpackConfig).toHaveBeenCalledTimes(1); + const getConfig = jest.fn().mockReturnValue({ devtool: 'source-map' }); - expect(sentryWebpackPlugin).toHaveBeenCalledWith( - expect.objectContaining({ - sourcemaps: expect.objectContaining({ - filesToDeleteAfterUpload: ['./public/**/*.map'], + onCreateWebpackConfig({ actions, getConfig }, { deleteSourcemapsAfterUpload: true }); + + expect(actions.setWebpackConfig).toHaveBeenCalledTimes(1); + + expect(sentryWebpackPlugin).toHaveBeenCalledWith( + expect.objectContaining({ + sourcemaps: expect.objectContaining({ + filesToDeleteAfterUpload: ['./public/**/*.map'], + }), + }), + ); + }); + + test.each([ + { + name: 'without provided options: sets hidden source maps and deletes source maps', + initialConfig: undefined, + options: {}, + expected: { + devtool: 'hidden-source-map', + deleteSourceMaps: true, + }, + }, + { + name: "preserves enabled source-map and doesn't delete", + initialConfig: { devtool: 'source-map' }, + options: {}, + expected: { + devtool: 'source-map', + deleteSourceMaps: false, + }, + }, + { + name: "preserves enabled hidden-source-map and doesn't delete", + initialConfig: { devtool: 'hidden-source-map' }, + options: {}, + expected: { + devtool: 'hidden-source-map', + deleteSourceMaps: false, + }, + }, + { + name: 'deletes source maps, when user explicitly sets it', + initialConfig: { devtool: 'eval' }, + options: {}, + expected: { + devtool: 'hidden-source-map', + deleteSourceMaps: true, + }, + }, + { + name: 'explicit deleteSourcemapsAfterUpload true', + initialConfig: { devtool: 'source-map' }, + options: { deleteSourcemapsAfterUpload: true }, + expected: { + devtool: 'source-map', + deleteSourceMaps: true, + }, + }, + { + name: 'explicit deleteSourcemapsAfterUpload false', + initialConfig: { devtool: 'hidden-source-map' }, + options: { deleteSourcemapsAfterUpload: false }, + expected: { + devtool: 'hidden-source-map', + deleteSourceMaps: false, + }, + }, + ])('$name', ({ initialConfig, options, expected }) => { + getConfig.mockReturnValue(initialConfig); + + onCreateWebpackConfig({ actions: actions, getConfig: getConfig }, options); + + expect(actions.setWebpackConfig).toHaveBeenCalledTimes(1); + + expect(actions.setWebpackConfig).toHaveBeenCalledWith( + expect.objectContaining({ + devtool: expected.devtool, + plugins: expect.arrayContaining([expect.any(Object)]), + }), + ); + + expect(sentryWebpackPlugin).toHaveBeenCalledWith( + expect.objectContaining({ + sourcemaps: expect.objectContaining({ + assets: ['./public/**'], + filesToDeleteAfterUpload: expected.deleteSourceMaps ? ['./public/**/*.map'] : undefined, + }), }), - }), - ); + ); + }); }); });