import LifecycleUnit from '../lifecycle/Unit.ts' import {Unit} from '../lifecycle/decorators.ts' import {Handlebars} from '../external/http.ts' import {Logging} from '../service/logging/Logging.ts' import {fs} from '../external/std.ts' /** * Lifecycle unit which sets up and provides basic view engine services. * @extends LifecycleUnit */ @Unit() export default class ViewEngine extends LifecycleUnit { /** * The Handlebars instance. * @type Handlebars */ protected _handlebars!: Handlebars // TODO include basic app info in view data constructor( protected readonly logger: Logging, ) { super() } async up() { this.logger.info(`Setting views base dir: ${this.app.app_path('http', 'views')}`) this._handlebars = new Handlebars({ baseDir: this.app.app_path('http', 'views'), extname: '.hbs', layoutsDir: 'layouts', partialsDir: 'partials', defaultLayout: 'main', helpers: undefined, compilerOptions: undefined, }) const main_layout_path = this.app.app_path('http', 'views', 'layouts', 'main.hbs') if ( !(await fs.exists(main_layout_path)) ) { this.logger.warn(`Unable to open main view layout file: ${main_layout_path}`) this.logger.warn(`Unless you are using a custom layout, this could cause errors.`) } const partials_path = this.app.app_path('http', 'views', 'partials') if ( !(await fs.exists(partials_path)) ) { this.logger.warn(`Unable to open view partials directory: ${partials_path}`) this.logger.warn(`This directory must exist for the view engine to function, even if it is empty.`) } } /** * The handlebars instance. * @type Handlebars */ get handlebars(): Handlebars { return this._handlebars } /** * Render a view with the given name, using the specified arguments and layout. * @param {string} view * @param [args] * @param {string} [layout] * @return Promise */ async render(view: string, args?: any, layout?: string): Promise { this.logger.debug(`Rendering view: ${view}`) return this.handlebars.renderView(view, args, layout) } /** * Render a partial view with the given name, using the specified arguments. * @param {string} view * @param [args] */ async partial(view: string, args?: any) { const parts = `${view}.hbs`.split(':') const resolved = this.app.app_path('http', 'views', ...parts) this.logger.debug(`Rendering partial: ${view} from ${resolved}`) return this.handlebars.render(resolved, args) } }