Skip to content
This repository was archived by the owner on Nov 22, 2024. It is now read-only.

Commit 439b306

Browse files
author
Fabian Wiles
authored
feat(common): add CommonEngine to encapsulate rendering (#996)
1 parent 90d0221 commit 439b306

11 files changed

Lines changed: 206 additions & 2 deletions

File tree

modules/common/BUILD.bazel

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,20 @@ ng_module(
1111
"src/**/*.ts",
1212
]),
1313
module_name = "@nguniversal/common",
14-
deps = [],
14+
deps = [
15+
"//modules/common/tokens",
16+
],
1517
)
1618

1719
ng_package(
1820
name = "npm_package",
1921
srcs = [":package.json"],
2022
entry_point = "modules/common/index.js",
2123
readme_md = ":README.md",
22-
deps = [":common"],
24+
deps = [
25+
":common",
26+
"//modules/common/tokens",
27+
],
2328
)
2429

2530
ts_library(

modules/common/private_api.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
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+
export { FileLoader as ɵFileLoader, CommonEngine as ɵCommonEngine } from './src/common-engine';

modules/common/public_api.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@
77
*/
88
export { TransferHttpCacheModule } from './src/transfer_http';
99
export { StateTransferInitializerModule } from './src/state-transfer-initializer/module';
10+
export * from './private_api';
Lines changed: 92 additions & 0 deletions
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.io/license
7+
*/
8+
import {ResourceLoader} from '@angular/compiler';
9+
import {Compiler, Type, NgModuleFactory, CompilerFactory, StaticProvider} from '@angular/core';
10+
import {INITIAL_CONFIG, renderModuleFactory, platformDynamicServer} from '@angular/platform-server';
11+
import * as fs from 'fs';
12+
13+
import {FileLoader} from './file-loader';
14+
import {RenderOptions} from './interfaces';
15+
16+
/**
17+
* A common rendering engine utility. This abstracts the logic
18+
* for handling the platformServer compiler, the module cache, and
19+
* the document loader
20+
*/
21+
export class CommonEngine {
22+
23+
/** Return an instance of the platformServer compiler */
24+
getCompiler(): Compiler {
25+
const compilerFactory: CompilerFactory = platformDynamicServer().injector.get(CompilerFactory);
26+
return compilerFactory.createCompiler([
27+
{providers: [{provide: ResourceLoader, useClass: FileLoader, deps: []}]}
28+
]);
29+
}
30+
31+
private factoryCacheMap = new Map<Type<{}>, NgModuleFactory<{}>>();
32+
private templateCache: {[key: string]: string} = {};
33+
34+
constructor(private moduleOrFactory: Type<{}> | NgModuleFactory<{}>,
35+
private providers: StaticProvider[] = []) {}
36+
37+
/**
38+
* Render an HTML document for a specific URL with specified
39+
* render options
40+
*/
41+
render(filePath: string, opts: RenderOptions): Promise<string> {
42+
const extraProviders = [
43+
...(opts.providers || []),
44+
...(this.providers || []),
45+
[
46+
{
47+
provide: INITIAL_CONFIG,
48+
useValue: {
49+
document: opts.document || this.getDocument(filePath),
50+
url: opts.url
51+
}
52+
}
53+
]
54+
];
55+
56+
return this.getFactory()
57+
.then(factory => renderModuleFactory(factory, {extraProviders}));
58+
}
59+
60+
/** Return the factory for a given engine instance */
61+
getFactory(): Promise<NgModuleFactory<{}>> {
62+
// If module has been compiled AoT
63+
const moduleOrFactory = this.moduleOrFactory;
64+
if (moduleOrFactory instanceof NgModuleFactory) {
65+
return Promise.resolve(moduleOrFactory);
66+
} else {
67+
// we're in JIT mode
68+
let moduleFactory = this.factoryCacheMap.get(moduleOrFactory);
69+
70+
// If module factory is cached
71+
if (moduleFactory) {
72+
return Promise.resolve(moduleFactory);
73+
}
74+
75+
// Compile the module and cache it
76+
return this.getCompiler().compileModuleAsync(moduleOrFactory)
77+
.then((factory) => {
78+
this.factoryCacheMap.set(moduleOrFactory, factory);
79+
return factory;
80+
});
81+
}
82+
}
83+
84+
/** Retrieve the document from the cache or the filesystem */
85+
private getDocument(filePath: string): Promise<string> {
86+
const doc = this.templateCache[filePath] = this.templateCache[filePath] ||
87+
fs.readFileSync(filePath).toString();
88+
89+
// As promise so we can change the API later without breaking
90+
return Promise.resolve(doc);
91+
}
92+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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+
import * as fs from 'fs';
9+
import { ResourceLoader } from '@angular/compiler';
10+
11+
/**
12+
* ResourceLoader implementation for loading files
13+
* @internal
14+
*/
15+
export class FileLoader implements ResourceLoader {
16+
get(url: string): Promise<string> {
17+
return new Promise((resolve, reject) => {
18+
fs.readFile(url, (err: NodeJS.ErrnoException, buffer: Buffer) => {
19+
if (err) {
20+
return reject(err);
21+
}
22+
23+
resolve(buffer.toString());
24+
});
25+
});
26+
}
27+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
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+
export * from './interfaces';
9+
export * from './file-loader';
10+
export * from './engine';
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
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+
import {NgModuleFactory, StaticProvider, Type} from '@angular/core';
9+
10+
/** These are the allowed options for the engine */
11+
export interface NgSetupOptions {
12+
bootstrap: Type<{}> | NgModuleFactory<{}>;
13+
providers?: StaticProvider[];
14+
}
15+
16+
/** These are the allowed options for the render */
17+
export interface RenderOptions extends NgSetupOptions {
18+
req: any;
19+
res?: any;
20+
document?: string;
21+
url?: string;
22+
}

modules/common/tokens/BUILD.bazel

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package(default_visibility = ["//visibility:public"])
2+
3+
load("//tools:defaults.bzl", "ng_module")
4+
5+
ng_module(
6+
name = "tokens",
7+
srcs = glob([
8+
"src/*.ts",
9+
]),
10+
module_name = "@nguniversal/common/tokens",
11+
)
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
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+
export {ORIGIN_URL as ɵORIGIN_URL, REQUEST as ɵREQUEST, RESPONSE as ɵRESPONSE} from './src/tokens';
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
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+
export * from './private_api';

0 commit comments

Comments
 (0)