fix(@angular/build): support template updates that also trigger global stylesheet changes

In some cases a change to a component template may cause changes to other
aspects of the application. Tailwind, for instance, may cause changes to the
global stylesheets when class usage is changed in a template. To support hot
module replacement of the component template in these cases, stylesheet changes
are now analyzed and separated during the update process. This allows both a hot
update of the template and the global stylesheet during the rebuild instead
of the previously required full page reload.
This commit is contained in:
Charles Lyding 2025-01-24 12:36:40 -05:00 committed by Alan Agius
parent 0fcbfd30ed
commit 531dcdca46
2 changed files with 28 additions and 3 deletions

View File

@ -300,6 +300,8 @@ function* emitOutputResults(
},
};
let hasCssUpdates = false;
// Initially assume all previous output files have been removed
const removedOutputFiles = new Map(previousOutputInfo);
for (const file of outputFiles) {
@ -316,8 +318,10 @@ function* emitOutputResults(
}
if (needFile) {
// Updates to non-JS files must signal an update with the dev server
if (!/(?:\.m?js|\.map)$/.test(file.path)) {
if (file.path.endsWith('.css')) {
hasCssUpdates = true;
} else if (!/(?:\.m?js|\.map)$/.test(file.path)) {
// Updates to non-JS files must signal an update with the dev server
incrementalResult.background = false;
}
@ -345,6 +349,8 @@ function* emitOutputResults(
continue;
}
hasCssUpdates ||= destination.endsWith('.css');
incrementalResult.files[destination] = {
type: BuildOutputFileType.Browser,
inputPath: source,
@ -369,6 +375,21 @@ function* emitOutputResults(
// If there are template updates and the incremental update was background only, a component
// update is possible.
if (hasTemplateUpdates && incrementalResult.background) {
// Template changes may be accompanied by stylesheet changes and these should also be updated hot when possible.
if (hasCssUpdates) {
const styleResult: IncrementalResult = {
kind: ResultKind.Incremental,
added: incrementalResult.added.filter(isCssFilePath),
removed: incrementalResult.removed.filter(({ path }) => isCssFilePath(path)),
modified: incrementalResult.modified.filter(isCssFilePath),
files: Object.fromEntries(
Object.entries(incrementalResult.files).filter(([path]) => isCssFilePath(path)),
),
};
yield styleResult;
}
const updateResult: ComponentUpdateResult = {
kind: ResultKind.ComponentUpdate,
updates: Array.from(templateUpdates, ([id, content]) => ({
@ -381,3 +402,7 @@ function* emitOutputResults(
yield updateResult;
}
}
function isCssFilePath(filePath: string): boolean {
return /\.css(?:\.map)?$/i.test(filePath);
}

View File

@ -589,7 +589,7 @@ function handleUpdate(
type: 'update',
updates,
});
logger.info('HMR update sent to client(s).');
logger.info('Stylesheet update sent to client(s).');
return;
}