import {AppClass} from '../lifecycle/AppClass' import {Config} from '../service/Config' import {Container} from '../di' import {ErrorWithContext, UniversalPath} from '../util' /** * Abstract base class for rendering views via different view engines. */ export abstract class ViewEngine extends AppClass { protected readonly config: Config protected readonly debug: boolean protected readonly namespaces: {[key: string]: UniversalPath} = {} constructor() { super() this.config = Container.getContainer().make(Config) this.debug = (this.config.get('server.mode', 'production') === 'development' || this.config.get('server.debug', false)) } /** * Get the UniversalPath to the base directory where views are loaded from. */ public get path(): UniversalPath { return this.app().appPath(...['resources', 'views']) // FIXME allow configuring } /** * Given a template string and a set of variables for the view, render the string to HTML and return it. * @param templateString * @param locals */ public abstract renderString(templateString: string, locals: {[key: string]: any}): string | Promise /** * Given the canonical name of a template file, render the file using the provided variables. * @param templateName * @param locals */ public abstract renderByName(templateName: string, locals: {[key: string]: any}): string | Promise public registerNamespace(namespace: string, basePath: UniversalPath): this { if ( namespace.startsWith('@') ) { namespace = namespace.substr(1) } this.namespaces[namespace] = basePath return this } public resolveName(templateName: string): UniversalPath { let path = this.path if ( templateName.startsWith('@') ) { const [namespace, ...parts] = templateName.split(':') path = this.namespaces[namespace.substr(1)] if ( !path ) { throw new ErrorWithContext('Invalid template namespace: ' + namespace, { namespace, templateName, }) } templateName = parts.join(':') } if ( !templateName.endsWith('.pug') ) { templateName += '.pug' } return path.concat(...templateName.split(':')) } public resolveBasePath(templateName: string): UniversalPath { let path = this.path if ( templateName.startsWith('@') ) { const [namespace] = templateName.split(':') path = this.namespaces[namespace.substr(1)] if ( !path ) { throw new ErrorWithContext('Invalid template namespace: ' + namespace, { namespace, templateName, }) } } return path } }