fix(@angular/ssr): rename provideServerRoutesConfig to provideServerRouting

This commit renames `provideServerRoutesConfig` to `provideServerRouting` and updates the second parameter to use the `ServerRoutes` features.

This change improves alignment with the framework's API conventions and the way features are integrated.

### Example Usage:
Before:
```typescript
provideServerRoutesConfig(serverRoutes, { appShellRoute: 'shell' })
```

After:
```typescript
provideServerRouting(serverRoutes, withAppShell(AppShellComponent))
```
This commit is contained in:
Alan Agius 2025-01-27 09:54:52 +00:00 committed by Alan Agius
parent 6f9a764526
commit ec05c814ee
8 changed files with 203 additions and 195 deletions

View File

@ -4,7 +4,10 @@
```ts ```ts
import { DefaultExport } from '@angular/router';
import { EnvironmentProviders } from '@angular/core'; import { EnvironmentProviders } from '@angular/core';
import { Provider } from '@angular/core';
import { Type } from '@angular/core';
// @public // @public
export class AngularAppEngine { export class AngularAppEngine {
@ -23,9 +26,12 @@ export enum PrerenderFallback {
Server = 0 Server = 0
} }
// @public // @public @deprecated
export function provideServerRoutesConfig(routes: ServerRoute[], options?: ServerRoutesConfigOptions): EnvironmentProviders; export function provideServerRoutesConfig(routes: ServerRoute[], options?: ServerRoutesConfigOptions): EnvironmentProviders;
// @public
export function provideServerRouting(routes: ServerRoute[], ...features: ServerRoutesFeature<ServerRoutesFeatureKind>[]): EnvironmentProviders;
// @public // @public
export enum RenderMode { export enum RenderMode {
Client = 1, Client = 1,
@ -63,7 +69,7 @@ export interface ServerRoutePrerenderWithParams extends Omit<ServerRoutePrerende
getPrerenderParams: () => Promise<Record<string, string>[]>; getPrerenderParams: () => Promise<Record<string, string>[]>;
} }
// @public // @public @deprecated
export interface ServerRoutesConfigOptions { export interface ServerRoutesConfigOptions {
appShellRoute?: string; appShellRoute?: string;
} }
@ -73,6 +79,9 @@ export interface ServerRouteServer extends ServerRouteCommon {
renderMode: RenderMode.Server; renderMode: RenderMode.Server;
} }
// @public
export function withAppShell(component: Type<unknown> | (() => Promise<Type<unknown> | DefaultExport<Type<unknown>>>)): ServerRoutesFeature<ServerRoutesFeatureKind.AppShell>;
// (No @packageDocumentation comment for this package) // (No @packageDocumentation comment for this package)
``` ```

View File

@ -16,6 +16,8 @@ export {
type ServerRoute, type ServerRoute,
type ServerRoutesConfigOptions, type ServerRoutesConfigOptions,
provideServerRoutesConfig, provideServerRoutesConfig,
provideServerRouting,
withAppShell,
RenderMode, RenderMode,
type ServerRouteClient, type ServerRouteClient,
type ServerRoutePrerender, type ServerRoutePrerender,

View File

@ -6,11 +6,43 @@
* found in the LICENSE file at https://angular.dev/license * found in the LICENSE file at https://angular.dev/license
*/ */
import { EnvironmentProviders, InjectionToken, makeEnvironmentProviders } from '@angular/core'; import {
EnvironmentProviders,
InjectionToken,
Provider,
Type,
makeEnvironmentProviders,
} from '@angular/core';
import { type DefaultExport, ROUTES, type Route } from '@angular/router';
/**
* The internal path used for the app shell route.
* @internal
*/
const APP_SHELL_ROUTE = 'ng-app-shell';
/**
* Identifies a particular kind of `ServerRoutesFeatureKind`.
* @see {@link ServerRoutesFeature}
* @developerPreview
*/
enum ServerRoutesFeatureKind {
AppShell,
}
/**
* Helper type to represent a server routes feature.
* @see {@link ServerRoutesFeatureKind}
* @developerPreview
*/
interface ServerRoutesFeature<FeatureKind extends ServerRoutesFeatureKind> {
ɵkind: FeatureKind;
ɵproviders: Provider[];
}
/** /**
* Different rendering modes for server routes. * Different rendering modes for server routes.
* @see {@link provideServerRoutesConfig} * @see {@link provideServerRouting}
* @see {@link ServerRoute} * @see {@link ServerRoute}
* @developerPreview * @developerPreview
*/ */
@ -148,7 +180,7 @@ export interface ServerRouteServer extends ServerRouteCommon {
/** /**
* Server route configuration. * Server route configuration.
* @see {@link provideServerRoutesConfig} * @see {@link provideServerRouting}
* @developerPreview * @developerPreview
*/ */
export type ServerRoute = export type ServerRoute =
@ -163,8 +195,9 @@ export type ServerRoute =
* This interface defines the optional settings available for configuring server routes * This interface defines the optional settings available for configuring server routes
* in the server-side environment, such as specifying a path to the app shell route. * in the server-side environment, such as specifying a path to the app shell route.
* *
* @see {@link provideServerRoutesConfig} *
* @developerPreview * @see {@link provideServerRouting}
* @deprecated use `provideServerRouting`. This will be removed in version 20.
*/ */
export interface ServerRoutesConfigOptions { export interface ServerRoutesConfigOptions {
@ -172,8 +205,6 @@ export interface ServerRoutesConfigOptions {
* Defines the route to be used as the app shell, which serves as the main entry * Defines the route to be used as the app shell, which serves as the main entry
* point for the application. This route is often used to enable server-side rendering * point for the application. This route is often used to enable server-side rendering
* of the application shell for requests that do not match any specific server route. * of the application shell for requests that do not match any specific server route.
*
* @see {@link https://angular.dev/ecosystem/service-workers/app-shell | App shell pattern on Angular.dev}
*/ */
appShellRoute?: string; appShellRoute?: string;
} }
@ -182,7 +213,13 @@ export interface ServerRoutesConfigOptions {
* Configuration value for server routes configuration. * Configuration value for server routes configuration.
* @internal * @internal
*/ */
export interface ServerRoutesConfig extends ServerRoutesConfigOptions { export interface ServerRoutesConfig {
/**
* Defines the route to be used as the app shell.
*/
appShellRoute?: string;
/** List of server routes for the application. */
routes: ServerRoute[]; routes: ServerRoute[];
} }
@ -204,6 +241,8 @@ export const SERVER_ROUTES_CONFIG = new InjectionToken<ServerRoutesConfig>('SERV
* *
* @see {@link ServerRoute} * @see {@link ServerRoute}
* @see {@link ServerRoutesConfigOptions} * @see {@link ServerRoutesConfigOptions}
* @see {@link provideServerRouting}
* @deprecated use `provideServerRouting`. This will be removed in version 20.
* @developerPreview * @developerPreview
*/ */
export function provideServerRoutesConfig( export function provideServerRoutesConfig(
@ -223,3 +262,83 @@ export function provideServerRoutesConfig(
}, },
]); ]);
} }
/**
* Sets up the necessary providers for configuring server routes.
* This function accepts an array of server routes and optional configuration
* options, returning an `EnvironmentProviders` object that encapsulates
* the server routes and configuration settings.
*
* @param routes - An array of server routes to be provided.
* @param features - (Optional) server routes features.
* @returns An `EnvironmentProviders` instance with the server routes configuration.
*
* @see {@link ServerRoute}
* @see {@link withAppShell}
* @developerPreview
*/
export function provideServerRouting(
routes: ServerRoute[],
...features: ServerRoutesFeature<ServerRoutesFeatureKind>[]
): EnvironmentProviders {
const config: ServerRoutesConfig = { routes };
const hasAppShell = features.some((f) => f.ɵkind === ServerRoutesFeatureKind.AppShell);
if (hasAppShell) {
config.appShellRoute = APP_SHELL_ROUTE;
}
const providers: Provider[] = [
{
provide: SERVER_ROUTES_CONFIG,
useValue: config,
},
];
for (const feature of features) {
providers.push(...feature.ɵproviders);
}
return makeEnvironmentProviders(providers);
}
/**
* Configures the app shell route with the provided component.
*
* The app shell serves as the main entry point for the application and is commonly used
* to enable server-side rendering (SSR) of the application shell. It handles requests
* that do not match any specific server route, providing a fallback mechanism and improving
* perceived performance during navigation.
*
* This configuration is particularly useful in applications leveraging Progressive Web App (PWA)
* patterns, such as service workers, to deliver a seamless user experience.
*
* @param component The Angular component to render for the app shell route.
* @returns A server routes feature configuration for the app shell.
*
* @see {@link provideServerRouting}
* @see {@link https://angular.dev/ecosystem/service-workers/app-shell | App shell pattern on Angular.dev}
*/
export function withAppShell(
component: Type<unknown> | (() => Promise<Type<unknown> | DefaultExport<Type<unknown>>>),
): ServerRoutesFeature<ServerRoutesFeatureKind.AppShell> {
const routeConfig: Route = {
path: APP_SHELL_ROUTE,
};
if ('ɵcmp' in component) {
routeConfig.component = component as Type<unknown>;
} else {
routeConfig.loadComponent = component as () => Promise<Type<unknown>>;
}
return {
ɵkind: ServerRoutesFeatureKind.AppShell,
ɵproviders: [
{
provide: ROUTES,
useValue: routeConfig,
multi: true,
},
],
};
}

View File

@ -18,7 +18,7 @@ import { provideServerRendering } from '@angular/platform-server';
import { RouterOutlet, Routes, provideRouter } from '@angular/router'; import { RouterOutlet, Routes, provideRouter } from '@angular/router';
import { destroyAngularServerApp } from '../src/app'; import { destroyAngularServerApp } from '../src/app';
import { ServerAsset, setAngularAppManifest } from '../src/manifest'; import { ServerAsset, setAngularAppManifest } from '../src/manifest';
import { ServerRoute, provideServerRoutesConfig } from '../src/routes/route-config'; import { ServerRoute, provideServerRouting } from '../src/routes/route-config';
@Component({ @Component({
standalone: true, standalone: true,
@ -97,7 +97,7 @@ export function setAngularAppTestingManifest(
provideServerRendering(), provideServerRendering(),
provideExperimentalZonelessChangeDetection(), provideExperimentalZonelessChangeDetection(),
provideRouter(routes), provideRouter(routes),
provideServerRoutesConfig(serverRoutes), provideServerRouting(serverRoutes),
...extraProviders, ...extraProviders,
], ],
}); });

View File

@ -17,7 +17,6 @@ import {
import { dirname, join } from 'node:path/posix'; import { dirname, join } from 'node:path/posix';
import ts from '../third_party/github.com/Microsoft/TypeScript/lib/typescript'; import ts from '../third_party/github.com/Microsoft/TypeScript/lib/typescript';
import { import {
addImportToModule,
addSymbolToNgModuleMetadata, addSymbolToNgModuleMetadata,
findNode, findNode,
findNodes, findNodes,
@ -140,19 +139,6 @@ function validateProject(mainPath: string): Rule {
}; };
} }
function addRouterModule(mainPath: string): Rule {
return (host: Tree) => {
const modulePath = getAppModulePath(host, mainPath);
const moduleSource = getSourceFile(host, modulePath);
const changes = addImportToModule(moduleSource, modulePath, 'RouterModule', '@angular/router');
const recorder = host.beginUpdate(modulePath);
applyToUpdateRecorder(recorder, changes);
host.commitUpdate(recorder);
return host;
};
}
function getMetadataProperty(metadata: ts.Node, propertyName: string): ts.PropertyAssignment { function getMetadataProperty(metadata: ts.Node, propertyName: string): ts.PropertyAssignment {
const properties = (metadata as ts.ObjectLiteralExpression).properties; const properties = (metadata as ts.ObjectLiteralExpression).properties;
const property = properties.filter(ts.isPropertyAssignment).filter((prop) => { const property = properties.filter(ts.isPropertyAssignment).filter((prop) => {
@ -265,7 +251,7 @@ function addStandaloneServerRoute(options: AppShellOptions): Rule {
throw new SchematicsException(`Cannot find "${configFilePath}".`); throw new SchematicsException(`Cannot find "${configFilePath}".`);
} }
let recorder = host.beginUpdate(configFilePath); const recorder = host.beginUpdate(configFilePath);
let configSourceFile = getSourceFile(host, configFilePath); let configSourceFile = getSourceFile(host, configFilePath);
if (!isImported(configSourceFile, 'ROUTES', '@angular/router')) { if (!isImported(configSourceFile, 'ROUTES', '@angular/router')) {
const routesChange = insertImport( const routesChange = insertImport(
@ -306,38 +292,19 @@ function addStandaloneServerRoute(options: AppShellOptions): Rule {
recorder.insertRight(providersLiteral.getStart(), `[\n${updatedProvidersString.join(',\n')}]`); recorder.insertRight(providersLiteral.getStart(), `[\n${updatedProvidersString.join(',\n')}]`);
if (options.serverRouting) { applyToUpdateRecorder(recorder, [
host.commitUpdate(recorder); insertImport(
configSourceFile = getSourceFile(host, configFilePath);
const functionCall = findNodes(configSourceFile, ts.isCallExpression).find(
(n) =>
ts.isIdentifier(n.expression) && n.expression.getText() === 'provideServerRoutesConfig',
);
if (!functionCall) {
throw new SchematicsException(
`Cannot find the "provideServerRoutesConfig" function call in "${configFilePath}".`,
);
}
recorder = host.beginUpdate(configFilePath);
recorder.insertLeft(functionCall.end - 1, `, { appShellRoute: '${APP_SHELL_ROUTE}' }`);
}
// Add AppShellComponent import
const appShellImportChange = insertImport(
configSourceFile, configSourceFile,
configFilePath, configFilePath,
'AppShellComponent', 'AppShellComponent',
'./app-shell/app-shell.component', './app-shell/app-shell.component',
); ),
]);
applyToUpdateRecorder(recorder, [appShellImportChange]);
host.commitUpdate(recorder); host.commitUpdate(recorder);
}; };
} }
function addServerRoutingConfig(options: AppShellOptions): Rule { function addServerRoutingConfig(options: AppShellOptions, isStandalone: boolean): Rule {
return async (host: Tree) => { return async (host: Tree) => {
const workspace = await getWorkspace(host); const workspace = await getWorkspace(host);
const project = workspace.projects.get(options.project); const project = workspace.projects.get(options.project);
@ -345,39 +312,43 @@ function addServerRoutingConfig(options: AppShellOptions): Rule {
throw new SchematicsException(`Project name "${options.project}" doesn't not exist.`); throw new SchematicsException(`Project name "${options.project}" doesn't not exist.`);
} }
const configFilePath = join(project.sourceRoot ?? 'src', 'app/app.routes.server.ts'); const configFilePath = isStandalone
if (!host.exists(configFilePath)) { ? join(project.sourceRoot ?? 'src', 'app/app.config.server.ts')
: getServerModulePath(host, project.sourceRoot || 'src', 'main.server.ts');
if (!configFilePath || !host.exists(configFilePath)) {
throw new SchematicsException(`Cannot find "${configFilePath}".`); throw new SchematicsException(`Cannot find "${configFilePath}".`);
} }
const sourceFile = getSourceFile(host, configFilePath); let recorder = host.beginUpdate(configFilePath);
const nodes = getSourceNodes(sourceFile); const configSourceFile = getSourceFile(host, configFilePath);
const functionCall = findNodes(
configSourceFile,
ts.isCallExpression,
/** max */ undefined,
/** recursive */ true,
).find(
(n) => ts.isIdentifier(n.expression) && n.expression.getText() === 'provideServerRouting',
);
// Find the serverRoutes variable declaration if (!functionCall) {
const serverRoutesNode = nodes.find(
(node) =>
ts.isVariableDeclaration(node) &&
node.initializer &&
ts.isArrayLiteralExpression(node.initializer) &&
node.type &&
ts.isArrayTypeNode(node.type) &&
node.type.getText().includes('ServerRoute'),
) as ts.VariableDeclaration | undefined;
if (!serverRoutesNode) {
throw new SchematicsException( throw new SchematicsException(
`Cannot find the "ServerRoute" configuration in "${configFilePath}".`, `Cannot find the "provideServerRouting" function call in "${configFilePath}".`,
); );
} }
const recorder = host.beginUpdate(configFilePath);
const arrayLiteral = serverRoutesNode.initializer as ts.ArrayLiteralExpression; recorder = host.beginUpdate(configFilePath);
const firstElementPosition = recorder.insertLeft(functionCall.end - 1, `, withAppShell(AppShellComponent)`);
arrayLiteral.elements[0]?.getStart() ?? arrayLiteral.getStart() + 1;
const newRouteString = `{ applyToUpdateRecorder(recorder, [
path: '${APP_SHELL_ROUTE}', insertImport(configSourceFile, configFilePath, 'withAppShell', '@angular/ssr'),
renderMode: RenderMode.AppShell insertImport(
},\n`; configSourceFile,
recorder.insertLeft(firstElementPosition, newRouteString); configFilePath,
'AppShellComponent',
'./app-shell/app-shell.component',
),
]);
host.commitUpdate(recorder); host.commitUpdate(recorder);
}; };
@ -391,10 +362,14 @@ export default function (options: AppShellOptions): Rule {
return chain([ return chain([
validateProject(browserEntryPoint), validateProject(browserEntryPoint),
schematic('server', options), schematic('server', options),
...(isStandalone ...(options.serverRouting
? [noop()]
: isStandalone
? [addStandaloneServerRoute(options)] ? [addStandaloneServerRoute(options)]
: [addRouterModule(browserEntryPoint), addServerRoutes(options)]), : [addServerRoutes(options)]),
options.serverRouting ? noop() : addAppShellConfigToWorkspace(options), options.serverRouting
? addServerRoutingConfig(options, isStandalone)
: addAppShellConfigToWorkspace(options),
schematic('component', { schematic('component', {
name: 'app-shell', name: 'app-shell',
module: 'app.module.server.ts', module: 'app.module.server.ts',

View File

@ -69,13 +69,6 @@ describe('App Shell Schematic', () => {
expect(tree.exists(filePath)).toEqual(true); expect(tree.exists(filePath)).toEqual(true);
}); });
it('should add router module to client app module', async () => {
const tree = await schematicRunner.runSchematic('app-shell', defaultOptions, appTree);
const filePath = '/projects/bar/src/app/app.module.ts';
const content = tree.readContent(filePath);
expect(content).toMatch(/import { RouterModule } from '@angular\/router';/);
});
it('should not fail when AppModule have imported RouterModule already', async () => { it('should not fail when AppModule have imported RouterModule already', async () => {
const updateRecorder = appTree.beginUpdate('/projects/bar/src/app/app.module.ts'); const updateRecorder = appTree.beginUpdate('/projects/bar/src/app/app.module.ts');
updateRecorder.insertLeft(0, "import { RouterModule } from '@angular/router';"); updateRecorder.insertLeft(0, "import { RouterModule } from '@angular/router';");
@ -87,81 +80,12 @@ describe('App Shell Schematic', () => {
expect(content).toMatch(/import { RouterModule } from '@angular\/router';/); expect(content).toMatch(/import { RouterModule } from '@angular\/router';/);
}); });
describe('Add router-outlet', () => {
function makeInlineTemplate(tree: UnitTestTree, template?: string): void {
template =
template ||
`
<p>
App works!
</p>`;
const newText = `
import { Component } from '@angular/core';
@Component({
selector: ''
template: \`
${template}
\`,
styleUrls: ['./app.component.css']
})
export class AppComponent { }
`;
tree.overwrite('/projects/bar/src/app/app.component.ts', newText);
tree.delete('/projects/bar/src/app/app.component.html');
}
it('should not re-add the router outlet (external template)', async () => {
const htmlPath = '/projects/bar/src/app/app.component.html';
appTree.overwrite(htmlPath, '<router-outlet></router-outlet>');
const tree = await schematicRunner.runSchematic('app-shell', defaultOptions, appTree);
const content = tree.readContent(htmlPath);
const matches = content.match(/<router-outlet><\/router-outlet>/g);
const numMatches = matches ? matches.length : 0;
expect(numMatches).toEqual(1);
});
it('should not re-add the router outlet (inline template)', async () => {
makeInlineTemplate(appTree, '<router-outlet></router-outlet>');
const tree = await schematicRunner.runSchematic('app-shell', defaultOptions, appTree);
const content = tree.readContent('/projects/bar/src/app/app.component.ts');
const matches = content.match(/<router-outlet><\/router-outlet>/g);
const numMatches = matches ? matches.length : 0;
expect(numMatches).toEqual(1);
});
});
it('should add router imports to server module', async () => {
const tree = await schematicRunner.runSchematic('app-shell', defaultOptions, appTree);
const filePath = '/projects/bar/src/app/app.module.server.ts';
const content = tree.readContent(filePath);
expect(content).toMatch(/import { Routes, RouterModule } from '@angular\/router';/);
});
it('should work if server config was added prior to running the app-shell schematic', async () => { it('should work if server config was added prior to running the app-shell schematic', async () => {
let tree = await schematicRunner.runSchematic('server', defaultOptions, appTree); let tree = await schematicRunner.runSchematic('server', defaultOptions, appTree);
tree = await schematicRunner.runSchematic('app-shell', defaultOptions, tree); tree = await schematicRunner.runSchematic('app-shell', defaultOptions, tree);
expect(tree.exists('/projects/bar/src/app/app-shell/app-shell.component.ts')).toBe(true); expect(tree.exists('/projects/bar/src/app/app-shell/app-shell.component.ts')).toBe(true);
}); });
it('should define a server route', async () => {
const tree = await schematicRunner.runSchematic('app-shell', defaultOptions, appTree);
const filePath = '/projects/bar/src/app/app.module.server.ts';
const content = tree.readContent(filePath);
expect(content).toMatch(/const routes: Routes = \[/);
});
it('should import RouterModule with forRoot', async () => {
const tree = await schematicRunner.runSchematic('app-shell', defaultOptions, appTree);
const filePath = '/projects/bar/src/app/app.module.server.ts';
const content = tree.readContent(filePath);
expect(content).toMatch(
/const routes: Routes = \[ { path: 'shell', component: AppShellComponent }\];/,
);
expect(content).toContain(`ServerModule, RouterModule.forRoot(routes)]`);
});
it('should create the shell component', async () => { it('should create the shell component', async () => {
const tree = await schematicRunner.runSchematic('app-shell', defaultOptions, appTree); const tree = await schematicRunner.runSchematic('app-shell', defaultOptions, appTree);
expect(tree.exists('/projects/bar/src/app/app-shell/app-shell.component.ts')).toBe(true); expect(tree.exists('/projects/bar/src/app/app-shell/app-shell.component.ts')).toBe(true);
@ -200,35 +124,14 @@ describe('App Shell Schematic', () => {
expect(content).toMatch(/app-shell\.component/); expect(content).toMatch(/app-shell\.component/);
}); });
it(`should update the 'provideServerRoutesConfig' call to include 'appShellRoute`, async () => { it(`should update the 'provideServerRouting' call to include 'withAppShell'`, async () => {
const tree = await schematicRunner.runSchematic('app-shell', defaultOptions, appTree); const tree = await schematicRunner.runSchematic('app-shell', defaultOptions, appTree);
const content = tree.readContent('/projects/bar/src/app/app.config.server.ts'); const content = tree.readContent('/projects/bar/src/app/app.config.server.ts');
expect(tags.oneLine`${content}`).toContain( expect(tags.oneLine`${content}`).toContain(
tags.oneLine`provideServerRoutesConfig(serverRoutes, { appShellRoute: 'shell' })`, tags.oneLine`provideServerRouting(serverRoutes, withAppShell(AppShellComponent))`,
); );
}); });
it('should define a server route', async () => {
const tree = await schematicRunner.runSchematic('app-shell', defaultOptions, appTree);
const filePath = '/projects/bar/src/app/app.config.server.ts';
const content = tree.readContent(filePath);
expect(tags.oneLine`${content}`).toContain(tags.oneLine`{
provide: ROUTES,
multi: true,
useValue: [{
path: 'shell',
component: AppShellComponent
}]
}`);
});
it(`should add import to 'ROUTES' token from '@angular/router'`, async () => {
const tree = await schematicRunner.runSchematic('app-shell', defaultOptions, appTree);
const filePath = '/projects/bar/src/app/app.config.server.ts';
const content = tree.readContent(filePath);
expect(content).toContain(`import { ROUTES } from '@angular/router';`);
});
it(`should add import to 'AppShellComponent'`, async () => { it(`should add import to 'AppShellComponent'`, async () => {
const tree = await schematicRunner.runSchematic('app-shell', defaultOptions, appTree); const tree = await schematicRunner.runSchematic('app-shell', defaultOptions, appTree);
const filePath = '/projects/bar/src/app/app.config.server.ts'; const filePath = '/projects/bar/src/app/app.config.server.ts';

View File

@ -1,13 +1,13 @@
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { ServerModule } from '@angular/platform-server';<% if(serverRouting) { %> import { ServerModule } from '@angular/platform-server';<% if(serverRouting) { %>
import { provideServerRoutesConfig } from '@angular/ssr';<% } %> import { provideServerRouting } from '@angular/ssr';<% } %>
import { AppComponent } from './app.component'; import { AppComponent } from './app.component';
import { AppModule } from './app.module';<% if(serverRouting) { %> import { AppModule } from './app.module';<% if(serverRouting) { %>
import { serverRoutes } from './app.routes.server';<% } %> import { serverRoutes } from './app.routes.server';<% } %>
@NgModule({ @NgModule({
imports: [AppModule, ServerModule],<% if(serverRouting) { %> imports: [AppModule, ServerModule],<% if(serverRouting) { %>
providers: [provideServerRoutesConfig(serverRoutes)],<% } %> providers: [provideServerRouting(serverRoutes)],<% } %>
bootstrap: [AppComponent], bootstrap: [AppComponent],
}) })
export class AppServerModule {} export class AppServerModule {}

View File

@ -1,13 +1,13 @@
import { mergeApplicationConfig, ApplicationConfig } from '@angular/core'; import { mergeApplicationConfig, ApplicationConfig } from '@angular/core';
import { provideServerRendering } from '@angular/platform-server';<% if(serverRouting) { %> import { provideServerRendering } from '@angular/platform-server';<% if(serverRouting) { %>
import { provideServerRoutesConfig } from '@angular/ssr';<% } %> import { provideServerRouting } from '@angular/ssr';<% } %>
import { appConfig } from './app.config';<% if(serverRouting) { %> import { appConfig } from './app.config';<% if(serverRouting) { %>
import { serverRoutes } from './app.routes.server';<% } %> import { serverRoutes } from './app.routes.server';<% } %>
const serverConfig: ApplicationConfig = { const serverConfig: ApplicationConfig = {
providers: [ providers: [
provideServerRendering(),<% if(serverRouting) { %> provideServerRendering(),<% if(serverRouting) { %>
provideServerRoutesConfig(serverRoutes)<% } %> provideServerRouting(serverRoutes)<% } %>
] ]
}; };