@nguniversal/express-engine
Version:
Express Engine for running Server Angular Apps
205 lines (193 loc) • 6.74 kB
JavaScript
import { readFile, readFileSync } from 'fs';
import { __awaiter } from 'tslib';
import { ResourceLoader } from '@angular/compiler';
import { CompilerFactory, NgModuleFactory } from '@angular/core';
import { platformDynamicServer, INITIAL_CONFIG, renderModuleFactory } from '@angular/platform-server';
import { REQUEST, RESPONSE } from '@nguniversal/express-engine/tokens';
/** ResourceLoader implementation for loading files */
class FileLoader {
get(url) {
return new Promise((resolve, reject) => {
readFile(url, (err, data) => {
if (err) {
return reject(err);
}
resolve(data.toString());
});
});
}
}
/**
* A common rendering engine utility. This abstracts the logic
* for handling the platformServer compiler, the module cache, and
* the document loader
*/
class CommonEngine {
constructor(moduleOrFactory, providers = []) {
this.moduleOrFactory = moduleOrFactory;
this.providers = providers;
this.factoryCacheMap = new Map();
this.templateCache = {};
}
/** Return an instance of the platformServer compiler */
getCompiler() {
const compilerFactory = platformDynamicServer().injector.get(CompilerFactory);
return compilerFactory.createCompiler([
{ providers: [{ provide: ResourceLoader, useClass: FileLoader, deps: [] }] }
]);
}
/**
* Render an HTML document for a specific URL with specified
* render options
*/
render(opts) {
return __awaiter(this, void 0, void 0, function* () {
// if opts.document dosen't exist then opts.documentFilePath must
const doc = opts.document || (yield this.getDocument(opts.documentFilePath));
const extraProviders = [
...(opts.providers || []),
...(this.providers || []),
{
provide: INITIAL_CONFIG,
useValue: {
document: doc,
url: opts.url
}
}
];
const moduleOrFactory = this.moduleOrFactory || opts.bootstrap;
const factory = yield this.getFactory(moduleOrFactory);
return renderModuleFactory(factory, { extraProviders });
});
}
/** Return the factory for a given engine instance */
getFactory(moduleOrFactory) {
return __awaiter(this, void 0, void 0, function* () {
// If module has been compiled AoT
if (moduleOrFactory instanceof NgModuleFactory) {
return moduleOrFactory;
}
else {
// we're in JIT mode
const moduleFactory = this.factoryCacheMap.get(moduleOrFactory);
// If module factory is cached
if (moduleFactory) {
return moduleFactory;
}
// Compile the module and cache it
const factory = yield this.getCompiler().compileModuleAsync(moduleOrFactory);
this.factoryCacheMap.set(moduleOrFactory, factory);
return factory;
}
});
}
/** Retrieve the document from the cache or the filesystem */
getDocument(filePath) {
const doc = this.templateCache[filePath] = this.templateCache[filePath] ||
readFileSync(filePath).toString();
// As promise so we can change the API later without breaking
return Promise.resolve(doc);
}
}
/**
* @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.io/license
*/
/**
* @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.io/license
*/
/**
* @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.io/license
*/
/**
* @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.io/license
*/
/**
* This holds a cached version of each index used.
*/
const templateCache = {};
/**
* This is an express engine for handling Angular Applications
*/
function ngExpressEngine(setupOptions) {
const engine = new CommonEngine(setupOptions.bootstrap, setupOptions.providers);
return function (filePath, options, callback) {
try {
const renderOptions = Object.assign({}, options);
if (!setupOptions.bootstrap && !renderOptions.bootstrap) {
throw new Error('You must pass in a NgModule or NgModuleFactory to be bootstrapped');
}
const req = renderOptions.req;
const res = renderOptions.res || req.res;
renderOptions.url =
renderOptions.url || `${req.protocol}://${(req.get('host') || '')}${req.originalUrl}`;
renderOptions.document = renderOptions.document || getDocument(filePath);
renderOptions.providers = renderOptions.providers || [];
renderOptions.providers = renderOptions.providers.concat(getReqResProviders(req, res));
engine.render(renderOptions)
.then(html => callback(null, html))
.catch(callback);
}
catch (err) {
callback(err);
}
};
}
/**
* Get providers of the request and response
*/
function getReqResProviders(req, res) {
const providers = [
{
provide: REQUEST,
useValue: req
}
];
if (res) {
providers.push({
provide: RESPONSE,
useValue: res
});
}
return providers;
}
/**
* Get the document at the file path
*/
function getDocument(filePath) {
return templateCache[filePath] = templateCache[filePath] || readFileSync(filePath).toString();
}
/**
* @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.io/license
*/
/**
* @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.io/license
*/
/**
* Generated bundle index. Do not edit.
*/
export { ngExpressEngine };
//# sourceMappingURL=express-engine.js.map