Skip to content

Commit 3040272

Browse files
committedJan 17, 2025·
fix(@angular/build): prevent full page reload on HMR updates with SSR enabled
This commit resolves an issue where HMR would incorrectly trigger a full page reload when used with SSR. Closes #29372 (cherry picked from commit 03ac417)
1 parent 939d161 commit 3040272

File tree

2 files changed

+37
-34
lines changed

2 files changed

+37
-34
lines changed
 

‎packages/angular/build/src/builders/application/build-action.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ function* emitOutputResults(
295295

296296
if (needFile) {
297297
// Updates to non-JS files must signal an update with the dev server
298-
if (!/(?:\.js|\.map)?$/.test(file.path)) {
298+
if (!/(?:\.m?js|\.map)?$/.test(file.path)) {
299299
incrementalResult.background = false;
300300
}
301301

‎packages/angular/build/src/builders/dev-server/vite-server.ts

+36-33
Original file line numberDiff line numberDiff line change
@@ -250,11 +250,6 @@ export async function* serveWithVite(
250250
);
251251
}
252252

253-
// Invalidate SSR module graph to ensure that only new rebuild is used and not stale component updates
254-
if (server && browserOptions.ssr && templateUpdates.size > 0) {
255-
server.moduleGraph.invalidateAll();
256-
}
257-
258253
// Clear stale template updates on code rebuilds
259254
templateUpdates.clear();
260255

@@ -303,16 +298,6 @@ export async function* serveWithVite(
303298
'Builder must provide an initial full build before component update results.',
304299
);
305300

306-
// Invalidate SSR module graph to ensure that new component updates are used
307-
// TODO: Use fine-grained invalidation of only the component update modules
308-
if (browserOptions.ssr) {
309-
server.moduleGraph.invalidateAll();
310-
const { ɵresetCompiledComponents } = (await server.ssrLoadModule('/main.server.mjs')) as {
311-
ɵresetCompiledComponents: () => void;
312-
};
313-
ɵresetCompiledComponents();
314-
}
315-
316301
for (const componentUpdate of result.updates) {
317302
if (componentUpdate.type === 'template') {
318303
templateUpdates.set(componentUpdate.id, componentUpdate.content);
@@ -322,6 +307,7 @@ export async function* serveWithVite(
322307
});
323308
}
324309
}
310+
325311
context.logger.info('Component update sent to client(s).');
326312
continue;
327313
default:
@@ -367,16 +353,15 @@ export async function* serveWithVite(
367353
]),
368354
];
369355

356+
const updatedFiles = await invalidateUpdatedFiles(
357+
normalizePath,
358+
generatedFiles,
359+
assetFiles,
360+
server,
361+
);
362+
370363
if (needClientUpdate) {
371-
await handleUpdate(
372-
normalizePath,
373-
generatedFiles,
374-
assetFiles,
375-
server,
376-
serverOptions,
377-
context.logger,
378-
componentStyles,
379-
);
364+
handleUpdate(server, serverOptions, context.logger, componentStyles, updatedFiles);
380365
}
381366
} else {
382367
const projectName = context.target?.project;
@@ -483,15 +468,18 @@ export async function* serveWithVite(
483468
await new Promise<void>((resolve) => (deferred = resolve));
484469
}
485470

486-
async function handleUpdate(
471+
/**
472+
* Invalidates any updated asset or generated files and resets their `updated` state.
473+
* This function also clears the server application cache when necessary.
474+
*
475+
* @returns A list of files that were updated and invalidated.
476+
*/
477+
async function invalidateUpdatedFiles(
487478
normalizePath: (id: string) => string,
488479
generatedFiles: Map<string, OutputFileRecord>,
489480
assetFiles: Map<string, OutputAssetRecord>,
490481
server: ViteDevServer,
491-
serverOptions: NormalizedDevServerOptions,
492-
logger: BuilderContext['logger'],
493-
componentStyles: Map<string, ComponentStyleRecord>,
494-
): Promise<void> {
482+
): Promise<string[]> {
495483
const updatedFiles: string[] = [];
496484

497485
// Invalidate any updated asset
@@ -531,15 +519,30 @@ async function handleUpdate(
531519
updatedModules?.forEach((m) => server.moduleGraph.invalidateModule(m));
532520
}
533521

534-
if (!updatedFiles.length) {
535-
return;
536-
}
537-
538522
if (destroyAngularServerAppCalled) {
539523
// Trigger module evaluation before reload to initiate dependency optimization.
540524
await server.ssrLoadModule('/main.server.mjs');
541525
}
542526

527+
return updatedFiles;
528+
}
529+
530+
/**
531+
* Handles updates for the client by sending HMR or full page reload commands
532+
* based on the updated files. It also ensures proper tracking of component styles and determines if
533+
* a full reload is needed.
534+
*/
535+
function handleUpdate(
536+
server: ViteDevServer,
537+
serverOptions: NormalizedDevServerOptions,
538+
logger: BuilderContext['logger'],
539+
componentStyles: Map<string, ComponentStyleRecord>,
540+
updatedFiles: string[],
541+
): void {
542+
if (!updatedFiles.length) {
543+
return;
544+
}
545+
543546
if (serverOptions.hmr) {
544547
if (updatedFiles.every((f) => f.endsWith('.css'))) {
545548
let requiresReload = false;

0 commit comments

Comments
 (0)
Please sign in to comment.