From b2abfaf538da0ffb2e96a3d56b017c6bacfbf1c6 Mon Sep 17 00:00:00 2001 From: Alan Agius <17563226+alan-agius4@users.noreply.github.com> Date: Thu, 13 Mar 2025 12:53:28 +0000 Subject: [PATCH 1/2] feat(@angular/ssr): stabilize `AngularNodeAppEngine`, `AngularAppEngine`, and `provideServerRouting` APIs This commit promotes the `AngularNodeAppEngine`, `AngularAppEngine`, and `provideServerRouting` APIs from dev preview to stable. These APIs enhance server-side rendering (SSR) capabilities in Angular applications, improving routing and server integration for better performance and reliability. --- packages/angular/ssr/node/src/app-engine.ts | 2 -- packages/angular/ssr/node/src/handler.ts | 2 -- packages/angular/ssr/node/src/module.ts | 1 - packages/angular/ssr/node/src/request.ts | 1 - packages/angular/ssr/node/src/response.ts | 1 - packages/angular/ssr/src/app-engine.ts | 2 -- packages/angular/ssr/src/handler.ts | 2 -- packages/angular/ssr/src/routes/route-config.ts | 11 ----------- 8 files changed, 22 deletions(-) diff --git a/packages/angular/ssr/node/src/app-engine.ts b/packages/angular/ssr/node/src/app-engine.ts index a59502ee47a8..f8fd03a8e21c 100644 --- a/packages/angular/ssr/node/src/app-engine.ts +++ b/packages/angular/ssr/node/src/app-engine.ts @@ -18,8 +18,6 @@ import { createWebRequestFromNodeRequest } from './request'; * * @remarks This class should be instantiated once and used as a singleton across the server-side * application to ensure consistent handling of rendering requests and resource management. - * - * @developerPreview */ export class AngularNodeAppEngine { private readonly angularAppEngine = new AngularAppEngine(); diff --git a/packages/angular/ssr/node/src/handler.ts b/packages/angular/ssr/node/src/handler.ts index be67c1dcb339..89452b3099b1 100644 --- a/packages/angular/ssr/node/src/handler.ts +++ b/packages/angular/ssr/node/src/handler.ts @@ -16,7 +16,6 @@ import type { IncomingMessage, ServerResponse } from 'node:http'; * @param next - A callback function that signals the completion of the middleware or forwards the error if provided. * * @returns A Promise that resolves to void or simply void. The handler can be asynchronous. - * @developerPreview */ export type NodeRequestHandlerFunction = ( req: IncomingMessage, @@ -66,7 +65,6 @@ export type NodeRequestHandlerFunction = ( * res.send('Hello from Fastify with Node Next Handler!'); * })); * ``` - * @developerPreview */ export function createNodeRequestHandler(handler: T): T { (handler as T & { __ng_node_request_handler__?: boolean })['__ng_node_request_handler__'] = true; diff --git a/packages/angular/ssr/node/src/module.ts b/packages/angular/ssr/node/src/module.ts index f53943a89727..bd52455940a2 100644 --- a/packages/angular/ssr/node/src/module.ts +++ b/packages/angular/ssr/node/src/module.ts @@ -23,7 +23,6 @@ import { fileURLToPath } from 'node:url'; * * @param url The URL of the module to check. This should typically be `import.meta.url`. * @returns `true` if the provided URL represents the main entry point, otherwise `false`. - * @developerPreview */ export function isMainModule(url: string): boolean { return url.startsWith('file:') && argv[1] === fileURLToPath(url); diff --git a/packages/angular/ssr/node/src/request.ts b/packages/angular/ssr/node/src/request.ts index 78ec7f2ef712..51331a18cc35 100644 --- a/packages/angular/ssr/node/src/request.ts +++ b/packages/angular/ssr/node/src/request.ts @@ -27,7 +27,6 @@ const HTTP2_PSEUDO_HEADERS = new Set([':method', ':scheme', ':authority', ':path * * @param nodeRequest - The Node.js request object (`IncomingMessage` or `Http2ServerRequest`) to convert. * @returns A Web Standard `Request` object. - * @developerPreview */ export function createWebRequestFromNodeRequest( nodeRequest: IncomingMessage | Http2ServerRequest, diff --git a/packages/angular/ssr/node/src/response.ts b/packages/angular/ssr/node/src/response.ts index 8f26bc319361..56f091deed5f 100644 --- a/packages/angular/ssr/node/src/response.ts +++ b/packages/angular/ssr/node/src/response.ts @@ -19,7 +19,6 @@ import type { Http2ServerResponse } from 'node:http2'; * @param source - The web-standard `Response` object to stream from. * @param destination - The Node.js response object (`ServerResponse` or `Http2ServerResponse`) to stream into. * @returns A promise that resolves once the streaming operation is complete. - * @developerPreview */ export async function writeResponseToNodeResponse( source: Response, diff --git a/packages/angular/ssr/src/app-engine.ts b/packages/angular/ssr/src/app-engine.ts index c1e2e7fcd5a4..0ce5d23c30d1 100644 --- a/packages/angular/ssr/src/app-engine.ts +++ b/packages/angular/ssr/src/app-engine.ts @@ -19,8 +19,6 @@ import { joinUrlParts } from './utils/url'; * * @remarks This class should be instantiated once and used as a singleton across the server-side * application to ensure consistent handling of rendering requests and resource management. - * - * @developerPreview */ export class AngularAppEngine { /** diff --git a/packages/angular/ssr/src/handler.ts b/packages/angular/ssr/src/handler.ts index e375b327f829..5969e06907f5 100644 --- a/packages/angular/ssr/src/handler.ts +++ b/packages/angular/ssr/src/handler.ts @@ -12,7 +12,6 @@ * @param request - The incoming HTTP request object. * @returns A Promise resolving to a `Response` object, `null`, or directly a `Response`, * supporting both synchronous and asynchronous handling. - * @developerPreview */ export type RequestHandlerFunction = ( request: Request, @@ -39,7 +38,6 @@ export type RequestHandlerFunction = ( * const handler = toWebHandler(app); * export default createRequestHandler(handler); * ``` - * @developerPreview */ export function createRequestHandler(handler: RequestHandlerFunction): RequestHandlerFunction { (handler as RequestHandlerFunction & { __ng_request_handler__?: boolean })[ diff --git a/packages/angular/ssr/src/routes/route-config.ts b/packages/angular/ssr/src/routes/route-config.ts index 26f115ad6256..d0a2306134c6 100644 --- a/packages/angular/ssr/src/routes/route-config.ts +++ b/packages/angular/ssr/src/routes/route-config.ts @@ -24,7 +24,6 @@ const APP_SHELL_ROUTE = 'ng-app-shell'; /** * Identifies a particular kind of `ServerRoutesFeatureKind`. * @see {@link ServerRoutesFeature} - * @developerPreview */ enum ServerRoutesFeatureKind { AppShell, @@ -33,7 +32,6 @@ enum ServerRoutesFeatureKind { /** * Helper type to represent a server routes feature. * @see {@link ServerRoutesFeatureKind} - * @developerPreview */ interface ServerRoutesFeature { ɵkind: FeatureKind; @@ -44,7 +42,6 @@ interface ServerRoutesFeature { * Different rendering modes for server routes. * @see {@link provideServerRouting} * @see {@link ServerRoute} - * @developerPreview */ export enum RenderMode { /** Server-Side Rendering (SSR) mode, where content is rendered on the server for each request. */ @@ -61,7 +58,6 @@ export enum RenderMode { * Defines the fallback strategies for Static Site Generation (SSG) routes when a pre-rendered path is not available. * This is particularly relevant for routes with parameterized URLs where some paths might not be pre-rendered at build time. * @see {@link ServerRoutePrerenderWithParams} - * @developerPreview */ export enum PrerenderFallback { /** @@ -85,7 +81,6 @@ export enum PrerenderFallback { /** * Common interface for server routes, providing shared properties. - * @developerPreview */ export interface ServerRouteCommon { /** The path associated with this route. */ @@ -101,7 +96,6 @@ export interface ServerRouteCommon { /** * A server route that uses Client-Side Rendering (CSR) mode. * @see {@link RenderMode} - * @developerPreview */ export interface ServerRouteClient extends ServerRouteCommon { /** Specifies that the route uses Client-Side Rendering (CSR) mode. */ @@ -111,7 +105,6 @@ export interface ServerRouteClient extends ServerRouteCommon { /** * A server route that uses Static Site Generation (SSG) mode. * @see {@link RenderMode} - * @developerPreview */ export interface ServerRoutePrerender extends Omit { /** Specifies that the route uses Static Site Generation (SSG) mode. */ @@ -126,7 +119,6 @@ export interface ServerRoutePrerender extends Omit * @see {@link RenderMode} * @see {@link ServerRoutePrerender} * @see {@link PrerenderFallback} - * @developerPreview */ export interface ServerRoutePrerenderWithParams extends Omit { /** @@ -171,7 +163,6 @@ export interface ServerRoutePrerenderWithParams extends Omit('SERV * * @see {@link ServerRoute} * @see {@link withAppShell} - * @developerPreview */ export function provideServerRouting( routes: ServerRoute[], From c327d3f2d4d4afc37263d47a4bf4595a59ac55c7 Mon Sep 17 00:00:00 2001 From: Alan Agius <17563226+alan-agius4@users.noreply.github.com> Date: Thu, 13 Mar 2025 14:29:06 +0000 Subject: [PATCH 2/2] feat(@schematics/angular): remove `--server-routing` option Server-side rendering (SSR) will always enable server routing when using the application builder. BREAKING CHANGE: `--server-routing` option has been removed from several schematics. Server routing will be used when using the application builder. --- .../ssr/schematics/ng-add/index_spec.ts | 1 - .../angular/ssr/schematics/ng-add/schema.json | 4 - .../schematics/angular/app-shell/index.ts | 39 ++---- .../angular/app-shell/index_spec.ts | 1 - .../schematics/angular/app-shell/schema.json | 5 - .../schematics/angular/application/index.ts | 1 - .../angular/application/index_spec.ts | 1 - .../angular/application/schema.json | 4 - packages/schematics/angular/ng-new/index.ts | 1 - .../schematics/angular/ng-new/schema.json | 4 - .../app/app.module.server.ts.template | 12 +- .../app/app.config.server.ts.template | 12 +- packages/schematics/angular/server/index.ts | 19 +-- .../schematics/angular/server/schema.json | 4 - .../server.ts.template | 67 ---------- packages/schematics/angular/ssr/index.ts | 119 ++++-------------- packages/schematics/angular/ssr/index_spec.ts | 112 ----------------- packages/schematics/angular/ssr/schema.json | 4 - .../angular/utility/project-targets.ts | 10 ++ .../prerender/discover-routes-ngmodule.ts | 36 ++---- .../prerender/discover-routes-standalone.ts | 10 +- .../build/prerender/http-requests-assets.ts | 2 +- .../express-engine-csp-nonce.ts | 2 +- .../express-engine-standalone.ts | 2 +- ...utput-mode-server-external-dependencies.ts | 2 +- ...outes-output-mode-server-i18n-base-href.ts | 2 +- ...routes-output-mode-server-i18n-sub-path.ts | 2 +- .../server-routes-output-mode-server-i18n.ts | 2 +- ...tes-output-mode-server-platform-neutral.ts | 2 +- .../server-routes-output-mode-server.ts | 2 +- ...er-routes-output-mode-static-http-calls.ts | 2 +- ...s-output-mode-static-i18n_APP_BASE_HREF.ts | 2 +- .../server-routes-output-mode-static.ts | 2 +- .../server-routes-preload-links.ts | 2 +- .../serve/ssr-http-requests-assets.ts | 2 +- .../legacy-cli/e2e/tests/vite/ssr-default.ts | 2 +- .../e2e/tests/vite/ssr-entry-express.ts | 2 +- .../e2e/tests/vite/ssr-entry-fastify.ts | 2 +- .../legacy-cli/e2e/tests/vite/ssr-entry-h3.ts | 2 +- .../e2e/tests/vite/ssr-entry-hono.ts | 2 +- .../e2e/tests/vite/ssr-error-stack.ts | 2 +- .../tests/vite/ssr-new-dep-optimization.ts | 2 +- .../vite/ssr-no-server-entry-sub-path.ts | 54 -------- .../legacy-cli/e2e/tests/vite/ssr-with-ssl.ts | 2 +- 44 files changed, 95 insertions(+), 469 deletions(-) delete mode 100644 packages/schematics/angular/ssr/files/application-builder-common-engine/server.ts.template delete mode 100644 tests/legacy-cli/e2e/tests/vite/ssr-no-server-entry-sub-path.ts diff --git a/packages/angular/ssr/schematics/ng-add/index_spec.ts b/packages/angular/ssr/schematics/ng-add/index_spec.ts index bdf5474e0d70..b93a509200b1 100644 --- a/packages/angular/ssr/schematics/ng-add/index_spec.ts +++ b/packages/angular/ssr/schematics/ng-add/index_spec.ts @@ -14,7 +14,6 @@ import { join } from 'node:path'; describe('@angular/ssr ng-add schematic', () => { const defaultOptions = { project: 'test-app', - serverRouting: false, }; const schematicRunner = new SchematicTestRunner( diff --git a/packages/angular/ssr/schematics/ng-add/schema.json b/packages/angular/ssr/schematics/ng-add/schema.json index f82d7373620b..2f0df90db22b 100644 --- a/packages/angular/ssr/schematics/ng-add/schema.json +++ b/packages/angular/ssr/schematics/ng-add/schema.json @@ -16,10 +16,6 @@ "description": "Skip the automatic installation of packages. You will need to manually install the dependencies later.", "type": "boolean", "default": false - }, - "serverRouting": { - "description": "Configure the server application to use the Angular Server Routing API and App Engine APIs (currently in Developer Preview).", - "type": "boolean" } }, "required": ["project"], diff --git a/packages/schematics/angular/app-shell/index.ts b/packages/schematics/angular/app-shell/index.ts index 725f6d126785..25b4094b51ca 100644 --- a/packages/schematics/angular/app-shell/index.ts +++ b/packages/schematics/angular/app-shell/index.ts @@ -27,9 +27,9 @@ import { } from '../utility/ast-utils'; import { applyToUpdateRecorder } from '../utility/change'; import { getAppModulePath, isStandaloneApp } from '../utility/ng-ast-utils'; +import { isUsingApplicationBuilder, targetBuildNotFoundError } from '../utility/project-targets'; import { findBootstrapApplicationCall, getMainFilePath } from '../utility/standalone/util'; -import { getWorkspace, updateWorkspace } from '../utility/workspace'; -import { Builders } from '../utility/workspace-models'; +import { getWorkspace } from '../utility/workspace'; import { Schema as AppShellOptions } from './schema'; const APP_SHELL_ROUTE = 'shell'; @@ -156,29 +156,6 @@ function getMetadataProperty(metadata: ts.Node, propertyName: string): ts.Proper return property; } -function addAppShellConfigToWorkspace(options: AppShellOptions): Rule { - return updateWorkspace((workspace) => { - const project = workspace.projects.get(options.project); - if (!project) { - return; - } - const buildTarget = project.targets.get('build'); - if ( - buildTarget?.builder === Builders.Application || - buildTarget?.builder === Builders.BuildApplication - ) { - // Application builder configuration. - const prodConfig = buildTarget.configurations?.production; - if (!prodConfig) { - throw new SchematicsException( - `A "production" configuration is not defined for the "build" builder.`, - ); - } - prodConfig.appShell = true; - } - }); -} - function addServerRoutes(options: AppShellOptions): Rule { return async (host: Tree) => { // The workspace gets updated so this needs to be reloaded @@ -359,17 +336,21 @@ export default function (options: AppShellOptions): Rule { const browserEntryPoint = await getMainFilePath(tree, options.project); const isStandalone = isStandaloneApp(tree, browserEntryPoint); + const workspace = await getWorkspace(tree); + const project = workspace.projects.get(options.project); + if (!project) { + throw targetBuildNotFoundError(); + } + return chain([ validateProject(browserEntryPoint), schematic('server', options), - ...(options.serverRouting + ...(isUsingApplicationBuilder(project) ? [noop()] : isStandalone ? [addStandaloneServerRoute(options)] : [addServerRoutes(options)]), - options.serverRouting - ? addServerRoutingConfig(options, isStandalone) - : addAppShellConfigToWorkspace(options), + addServerRoutingConfig(options, isStandalone), schematic('component', { name: 'app-shell', module: 'app.module.server.ts', diff --git a/packages/schematics/angular/app-shell/index_spec.ts b/packages/schematics/angular/app-shell/index_spec.ts index 09fbe4ba6e17..77c9742a73ec 100644 --- a/packages/schematics/angular/app-shell/index_spec.ts +++ b/packages/schematics/angular/app-shell/index_spec.ts @@ -19,7 +19,6 @@ describe('App Shell Schematic', () => { ); const defaultOptions: AppShellOptions = { project: 'bar', - serverRouting: true, }; const workspaceOptions: WorkspaceOptions = { diff --git a/packages/schematics/angular/app-shell/schema.json b/packages/schematics/angular/app-shell/schema.json index 262fb3fa45b0..c087a1e1cfe1 100644 --- a/packages/schematics/angular/app-shell/schema.json +++ b/packages/schematics/angular/app-shell/schema.json @@ -12,11 +12,6 @@ "$default": { "$source": "projectName" } - }, - "serverRouting": { - "description": "Set up a server application using the Server Routing and App Engine APIs (Developer Preview).", - "type": "boolean", - "default": false } }, "required": ["project"] diff --git a/packages/schematics/angular/application/index.ts b/packages/schematics/angular/application/index.ts index 1704d8d89b4d..790e90fce784 100644 --- a/packages/schematics/angular/application/index.ts +++ b/packages/schematics/angular/application/index.ts @@ -101,7 +101,6 @@ export default function (options: ApplicationOptions): Rule { options.ssr ? schematic('ssr', { project: options.name, - serverRouting: options.serverRouting, skipInstall: true, }) : noop(), diff --git a/packages/schematics/angular/application/index_spec.ts b/packages/schematics/angular/application/index_spec.ts index 2979775eb1ea..50f6a0650b96 100644 --- a/packages/schematics/angular/application/index_spec.ts +++ b/packages/schematics/angular/application/index_spec.ts @@ -32,7 +32,6 @@ describe('Application Schematic', () => { const defaultOptions: ApplicationOptions = { name: 'foo', skipPackageJson: false, - serverRouting: false, }; let workspaceTree: UnitTestTree; diff --git a/packages/schematics/angular/application/schema.json b/packages/schematics/angular/application/schema.json index b7d8e382aad2..7a6ea47a2020 100644 --- a/packages/schematics/angular/application/schema.json +++ b/packages/schematics/angular/application/schema.json @@ -118,10 +118,6 @@ "default": false, "x-user-analytics": "ep.ng_ssr" }, - "serverRouting": { - "description": "Set up a server application using the Server Routing and App Engine APIs (Developer Preview).", - "type": "boolean" - }, "experimentalZoneless": { "description": "Generate an application that does not use `zone.js`.", "type": "boolean", diff --git a/packages/schematics/angular/ng-new/index.ts b/packages/schematics/angular/ng-new/index.ts index 133b1083ee5c..f5e37ccccf40 100644 --- a/packages/schematics/angular/ng-new/index.ts +++ b/packages/schematics/angular/ng-new/index.ts @@ -57,7 +57,6 @@ export default function (options: NgNewOptions): Rule { minimal: options.minimal, standalone: options.standalone, ssr: options.ssr, - serverRouting: options.serverRouting, experimentalZoneless: options.experimentalZoneless, }; diff --git a/packages/schematics/angular/ng-new/schema.json b/packages/schematics/angular/ng-new/schema.json index 5b214c73b141..be65f561bc88 100644 --- a/packages/schematics/angular/ng-new/schema.json +++ b/packages/schematics/angular/ng-new/schema.json @@ -139,10 +139,6 @@ "type": "boolean", "x-user-analytics": "ep.ng_ssr" }, - "serverRouting": { - "description": "Create a server application in the initial project using the Server Routing and App Engine APIs (Developer Preview).", - "type": "boolean" - }, "experimentalZoneless": { "description": "Create an initial application that does not utilize `zone.js`.", "type": "boolean", diff --git a/packages/schematics/angular/server/files/application-builder/ngmodule-src/app/app.module.server.ts.template b/packages/schematics/angular/server/files/application-builder/ngmodule-src/app/app.module.server.ts.template index 107232f9107f..d1ff23ae44c8 100644 --- a/packages/schematics/angular/server/files/application-builder/ngmodule-src/app/app.module.server.ts.template +++ b/packages/schematics/angular/server/files/application-builder/ngmodule-src/app/app.module.server.ts.template @@ -1,13 +1,13 @@ import { NgModule } from '@angular/core'; -import { ServerModule } from '@angular/platform-server';<% if(serverRouting) { %> -import { provideServerRouting } from '@angular/ssr';<% } %> +import { ServerModule } from '@angular/platform-server'; +import { provideServerRouting } from '@angular/ssr'; import { AppComponent } from './app.component'; -import { AppModule } from './app.module';<% if(serverRouting) { %> -import { serverRoutes } from './app.routes.server';<% } %> +import { AppModule } from './app.module'; +import { serverRoutes } from './app.routes.server'; @NgModule({ - imports: [AppModule, ServerModule],<% if(serverRouting) { %> - providers: [provideServerRouting(serverRoutes)],<% } %> + imports: [AppModule, ServerModule], + providers: [provideServerRouting(serverRoutes)], bootstrap: [AppComponent], }) export class AppServerModule {} diff --git a/packages/schematics/angular/server/files/application-builder/standalone-src/app/app.config.server.ts.template b/packages/schematics/angular/server/files/application-builder/standalone-src/app/app.config.server.ts.template index a7fd3d0b4fe7..012518913eed 100644 --- a/packages/schematics/angular/server/files/application-builder/standalone-src/app/app.config.server.ts.template +++ b/packages/schematics/angular/server/files/application-builder/standalone-src/app/app.config.server.ts.template @@ -1,13 +1,13 @@ import { mergeApplicationConfig, ApplicationConfig } from '@angular/core'; -import { provideServerRendering } from '@angular/platform-server';<% if(serverRouting) { %> -import { provideServerRouting } from '@angular/ssr';<% } %> -import { appConfig } from './app.config';<% if(serverRouting) { %> -import { serverRoutes } from './app.routes.server';<% } %> +import { provideServerRendering } from '@angular/platform-server'; +import { provideServerRouting } from '@angular/ssr'; +import { appConfig } from './app.config'; +import { serverRoutes } from './app.routes.server'; const serverConfig: ApplicationConfig = { providers: [ - provideServerRendering(),<% if(serverRouting) { %> - provideServerRouting(serverRoutes)<% } %> + provideServerRendering(), + provideServerRouting(serverRoutes) ] }; diff --git a/packages/schematics/angular/server/index.ts b/packages/schematics/angular/server/index.ts index 846125a36f2a..6467a1e532fe 100644 --- a/packages/schematics/angular/server/index.ts +++ b/packages/schematics/angular/server/index.ts @@ -28,7 +28,7 @@ import { JSONFile } from '../utility/json-file'; import { latestVersions } from '../utility/latest-versions'; import { isStandaloneApp } from '../utility/ng-ast-utils'; import { relativePathToWorkspaceRoot } from '../utility/paths'; -import { targetBuildNotFoundError } from '../utility/project-targets'; +import { isUsingApplicationBuilder, targetBuildNotFoundError } from '../utility/project-targets'; import { getMainFilePath } from '../utility/standalone/util'; import { getWorkspace, updateWorkspace } from '../utility/workspace'; import { Builders } from '../utility/workspace-models'; @@ -113,9 +113,7 @@ function updateConfigFileApplicationBuilder(options: ServerOptions): Rule { serverMainEntryName, ); - if (options.serverRouting) { - buildTarget.options['outputMode'] = 'static'; - } + buildTarget.options['outputMode'] = 'static'; }); } @@ -173,13 +171,11 @@ export default function (options: ServerOptions): Rule { throw targetBuildNotFoundError(); } - const isUsingApplicationBuilder = - clientBuildTarget.builder === Builders.Application || - clientBuildTarget.builder === Builders.BuildApplication; + const usingApplicationBuilder = isUsingApplicationBuilder(clientProject); if ( clientProject.targets.has('server') || - (isUsingApplicationBuilder && clientBuildTarget.options?.server !== undefined) + (usingApplicationBuilder && clientBuildTarget.options?.server !== undefined) ) { // Server has already been added. return; @@ -190,13 +186,10 @@ export default function (options: ServerOptions): Rule { const isStandalone = isStandaloneApp(host, browserEntryPoint); const sourceRoot = clientProject.sourceRoot ?? join(normalize(clientProject.root), 'src'); - let filesUrl = `./files/${isUsingApplicationBuilder ? 'application-builder/' : 'server-builder/'}`; + let filesUrl = `./files/${usingApplicationBuilder ? 'application-builder/' : 'server-builder/'}`; filesUrl += isStandalone ? 'standalone-src' : 'ngmodule-src'; const templateSource = apply(url(filesUrl), [ - options.serverRouting - ? noop() - : filter((path) => !path.endsWith('app.routes.server.ts.template')), applyTemplates({ ...strings, ...options, @@ -210,7 +203,7 @@ export default function (options: ServerOptions): Rule { return chain([ mergeWith(templateSource), - ...(isUsingApplicationBuilder + ...(usingApplicationBuilder ? [ updateConfigFileApplicationBuilder(options), updateTsConfigFile(clientBuildOptions.tsConfig), diff --git a/packages/schematics/angular/server/schema.json b/packages/schematics/angular/server/schema.json index bad36a9ee36a..225574d92155 100644 --- a/packages/schematics/angular/server/schema.json +++ b/packages/schematics/angular/server/schema.json @@ -17,10 +17,6 @@ "description": "Skip the automatic installation of packages. You will need to manually install the dependencies later.", "type": "boolean", "default": false - }, - "serverRouting": { - "description": "Configure the server application to use the Server Routing and App Engine APIs (Developer Preview).", - "type": "boolean" } }, "required": ["project"] diff --git a/packages/schematics/angular/ssr/files/application-builder-common-engine/server.ts.template b/packages/schematics/angular/ssr/files/application-builder-common-engine/server.ts.template deleted file mode 100644 index 63a70ae893f6..000000000000 --- a/packages/schematics/angular/ssr/files/application-builder-common-engine/server.ts.template +++ /dev/null @@ -1,67 +0,0 @@ -import { APP_BASE_HREF } from '@angular/common'; -import { CommonEngine, isMainModule } from '@angular/ssr/node'; -import express from 'express'; -import { dirname, join, resolve } from 'node:path'; -import { fileURLToPath } from 'node:url'; -import <% if (isStandalone) { %>bootstrap<% } else { %>AppServerModule<% } %> from './main.server'; - -const serverDistFolder = dirname(fileURLToPath(import.meta.url)); -const browserDistFolder = resolve(serverDistFolder, '../<%= browserDistDirectory %>'); -const indexHtml = join(serverDistFolder, 'index.server.html'); - -const app = express(); -const commonEngine = new CommonEngine(); - -/** - * Example Express Rest API endpoints can be defined here. - * Uncomment and define endpoints as necessary. - * - * Example: - * ```ts - * app.get('/api/**', (req, res) => { - * // Handle API request - * }); - * ``` - */ - -/** - * Serve static files from /<%= browserDistDirectory %> - */ -app.get( - '**', - express.static(browserDistFolder, { - maxAge: '1y', - index: 'index.html' - }), -); - -/** - * Handle all other requests by rendering the Angular application. - */ -app.get('**', (req, res, next) => { - const { protocol, originalUrl, baseUrl, headers } = req; - - commonEngine - .render({ - <% if (isStandalone) { %>bootstrap<% } else { %>bootstrap: AppServerModule<% } %>, - documentFilePath: indexHtml, - url: `${protocol}://${headers.host}${originalUrl}`, - publicPath: browserDistFolder, - providers: [{ provide: APP_BASE_HREF, useValue: baseUrl }], - }) - .then((html) => res.send(html)) - .catch((err) => next(err)); -}); - -/** - * Start the server if this module is the main entry point. - * The server listens on the port defined by the `PORT` environment variable, or defaults to 4000. - */ -if (isMainModule(import.meta.url)) { - const port = process.env['PORT'] || 4000; - app.listen(port, () => { - console.log(`Node Express server listening on http://localhost:${port}`); - }); -} - -export default app; diff --git a/packages/schematics/angular/ssr/index.ts b/packages/schematics/angular/ssr/index.ts index 6249778a8594..b73c161bd5b5 100644 --- a/packages/schematics/angular/ssr/index.ts +++ b/packages/schematics/angular/ssr/index.ts @@ -32,13 +32,11 @@ import { import { JSONFile } from '../utility/json-file'; import { latestVersions } from '../utility/latest-versions'; import { isStandaloneApp } from '../utility/ng-ast-utils'; -import { targetBuildNotFoundError } from '../utility/project-targets'; +import { isUsingApplicationBuilder, targetBuildNotFoundError } from '../utility/project-targets'; import { getMainFilePath } from '../utility/standalone/util'; -import { ProjectDefinition, getWorkspace } from '../utility/workspace'; -import { Builders } from '../utility/workspace-models'; +import { getWorkspace } from '../utility/workspace'; import { Schema as SSROptions } from './schema'; -import { isTTY } from './tty'; const SERVE_SSR_TARGET_NAME = 'serve-ssr'; const PRERENDER_TARGET_NAME = 'prerender'; @@ -202,8 +200,7 @@ function updateApplicationBuilderWorkspaceConfigRule( buildTarget.options = { ...buildTarget.options, outputPath, - outputMode: options.serverRouting ? 'server' : undefined, - prerender: options.serverRouting ? undefined : true, + outputMode: 'server', ssr: { entry: join(normalize(projectSourceRoot), 'server.ts'), }, @@ -336,46 +333,37 @@ function addServerFile( if (!project) { throw new SchematicsException(`Invalid project name (${projectName})`); } - const isUsingApplicationBuilder = usingApplicationBuilder(project); - - const browserDistDirectory = isUsingApplicationBuilder + const usingApplicationBuilder = isUsingApplicationBuilder(project); + const browserDistDirectory = usingApplicationBuilder ? (await getApplicationBuilderOutputPaths(host, projectName)).browser : await getLegacyOutputPaths(host, projectName, 'build'); - const applicationBuilderFiles = - 'application-builder' + (options.serverRouting ? '' : '-common-engine'); - return mergeWith( - apply( - url(`./files/${isUsingApplicationBuilder ? applicationBuilderFiles : 'server-builder'}`), - [ - applyTemplates({ - ...strings, - ...options, - browserDistDirectory, - isStandalone, - }), - move(projectSourceRoot), - ], - ), + apply(url(`./files/${usingApplicationBuilder ? 'application-builder' : 'server-builder'}`), [ + applyTemplates({ + ...strings, + ...options, + browserDistDirectory, + isStandalone, + }), + move(projectSourceRoot), + ]), ); }; } -export default function (inputOptions: SSROptions): Rule { +export default function (options: SSROptions): Rule { return async (host, context) => { - const browserEntryPoint = await getMainFilePath(host, inputOptions.project); + const browserEntryPoint = await getMainFilePath(host, options.project); const isStandalone = isStandaloneApp(host, browserEntryPoint); const workspace = await getWorkspace(host); - const clientProject = workspace.projects.get(inputOptions.project); + const clientProject = workspace.projects.get(options.project); if (!clientProject) { throw targetBuildNotFoundError(); } - const isUsingApplicationBuilder = usingApplicationBuilder(clientProject); - const serverRouting = await isServerRoutingEnabled(isUsingApplicationBuilder, inputOptions); - const options = { ...inputOptions, serverRouting }; + const usingApplicationBuilder = isUsingApplicationBuilder(clientProject); const sourceRoot = clientProject.sourceRoot ?? posix.join(clientProject.root, 'src'); return chain([ @@ -383,7 +371,7 @@ export default function (inputOptions: SSROptions): Rule { ...options, skipInstall: true, }), - ...(isUsingApplicationBuilder + ...(usingApplicationBuilder ? [ updateApplicationBuilderWorkspaceConfigRule(sourceRoot, options, context), updateApplicationBuilderTsConfigRule(options), @@ -393,73 +381,8 @@ export default function (inputOptions: SSROptions): Rule { updateWebpackBuilderWorkspaceConfigRule(sourceRoot, options), ]), addServerFile(sourceRoot, options, isStandalone), - addScriptsRule(options, isUsingApplicationBuilder), - addDependencies(options, isUsingApplicationBuilder), + addScriptsRule(options, usingApplicationBuilder), + addDependencies(options, usingApplicationBuilder), ]); }; } - -function usingApplicationBuilder(project: ProjectDefinition) { - const buildBuilder = project.targets.get('build')?.builder; - const isUsingApplicationBuilder = - buildBuilder === Builders.Application || buildBuilder === Builders.BuildApplication; - - return isUsingApplicationBuilder; -} - -// Wrap inquirer in a `prompt` function. -export type Prompt = (message: string, defaultValue: boolean) => Promise; -const defaultPrompter: Prompt = async (message, defaultValue) => { - const { confirm } = await import('@inquirer/prompts'); - - return await confirm({ - message, - default: defaultValue, - }); -}; - -// Allow the prompt functionality to be overridden to facilitate testing. -let prompt = defaultPrompter; -export function setPrompterForTestOnly(prompter?: Prompt): void { - prompt = prompter ?? defaultPrompter; -} - -/** Returns whether or not server routing is enabled, potentially prompting the user if necessary. */ -async function isServerRoutingEnabled( - isUsingApplicationBuilder: boolean, - options: SSROptions, -): Promise { - if (!isUsingApplicationBuilder) { - if (options.serverRouting) { - throw new SchematicsException( - 'Server routing APIs can only be added to a project using `application` builder.', - ); - } else { - return false; - } - } - - // Use explicit option if provided. - if (options.serverRouting !== undefined) { - return options.serverRouting; - } - - const serverRoutingDefault = false; - - // Use the default if not in an interactive terminal. - if (!isTTY()) { - return serverRoutingDefault; - } - - // `inquirer` requires `async_hooks` which isn't supported by webcontainers, therefore we can't prompt in that context. - // See: https://github.com/SBoudrias/Inquirer.js/issues/1426 - if (process.versions.webcontainer) { - return serverRoutingDefault; - } - - // Prompt the user if in an interactive terminal and no option was provided. - return await prompt( - 'Would you like to use the Server Routing and App Engine APIs (Developer Preview) for this server application?', - /* defaultValue */ serverRoutingDefault, - ); -} diff --git a/packages/schematics/angular/ssr/index_spec.ts b/packages/schematics/angular/ssr/index_spec.ts index a7de8d12f208..0330e0f4e623 100644 --- a/packages/schematics/angular/ssr/index_spec.ts +++ b/packages/schematics/angular/ssr/index_spec.ts @@ -10,12 +10,10 @@ import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/te import { join } from 'node:path'; import { Schema as ServerOptions } from './schema'; -import { Prompt, setPrompterForTestOnly } from './index'; describe('SSR Schematic', () => { const defaultOptions: ServerOptions = { project: 'test-app', - serverRouting: false, }; const schematicRunner = new SchematicTestRunner( @@ -32,10 +30,6 @@ describe('SSR Schematic', () => { }; beforeEach(async () => { - setPrompterForTestOnly((message) => { - return fail(`Unmocked prompt: ${message}`) as never; - }); - appTree = await schematicRunner.runExternalSchematic( '@schematics/angular', 'workspace', @@ -163,90 +157,6 @@ describe('SSR Schematic', () => { server: 'node-server', }); }); - - it('generates server routing configuration when enabled', async () => { - const tree = await schematicRunner.runSchematic( - 'ssr', - { ...defaultOptions, serverRouting: true }, - appTree, - ); - - expect(tree.exists('/projects/test-app/src/app/app.routes.server.ts')).toBeTrue(); - }); - - it('does not generate server routing configuration when disabled', async () => { - const tree = await schematicRunner.runSchematic( - 'ssr', - { ...defaultOptions, serverRouting: false }, - appTree, - ); - - expect(tree.exists('/projects/test-app/src/app/app.routes.server.ts')).toBeFalse(); - }); - - it('generates server routing configuration when prompt is accepted by the user', async () => { - const prompter = jasmine.createSpy('prompt').and.resolveTo(true); - setPrompterForTestOnly(prompter); - - process.env['NG_FORCE_TTY'] = 'TRUE'; - const tree = await schematicRunner.runSchematic( - 'ssr', - { ...defaultOptions, serverRouting: undefined }, - appTree, - ); - - expect(prompter).toHaveBeenCalledTimes(1); - - expect(tree.exists('/projects/test-app/src/app/app.routes.server.ts')).toBeTrue(); - }); - - it('does not generate server routing configuration when prompt is rejected by the user', async () => { - const prompter = jasmine.createSpy('prompt').and.resolveTo(false); - setPrompterForTestOnly(prompter); - - process.env['NG_FORCE_TTY'] = 'TRUE'; - const tree = await schematicRunner.runSchematic( - 'ssr', - { ...defaultOptions, serverRouting: undefined }, - appTree, - ); - - expect(prompter).toHaveBeenCalledTimes(1); - - expect(tree.exists('/projects/test-app/src/app/app.routes.server.ts')).toBeFalse(); - }); - - it('defaults to skipping server route generation when not in an interactive terminal', async () => { - const prompter = jasmine.createSpy('prompt').and.resolveTo(false); - setPrompterForTestOnly(prompter); - - process.env['NG_FORCE_TTY'] = 'FALSE'; - const tree = await schematicRunner.runSchematic( - 'ssr', - { ...defaultOptions, serverRouting: undefined }, - appTree, - ); - - expect(prompter).not.toHaveBeenCalled(); - - expect(tree.exists('/projects/test-app/src/app/app.routes.server.ts')).toBeFalse(); - }); - - it('does not prompt when running in a web container', async () => { - const prompter = jasmine.createSpy('prompt').and.resolveTo(false); - setPrompterForTestOnly(prompter); - - process.versions.webcontainer = 'abc123'; // Simulate webcontainer. - const tree = await schematicRunner.runSchematic( - 'ssr', - { ...defaultOptions, serverRouting: undefined }, - appTree, - ); - - expect(prompter).not.toHaveBeenCalled(); - - expect(tree.exists('/projects/test-app/src/app/app.routes.server.ts')).toBeFalse(); - }); }); describe('Legacy browser builder', () => { @@ -313,27 +223,5 @@ describe('SSR Schematic', () => { const content = tree.readContent('/projects/test-app/src/server.ts'); expect(content).toContain(`const distFolder = join(process.cwd(), 'dist/test-app/browser');`); }); - - it('throws an exception when used with `serverRouting`', async () => { - await expectAsync( - schematicRunner.runSchematic('ssr', { ...defaultOptions, serverRouting: true }, appTree), - ).toBeRejectedWithError(/Server routing APIs.*`application` builder/); - }); - - it('automatically disables `serverRouting` and does not prompt for it', async () => { - const prompter = jasmine.createSpy('prompt').and.resolveTo(false); - setPrompterForTestOnly(prompter); - - process.env['NG_FORCE_TTY'] = 'TRUE'; - const tree = await schematicRunner.runSchematic( - 'ssr', - { ...defaultOptions, serverRouting: undefined }, - appTree, - ); - - expect(prompter).not.toHaveBeenCalled(); - - expect(tree.exists('/projects/test-app/src/app/app.routes.server.ts')).toBeFalse(); - }); }); }); diff --git a/packages/schematics/angular/ssr/schema.json b/packages/schematics/angular/ssr/schema.json index 669f1449ad13..aeaf6262908a 100644 --- a/packages/schematics/angular/ssr/schema.json +++ b/packages/schematics/angular/ssr/schema.json @@ -16,10 +16,6 @@ "description": "Skip the automatic installation of packages. You will need to manually install the dependencies later.", "type": "boolean", "default": false - }, - "serverRouting": { - "description": "Configure the server application to use the Angular Server Routing API and App Engine APIs (currently in Developer Preview).", - "type": "boolean" } }, "required": ["project"], diff --git a/packages/schematics/angular/utility/project-targets.ts b/packages/schematics/angular/utility/project-targets.ts index 584922c8af9c..8897a3ddab66 100644 --- a/packages/schematics/angular/utility/project-targets.ts +++ b/packages/schematics/angular/utility/project-targets.ts @@ -7,7 +7,17 @@ */ import { SchematicsException } from '@angular-devkit/schematics'; +import { ProjectDefinition } from './workspace'; +import { Builders } from './workspace-models'; export function targetBuildNotFoundError(): SchematicsException { return new SchematicsException(`Project target "build" not found.`); } + +export function isUsingApplicationBuilder(project: ProjectDefinition): boolean { + const buildBuilder = project.targets.get('build')?.builder; + const isUsingApplicationBuilder = + buildBuilder === Builders.Application || buildBuilder === Builders.BuildApplication; + + return isUsingApplicationBuilder; +} diff --git a/tests/legacy-cli/e2e/tests/build/prerender/discover-routes-ngmodule.ts b/tests/legacy-cli/e2e/tests/build/prerender/discover-routes-ngmodule.ts index 8a4e074f6045..5df9bb73c72a 100644 --- a/tests/legacy-cli/e2e/tests/build/prerender/discover-routes-ngmodule.ts +++ b/tests/legacy-cli/e2e/tests/build/prerender/discover-routes-ngmodule.ts @@ -1,7 +1,7 @@ import { join } from 'node:path'; import { getGlobalVariable } from '../../../utils/env'; -import { expectFileToMatch, rimraf, writeFile } from '../../../utils/fs'; -import { installWorkspacePackages } from '../../../utils/packages'; +import { expectFileToMatch, writeFile } from '../../../utils/fs'; +import { installWorkspacePackages, uninstallPackage } from '../../../utils/packages'; import { ng } from '../../../utils/process'; import { updateJsonFile, useSha } from '../../../utils/project'; @@ -31,29 +31,15 @@ export default async function () { } // Forcibly remove in case another test doesn't clean itself up. - await rimraf('node_modules/@angular/ssr'); - if (useWebpackBuilder) { - await ng( - 'add', - '@angular/ssr', - '--project', - projectName, - '--skip-confirmation', - '--skip-install', - // Server routing is not supported on `browser` builder. - // '--server-routing', - ); - } else { - await ng( - 'add', - '@angular/ssr', - '--project', - projectName, - '--skip-confirmation', - '--skip-install', - '--server-routing', - ); - } + await uninstallPackage('@angular/ssr'); + await ng( + 'add', + '@angular/ssr', + '--project', + projectName, + '--skip-confirmation', + '--skip-install', + ); await useSha(); await installWorkspacePackages(); diff --git a/tests/legacy-cli/e2e/tests/build/prerender/discover-routes-standalone.ts b/tests/legacy-cli/e2e/tests/build/prerender/discover-routes-standalone.ts index 25c63781de11..7deda0fd2584 100644 --- a/tests/legacy-cli/e2e/tests/build/prerender/discover-routes-standalone.ts +++ b/tests/legacy-cli/e2e/tests/build/prerender/discover-routes-standalone.ts @@ -1,7 +1,7 @@ import { join } from 'node:path'; import { getGlobalVariable } from '../../../utils/env'; -import { expectFileToMatch, readFile, rimraf, writeFile } from '../../../utils/fs'; -import { installWorkspacePackages } from '../../../utils/packages'; +import { expectFileToMatch, readFile, writeFile } from '../../../utils/fs'; +import { installWorkspacePackages, uninstallPackage } from '../../../utils/packages'; import { ng } from '../../../utils/process'; import { useSha } from '../../../utils/project'; import { deepStrictEqual } from 'node:assert'; @@ -9,7 +9,7 @@ import { deepStrictEqual } from 'node:assert'; export default async function () { const useWebpackBuilder = !getGlobalVariable('argv')['esbuild']; // Forcibly remove in case another test doesn't clean itself up. - await rimraf('node_modules/@angular/ssr'); + await uninstallPackage('@angular/ssr'); await ng('add', '@angular/ssr', '--skip-confirmation', '--skip-install'); await useSha(); @@ -59,10 +59,6 @@ export default async function () { path: 'lazy-two', loadComponent: () => import('./lazy-two/lazy-two.component').then(c => c.LazyTwoComponent), }, - { - path: ':param', - component: OneComponent, - }, ]; `, ); diff --git a/tests/legacy-cli/e2e/tests/build/prerender/http-requests-assets.ts b/tests/legacy-cli/e2e/tests/build/prerender/http-requests-assets.ts index 81abed0008ba..64f9ca1d52b7 100644 --- a/tests/legacy-cli/e2e/tests/build/prerender/http-requests-assets.ts +++ b/tests/legacy-cli/e2e/tests/build/prerender/http-requests-assets.ts @@ -13,7 +13,7 @@ export default async function () { // Forcibly remove in case another test doesn't clean itself up. await rimraf('node_modules/@angular/ssr'); - await ng('add', '@angular/ssr', '--server-routing', '--skip-confirmation'); + await ng('add', '@angular/ssr', '--skip-confirmation'); await useSha(); await installWorkspacePackages(); diff --git a/tests/legacy-cli/e2e/tests/build/server-rendering/express-engine-csp-nonce.ts b/tests/legacy-cli/e2e/tests/build/server-rendering/express-engine-csp-nonce.ts index 96be34e524da..92d83e029576 100644 --- a/tests/legacy-cli/e2e/tests/build/server-rendering/express-engine-csp-nonce.ts +++ b/tests/legacy-cli/e2e/tests/build/server-rendering/express-engine-csp-nonce.ts @@ -14,7 +14,7 @@ export default async function () { // `--server-routing` not supported in `browser` builder. await ng('add', '@angular/ssr', '--skip-confirmation', '--skip-install'); } else { - await ng('add', '@angular/ssr', '--server-routing', '--skip-confirmation', '--skip-install'); + await ng('add', '@angular/ssr', '--skip-confirmation', '--skip-install'); } await useSha(); diff --git a/tests/legacy-cli/e2e/tests/build/server-rendering/express-engine-standalone.ts b/tests/legacy-cli/e2e/tests/build/server-rendering/express-engine-standalone.ts index b697ac513ab4..10d8e3018b60 100644 --- a/tests/legacy-cli/e2e/tests/build/server-rendering/express-engine-standalone.ts +++ b/tests/legacy-cli/e2e/tests/build/server-rendering/express-engine-standalone.ts @@ -15,7 +15,7 @@ export default async function () { // `--server-routing` not supported in `browser` builder. await ng('add', '@angular/ssr', '--skip-confirmation', '--skip-install'); } else { - await ng('add', '@angular/ssr', '--server-routing', '--skip-confirmation', '--skip-install'); + await ng('add', '@angular/ssr', '--skip-confirmation', '--skip-install'); } if (!useWebpackBuilder) { diff --git a/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-server-external-dependencies.ts b/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-server-external-dependencies.ts index 9d01f375a211..52ceafa7b05f 100644 --- a/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-server-external-dependencies.ts +++ b/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-server-external-dependencies.ts @@ -12,7 +12,7 @@ export default async function () { // Forcibly remove in case another test doesn't clean itself up. await uninstallPackage('@angular/ssr'); - await ng('add', '@angular/ssr', '--server-routing', '--skip-confirmation', '--skip-install'); + await ng('add', '@angular/ssr', '--skip-confirmation', '--skip-install'); await useSha(); await installWorkspacePackages(); diff --git a/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-server-i18n-base-href.ts b/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-server-i18n-base-href.ts index c4c2065f8b64..a35843bcc7ef 100644 --- a/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-server-i18n-base-href.ts +++ b/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-server-i18n-base-href.ts @@ -19,7 +19,7 @@ export default async function () { // Forcibly remove in case another test doesn't clean itself up. await uninstallPackage('@angular/ssr'); - await ng('add', '@angular/ssr', '--server-routing', '--skip-confirmation', '--skip-install'); + await ng('add', '@angular/ssr', '--skip-confirmation', '--skip-install'); await useSha(); await installWorkspacePackages(); diff --git a/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-server-i18n-sub-path.ts b/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-server-i18n-sub-path.ts index 9b7f75f04a87..11d32dd1b7fe 100644 --- a/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-server-i18n-sub-path.ts +++ b/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-server-i18n-sub-path.ts @@ -44,7 +44,7 @@ export default async function () { // Forcibly remove in case another test doesn't clean itself up. await uninstallPackage('@angular/ssr'); - await ng('add', '@angular/ssr', '--server-routing', '--skip-confirmation', '--skip-install'); + await ng('add', '@angular/ssr', '--skip-confirmation', '--skip-install'); await useSha(); await installWorkspacePackages(); diff --git a/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-server-i18n.ts b/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-server-i18n.ts index 0f10a959a9de..c7f68267fc4f 100644 --- a/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-server-i18n.ts +++ b/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-server-i18n.ts @@ -19,7 +19,7 @@ export default async function () { // Forcibly remove in case another test doesn't clean itself up. await uninstallPackage('@angular/ssr'); - await ng('add', '@angular/ssr', '--server-routing', '--skip-confirmation', '--skip-install'); + await ng('add', '@angular/ssr', '--skip-confirmation', '--skip-install'); await useSha(); await installWorkspacePackages(); diff --git a/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-server-platform-neutral.ts b/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-server-platform-neutral.ts index 85b6891fa28c..b8f30483c303 100644 --- a/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-server-platform-neutral.ts +++ b/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-server-platform-neutral.ts @@ -21,7 +21,7 @@ export default async function () { // Forcibly remove in case another test doesn't clean itself up. await uninstallPackage('@angular/ssr'); - await ng('add', '@angular/ssr', '--server-routing', '--skip-confirmation', '--skip-install'); + await ng('add', '@angular/ssr', '--skip-confirmation', '--skip-install'); await useSha(); await installWorkspacePackages(); await installPackage('h3@1'); diff --git a/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-server.ts b/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-server.ts index 891b646bfc38..9e79e83ecba2 100644 --- a/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-server.ts +++ b/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-server.ts @@ -16,7 +16,7 @@ export default async function () { // Forcibly remove in case another test doesn't clean itself up. await uninstallPackage('@angular/ssr'); - await ng('add', '@angular/ssr', '--server-routing', '--skip-confirmation', '--skip-install'); + await ng('add', '@angular/ssr', '--skip-confirmation', '--skip-install'); await useSha(); await installWorkspacePackages(); diff --git a/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-static-http-calls.ts b/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-static-http-calls.ts index 734f15e666e3..d06c3f707463 100644 --- a/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-static-http-calls.ts +++ b/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-static-http-calls.ts @@ -13,7 +13,7 @@ export default async function () { // Forcibly remove in case another test doesn't clean itself up. await uninstallPackage('@angular/ssr'); - await ng('add', '@angular/ssr', '--server-routing', '--skip-confirmation', '--skip-install'); + await ng('add', '@angular/ssr', '--skip-confirmation', '--skip-install'); await useSha(); await installWorkspacePackages(); diff --git a/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-static-i18n_APP_BASE_HREF.ts b/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-static-i18n_APP_BASE_HREF.ts index 1d5d7847fca6..10898aae5338 100644 --- a/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-static-i18n_APP_BASE_HREF.ts +++ b/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-static-i18n_APP_BASE_HREF.ts @@ -19,7 +19,7 @@ export default async function () { // Forcibly remove in case another test doesn't clean itself up. await uninstallPackage('@angular/ssr'); - await ng('add', '@angular/ssr', '--server-routing', '--skip-confirmation', '--skip-install'); + await ng('add', '@angular/ssr', '--skip-confirmation', '--skip-install'); await useSha(); await installWorkspacePackages(); diff --git a/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-static.ts b/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-static.ts index 4fe4c249803d..434530aedce2 100644 --- a/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-static.ts +++ b/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-static.ts @@ -21,7 +21,7 @@ export default async function () { // Forcibly remove in case another test doesn't clean itself up. await uninstallPackage('@angular/ssr'); - await ng('add', '@angular/ssr', '--server-routing', '--skip-confirmation', '--skip-install'); + await ng('add', '@angular/ssr', '--skip-confirmation', '--skip-install'); await useSha(); await installWorkspacePackages(); diff --git a/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-preload-links.ts b/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-preload-links.ts index 92c154db3891..e0cfd9357517 100644 --- a/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-preload-links.ts +++ b/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-preload-links.ts @@ -14,7 +14,7 @@ export default async function () { // Forcibly remove in case another test doesn't clean itself up. await uninstallPackage('@angular/ssr'); - await ng('add', '@angular/ssr', '--server-routing', '--skip-confirmation', '--skip-install'); + await ng('add', '@angular/ssr', '--skip-confirmation', '--skip-install'); await useSha(); await installWorkspacePackages(); diff --git a/tests/legacy-cli/e2e/tests/commands/serve/ssr-http-requests-assets.ts b/tests/legacy-cli/e2e/tests/commands/serve/ssr-http-requests-assets.ts index 972c35be4452..59c8ec9c7042 100644 --- a/tests/legacy-cli/e2e/tests/commands/serve/ssr-http-requests-assets.ts +++ b/tests/legacy-cli/e2e/tests/commands/serve/ssr-http-requests-assets.ts @@ -15,7 +15,7 @@ export default async function () { // `--server-routing` not supported in `browser` builder. await ng('add', '@angular/ssr', '--skip-confirmation', '--skip-install'); } else { - await ng('add', '@angular/ssr', '--server-routing', '--skip-confirmation', '--skip-install'); + await ng('add', '@angular/ssr', '--skip-confirmation', '--skip-install'); } await useSha(); diff --git a/tests/legacy-cli/e2e/tests/vite/ssr-default.ts b/tests/legacy-cli/e2e/tests/vite/ssr-default.ts index cfaece9551ef..8b64a4b30f67 100644 --- a/tests/legacy-cli/e2e/tests/vite/ssr-default.ts +++ b/tests/legacy-cli/e2e/tests/vite/ssr-default.ts @@ -16,7 +16,7 @@ export default async function () { // Forcibly remove in case another test doesn't clean itself up. await uninstallPackage('@angular/ssr'); - await ng('add', '@angular/ssr', '--server-routing', '--skip-confirmation', '--skip-install'); + await ng('add', '@angular/ssr', '--skip-confirmation', '--skip-install'); await useSha(); await installWorkspacePackages(); diff --git a/tests/legacy-cli/e2e/tests/vite/ssr-entry-express.ts b/tests/legacy-cli/e2e/tests/vite/ssr-entry-express.ts index d5e045abef03..387d14ed67f6 100644 --- a/tests/legacy-cli/e2e/tests/vite/ssr-entry-express.ts +++ b/tests/legacy-cli/e2e/tests/vite/ssr-entry-express.ts @@ -14,7 +14,7 @@ export default async function () { // Forcibly remove in case another test doesn't clean itself up. await uninstallPackage('@angular/ssr'); - await ng('add', '@angular/ssr', '--server-routing', '--skip-confirmation', '--skip-install'); + await ng('add', '@angular/ssr', '--skip-confirmation', '--skip-install'); await useSha(); await installWorkspacePackages(); diff --git a/tests/legacy-cli/e2e/tests/vite/ssr-entry-fastify.ts b/tests/legacy-cli/e2e/tests/vite/ssr-entry-fastify.ts index 0dceea441dda..851ccfef87f7 100644 --- a/tests/legacy-cli/e2e/tests/vite/ssr-entry-fastify.ts +++ b/tests/legacy-cli/e2e/tests/vite/ssr-entry-fastify.ts @@ -14,7 +14,7 @@ export default async function () { // Forcibly remove in case another test doesn't clean itself up. await uninstallPackage('@angular/ssr'); - await ng('add', '@angular/ssr', '--server-routing', '--skip-confirmation', '--skip-install'); + await ng('add', '@angular/ssr', '--skip-confirmation', '--skip-install'); await useSha(); await installWorkspacePackages(); await installPackage('fastify@5'); diff --git a/tests/legacy-cli/e2e/tests/vite/ssr-entry-h3.ts b/tests/legacy-cli/e2e/tests/vite/ssr-entry-h3.ts index 5d2898370405..81950a061979 100644 --- a/tests/legacy-cli/e2e/tests/vite/ssr-entry-h3.ts +++ b/tests/legacy-cli/e2e/tests/vite/ssr-entry-h3.ts @@ -14,7 +14,7 @@ export default async function () { // Forcibly remove in case another test doesn't clean itself up. await uninstallPackage('@angular/ssr'); - await ng('add', '@angular/ssr', '--server-routing', '--skip-confirmation', '--skip-install'); + await ng('add', '@angular/ssr', '--skip-confirmation', '--skip-install'); await useSha(); await installWorkspacePackages(); await installPackage('h3@1'); diff --git a/tests/legacy-cli/e2e/tests/vite/ssr-entry-hono.ts b/tests/legacy-cli/e2e/tests/vite/ssr-entry-hono.ts index a71557a43948..2d65f0e7b9ff 100644 --- a/tests/legacy-cli/e2e/tests/vite/ssr-entry-hono.ts +++ b/tests/legacy-cli/e2e/tests/vite/ssr-entry-hono.ts @@ -14,7 +14,7 @@ export default async function () { // Forcibly remove in case another test doesn't clean itself up. await uninstallPackage('@angular/ssr'); - await ng('add', '@angular/ssr', '--server-routing', '--skip-confirmation', '--skip-install'); + await ng('add', '@angular/ssr', '--skip-confirmation', '--skip-install'); await useSha(); await installWorkspacePackages(); await installPackage('hono@4'); diff --git a/tests/legacy-cli/e2e/tests/vite/ssr-error-stack.ts b/tests/legacy-cli/e2e/tests/vite/ssr-error-stack.ts index 7061e881fdff..6731c15b67cc 100644 --- a/tests/legacy-cli/e2e/tests/vite/ssr-error-stack.ts +++ b/tests/legacy-cli/e2e/tests/vite/ssr-error-stack.ts @@ -14,7 +14,7 @@ export default async function () { // `--server-routing` not supported in `browser` builder. await ng('add', '@angular/ssr', '--skip-confirmation', '--skip-install'); } else { - await ng('add', '@angular/ssr', '--server-routing', '--skip-confirmation', '--skip-install'); + await ng('add', '@angular/ssr', '--skip-confirmation', '--skip-install'); } await useSha(); diff --git a/tests/legacy-cli/e2e/tests/vite/ssr-new-dep-optimization.ts b/tests/legacy-cli/e2e/tests/vite/ssr-new-dep-optimization.ts index be814b01bf89..d7b8a63813eb 100644 --- a/tests/legacy-cli/e2e/tests/vite/ssr-new-dep-optimization.ts +++ b/tests/legacy-cli/e2e/tests/vite/ssr-new-dep-optimization.ts @@ -22,7 +22,7 @@ export default async function () { // Forcibly remove in case another test doesn't clean itself up. await uninstallPackage('@angular/ssr'); - await ng('add', '@angular/ssr', '--server-routing', '--skip-confirmation', '--skip-install'); + await ng('add', '@angular/ssr', '--skip-confirmation', '--skip-install'); await useSha(); await installWorkspacePackages(); diff --git a/tests/legacy-cli/e2e/tests/vite/ssr-no-server-entry-sub-path.ts b/tests/legacy-cli/e2e/tests/vite/ssr-no-server-entry-sub-path.ts deleted file mode 100644 index a4d4ac2cfc61..000000000000 --- a/tests/legacy-cli/e2e/tests/vite/ssr-no-server-entry-sub-path.ts +++ /dev/null @@ -1,54 +0,0 @@ -import assert from 'node:assert'; -import { - execAndWaitForOutputToMatch, - ng, - silentNg, - waitForAnyProcessOutputToMatch, -} from '../../utils/process'; -import { installWorkspacePackages, uninstallPackage } from '../../utils/packages'; -import { useSha } from '../../utils/project'; -import { getGlobalVariable } from '../../utils/env'; -import { findFreePort } from '../../utils/network'; -import { writeFile } from '../../utils/fs'; - -export default async function () { - assert( - getGlobalVariable('argv')['esbuild'], - 'This test should not be called in the Webpack suite.', - ); - - // Forcibly remove in case another test doesn't clean itself up. - await uninstallPackage('@angular/ssr'); - await ng('add', '@angular/ssr', '--no-server-routing', '--skip-confirmation', '--skip-install'); - await useSha(); - await installWorkspacePackages(); - - await silentNg('generate', 'component', 'home'); - await writeFile( - 'src/app/app.routes.ts', - ` - import { Routes } from '@angular/router'; - import {HomeComponent} from './home/home.component'; - - export const routes: Routes = [{ - path: 'sub/home', - component: HomeComponent - }]; - `, - ); - - const port = await findFreePort(); - await execAndWaitForOutputToMatch('ng', ['serve', '--port', `${port}`], /complete/, { - ...process.env, - NO_COLOR: 'true', - }); - - const [, response] = await Promise.all([ - assert.rejects( - waitForAnyProcessOutputToMatch(/Pre-transform error: Failed to load url/, 8_000), - ), - fetch(`http://localhost:${port}/sub/home`), - ]); - - assert(response.ok, `Expected 'response.ok' to be 'true'.`); -} diff --git a/tests/legacy-cli/e2e/tests/vite/ssr-with-ssl.ts b/tests/legacy-cli/e2e/tests/vite/ssr-with-ssl.ts index 5e0a74638527..d40c9853951f 100644 --- a/tests/legacy-cli/e2e/tests/vite/ssr-with-ssl.ts +++ b/tests/legacy-cli/e2e/tests/vite/ssr-with-ssl.ts @@ -13,7 +13,7 @@ export default async function () { // Forcibly remove in case another test doesn't clean itself up. await uninstallPackage('@angular/ssr'); - await ng('add', '@angular/ssr', '--server-routing', '--skip-confirmation', '--skip-install'); + await ng('add', '@angular/ssr', '--skip-confirmation', '--skip-install'); await useSha(); await installWorkspacePackages();