Skip to content

feat(@angular/build): utilize ssr.entry in Vite dev-server when available #28463

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Sep 23, 2024
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
3 changes: 3 additions & 0 deletions goldens/public-api/angular/ssr/index.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ export class AngularAppEngine {
static ɵhooks: Hooks;
}

// @public
export function createRequestHandler(handler: RequestHandlerFunction): RequestHandlerFunction;

// @public
export enum PrerenderFallback {
Client = 1,
Expand Down
3 changes: 3 additions & 0 deletions goldens/public-api/angular/ssr/node/index.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ export interface CommonEngineRenderOptions {
url?: string;
}

// @public
export function createNodeRequestHandler<T extends RequestHandlerFunction>(handler: T): T;

// @public
export function createWebRequestFromNodeRequest(nodeRequest: IncomingMessage): Request;

Expand Down
4 changes: 2 additions & 2 deletions packages/angular/build/src/builders/application/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,15 +88,15 @@ export async function* buildApplicationInternal(

yield* runEsBuildBuildAction(
async (rebuildState) => {
const { serverEntryPoint, jsonLogs } = normalizedOptions;
const { serverEntryPoint, jsonLogs, disableFullServerManifestGeneration } = normalizedOptions;

const startTime = process.hrtime.bigint();
const result = await executeBuild(normalizedOptions, context, rebuildState);

if (jsonLogs) {
result.addLog(await createJsonBuildManifest(result, normalizedOptions));
} else {
if (serverEntryPoint) {
if (serverEntryPoint && !disableFullServerManifestGeneration) {
const prerenderedRoutesLength = Object.keys(result.prerenderedRoutes).length;
let prerenderMsg = `Prerendered ${prerenderedRoutesLength} static route`;
prerenderMsg += prerenderedRoutesLength !== 1 ? 's.' : '.';
Expand Down
88 changes: 53 additions & 35 deletions packages/angular/build/src/builders/dev-server/vite-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,14 @@ import { readFile } from 'node:fs/promises';
import { builtinModules, isBuiltin } from 'node:module';
import { join } from 'node:path';
import type { Connect, DepOptimizationConfig, InlineConfig, ViteDevServer } from 'vite';
import { createAngularMemoryPlugin } from '../../tools/vite/angular-memory-plugin';
import { createAngularLocaleDataPlugin } from '../../tools/vite/i18n-locale-plugin';
import { createRemoveIdPrefixPlugin } from '../../tools/vite/id-prefix-plugin';
import {
ServerSsrMode,
createAngularLocaleDataPlugin,
createAngularMemoryPlugin,
createAngularSetupMiddlewaresPlugin,
createAngularSsrTransformPlugin,
createRemoveIdPrefixPlugin,
} from '../../tools/vite/plugins';
import { loadProxyConfiguration, normalizeSourceMaps } from '../../utils';
import { loadEsmModule } from '../../utils/load-esm';
import { Result, ResultFile, ResultKind } from '../application/results';
Expand Down Expand Up @@ -313,14 +318,25 @@ export async function* serveWithVite(
? browserOptions.polyfills
: [browserOptions.polyfills];

let ssrMode: ServerSsrMode = ServerSsrMode.NoSsr;
if (
browserOptions.outputMode &&
typeof browserOptions.ssr === 'object' &&
browserOptions.ssr.entry
) {
ssrMode = ServerSsrMode.ExternalSsrMiddleware;
} else if (browserOptions.server) {
ssrMode = ServerSsrMode.InternalSsrMiddleware;
}

// Setup server and start listening
const serverConfiguration = await setupServer(
serverOptions,
generatedFiles,
assetFiles,
browserOptions.preserveSymlinks,
externalMetadata,
!!browserOptions.ssr,
ssrMode,
prebundleTransformer,
target,
isZonelessApp(polyfills),
Expand All @@ -334,12 +350,6 @@ export async function* serveWithVite(
server = await createServer(serverConfiguration);
await server.listen();

if (browserOptions.ssr && serverOptions.prebundle !== false) {
// Warm up the SSR request and begin optimizing dependencies.
// Without this, Vite will only start optimizing SSR modules when the first request is made.
void server.warmupRequest('./main.server.mjs', { ssr: true });
}

const urls = server.resolvedUrls;
if (urls && (urls.local.length || urls.network.length)) {
serverUrl = new URL(urls.local[0] ?? urls.network[0]);
Expand Down Expand Up @@ -385,34 +395,37 @@ async function handleUpdate(
usedComponentStyles: Map<string, string[]>,
): Promise<void> {
const updatedFiles: string[] = [];
let isServerFileUpdated = false;
let destroyAngularServerAppCalled = false;

// Invalidate any updated files
for (const [file, record] of generatedFiles) {
if (record.updated) {
updatedFiles.push(file);
isServerFileUpdated ||= record.type === BuildOutputFileType.ServerApplication;
for (const [file, { updated, type }] of generatedFiles) {
if (!updated) {
continue;
}

const updatedModules = server.moduleGraph.getModulesByFile(
normalizePath(join(server.config.root, file)),
);
updatedModules?.forEach((m) => server?.moduleGraph.invalidateModule(m));
if (type === BuildOutputFileType.ServerApplication && !destroyAngularServerAppCalled) {
// Clear the server app cache
// This must be done before module invalidation.
const { ɵdestroyAngularServerApp } = (await server.ssrLoadModule('/main.server.mjs')) as {
ɵdestroyAngularServerApp: typeof destroyAngularServerApp;
};

ɵdestroyAngularServerApp();
destroyAngularServerAppCalled = true;
}

updatedFiles.push(file);

const updatedModules = server.moduleGraph.getModulesByFile(
normalizePath(join(server.config.root, file)),
);
updatedModules?.forEach((m) => server.moduleGraph.invalidateModule(m));
}

if (!updatedFiles.length) {
return;
}

// clean server apps cache
if (isServerFileUpdated) {
const { ɵdestroyAngularServerApp } = (await server.ssrLoadModule('/main.server.mjs')) as {
ɵdestroyAngularServerApp: typeof destroyAngularServerApp;
};

ɵdestroyAngularServerApp();
}

if (serverOptions.liveReload || serverOptions.hmr) {
if (updatedFiles.every((f) => f.endsWith('.css'))) {
const timestamp = Date.now();
Expand Down Expand Up @@ -534,7 +547,7 @@ export async function setupServer(
assets: Map<string, string>,
preserveSymlinks: boolean | undefined,
externalMetadata: DevServerExternalResultMetadata,
ssr: boolean,
ssrMode: ServerSsrMode,
prebundleTransformer: JavaScriptTransformer,
target: string[],
zoneless: boolean,
Expand Down Expand Up @@ -587,6 +600,9 @@ export async function setupServer(
preserveSymlinks,
},
server: {
warmup: {
ssrFiles: ['./main.server.mjs', './server.mjs'],
},
port: serverOptions.port,
strictPort: true,
host: serverOptions.host,
Expand Down Expand Up @@ -637,19 +653,21 @@ export async function setupServer(
},
plugins: [
createAngularLocaleDataPlugin(),
createAngularMemoryPlugin({
workspaceRoot: serverOptions.workspaceRoot,
virtualProjectRoot,
createAngularSetupMiddlewaresPlugin({
outputFiles,
assets,
ssr,
external: externalMetadata.explicitBrowser,
indexHtmlTransformer,
extensionMiddleware,
normalizePath,
usedComponentStyles,
ssrMode,
}),
createRemoveIdPrefixPlugin(externalMetadata.explicitBrowser),
await createAngularSsrTransformPlugin(serverOptions.workspaceRoot),
await createAngularMemoryPlugin({
virtualProjectRoot,
outputFiles,
external: externalMetadata.explicitBrowser,
}),
],
// Browser only optimizeDeps. (This does not run for SSR dependencies).
optimizeDeps: getDepOptimizationConfig({
Expand Down
170 changes: 0 additions & 170 deletions packages/angular/build/src/tools/vite/angular-memory-plugin.ts

This file was deleted.

5 changes: 4 additions & 1 deletion packages/angular/build/src/tools/vite/middlewares/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,8 @@
export { createAngularAssetsMiddleware } from './assets-middleware';
export { angularHtmlFallbackMiddleware } from './html-fallback-middleware';
export { createAngularIndexHtmlMiddleware } from './index-html-middleware';
export { createAngularSSRMiddleware } from './ssr-middleware';
export {
createAngularSsrExternalMiddleware,
createAngularSsrInternalMiddleware,
} from './ssr-middleware';
export { createAngularHeadersMiddleware } from './headers-middleware';
Loading