Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions packages/angular/build/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,11 @@
"lmdb": "3.1.3"
},
"peerDependencies": {
"@angular/compiler": "^19.0.0-next.0",
"@angular/compiler-cli": "^19.0.0-next.0",
"@angular/localize": "^19.0.0-next.0",
"@angular/platform-server": "^19.0.0-next.0",
"@angular/service-worker": "^19.0.0-next.0",
"@angular/compiler": "^19.0.0-next.9",
"@angular/compiler-cli": "^19.0.0-next.9",
"@angular/localize": "^19.0.0-next.9",
"@angular/platform-server": "^19.0.0-next.9",
"@angular/service-worker": "^19.0.0-next.9",
"@angular/ssr": "^0.0.0-PLACEHOLDER",
"less": "^4.2.0",
"postcss": "^8.4.0",
Expand Down
17 changes: 11 additions & 6 deletions packages/angular/build/src/builders/dev-server/vite-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,13 @@ export async function* serveWithVite(
process.setSourceMapsEnabled(true);
}

// TODO: Enable by default once full support across CLI and FW is integrated
browserOptions.externalRuntimeStyles = useComponentStyleHmr;
// Enable to support component style hot reloading (`NG_HMR_CSTYLES=0` can be used to disable)
browserOptions.externalRuntimeStyles = !!serverOptions.liveReload && useComponentStyleHmr;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider: Prefer Boolean(...) over !! to be more clear about your intent.

if (browserOptions.externalRuntimeStyles) {
// Preload the @angular/compiler package to avoid first stylesheet request delays.
// Once @angular/build is native ESM, this should be re-evaluated.
void loadEsmModule('@angular/compiler');
}

// Setup the prebundling transformer that will be shared across Vite prebundling requests
const prebundleTransformer = new JavaScriptTransformer(
Expand Down Expand Up @@ -166,7 +171,7 @@ export async function* serveWithVite(
explicitBrowser: [],
explicitServer: [],
};
const usedComponentStyles = new Map<string, string[]>();
const usedComponentStyles = new Map<string, Set<string>>();
const templateUpdates = new Map<string, string>();

// Add cleanup logic via a builder teardown.
Expand Down Expand Up @@ -423,7 +428,7 @@ async function handleUpdate(
server: ViteDevServer,
serverOptions: NormalizedDevServerOptions,
logger: BuilderContext['logger'],
usedComponentStyles: Map<string, string[]>,
usedComponentStyles: Map<string, Set<string>>,
): Promise<void> {
const updatedFiles: string[] = [];
let destroyAngularServerAppCalled = false;
Expand Down Expand Up @@ -470,7 +475,7 @@ async function handleUpdate(
// are not typically reused across components.
const componentIds = usedComponentStyles.get(filePath);
if (componentIds) {
return componentIds.map((id) => ({
return Array.from(componentIds).map((id) => ({
type: 'css-update',
timestamp,
path: `${filePath}?ngcomp` + (id ? `=${id}` : ''),
Expand Down Expand Up @@ -582,7 +587,7 @@ export async function setupServer(
prebundleTransformer: JavaScriptTransformer,
target: string[],
zoneless: boolean,
usedComponentStyles: Map<string, string[]>,
usedComponentStyles: Map<string, Set<string>>,
templateUpdates: Map<string, string>,
prebundleLoaderExtensions: EsbuildLoaderOption | undefined,
extensionMiddleware?: Connect.NextHandleFunction[],
Expand Down
6 changes: 3 additions & 3 deletions packages/angular/build/src/tools/angular/angular-host.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export interface AngularHostOptions {
containingFile: string,
stylesheetFile?: string,
order?: number,
className?: string,
): Promise<string | null>;
processWebWorker(workerFile: string, containingFile: string): string;
}
Expand Down Expand Up @@ -197,9 +198,8 @@ export function createAngularCompilerHost(
data,
context.containingFile,
context.resourceFile ?? undefined,
// TODO: Remove once available in compiler-cli types
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(context as any).order,
context.order,
context.className,
);

return typeof result === 'string' ? { content: result } : null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,15 @@ export class ParallelCompilation extends AngularCompilation {
}> {
const stylesheetChannel = new MessageChannel();
// The request identifier is required because Angular can issue multiple concurrent requests
stylesheetChannel.port1.on('message', ({ requestId, data, containingFile, stylesheetFile }) => {
hostOptions
.transformStylesheet(data, containingFile, stylesheetFile)
.then((value) => stylesheetChannel.port1.postMessage({ requestId, value }))
.catch((error) => stylesheetChannel.port1.postMessage({ requestId, error }));
});
stylesheetChannel.port1.on(
'message',
({ requestId, data, containingFile, stylesheetFile, order, className }) => {
hostOptions
.transformStylesheet(data, containingFile, stylesheetFile, order, className)
.then((value) => stylesheetChannel.port1.postMessage({ requestId, value }))
.catch((error) => stylesheetChannel.port1.postMessage({ requestId, error }));
},
);

// The web worker processing is a synchronous operation and uses shared memory combined with
// the Atomics API to block execution here until a response is received.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export async function initialize(request: InitRequest) {
fileReplacements: request.fileReplacements,
sourceFileCache,
modifiedFiles: sourceFileCache.modifiedFiles,
transformStylesheet(data, containingFile, stylesheetFile) {
transformStylesheet(data, containingFile, stylesheetFile, order, className) {
const requestId = randomUUID();
const resultPromise = new Promise<string>((resolve, reject) =>
stylesheetRequests.set(requestId, [resolve, reject]),
Expand All @@ -59,6 +59,8 @@ export async function initialize(request: InitRequest) {
data,
containingFile,
stylesheetFile,
order,
className,
});

return resultPromise;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ export function createCompilerPlugin(
fileReplacements: pluginOptions.fileReplacements,
modifiedFiles,
sourceFileCache: pluginOptions.sourceFileCache,
async transformStylesheet(data, containingFile, stylesheetFile, order) {
async transformStylesheet(data, containingFile, stylesheetFile, order, className) {
let stylesheetResult;

// Stylesheet file only exists for external stylesheets
Expand All @@ -202,6 +202,7 @@ export function createCompilerPlugin(
? createHash('sha-256')
.update(containingFile)
.update((order ?? 0).toString())
.update(className ?? '')
.digest('hex')
: undefined,
);
Expand Down
13 changes: 12 additions & 1 deletion packages/angular/build/src/tools/esbuild/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export function logBuildStats(
const browserStats: BundleStats[] = [];
const serverStats: BundleStats[] = [];
let unchangedCount = 0;
let componentStyleChange = false;

for (const { path: file, size, type } of outputFiles) {
// Only display JavaScript and CSS files
Expand All @@ -63,6 +64,12 @@ export function logBuildStats(
continue;
}

// Skip logging external component stylesheets used for HMR
if (metafile.outputs[file] && 'ng-component' in metafile.outputs[file]) {
componentStyleChange = true;
continue;
}

const name = initial.get(file)?.name ?? getChunkNameFromMetafile(metafile, file);
const stat: BundleStats = {
initial: initial.has(file),
Expand All @@ -88,7 +95,11 @@ export function logBuildStats(

return tableText + '\n';
} else if (changedFiles !== undefined) {
return '\nNo output file changes.\n';
if (componentStyleChange) {
return '\nComponent stylesheet(s) changed.\n';
} else {
return '\nNo output file changes.\n';
}
}
if (unchangedCount > 0) {
return `Unchanged output files: ${unchangedCount}`;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export function createAngularAssetsMiddleware(
server: ViteDevServer,
assets: Map<string, string>,
outputFiles: AngularMemoryOutputFiles,
usedComponentStyles: Map<string, string[]>,
usedComponentStyles: Map<string, Set<string>>,
): Connect.NextHandleFunction {
return function angularAssetsMiddleware(req, res, next) {
if (req.url === undefined || res.writableEnded) {
Expand Down Expand Up @@ -81,9 +81,9 @@ export function createAngularAssetsMiddleware(
// Record the component style usage for HMR updates
const usedIds = usedComponentStyles.get(pathname);
if (usedIds === undefined) {
usedComponentStyles.set(pathname, [componentId]);
usedComponentStyles.set(pathname, new Set([componentId]));
} else {
usedIds.push(componentId);
usedIds.add(componentId);
}
// Shim the stylesheet if a component ID is provided
if (componentId.length > 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ interface AngularSetupMiddlewaresPluginOptions {
assets: Map<string, string>;
extensionMiddleware?: Connect.NextHandleFunction[];
indexHtmlTransformer?: (content: string) => Promise<string>;
usedComponentStyles: Map<string, string[]>;
usedComponentStyles: Map<string, Set<string>>;
templateUpdates: Map<string, string>;
ssrMode: ServerSsrMode;
}
Expand Down
2 changes: 1 addition & 1 deletion packages/angular/build/src/utils/environment-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ export const shouldOptimizeChunks =

const hmrComponentStylesVariable = process.env['NG_HMR_CSTYLES'];
export const useComponentStyleHmr =
isPresent(hmrComponentStylesVariable) && isEnabled(hmrComponentStylesVariable);
!isPresent(hmrComponentStylesVariable) || !isDisabled(hmrComponentStylesVariable);

const partialSsrBuildVariable = process.env['NG_BUILD_PARTIAL_SSR'];
export const usePartialSsrBuild =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Component } from '@angular/core';

@Component({
selector: 'app-root',
standalone: false,
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
Expand Down
7 changes: 7 additions & 0 deletions tests/legacy-cli/e2e/tests/basic/rebuild.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,14 @@ export default async function () {
const validBundleRegEx = esbuild ? /complete\./ : /Compiled successfully\./;
const lazyBundleRegEx = esbuild ? /chunk-/ : /src_app_lazy_lazy_component_ts\.js/;

// Disable component stylesheet HMR to support page reload based rebuild testing.
// Ideally this environment variable would be passed directly to the new serve process
// but this would require signficant test changes due to the existing `ngServe` signature.
const oldHMRValue = process.env['NG_HMR_CSTYLES'];
process.env['NG_HMR_CSTYLES'] = '0';
const port = await ngServe();
process.env['NG_HMR_CSTYLES'] = oldHMRValue;

// Add a lazy route.
await silentNg('generate', 'component', 'lazy');

Expand Down
10 changes: 5 additions & 5 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -401,11 +401,11 @@ __metadata:
vite: "npm:5.4.9"
watchpack: "npm:2.4.2"
peerDependencies:
"@angular/compiler": ^19.0.0-next.0
"@angular/compiler-cli": ^19.0.0-next.0
"@angular/localize": ^19.0.0-next.0
"@angular/platform-server": ^19.0.0-next.0
"@angular/service-worker": ^19.0.0-next.0
"@angular/compiler": ^19.0.0-next.9
"@angular/compiler-cli": ^19.0.0-next.9
"@angular/localize": ^19.0.0-next.9
"@angular/platform-server": ^19.0.0-next.9
"@angular/service-worker": ^19.0.0-next.9
"@angular/ssr": ^0.0.0-PLACEHOLDER
less: ^4.2.0
postcss: ^8.4.0
Expand Down