UNPKG

@hapiness/ng-universal

Version:

This is a Hapiness Engine for running Angular Apps on the server for server side rendering.

274 lines 10.2 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var __param = (this && this.__param) || function (paramIndex, decorator) { return function (target, key) { decorator(target, key, paramIndex); } }; Object.defineProperty(exports, "__esModule", { value: true }); const core_1 = require("@angular/core"); const platform_server_1 = require("@angular/platform-server"); const compiler_1 = require("@angular/compiler"); const module_map_ngfactory_loader_1 = require("@nguniversal/module-map-ngfactory-loader"); const rxjs_1 = require("rxjs"); const operators_1 = require("rxjs/operators"); const fs = require("fs"); const path_1 = require("path"); const Mimos = require("@hapi/mimos"); const interfaces_1 = require("../../interfaces"); const injection_1 = require("../../../injection"); const core_2 = require("@hapiness/core"); const httpserver_1 = require("@hapiness/core/httpserver"); const reply_1 = require("../reply"); const utils_1 = require("../utils"); let NgEngineService = class NgEngineService { /** * Service constructor * * @param {NgSetupOptions} _config * @param {HttpServerRequest} _request * @param {HttpServerReply} _reply helper to modify the response * @param {HttpUtils} _utils helper to manage data in request/response like cookies */ constructor(_config, _request, _reply, _utils) { this._config = _config; this._request = _request; this._reply = _reply; this._utils = _utils; this._dataCache = {}; this._factoryCacheMap = new Map(); this._compilerFactory = platform_server_1.platformDynamicServer().injector.get(core_1.CompilerFactory); this._compiler = this._compilerFactory.createCompiler([ { providers: [ { provide: compiler_1.ResourceLoader, useClass: FileLoader, deps: [] } ] } ]); this._renderModuleFactory = platform_server_1.renderModuleFactory; this._provideModuleMap = module_map_ngfactory_loader_1.provideModuleMap; this._mimos = new Mimos(); } /** * Returns universal rendering of HTML * * @return {Observable<HttpResponse<any>>} */ universal() { return rxjs_1.merge(this._checkRequest(), this._checkConfig()) .pipe(operators_1.toArray(), operators_1.map(_ => ({ config: _.pop() })), operators_1.map(_ => Object.assign(_, { mime: this._mimos.path(this._request.raw.url).type })), operators_1.flatMap(_ => rxjs_1.merge(this._getStaticContent(_), this._getFactoryContent(_)))); } /** * Returns HttpResponse<any> from static content * * @param _ * * @returns {Observable<HttpResponse<any>>} * * @private */ _getStaticContent(_) { return rxjs_1.of(_) .pipe(operators_1.filter(__ => !!__.mime), operators_1.map(__ => ({ value: this._getDocument(this._buildFilePath(__.config.staticContent, __.mime, this._request.raw.url)), headers: { 'content-type': __.mime } }))); } /** * Returns content from NgFactoryModule * * @param _ * * @returns {Observable<HttpResponse<any>>} * * @private */ _getFactoryContent(_) { return rxjs_1.of(_) .pipe(operators_1.filter(__ => !__.mime), operators_1.map(__ => ({ moduleOrFactory: __.config.bootstrap, extraProviders: this._extraProviders(__.config.providers, __.config.lazyModuleMap, this._buildFilePath(__.config.staticContent)) })), operators_1.flatMap(__ => this._getFactory(__.moduleOrFactory) .pipe(operators_1.flatMap(factory => rxjs_1.from(this._renderModuleFactory(factory, { extraProviders: __.extraProviders }))), operators_1.map(html => ({ value: html, headers: { 'content-type': 'text/html' } }))))); } /** * Function to check request parameter * * @returns {Observable<true>} * * @private */ _checkRequest() { return rxjs_1.of(this._request) .pipe(operators_1.flatMap(_ => (!!_ && !!_.raw && _.raw.url !== undefined) ? rxjs_1.of(true) : rxjs_1.throwError(new Error('url is undefined')))); } /** * Function to check module config * * @returns {Observable<NgSetupOptions>} * * @private */ _checkConfig() { return rxjs_1.of(this._config) .pipe(operators_1.flatMap(_ => (!!_ && !!_.bootstrap) ? rxjs_1.of(_) : rxjs_1.throwError(new Error('You must pass in config a NgModule or NgModuleFactory to be bootstrapped'))), operators_1.flatMap(_ => (!!_ && !!_.lazyModuleMap) ? rxjs_1.of(_) : rxjs_1.throwError(new Error('You must pass in config lazy module map'))), operators_1.flatMap(_ => (!!_ && !!_.staticContent) ? rxjs_1.of(_) : rxjs_1.throwError(new Error('You must pass in config the static content object'))), operators_1.flatMap(_ => (!!_ && !!_.staticContent.indexFile) ? rxjs_1.of(_) : rxjs_1.throwError(new Error('You must pass in config the static content object with index file'))), operators_1.flatMap(_ => (!!_ && !!_.staticContent.rootPath) ? rxjs_1.of(_) : rxjs_1.throwError(new Error('You must pass in config the static content object with root path'))), operators_1.flatMap(_ => rxjs_1.of({ bootstrap: _.bootstrap, lazyModuleMap: _.lazyModuleMap, staticContent: _.staticContent, providers: _.providers || [] }))); } /** * Builds extra providers * * @param {StaticProvider[]} providers * @param {ModuleMap} lazyModuleMap * @param {string} filePath * * @return {Provider[]} * * @private */ _extraProviders(providers, lazyModuleMap, filePath) { return providers.concat(providers, this._provideModuleMap(lazyModuleMap), this._getAdditionalProviders(), [ { provide: platform_server_1.INITIAL_CONFIG, useValue: { document: this._getDocument(filePath).toString(), url: this._request.raw.url } } ]); } /** * Get a factory from a bootstrapped module / module factory * * @param {Type<{}> | NgModuleFactory<{}>} moduleOrFactory * * @return {Observable<NgModuleFactory<{}>>} * * @private */ _getFactory(moduleOrFactory) { return rxjs_1.of(rxjs_1.of(moduleOrFactory)) .pipe(operators_1.flatMap(obs => rxjs_1.merge(obs .pipe(operators_1.filter(_ => _ instanceof core_1.NgModuleFactory)), obs .pipe(operators_1.filter(_ => !(_ instanceof core_1.NgModuleFactory)), operators_1.map((_) => this._factoryCacheMap.get(_)), operators_1.flatMap(_ => !!_ ? rxjs_1.of(_) : this._compile(moduleOrFactory)))))); } /** * Compile the module and cache it * * @param {Type<{}>} module to compile and cache * * @return {Observable<NgModuleFactory<{}>>} * * @private */ _compile(module) { return rxjs_1.from(this._compiler.compileModuleAsync(module)) .pipe(operators_1.tap(_ => this._factoryCacheMap.set(module, _))); } /** * Get providers of the request and response * * @return {StaticProvider[]} * * @private */ _getAdditionalProviders() { return [ { provide: injection_1.REQUEST, useValue: this._request }, { provide: injection_1.REPLY, useValue: this._reply }, { provide: injection_1.UTILS, useValue: this._utils } ]; } /** * Returns document path * * @param {StaticContent} staticContent * @param {string} mime * @param {string} staticFileUrl * * @returns {string} * * @private */ _buildFilePath(staticContent, mime, staticFileUrl) { return (!!mime && !!staticFileUrl) ? path_1.join(staticContent.rootPath, staticFileUrl) : path_1.join(staticContent.rootPath, staticContent.indexFile); } /** * Returns document from cache or file system * * @param {string} filePath path to the file * * @return {Buffer} * * @private */ _getDocument(filePath) { return this._dataCache[filePath] = this._dataCache[filePath] || fs.readFileSync(filePath); } }; NgEngineService = __decorate([ core_2.Service(), __param(0, core_2.Inject(interfaces_1.NG_UNIVERSAL_MODULE_CONFIG)), __metadata("design:paramtypes", [Object, httpserver_1.HttpServerRequest, reply_1.HttpServerReply, utils_1.HttpUtils]) ], NgEngineService); exports.NgEngineService = NgEngineService; /** * FileLoader implementation */ class FileLoader { /* istanbul ignore next */ get(url) { return new Promise((resolve, reject) => { fs.readFile(url, (err, buffer) => { if (err) { return reject(err); } resolve(buffer.toString()); }); }); } } //# sourceMappingURL=ng.service.js.map