From 8dcde16f7806ee23c6f91d3e6a5f8b8a4baf4da7 Mon Sep 17 00:00:00 2001 From: Charles Lyding <19598772+clydin@users.noreply.github.com> Date: Sun, 3 Nov 2024 09:25:31 -0500 Subject: [PATCH 1/2] fix(@angular/build): correctly use dev-server hmr option to control stylesheet hot replacement The development server's `hmr` option will now disable both global and component stylesheet hot replacement if explicitly disabled. These features are enabled by default for all projects. --- packages/angular/build/src/builders/dev-server/options.ts | 4 ++-- .../angular/build/src/builders/dev-server/schema.json | 3 +-- .../angular/build/src/builders/dev-server/vite-server.ts | 7 ++++--- .../build_angular/src/builders/dev-server/builder.ts | 8 +++++++- .../build_angular/src/builders/dev-server/options.ts | 2 +- .../build_angular/src/builders/dev-server/schema.json | 3 +-- 6 files changed, 16 insertions(+), 11 deletions(-) diff --git a/packages/angular/build/src/builders/dev-server/options.ts b/packages/angular/build/src/builders/dev-server/options.ts index 080e168699bc..237c26b8c2f6 100644 --- a/packages/angular/build/src/builders/dev-server/options.ts +++ b/packages/angular/build/src/builders/dev-server/options.ts @@ -114,8 +114,8 @@ export async function normalizeOptions( open, verbose, watch, - liveReload, - hmr, + liveReload: !!liveReload, + hmr: hmr ?? !!liveReload, headers, workspaceRoot, projectRoot, diff --git a/packages/angular/build/src/builders/dev-server/schema.json b/packages/angular/build/src/builders/dev-server/schema.json index 3adce45eb71a..2eb16987b8ca 100644 --- a/packages/angular/build/src/builders/dev-server/schema.json +++ b/packages/angular/build/src/builders/dev-server/schema.json @@ -67,8 +67,7 @@ }, "hmr": { "type": "boolean", - "description": "Enable hot module replacement.", - "default": false + "description": "Enable hot module replacement. Defaults to the value of 'liveReload'. Currently, only global and component stylesheets are supported." }, "watch": { "type": "boolean", 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 3223a9280fc2..454b7ad21022 100644 --- a/packages/angular/build/src/builders/dev-server/vite-server.ts +++ b/packages/angular/build/src/builders/dev-server/vite-server.ts @@ -137,8 +137,9 @@ export async function* serveWithVite( process.setSourceMapsEnabled(true); } - // Enable to support component style hot reloading (`NG_HMR_CSTYLES=0` can be used to disable) - browserOptions.externalRuntimeStyles = !!serverOptions.liveReload && useComponentStyleHmr; + // Enable to support component style hot reloading (`NG_HMR_CSTYLES=0` can be used to disable selectively) + browserOptions.externalRuntimeStyles = + serverOptions.liveReload && serverOptions.hmr && useComponentStyleHmr; // Enable to support component template hot replacement (`NG_HMR_TEMPLATE=1` can be used to enable) browserOptions.templateUpdates = !!serverOptions.liveReload && useComponentTemplateHmr; @@ -465,7 +466,7 @@ async function handleUpdate( return; } - if (serverOptions.liveReload || serverOptions.hmr) { + if (serverOptions.hmr) { if (updatedFiles.every((f) => f.endsWith('.css'))) { const timestamp = Date.now(); server.ws.send({ diff --git a/packages/angular_devkit/build_angular/src/builders/dev-server/builder.ts b/packages/angular_devkit/build_angular/src/builders/dev-server/builder.ts index 3b244a008c2c..76f8e282e8e4 100644 --- a/packages/angular_devkit/build_angular/src/builders/dev-server/builder.ts +++ b/packages/angular_devkit/build_angular/src/builders/dev-server/builder.ts @@ -85,12 +85,15 @@ export function execute( ); } + // New build system defaults hmr option to the value of liveReload + normalizedOptions.hmr ??= normalizedOptions.liveReload; + return defer(() => Promise.all([import('@angular/build/private'), import('../browser-esbuild')]), ).pipe( switchMap(([{ serveWithVite, buildApplicationInternal }, { convertBrowserOptions }]) => serveWithVite( - normalizedOptions, + normalizedOptions as typeof normalizedOptions & { hmr: boolean }, builderName, (options, context, codePlugins) => { return builderName === '@angular-devkit/build-angular:browser-esbuild' @@ -124,6 +127,9 @@ export function execute( ); } + // Webpack based build systems default to false for hmr option + normalizedOptions.hmr ??= false; + // Use Webpack for all other browser targets return defer(() => import('./webpack-server')).pipe( switchMap(({ serveWebpackBrowser }) => diff --git a/packages/angular_devkit/build_angular/src/builders/dev-server/options.ts b/packages/angular_devkit/build_angular/src/builders/dev-server/options.ts index 48bdc2763a17..6fa23123a236 100644 --- a/packages/angular_devkit/build_angular/src/builders/dev-server/options.ts +++ b/packages/angular_devkit/build_angular/src/builders/dev-server/options.ts @@ -116,7 +116,7 @@ export async function normalizeOptions( open, verbose, watch, - liveReload, + liveReload: !!liveReload, hmr, headers, workspaceRoot, diff --git a/packages/angular_devkit/build_angular/src/builders/dev-server/schema.json b/packages/angular_devkit/build_angular/src/builders/dev-server/schema.json index 715d9573e304..a1001107f86f 100644 --- a/packages/angular_devkit/build_angular/src/builders/dev-server/schema.json +++ b/packages/angular_devkit/build_angular/src/builders/dev-server/schema.json @@ -84,8 +84,7 @@ }, "hmr": { "type": "boolean", - "description": "Enable hot module replacement.", - "default": false + "description": "Enable hot module replacement." }, "watch": { "type": "boolean", From fdefd47f1fdeff84baa6a8844dad04fa1c434819 Mon Sep 17 00:00:00 2001 From: Charles Lyding <19598772+clydin@users.noreply.github.com> Date: Wed, 2 Oct 2024 11:30:34 -0400 Subject: [PATCH 2/2] fix(@angular/build): disable dev-server websocket when live reload is disabled When live reload is disabled (`"liveReload": false`/`no-live-reload`) within the development server, the Vite websocket server will no longer be initialized. Additionally, the client code will not be loaded by the browser. This allows the development server to be used in test scenarios that would prefer to more fully represent the production fielded configuration such as E2E testing or other forms of browser-based testing. --- .../angular/build/src/builders/dev-server/vite-server.ts | 3 +++ .../src/tools/vite/plugins/angular-memory-plugin.ts | 9 ++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) 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 454b7ad21022..e76b185904db 100644 --- a/packages/angular/build/src/builders/dev-server/vite-server.ts +++ b/packages/angular/build/src/builders/dev-server/vite-server.ts @@ -650,6 +650,8 @@ export async function setupServer( host: serverOptions.host, open: serverOptions.open, headers: serverOptions.headers, + // Disable the websocket if live reload is disabled (false/undefined are the only valid values) + ws: serverOptions.liveReload === false && serverOptions.hmr === false ? false : undefined, proxy, cors: { // Allow preflight requests to be proxied. @@ -710,6 +712,7 @@ export async function setupServer( virtualProjectRoot, outputFiles, external: externalMetadata.explicitBrowser, + skipViteClient: serverOptions.liveReload === false && serverOptions.hmr === false, }), ], // Browser only optimizeDeps. (This does not run for SSR dependencies). diff --git a/packages/angular/build/src/tools/vite/plugins/angular-memory-plugin.ts b/packages/angular/build/src/tools/vite/plugins/angular-memory-plugin.ts index 9d6510588ffa..af1dc45d3ec7 100644 --- a/packages/angular/build/src/tools/vite/plugins/angular-memory-plugin.ts +++ b/packages/angular/build/src/tools/vite/plugins/angular-memory-plugin.ts @@ -17,6 +17,7 @@ interface AngularMemoryPluginOptions { virtualProjectRoot: string; outputFiles: AngularMemoryOutputFiles; external?: string[]; + skipViteClient?: boolean; } export async function createAngularMemoryPlugin( @@ -63,9 +64,11 @@ export async function createAngularMemoryPlugin( const relativeFile = '/' + normalizePath(relative(virtualProjectRoot, file)); const codeContents = outputFiles.get(relativeFile)?.contents; if (codeContents === undefined) { - return relativeFile.endsWith('/node_modules/vite/dist/client/client.mjs') - ? loadViteClientCode(file) - : undefined; + if (relativeFile.endsWith('/node_modules/vite/dist/client/client.mjs')) { + return options.skipViteClient ? '' : loadViteClientCode(file); + } + + return undefined; } const code = Buffer.from(codeContents).toString('utf-8');