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 4c868c90bf..f3309d94f6 100644 --- a/packages/angular/build/src/builders/dev-server/vite-server.ts +++ b/packages/angular/build/src/builders/dev-server/vite-server.ts @@ -735,6 +735,7 @@ export async function setupServer( await createAngularMemoryPlugin({ virtualProjectRoot, outputFiles, + templateUpdates, external: externalMetadata.explicitBrowser, skipViteClient: serverOptions.liveReload === false && serverOptions.hmr === false, }), 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 0af30710f2..dc83f131f2 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 @@ -16,10 +16,13 @@ import { AngularMemoryOutputFiles } from '../utils'; interface AngularMemoryPluginOptions { virtualProjectRoot: string; outputFiles: AngularMemoryOutputFiles; + templateUpdates?: ReadonlyMap; external?: string[]; skipViteClient?: boolean; } +const ANGULAR_PREFIX = '/@ng/'; + export async function createAngularMemoryPlugin( options: AngularMemoryPluginOptions, ): Promise { @@ -30,7 +33,12 @@ export async function createAngularMemoryPlugin( name: 'vite:angular-memory', // Ensures plugin hooks run before built-in Vite hooks enforce: 'pre', - async resolveId(source, importer) { + async resolveId(source, importer, { ssr }) { + // For SSR with component HMR, pass through as a virtual module + if (ssr && source.startsWith(ANGULAR_PREFIX)) { + return '\0' + source; + } + // Prevent vite from resolving an explicit external dependency (`externalDependencies` option) if (external?.includes(source)) { // This is still not ideal since Vite will still transform the import specifier to @@ -51,7 +59,16 @@ export async function createAngularMemoryPlugin( return join(virtualProjectRoot, source); } }, - load(id) { + load(id, loadOptions) { + // For SSR component updates, return the component update module or empty if none + if (loadOptions?.ssr && id.startsWith(`\0${ANGULAR_PREFIX}`)) { + // Extract component identifier (first character is rollup virtual module null) + const requestUrl = new URL(id.slice(1), 'http://localhost'); + const componentId = requestUrl.searchParams.get('c'); + + return (componentId && options.templateUpdates?.get(componentId)) ?? ''; + } + const [file] = id.split('?', 1); const relativeFile = '/' + normalizePath(relative(virtualProjectRoot, file)); const codeContents = outputFiles.get(relativeFile)?.contents;