Skip to content

Commit dc5cc89

Browse files
Broccoangular-robot[bot]
authored andcommitted
feat(@schematics/angular): Update universal schematic to support standalone applications
1 parent 66a33c6 commit dc5cc89

File tree

5 files changed

+103
-1
lines changed

5 files changed

+103
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import { mergeApplicationConfig, ApplicationConfig } from '@angular/core';
10+
import { provideServerSupport } from '@angular/platform-server';
11+
import { appConfig } from './app.config';
12+
13+
const serverConfig: ApplicationConfig = {
14+
providers: [
15+
provideServerSupport()
16+
]
17+
};
18+
19+
export const config = mergeApplicationConfig(appConfig, serverConfig);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import { bootstrapApplication } from '@angular/platform-browser';
10+
import { AppComponent } from './app/app.component';
11+
import { config } from './app/app.config.server';
12+
13+
const bootstrap = () => bootstrapApplication(AppComponent, config);
14+
15+
export default bootstrap;

packages/schematics/angular/universal/index.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
chain,
1818
mergeWith,
1919
move,
20+
noop,
2021
strings,
2122
url,
2223
} from '@angular-devkit/schematics';
@@ -27,6 +28,7 @@ import {
2728
getPackageJsonDependency,
2829
} from '../utility/dependencies';
2930
import { latestVersions } from '../utility/latest-versions';
31+
import { isStandaloneApp } from '../utility/ng-ast-utils';
3032
import { relativePathToWorkspaceRoot } from '../utility/paths';
3133
import { targetBuildNotFoundError } from '../utility/project-targets';
3234
import { getWorkspace, updateWorkspace } from '../utility/workspace';
@@ -133,7 +135,9 @@ export default function (options: UniversalOptions): Rule {
133135
context.addTask(new NodePackageInstallTask());
134136
}
135137

136-
const templateSource = apply(url('./files/src'), [
138+
const isStandalone = isStandaloneApp(host, clientBuildOptions.main);
139+
140+
const templateSource = apply(url(isStandalone ? './files/standalone-src' : './files/src'), [
137141
applyTemplates({
138142
...strings,
139143
...options,

packages/schematics/angular/universal/index_spec.ts

+51
Original file line numberDiff line numberDiff line change
@@ -188,4 +188,55 @@ describe('Universal Schematic', () => {
188188
};
189189
expect(compilerOptions.types).toContain('@angular/localize');
190190
});
191+
192+
describe('standalone application', () => {
193+
let standaloneAppOptions;
194+
let defaultStandaloneOptions: UniversalOptions;
195+
beforeEach(async () => {
196+
const standaloneAppName = 'baz';
197+
standaloneAppOptions = {
198+
...appOptions,
199+
name: standaloneAppName,
200+
standalone: true,
201+
};
202+
defaultStandaloneOptions = {
203+
project: standaloneAppName,
204+
};
205+
appTree = await schematicRunner.runSchematic('application', standaloneAppOptions, appTree);
206+
});
207+
208+
it('should create not root module file', async () => {
209+
const tree = await schematicRunner.runSchematic(
210+
'universal',
211+
defaultStandaloneOptions,
212+
appTree,
213+
);
214+
const filePath = '/projects/baz/src/app/app.server.module.ts';
215+
expect(tree.exists(filePath)).toEqual(false);
216+
});
217+
218+
it('should create a main file', async () => {
219+
const tree = await schematicRunner.runSchematic(
220+
'universal',
221+
defaultStandaloneOptions,
222+
appTree,
223+
);
224+
const filePath = '/projects/baz/src/main.server.ts';
225+
expect(tree.exists(filePath)).toEqual(true);
226+
const contents = tree.readContent(filePath);
227+
expect(contents).toContain(`bootstrapApplication(AppComponent, config)`);
228+
});
229+
230+
it('should create server app config file', async () => {
231+
const tree = await schematicRunner.runSchematic(
232+
'universal',
233+
defaultStandaloneOptions,
234+
appTree,
235+
);
236+
const filePath = '/projects/baz/src/app/app.config.server.ts';
237+
expect(tree.exists(filePath)).toEqual(true);
238+
const contents = tree.readContent(filePath);
239+
expect(contents).toContain(`const serverConfig: ApplicationConfig = {`);
240+
});
241+
});
191242
});

packages/schematics/angular/utility/ng-ast-utils.ts

+13
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import { normalize } from '@angular-devkit/core';
1010
import { SchematicsException, Tree } from '@angular-devkit/schematics';
1111
import { dirname } from 'path';
12+
import { findBootstrapApplicationCall } from '../private/standalone';
1213
import * as ts from '../third_party/github.com/Microsoft/TypeScript/lib/typescript';
1314
import { findNode, getSourceNodes } from '../utility/ast-utils';
1415

@@ -78,3 +79,15 @@ export function getAppModulePath(host: Tree, mainPath: string): string {
7879

7980
return modulePath;
8081
}
82+
83+
export function isStandaloneApp(host: Tree, mainPath: string): boolean {
84+
const source = ts.createSourceFile(
85+
mainPath,
86+
host.readText(mainPath),
87+
ts.ScriptTarget.Latest,
88+
true,
89+
);
90+
const bootstrapCall = findBootstrapApplicationCall(source);
91+
92+
return bootstrapCall !== null;
93+
}

0 commit comments

Comments
 (0)