UNPKG

@nguniversal/express-engine

Version:

Express Engine for running Server Angular Apps

205 lines (193 loc) 6.74 kB
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