mirror of
https://github.com/angular/angular-cli.git
synced 2025-05-17 19:13:34 +08:00
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:
parent
6f9a764526
commit
ec05c814ee
@ -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)
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
@ -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,
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
@ -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',
|
||||||
|
@ -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';
|
||||||
|
@ -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 {}
|
||||||
|
@ -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)<% } %>
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user