@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
JavaScript
;
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