Skip to content

Commit

Permalink
feat(@angular/ssr): move CommonEngine API to /node entry-point
Browse files Browse the repository at this point in the history
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';
```
  • Loading branch information
alan-agius4 committed Aug 27, 2024
1 parent ac6935d commit 4b09887
Show file tree
Hide file tree
Showing 21 changed files with 257 additions and 45 deletions.
31 changes: 0 additions & 31 deletions goldens/public-api/angular/ssr/index.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,37 +4,6 @@
```ts

import { ApplicationRef } from '@angular/core';
import { StaticProvider } from '@angular/core';
import { Type } from '@angular/core';

// @public
export class CommonEngine {
constructor(options?: CommonEngineOptions | undefined);
render(opts: CommonEngineRenderOptions): Promise<string>;
}

// @public (undocumented)
export interface CommonEngineOptions {
bootstrap?: Type<{}> | (() => Promise<ApplicationRef>);
enablePerformanceProfiler?: boolean;
providers?: StaticProvider[];
}

// @public (undocumented)
export interface CommonEngineRenderOptions {
bootstrap?: Type<{}> | (() => Promise<ApplicationRef>);
// (undocumented)
document?: string;
// (undocumented)
documentFilePath?: string;
inlineCriticalCss?: boolean;
providers?: StaticProvider[];
publicPath?: string;
// (undocumented)
url?: string;
}

// (No @packageDocumentation comment for this package)

```
40 changes: 40 additions & 0 deletions goldens/public-api/angular/ssr/node/index.api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
## API Report File for "@angular/ssr_node"

> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
```ts

import { ApplicationRef } from '@angular/core';
import { StaticProvider } from '@angular/core';
import { Type } from '@angular/core';

// @public
export class CommonEngine {
constructor(options?: CommonEngineOptions | undefined);
render(opts: CommonEngineRenderOptions): Promise<string>;
}

// @public (undocumented)
export interface CommonEngineOptions {
bootstrap?: Type<{}> | (() => Promise<ApplicationRef>);
enablePerformanceProfiler?: boolean;
providers?: StaticProvider[];
}

// @public (undocumented)
export interface CommonEngineRenderOptions {
bootstrap?: Type<{}> | (() => Promise<ApplicationRef>);
// (undocumented)
document?: string;
// (undocumented)
documentFilePath?: string;
inlineCriticalCss?: boolean;
providers?: StaticProvider[];
publicPath?: string;
// (undocumented)
url?: string;
}

// (No @packageDocumentation comment for this package)

```
1 change: 1 addition & 0 deletions packages/angular/ssr/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ ng_package(
tags = ["release-package"],
deps = [
":ssr",
"//packages/angular/ssr/node",
],
)

Expand Down
20 changes: 20 additions & 0 deletions packages/angular/ssr/node/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
load("//tools:defaults.bzl", "ts_library")

package(default_visibility = ["//visibility:public"])

ts_library(
name = "node",
srcs = glob(
[
"*.ts",
"src/**/*.ts",
],
),
module_name = "@angular/ssr/node",
deps = [
"//packages/angular/ssr",
"@npm//@angular/core",
"@npm//@angular/platform-server",
"@npm//@types/node",
],
)
9 changes: 9 additions & 0 deletions packages/angular/ssr/node/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/

export * from './public_api';
13 changes: 13 additions & 0 deletions packages/angular/ssr/node/public_api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/

export {
CommonEngine,
type CommonEngineRenderOptions,
type CommonEngineOptions,
} from './src/common-engine/common-engine';
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
* found in the LICENSE file at https://angular.dev/license
*/

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

export class CommonEngineInlineCriticalCssProcessor {
private readonly resourceCache = new Map<string, string>();
Expand Down
2 changes: 2 additions & 0 deletions packages/angular/ssr/private_export.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,5 @@ export {
setAngularAppManifest as ɵsetAngularAppManifest,
setAngularAppEngineManifest as ɵsetAngularAppEngineManifest,
} from './src/manifest';

export { InlineCriticalCssProcessor as ɵInlineCriticalCssProcessor } from './src/utils/inline-critical-css';
6 changes: 0 additions & 6 deletions packages/angular/ssr/public_api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,4 @@
* found in the LICENSE file at https://angular.dev/license
*/

export {
CommonEngine,
type CommonEngineRenderOptions,
type CommonEngineOptions,
} from './src/common-engine/common-engine';

export * from './private_export';
2 changes: 1 addition & 1 deletion packages/angular_devkit/build_angular/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ LARGE_SPECS = {
"@npm//browser-sync",
"@npm//express",
"@npm//undici",
"//packages/angular/ssr",
"//packages/angular/ssr/node",
],
},
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ describe('Serve SSR Builder', () => {
'src/main.server.ts': `
import 'zone.js/node';
import { CommonEngine } from '@angular/ssr';
import { CommonEngine } from '@angular/ssr/node';
import * as express from 'express';
import { resolve, join } from 'node:path';
import { AppServerModule } from './app/app.module.server';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ describe('Serve SSR Builder', () => {
'src/main.server.ts': `
import 'zone.js/node';
import { CommonEngine } from '@angular/ssr';
import { CommonEngine } from '@angular/ssr/node';
import * as express from 'express';
import { resolve, join } from 'node:path';
import { AppServerModule } from './app/app.module.server';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ describe('Serve SSR Builder', () => {
'src/main.server.ts': `
import 'zone.js/node';
import { CommonEngine } from '@angular/ssr';
import { CommonEngine } from '@angular/ssr/node';
import * as express from 'express';
import { resolve, join } from 'node:path';
import { AppServerModule } from './app/app.module.server';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@
"version": "19.0.0",
"factory": "./update-workspace-config/migration",
"description": "Update the workspace configuration by replacing deprecated options in 'angular.json' for compatibility with the latest Angular CLI changes."
},
"update-ssr-imports": {
"version": "19.0.0",
"factory": "./update-ssr-imports/migration",
"description": "Update '@angular/ssr' import paths to use the new '/node' entry point when 'CommonEngine' is detected."
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/

import { DirEntry, Rule, UpdateRecorder } from '@angular-devkit/schematics';
import * as ts from '../../third_party/github.com/Microsoft/TypeScript/lib/typescript';

function* visit(directory: DirEntry): IterableIterator<ts.SourceFile> {
for (const path of directory.subfiles) {
if (path.endsWith('.ts') && !path.endsWith('.d.ts')) {
const entry = directory.file(path);
if (entry) {
const content = entry.content;
if (content.includes('CommonEngine') && !content.includes('@angular/ssr/node')) {
const source = ts.createSourceFile(
entry.path,
content.toString().replace(/^\uFEFF/, ''),
ts.ScriptTarget.Latest,
true,
);

yield source;
}
}
}
}

for (const path of directory.subdirs) {
if (path === 'node_modules' || path.startsWith('.')) {
continue;
}

yield* visit(directory.dir(path));
}
}

/**
* Schematics rule that identifies and updates import declarations in TypeScript files.
* Specifically, it modifies imports of '@angular/ssr' by appending '/node' if the
* `CommonEngine` is used from the old entry point.
*
*/
export default function (): Rule {
return (tree) => {
for (const sourceFile of visit(tree.root)) {
let recorder: UpdateRecorder | undefined;

const allImportDeclarations = sourceFile.statements.filter((n) => ts.isImportDeclaration(n));
if (allImportDeclarations.length === 0) {
continue;
}

const ssrImports = allImportDeclarations.filter(
(n) => ts.isStringLiteral(n.moduleSpecifier) && n.moduleSpecifier.text === '@angular/ssr',
);
for (const ssrImport of ssrImports) {
const ssrNamedBinding = getNamedImports(ssrImport);
if (ssrNamedBinding) {
const isUsingOldEntryPoint = ssrNamedBinding.elements.some((e) =>
e.name.text.startsWith('CommonEngine'),
);

if (!isUsingOldEntryPoint) {
continue;
}

recorder ??= tree.beginUpdate(sourceFile.fileName);
recorder.insertRight(ssrImport.moduleSpecifier.getEnd() - 1, '/node');
}
}

if (recorder) {
tree.commitUpdate(recorder);
}
}
};
}

function getNamedImports(
importDeclaration: ts.ImportDeclaration | undefined,
): ts.NamedImports | undefined {
const namedBindings = importDeclaration?.importClause?.namedBindings;
if (namedBindings && ts.isNamedImports(namedBindings)) {
return namedBindings;
}

return undefined;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/

import { tags } from '@angular-devkit/core';
import { EmptyTree } from '@angular-devkit/schematics';
import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing';

describe('CommonEngine migration', () => {
const schematicRunner = new SchematicTestRunner(
'migrations',
require.resolve('../migration-collection.json'),
);

let tree: UnitTestTree;
beforeEach(() => {
tree = new UnitTestTree(new EmptyTree());
});

function runMigration(): Promise<UnitTestTree> {
return schematicRunner.runSchematic('update-ssr-imports', {}, tree);
}

it(`should replace 'CommonEngine*' imports from '@angular/ssr' to '@angular/ssr/node'`, async () => {
tree.create(
'/index.ts',
tags.stripIndents`
import { CommonEngine } from '@angular/ssr';
import type { CommonEngineOptions, CommonEngineRenderOptions } from '@angular/ssr';
`,
);

const newTree = await runMigration();
expect(newTree.readContent('/index.ts')).toBe(tags.stripIndents`
import { CommonEngine } from '@angular/ssr/node';
import type { CommonEngineOptions, CommonEngineRenderOptions } from '@angular/ssr/node';
`);
});

it(`should not replace 'CommonEngine*' imports from '@angular/ssr/node'`, async () => {
const input = tags.stripIndents`
import { CommonEngine } from '@angular/ssr/node';
import type { CommonEngineOptions, CommonEngineRenderOptions } from '@angular/ssr/node';
`;

tree.create('/index.ts', input);

const newTree = await runMigration();
expect(newTree.readContent('/index.ts')).toBe(input);
});

it(`should not replace 'CommonEngine*' imports from other package`, async () => {
const input = tags.stripIndents`
import { CommonEngine } from 'unknown';
import type { CommonEngineOptions, CommonEngineRenderOptions } from 'unknown';
`;

tree.create('/index.ts', input);

const newTree = await runMigration();
expect(newTree.readContent('/index.ts')).toBe(input);
});
});
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { APP_BASE_HREF } from '@angular/common';
import { CommonEngine } from '@angular/ssr';
import { CommonEngine } from '@angular/ssr/node';
import express from 'express';
import { fileURLToPath } from 'node:url';
import { dirname, join, resolve } from 'node:path';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import 'zone.js/node';

import { APP_BASE_HREF } from '@angular/common';
import { CommonEngine } from '@angular/ssr';
import { CommonEngine } from '@angular/ssr/node';
import * as express from 'express';
import { existsSync } from 'node:fs';
import { join } from 'node:path';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import 'zone.js/node';

import { APP_BASE_HREF } from '@angular/common';
import { CommonEngine } from '@angular/ssr';
import { CommonEngine } from '@angular/ssr/node';
import * as express from 'express';
import { existsSync } from 'node:fs';
import { join } from 'node:path';
Expand Down

0 comments on commit 4b09887

Please sign in to comment.