Skip to content

Commit 4b09887

Browse files
committed
feat(@angular/ssr): move CommonEngine API to /node entry-point
Refactored the `CommonEngine` API import path to remove Node.js dependencies from the `@angular/ssr` main entry-point. BREAKING CHANGE: The `CommonEngine` API now needs to be imported from `@angular/ssr/node`. **Before** ```ts import { CommonEngine } from '@angular/ssr'; ``` **After** ```ts import { CommonEngine } from '@angular/ssr/node'; ```
1 parent ac6935d commit 4b09887

File tree

21 files changed

+257
-45
lines changed

21 files changed

+257
-45
lines changed

goldens/public-api/angular/ssr/index.api.md

-31
Original file line numberDiff line numberDiff line change
@@ -4,37 +4,6 @@
44
55
```ts
66

7-
import { ApplicationRef } from '@angular/core';
8-
import { StaticProvider } from '@angular/core';
9-
import { Type } from '@angular/core';
10-
11-
// @public
12-
export class CommonEngine {
13-
constructor(options?: CommonEngineOptions | undefined);
14-
render(opts: CommonEngineRenderOptions): Promise<string>;
15-
}
16-
17-
// @public (undocumented)
18-
export interface CommonEngineOptions {
19-
bootstrap?: Type<{}> | (() => Promise<ApplicationRef>);
20-
enablePerformanceProfiler?: boolean;
21-
providers?: StaticProvider[];
22-
}
23-
24-
// @public (undocumented)
25-
export interface CommonEngineRenderOptions {
26-
bootstrap?: Type<{}> | (() => Promise<ApplicationRef>);
27-
// (undocumented)
28-
document?: string;
29-
// (undocumented)
30-
documentFilePath?: string;
31-
inlineCriticalCss?: boolean;
32-
providers?: StaticProvider[];
33-
publicPath?: string;
34-
// (undocumented)
35-
url?: string;
36-
}
37-
387
// (No @packageDocumentation comment for this package)
398

409
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
## API Report File for "@angular/ssr_node"
2+
3+
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
4+
5+
```ts
6+
7+
import { ApplicationRef } from '@angular/core';
8+
import { StaticProvider } from '@angular/core';
9+
import { Type } from '@angular/core';
10+
11+
// @public
12+
export class CommonEngine {
13+
constructor(options?: CommonEngineOptions | undefined);
14+
render(opts: CommonEngineRenderOptions): Promise<string>;
15+
}
16+
17+
// @public (undocumented)
18+
export interface CommonEngineOptions {
19+
bootstrap?: Type<{}> | (() => Promise<ApplicationRef>);
20+
enablePerformanceProfiler?: boolean;
21+
providers?: StaticProvider[];
22+
}
23+
24+
// @public (undocumented)
25+
export interface CommonEngineRenderOptions {
26+
bootstrap?: Type<{}> | (() => Promise<ApplicationRef>);
27+
// (undocumented)
28+
document?: string;
29+
// (undocumented)
30+
documentFilePath?: string;
31+
inlineCriticalCss?: boolean;
32+
providers?: StaticProvider[];
33+
publicPath?: string;
34+
// (undocumented)
35+
url?: string;
36+
}
37+
38+
// (No @packageDocumentation comment for this package)
39+
40+
```

packages/angular/ssr/BUILD.bazel

+1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ ng_package(
4444
tags = ["release-package"],
4545
deps = [
4646
":ssr",
47+
"//packages/angular/ssr/node",
4748
],
4849
)
4950

packages/angular/ssr/node/BUILD.bazel

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
load("//tools:defaults.bzl", "ts_library")
2+
3+
package(default_visibility = ["//visibility:public"])
4+
5+
ts_library(
6+
name = "node",
7+
srcs = glob(
8+
[
9+
"*.ts",
10+
"src/**/*.ts",
11+
],
12+
),
13+
module_name = "@angular/ssr/node",
14+
deps = [
15+
"//packages/angular/ssr",
16+
"@npm//@angular/core",
17+
"@npm//@angular/platform-server",
18+
"@npm//@types/node",
19+
],
20+
)

packages/angular/ssr/node/index.ts

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
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.dev/license
7+
*/
8+
9+
export * from './public_api';
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
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.dev/license
7+
*/
8+
9+
export {
10+
CommonEngine,
11+
type CommonEngineRenderOptions,
12+
type CommonEngineOptions,
13+
} from './src/common-engine/common-engine';

packages/angular/ssr/src/common-engine/inline-css-processor.ts packages/angular/ssr/node/src/common-engine/inline-css-processor.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
* found in the LICENSE file at https://angular.dev/license
77
*/
88

9+
import { ɵInlineCriticalCssProcessor as InlineCriticalCssProcessor } from '@angular/ssr';
910
import { readFile } from 'node:fs/promises';
10-
import { InlineCriticalCssProcessor } from '../utils/inline-critical-css';
1111

1212
export class CommonEngineInlineCriticalCssProcessor {
1313
private readonly resourceCache = new Map<string, string>();

packages/angular/ssr/private_export.ts

+2
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,5 @@ export {
1616
setAngularAppManifest as ɵsetAngularAppManifest,
1717
setAngularAppEngineManifest as ɵsetAngularAppEngineManifest,
1818
} from './src/manifest';
19+
20+
export { InlineCriticalCssProcessor as ɵInlineCriticalCssProcessor } from './src/utils/inline-critical-css';

packages/angular/ssr/public_api.ts

-6
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,4 @@
66
* found in the LICENSE file at https://angular.dev/license
77
*/
88

9-
export {
10-
CommonEngine,
11-
type CommonEngineRenderOptions,
12-
type CommonEngineOptions,
13-
} from './src/common-engine/common-engine';
14-
159
export * from './private_export';

packages/angular_devkit/build_angular/BUILD.bazel

+1-1
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,7 @@ LARGE_SPECS = {
365365
"@npm//browser-sync",
366366
"@npm//express",
367367
"@npm//undici",
368-
"//packages/angular/ssr",
368+
"//packages/angular/ssr/node",
369369
],
370370
},
371371
}

packages/angular_devkit/build_angular/src/builders/ssr-dev-server/specs/proxy_spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ describe('Serve SSR Builder', () => {
2525
'src/main.server.ts': `
2626
import 'zone.js/node';
2727
28-
import { CommonEngine } from '@angular/ssr';
28+
import { CommonEngine } from '@angular/ssr/node';
2929
import * as express from 'express';
3030
import { resolve, join } from 'node:path';
3131
import { AppServerModule } from './app/app.module.server';

packages/angular_devkit/build_angular/src/builders/ssr-dev-server/specs/ssl_spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ describe('Serve SSR Builder', () => {
2525
'src/main.server.ts': `
2626
import 'zone.js/node';
2727
28-
import { CommonEngine } from '@angular/ssr';
28+
import { CommonEngine } from '@angular/ssr/node';
2929
import * as express from 'express';
3030
import { resolve, join } from 'node:path';
3131
import { AppServerModule } from './app/app.module.server';

packages/angular_devkit/build_angular/src/builders/ssr-dev-server/specs/works_spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ describe('Serve SSR Builder', () => {
2424
'src/main.server.ts': `
2525
import 'zone.js/node';
2626
27-
import { CommonEngine } from '@angular/ssr';
27+
import { CommonEngine } from '@angular/ssr/node';
2828
import * as express from 'express';
2929
import { resolve, join } from 'node:path';
3030
import { AppServerModule } from './app/app.module.server';

packages/schematics/angular/migrations/migration-collection.json

+5
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@
1111
"version": "19.0.0",
1212
"factory": "./update-workspace-config/migration",
1313
"description": "Update the workspace configuration by replacing deprecated options in 'angular.json' for compatibility with the latest Angular CLI changes."
14+
},
15+
"update-ssr-imports": {
16+
"version": "19.0.0",
17+
"factory": "./update-ssr-imports/migration",
18+
"description": "Update '@angular/ssr' import paths to use the new '/node' entry point when 'CommonEngine' is detected."
1419
}
1520
}
1621
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
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.dev/license
7+
*/
8+
9+
import { DirEntry, Rule, UpdateRecorder } from '@angular-devkit/schematics';
10+
import * as ts from '../../third_party/github.com/Microsoft/TypeScript/lib/typescript';
11+
12+
function* visit(directory: DirEntry): IterableIterator<ts.SourceFile> {
13+
for (const path of directory.subfiles) {
14+
if (path.endsWith('.ts') && !path.endsWith('.d.ts')) {
15+
const entry = directory.file(path);
16+
if (entry) {
17+
const content = entry.content;
18+
if (content.includes('CommonEngine') && !content.includes('@angular/ssr/node')) {
19+
const source = ts.createSourceFile(
20+
entry.path,
21+
content.toString().replace(/^\uFEFF/, ''),
22+
ts.ScriptTarget.Latest,
23+
true,
24+
);
25+
26+
yield source;
27+
}
28+
}
29+
}
30+
}
31+
32+
for (const path of directory.subdirs) {
33+
if (path === 'node_modules' || path.startsWith('.')) {
34+
continue;
35+
}
36+
37+
yield* visit(directory.dir(path));
38+
}
39+
}
40+
41+
/**
42+
* Schematics rule that identifies and updates import declarations in TypeScript files.
43+
* Specifically, it modifies imports of '@angular/ssr' by appending '/node' if the
44+
* `CommonEngine` is used from the old entry point.
45+
*
46+
*/
47+
export default function (): Rule {
48+
return (tree) => {
49+
for (const sourceFile of visit(tree.root)) {
50+
let recorder: UpdateRecorder | undefined;
51+
52+
const allImportDeclarations = sourceFile.statements.filter((n) => ts.isImportDeclaration(n));
53+
if (allImportDeclarations.length === 0) {
54+
continue;
55+
}
56+
57+
const ssrImports = allImportDeclarations.filter(
58+
(n) => ts.isStringLiteral(n.moduleSpecifier) && n.moduleSpecifier.text === '@angular/ssr',
59+
);
60+
for (const ssrImport of ssrImports) {
61+
const ssrNamedBinding = getNamedImports(ssrImport);
62+
if (ssrNamedBinding) {
63+
const isUsingOldEntryPoint = ssrNamedBinding.elements.some((e) =>
64+
e.name.text.startsWith('CommonEngine'),
65+
);
66+
67+
if (!isUsingOldEntryPoint) {
68+
continue;
69+
}
70+
71+
recorder ??= tree.beginUpdate(sourceFile.fileName);
72+
recorder.insertRight(ssrImport.moduleSpecifier.getEnd() - 1, '/node');
73+
}
74+
}
75+
76+
if (recorder) {
77+
tree.commitUpdate(recorder);
78+
}
79+
}
80+
};
81+
}
82+
83+
function getNamedImports(
84+
importDeclaration: ts.ImportDeclaration | undefined,
85+
): ts.NamedImports | undefined {
86+
const namedBindings = importDeclaration?.importClause?.namedBindings;
87+
if (namedBindings && ts.isNamedImports(namedBindings)) {
88+
return namedBindings;
89+
}
90+
91+
return undefined;
92+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
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.dev/license
7+
*/
8+
9+
import { tags } from '@angular-devkit/core';
10+
import { EmptyTree } from '@angular-devkit/schematics';
11+
import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing';
12+
13+
describe('CommonEngine migration', () => {
14+
const schematicRunner = new SchematicTestRunner(
15+
'migrations',
16+
require.resolve('../migration-collection.json'),
17+
);
18+
19+
let tree: UnitTestTree;
20+
beforeEach(() => {
21+
tree = new UnitTestTree(new EmptyTree());
22+
});
23+
24+
function runMigration(): Promise<UnitTestTree> {
25+
return schematicRunner.runSchematic('update-ssr-imports', {}, tree);
26+
}
27+
28+
it(`should replace 'CommonEngine*' imports from '@angular/ssr' to '@angular/ssr/node'`, async () => {
29+
tree.create(
30+
'/index.ts',
31+
tags.stripIndents`
32+
import { CommonEngine } from '@angular/ssr';
33+
import type { CommonEngineOptions, CommonEngineRenderOptions } from '@angular/ssr';
34+
`,
35+
);
36+
37+
const newTree = await runMigration();
38+
expect(newTree.readContent('/index.ts')).toBe(tags.stripIndents`
39+
import { CommonEngine } from '@angular/ssr/node';
40+
import type { CommonEngineOptions, CommonEngineRenderOptions } from '@angular/ssr/node';
41+
`);
42+
});
43+
44+
it(`should not replace 'CommonEngine*' imports from '@angular/ssr/node'`, async () => {
45+
const input = tags.stripIndents`
46+
import { CommonEngine } from '@angular/ssr/node';
47+
import type { CommonEngineOptions, CommonEngineRenderOptions } from '@angular/ssr/node';
48+
`;
49+
50+
tree.create('/index.ts', input);
51+
52+
const newTree = await runMigration();
53+
expect(newTree.readContent('/index.ts')).toBe(input);
54+
});
55+
56+
it(`should not replace 'CommonEngine*' imports from other package`, async () => {
57+
const input = tags.stripIndents`
58+
import { CommonEngine } from 'unknown';
59+
import type { CommonEngineOptions, CommonEngineRenderOptions } from 'unknown';
60+
`;
61+
62+
tree.create('/index.ts', input);
63+
64+
const newTree = await runMigration();
65+
expect(newTree.readContent('/index.ts')).toBe(input);
66+
});
67+
});

packages/schematics/angular/ssr/files/application-builder/server.ts.template

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { APP_BASE_HREF } from '@angular/common';
2-
import { CommonEngine } from '@angular/ssr';
2+
import { CommonEngine } from '@angular/ssr/node';
33
import express from 'express';
44
import { fileURLToPath } from 'node:url';
55
import { dirname, join, resolve } from 'node:path';

packages/schematics/angular/ssr/files/server-builder/server.ts.template

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import 'zone.js/node';
22

33
import { APP_BASE_HREF } from '@angular/common';
4-
import { CommonEngine } from '@angular/ssr';
4+
import { CommonEngine } from '@angular/ssr/node';
55
import * as express from 'express';
66
import { existsSync } from 'node:fs';
77
import { join } from 'node:path';

tests/legacy-cli/e2e/assets/19-ssr-project-webpack/server.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import 'zone.js/node';
22

33
import { APP_BASE_HREF } from '@angular/common';
4-
import { CommonEngine } from '@angular/ssr';
4+
import { CommonEngine } from '@angular/ssr/node';
55
import * as express from 'express';
66
import { existsSync } from 'node:fs';
77
import { join } from 'node:path';

0 commit comments

Comments
 (0)