From fc7b18fe9799f1684584d5b1cd48aa58e0284e78 Mon Sep 17 00:00:00 2001 From: Alan Agius <17563226+alan-agius4@users.noreply.github.com> Date: Mon, 20 Oct 2025 07:39:49 +0000 Subject: [PATCH] fix(@angular/build): allow disabling sourcemaps in the dev server This change introduces the ability to disable sourcemaps when running the development server. A new Vite plugin has been added to remove sourcemap comments from generated assets. The dev server's configuration has been updated to conditionally apply this plugin and control CSS sourcemap generation based on the project's sourceMap setting. Closes #31331 --- .../tests/behavior/build-source-map_spec.ts | 49 ++++++++++++++++ .../src/builders/dev-server/vite/index.ts | 9 ++- .../src/builders/dev-server/vite/server.ts | 56 +++++++++++-------- .../build/src/tools/vite/plugins/index.ts | 1 + .../tools/vite/plugins/remove-sourcemaps.ts | 19 +++++++ 5 files changed, 107 insertions(+), 27 deletions(-) create mode 100644 packages/angular/build/src/builders/dev-server/tests/behavior/build-source-map_spec.ts create mode 100644 packages/angular/build/src/tools/vite/plugins/remove-sourcemaps.ts diff --git a/packages/angular/build/src/builders/dev-server/tests/behavior/build-source-map_spec.ts b/packages/angular/build/src/builders/dev-server/tests/behavior/build-source-map_spec.ts new file mode 100644 index 000000000000..232b1fed88a8 --- /dev/null +++ b/packages/angular/build/src/builders/dev-server/tests/behavior/build-source-map_spec.ts @@ -0,0 +1,49 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +import { executeDevServer } from '../../index'; +import { executeOnceAndFetch } from '../execute-fetch'; +import { describeServeBuilder } from '../jasmine-helpers'; +import { BASE_OPTIONS, DEV_SERVER_BUILDER_INFO } from '../setup'; + +describeServeBuilder(executeDevServer, DEV_SERVER_BUILDER_INFO, (harness, setupTarget) => { + describe('Behavior: "buildTarget sourceMap"', () => { + beforeEach(async () => { + // Application code is not needed for these tests + await harness.writeFile('src/main.ts', 'console.log("foo");'); + }); + + it('should not include sourcemaps when disabled', async () => { + setupTarget(harness, { + sourceMap: false, + }); + + harness.useTarget('serve', { + ...BASE_OPTIONS, + }); + + const { result, response } = await executeOnceAndFetch(harness, '/main.js'); + expect(result?.success).toBeTrue(); + expect(await response?.text()).not.toContain('//# sourceMappingURL='); + }); + + it('should include sourcemaps when enabled', async () => { + setupTarget(harness, { + sourceMap: true, + }); + + harness.useTarget('serve', { + ...BASE_OPTIONS, + }); + + const { result, response } = await executeOnceAndFetch(harness, '/main.js'); + expect(result?.success).toBeTrue(); + expect(await response?.text()).toContain('//# sourceMappingURL='); + }); + }); +}); diff --git a/packages/angular/build/src/builders/dev-server/vite/index.ts b/packages/angular/build/src/builders/dev-server/vite/index.ts index 27cb6d15adbb..8795052e16e4 100644 --- a/packages/angular/build/src/builders/dev-server/vite/index.ts +++ b/packages/angular/build/src/builders/dev-server/vite/index.ts @@ -130,9 +130,11 @@ export async function* serveWithVite( browserOptions.forceI18nFlatOutput = true; } - const { vendor: thirdPartySourcemaps, scripts: scriptsSourcemaps } = normalizeSourceMaps( - browserOptions.sourceMap ?? false, - ); + const { + vendor: thirdPartySourcemaps, + scripts: scriptsSourcemaps, + styles: stylesSourceMap, + } = normalizeSourceMaps(browserOptions.sourceMap ?? false); if (scriptsSourcemaps && browserOptions.server) { // https://nodejs.org/api/process.html#processsetsourcemapsenabledval @@ -441,6 +443,7 @@ export async function* serveWithVite( }, extensions?.middleware, transformers?.indexHtml, + scriptsSourcemaps || stylesSourceMap, thirdPartySourcemaps, ); diff --git a/packages/angular/build/src/builders/dev-server/vite/server.ts b/packages/angular/build/src/builders/dev-server/vite/server.ts index 0d48ce5325e2..624477853031 100644 --- a/packages/angular/build/src/builders/dev-server/vite/server.ts +++ b/packages/angular/build/src/builders/dev-server/vite/server.ts @@ -8,7 +8,7 @@ import { readFile } from 'node:fs/promises'; import { join } from 'node:path'; -import type { Connect, InlineConfig, SSROptions, ServerOptions } from 'vite'; +import type { Connect, InlineConfig, Plugin, SSROptions, ServerOptions } from 'vite'; import type { ComponentStyleRecord } from '../../../tools/vite/middlewares'; import { ServerSsrMode, @@ -16,6 +16,7 @@ import { createAngularSetupMiddlewaresPlugin, createAngularSsrTransformPlugin, createRemoveIdPrefixPlugin, + removeSourceMapsPlugin, } from '../../../tools/vite/plugins'; import { EsbuildLoaderOption, getDepOptimizationConfig } from '../../../tools/vite/utils'; import { loadProxyConfiguration } from '../../../utils'; @@ -150,6 +151,7 @@ export async function setupServer( define: ApplicationBuilderInternalOptions['define'], extensionMiddleware?: Connect.NextHandleFunction[], indexHtmlTransformer?: (content: string) => Promise, + sourceMaps = true, thirdPartySourcemaps = false, ): Promise { // dynamically import Vite for ESM compatibility @@ -171,6 +173,33 @@ export async function setupServer( externalMetadata.explicitBrowser.length === 0 && ssrMode === ServerSsrMode.NoSsr; const cacheDir = join(serverOptions.cacheOptions.path, serverOptions.buildTarget.project, 'vite'); + const plugins: Plugin[] = [ + createAngularSetupMiddlewaresPlugin({ + outputFiles, + assets, + indexHtmlTransformer, + extensionMiddleware, + componentStyles, + templateUpdates, + ssrMode, + resetComponentUpdates: () => templateUpdates.clear(), + projectRoot: serverOptions.projectRoot, + }), + createRemoveIdPrefixPlugin(externalMetadata.explicitBrowser), + await createAngularSsrTransformPlugin(serverOptions.workspaceRoot), + await createAngularMemoryPlugin({ + virtualProjectRoot, + outputFiles, + templateUpdates, + external: externalMetadata.explicitBrowser, + disableViteTransport: !serverOptions.liveReload, + }), + ]; + + if (!sourceMaps) { + plugins.push(removeSourceMapsPlugin); + } + const configuration: InlineConfig = { configFile: false, envFile: false, @@ -182,7 +211,7 @@ export async function setupServer( // We use custom as we do not rely on Vite's htmlFallbackMiddleware and indexHtmlMiddleware. appType: 'custom', css: { - devSourcemap: true, + devSourcemap: sourceMaps, }, // Ensure custom 'file' loader build option entries are handled by Vite in application code that // reference third-party libraries. Relative usage is handled directly by the build and not Vite. @@ -219,28 +248,7 @@ export async function setupServer( thirdPartySourcemaps, define, ), - plugins: [ - createAngularSetupMiddlewaresPlugin({ - outputFiles, - assets, - indexHtmlTransformer, - extensionMiddleware, - componentStyles, - templateUpdates, - ssrMode, - resetComponentUpdates: () => templateUpdates.clear(), - projectRoot: serverOptions.projectRoot, - }), - createRemoveIdPrefixPlugin(externalMetadata.explicitBrowser), - await createAngularSsrTransformPlugin(serverOptions.workspaceRoot), - await createAngularMemoryPlugin({ - virtualProjectRoot, - outputFiles, - templateUpdates, - external: externalMetadata.explicitBrowser, - disableViteTransport: !serverOptions.liveReload, - }), - ], + plugins, // Browser only optimizeDeps. (This does not run for SSR dependencies). optimizeDeps: getDepOptimizationConfig({ // Only enable with caching since it causes prebundle dependencies to be cached diff --git a/packages/angular/build/src/tools/vite/plugins/index.ts b/packages/angular/build/src/tools/vite/plugins/index.ts index ef697aa7395a..4d21e856ab68 100644 --- a/packages/angular/build/src/tools/vite/plugins/index.ts +++ b/packages/angular/build/src/tools/vite/plugins/index.ts @@ -10,3 +10,4 @@ export { createAngularMemoryPlugin } from './angular-memory-plugin'; export { createRemoveIdPrefixPlugin } from './id-prefix-plugin'; export { createAngularSetupMiddlewaresPlugin, ServerSsrMode } from './setup-middlewares-plugin'; export { createAngularSsrTransformPlugin } from './ssr-transform-plugin'; +export { removeSourceMapsPlugin } from './remove-sourcemaps'; diff --git a/packages/angular/build/src/tools/vite/plugins/remove-sourcemaps.ts b/packages/angular/build/src/tools/vite/plugins/remove-sourcemaps.ts new file mode 100644 index 000000000000..5942e8dbe98d --- /dev/null +++ b/packages/angular/build/src/tools/vite/plugins/remove-sourcemaps.ts @@ -0,0 +1,19 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +import type { Plugin } from 'vite'; + +export const removeSourceMapsPlugin: Plugin = { + name: 'vite:angular-remove-sourcemaps', + transform(code) { + return { + code, + map: { mappings: '' }, + }; + }, +};