Support deno 1.3.x/0.67.0; replace handlebars with view_engine library
This commit is contained in:
parent
0f182b592b
commit
ff34578d07
@ -1,2 +1,5 @@
|
||||
export * from '../../lib/src/module.ts'
|
||||
export * from '../../di/module.ts'
|
||||
|
||||
import * as std from '../../lib/src/external/std.ts'
|
||||
export { std }
|
||||
|
@ -1,5 +1,27 @@
|
||||
import { env } from '../../lib/src/unit/Scaffolding.ts';
|
||||
import { env } from '../../lib/src/unit/Scaffolding.ts'
|
||||
import {ViewEngine} from '../../lib/src/const/view_engines.ts'
|
||||
|
||||
export default {
|
||||
name: env('APP_NAME', 'Daton'),
|
||||
|
||||
views: {
|
||||
/*
|
||||
* View engine that should be used to render templates.
|
||||
* Options are Handlebars, Ejs, or Denjuck.
|
||||
*/
|
||||
engine: ViewEngine.Handlebars,
|
||||
|
||||
/*
|
||||
* Relative path from the app directory to the base directory where
|
||||
* view files should be looked up.
|
||||
*/
|
||||
base_dir: 'http/views',
|
||||
|
||||
/*
|
||||
* If using Handlebars, optionally, the path to the directory within the
|
||||
* base_dir that contains the partials. They will be automatically registered
|
||||
* with Handlebars.
|
||||
*/
|
||||
partials_dir: 'partials',
|
||||
},
|
||||
}
|
||||
|
@ -1,11 +1,13 @@
|
||||
import Controller from '../../../lib/src/http/Controller.ts'
|
||||
import {Request} from '../../../lib/src/http/Request.ts'
|
||||
import {view} from '../../../lib/src/http/response/helpers.ts'
|
||||
import {Injectable} from '../../../di/module.ts'
|
||||
|
||||
@Injectable()
|
||||
export default class HomeController extends Controller {
|
||||
|
||||
get_home(request: Request) {
|
||||
return view('home', { request })
|
||||
async get_home(request: Request) {
|
||||
return view('home', { greeting: 'Hello' })
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1 +1,6 @@
|
||||
<h1>Welcome to Daton!</h1>
|
||||
<html>
|
||||
{{> header }}
|
||||
<body>
|
||||
<h1>{{ greeting }} from Daton!</h1>
|
||||
</body>
|
||||
</html>
|
@ -1,14 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
{{#if title}}
|
||||
<title>{{ title }} | Daton</title>
|
||||
{{else}}
|
||||
<title>Daton</title>
|
||||
{{/if}}
|
||||
</head>
|
||||
<body>
|
||||
{{{ body }}}
|
||||
</body>
|
||||
</html>
|
3
app/http/views/partials/header.hbs
Normal file
3
app/http/views/partials/header.hbs
Normal file
@ -0,0 +1,3 @@
|
||||
<head>
|
||||
<title>Daton</title>
|
||||
</head>
|
@ -8,8 +8,11 @@ import units from './units.ts'
|
||||
* Let's get up and running. The scaffolding provides the bare minimum
|
||||
* amount of support required to get Daton up and running. The app handles
|
||||
* the rest.
|
||||
*
|
||||
* Daton will automatically load and process application resources, which is
|
||||
* why we need to pass in the base path of this script.
|
||||
*/
|
||||
const scaffolding = make(Scaffolding)
|
||||
const scaffolding = make(Scaffolding, import.meta.url)
|
||||
await scaffolding.up()
|
||||
|
||||
/*
|
||||
|
@ -17,11 +17,11 @@ export default [
|
||||
ConfigUnit,
|
||||
DatabaseUnit,
|
||||
ModelsUnit,
|
||||
ViewEngineUnit,
|
||||
HttpKernelUnit,
|
||||
MiddlewareUnit,
|
||||
ControllerUnit,
|
||||
ViewEngineUnit,
|
||||
RoutesUnit,
|
||||
RoutingUnit,
|
||||
// HttpServerUnit,
|
||||
HttpServerUnit,
|
||||
]
|
||||
|
20
lib/src/const/view_engines.ts
Normal file
20
lib/src/const/view_engines.ts
Normal file
@ -0,0 +1,20 @@
|
||||
|
||||
export enum ViewEngine {
|
||||
Denjuck = 'Denjuck',
|
||||
Ejs = 'Ejs',
|
||||
Handlebars = 'Handlebars',
|
||||
}
|
||||
|
||||
export enum ViewFileExtension {
|
||||
Denjuck = '.html',
|
||||
Ejs = '.ejs',
|
||||
Handlebars = '.hbs',
|
||||
}
|
||||
|
||||
export function isViewEngine(what: any): what is ViewEngine {
|
||||
return (['Denjuck', 'Ejs', 'Handlebars'].includes(String(what)))
|
||||
}
|
||||
|
||||
export function isViewFileExtension(what: any): what is ViewFileExtension {
|
||||
return (['.html', '.ejs', '.hbs']).includes(String(what))
|
||||
}
|
12
lib/src/error/ConfigError.ts
Normal file
12
lib/src/error/ConfigError.ts
Normal file
@ -0,0 +1,12 @@
|
||||
/**
|
||||
* An error to be thrown when a configuration value is invalid.
|
||||
*/
|
||||
export class ConfigError extends Error {
|
||||
constructor(
|
||||
public readonly key: string,
|
||||
public readonly supplied_value: any,
|
||||
message?: string
|
||||
) {
|
||||
super(message || `The value of the config key "${key}" is invalid. (${supplied_value} provided)`)
|
||||
}
|
||||
}
|
5
lib/src/external/db.ts
vendored
5
lib/src/external/db.ts
vendored
@ -1 +1,4 @@
|
||||
export * from 'https://deno.land/x/postgres/mod.ts'
|
||||
// export * from 'https://deno.land/x/postgres@v0.4.3/mod.ts'
|
||||
|
||||
// FIXME: waiting on https://github.com/deno-postgres/deno-postgres/pull/166
|
||||
export * from 'https://raw.githubusercontent.com/glmdev/deno-postgres/master/mod.ts'
|
||||
|
12
lib/src/external/http.ts
vendored
12
lib/src/external/http.ts
vendored
@ -1,3 +1,9 @@
|
||||
export * from 'https://deno.land/std@0.53.0/http/server.ts'
|
||||
export * from 'https://deno.land/std@0.53.0/http/cookie.ts'
|
||||
export { Handlebars } from 'https://deno.land/x/handlebars/mod.ts'
|
||||
export * from 'https://deno.land/std@0.67.0/http/server.ts'
|
||||
export * from 'https://deno.land/std@0.67.0/http/cookie.ts'
|
||||
|
||||
import * as views from 'https://deno.land/x/view_engine@v1.3.0/mod.ts'
|
||||
export { views }
|
||||
|
||||
import hb from 'https://dev.jspm.io/handlebars@4.7.6'
|
||||
const handlebars = <any>hb
|
||||
export { handlebars }
|
||||
|
8
lib/src/external/std.ts
vendored
8
lib/src/external/std.ts
vendored
@ -1,6 +1,6 @@
|
||||
export * from 'https://deno.land/std@0.53.0/fmt/colors.ts'
|
||||
export * from 'https://deno.land/std@0.67.0/fmt/colors.ts'
|
||||
export { config as dotenv } from 'https://deno.land/x/dotenv/mod.ts'
|
||||
export * as path from 'https://deno.land/std@0.53.0/path/mod.ts'
|
||||
export * as fs from 'https://deno.land/std@0.53.0/fs/mod.ts'
|
||||
export { generate as uuid } from 'https://deno.land/std/uuid/v4.ts'
|
||||
export * as path from 'https://deno.land/std@0.67.0/path/mod.ts'
|
||||
export * as fs from 'https://deno.land/std@0.67.0/fs/mod.ts'
|
||||
export { generate as uuid } from 'https://deno.land/std@0.67.0/uuid/v4.ts'
|
||||
// export { moment } from 'https://deno.land/x/moment/moment.ts'
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Injectable } from '../../../di/src/decorator/Injection.ts'
|
||||
import { getCookies, setCookie, delCookie, ServerRequest } from '../external/http.ts'
|
||||
import { getCookies, setCookie, deleteCookie, ServerRequest } from '../external/http.ts'
|
||||
import { InMemCache } from '../support/InMemCache.ts'
|
||||
import { HTTPRequest } from './type/HTTPRequest.ts'
|
||||
|
||||
@ -117,6 +117,6 @@ export class CookieJar {
|
||||
*/
|
||||
public async delete(key: string): Promise<void> {
|
||||
await this._cache.drop(key)
|
||||
delCookie(this.request.response, key)
|
||||
deleteCookie(this.request.response, key)
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ export default class HTMLResponseFactory extends ResponseFactory {
|
||||
|
||||
public async write(request: Request): Promise<Request> {
|
||||
request = await super.write(request)
|
||||
request.response.headers.set('Content-Type', 'text/html')
|
||||
request.response.headers.set('Content-Type', 'text/html; charset=utf-8')
|
||||
request.response.body = this.value
|
||||
return request
|
||||
}
|
||||
|
@ -1,29 +0,0 @@
|
||||
import ResponseFactory from './ResponseFactory.ts'
|
||||
import ViewEngine from '../../unit/ViewEngine.ts'
|
||||
import {Request} from '../Request.ts'
|
||||
|
||||
/**
|
||||
* Response factory that renders a partial view as HTML.
|
||||
* @return ResponseFactory
|
||||
*/
|
||||
export default class PartialViewResponseFactory extends ResponseFactory {
|
||||
constructor(
|
||||
/**
|
||||
* The view name.
|
||||
* @type string
|
||||
*/
|
||||
public readonly view: string,
|
||||
/**
|
||||
* Optionally, the response context.
|
||||
*/
|
||||
public readonly context?: any,
|
||||
) {
|
||||
super()
|
||||
}
|
||||
|
||||
public async write(request: Request) {
|
||||
const views: ViewEngine = this.make(ViewEngine)
|
||||
request.response.body = await views.partial(this.view, this.context)
|
||||
return request
|
||||
}
|
||||
}
|
@ -13,22 +13,19 @@ export default class ViewResponseFactory extends ResponseFactory {
|
||||
* @type string
|
||||
*/
|
||||
public readonly view: string,
|
||||
|
||||
/**
|
||||
* Optionally, the view context.
|
||||
* Optionally, the view data.
|
||||
*/
|
||||
public readonly context?: any,
|
||||
/**
|
||||
* Optionally, the layout name.
|
||||
* @type string
|
||||
*/
|
||||
public readonly layout?: string,
|
||||
public readonly data?: any,
|
||||
) {
|
||||
super()
|
||||
}
|
||||
|
||||
public async write(request: Request) {
|
||||
const views: ViewEngine = this.make(ViewEngine)
|
||||
request.response.body = await views.render(this.view, this.context, this.layout)
|
||||
request.response.body = await views.template(this.view, this.data)
|
||||
request.response.headers.set('Content-Type', 'text/html; charset=utf-8')
|
||||
return request
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,6 @@ import {HTTPStatus} from '../../const/http.ts'
|
||||
import HTTPErrorResponseFactory from './HTTPErrorResponseFactory.ts'
|
||||
import HTTPError from '../../error/HTTPError.ts'
|
||||
import ViewResponseFactory from './ViewResponseFactory.ts'
|
||||
import PartialViewResponseFactory from './PartialViewResponseFactory.ts'
|
||||
|
||||
/**
|
||||
* Get a new JSON response factory that writes the given object as JSON.
|
||||
@ -69,22 +68,11 @@ export function http(status: HTTPStatus, message?: string): HTTPErrorResponseFac
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a new view response factory for the given view name, passing along context and layout.
|
||||
* Get a new view response factory for the given view name, passing along any view data.
|
||||
* @param {string} view
|
||||
* @param [context]
|
||||
* @param {string} [layout]
|
||||
* @param [data]
|
||||
* @return ViewResponseFactory
|
||||
*/
|
||||
export function view(view: string, context?: any, layout?: string): ViewResponseFactory {
|
||||
return make(ViewResponseFactory, view, context, layout)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a new partial view response factory for the given view name, passing along context.
|
||||
* @param {string} view
|
||||
* @param [context]
|
||||
* @return PartialViewResponseFactory
|
||||
*/
|
||||
export function partial(view: string, context?: any): PartialViewResponseFactory {
|
||||
return make(PartialViewResponseFactory, view, context)
|
||||
export function view(view: string, data?: any): ViewResponseFactory {
|
||||
return make(ViewResponseFactory, view, data)
|
||||
}
|
||||
|
@ -8,6 +8,8 @@ import {Status} from '../const/status.ts'
|
||||
import Instantiable from '../../../di/src/type/Instantiable.ts'
|
||||
import {Collection} from '../collection/Collection.ts'
|
||||
import {path} from '../external/std.ts'
|
||||
import Scaffolding from '../unit/Scaffolding.ts'
|
||||
import {Container} from '../../../di/src/Container.ts'
|
||||
|
||||
/**
|
||||
* Central class for Daton applications.
|
||||
@ -22,6 +24,7 @@ export default class Application {
|
||||
|
||||
constructor(
|
||||
protected logger: Logging,
|
||||
protected injector: Container,
|
||||
protected rleh: RunLevelErrorHandler,
|
||||
/**
|
||||
* Array of unit classes to run for this application.
|
||||
@ -112,7 +115,7 @@ export default class Application {
|
||||
* @type string
|
||||
*/
|
||||
get root() {
|
||||
return path.resolve('.')
|
||||
return this.injector.make(Scaffolding).base_dir
|
||||
}
|
||||
|
||||
/**
|
||||
@ -120,7 +123,7 @@ export default class Application {
|
||||
* @type string
|
||||
*/
|
||||
get app_root() {
|
||||
return path.resolve('./app')
|
||||
return this.injector.make(Scaffolding).base_dir
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -9,6 +9,7 @@ import 'https://deno.land/x/dotenv/load.ts'
|
||||
import { Container } from '../../../di/src/Container.ts'
|
||||
import { Inject } from '../../../di/src/decorator/Injection.ts'
|
||||
import CacheFactory from '../support/CacheFactory.ts'
|
||||
import { path } from '../external/std.ts'
|
||||
|
||||
/**
|
||||
* Simple helper for loading ENV values with fallback.
|
||||
@ -29,11 +30,17 @@ export { env }
|
||||
*/
|
||||
@Unit()
|
||||
export default class Scaffolding extends LifecycleUnit {
|
||||
public base_dir!: string
|
||||
|
||||
constructor(
|
||||
protected logger: Logging,
|
||||
protected utility: Utility,
|
||||
@Inject('injector') protected injector: Container,
|
||||
) { super() }
|
||||
base_script: string
|
||||
) {
|
||||
super()
|
||||
this.base_dir = path.dirname(base_script).replace('file:///', '/')
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method for fetching environment variables.
|
||||
@ -47,6 +54,8 @@ export default class Scaffolding extends LifecycleUnit {
|
||||
public async up() {
|
||||
this.setup_logging()
|
||||
this.register_factories()
|
||||
|
||||
this.logger.info(`Base directory: ${this.base_dir}`)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,84 +1,197 @@
|
||||
import LifecycleUnit from '../lifecycle/Unit.ts'
|
||||
import Config from './Config.ts'
|
||||
import {isViewEngine, ViewEngine as Types, ViewFileExtension} from '../const/view_engines.ts'
|
||||
import {views, handlebars} from '../external/http.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'
|
||||
import {ConfigError} from '../error/ConfigError.ts'
|
||||
import {path, fs} from '../external/std.ts'
|
||||
|
||||
/**
|
||||
* Lifecycle unit which sets up and provides basic view engine services.
|
||||
* Error thrown when an action requiring a view engine is attempted, but no view
|
||||
* engine has been configured properly.
|
||||
*/
|
||||
export class MissingViewEngineError extends Error {
|
||||
constructor(message = 'Missing or invalid view engine type config.') {
|
||||
super(message)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Error thrown when an action requiring a template base directory is attempted,
|
||||
* but no such directory has been configured.
|
||||
*/
|
||||
export class MissingTemplateDirectoryError extends Error {
|
||||
constructor(message = 'Missing or invalid path to template base directory.') {
|
||||
super(message)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lifecycle unit which manages the initialization of the configured rendering
|
||||
* engine, as well as utilities for rendering template files and strings.
|
||||
* @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
|
||||
protected engine?: Types
|
||||
protected template_dir?: string
|
||||
|
||||
constructor(
|
||||
protected readonly config: Config,
|
||||
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,
|
||||
})
|
||||
public async up() {
|
||||
const config: unknown = this.config.get('app.views.engine')
|
||||
const base_dir: unknown = this.config.get('app.views.base_dir')
|
||||
|
||||
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.`)
|
||||
if ( !isViewEngine(config) ) {
|
||||
throw new ConfigError('app.views.engine', config)
|
||||
}
|
||||
|
||||
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.`)
|
||||
if ( !String(base_dir) ) {
|
||||
throw new ConfigError('app.views.base_dir', base_dir)
|
||||
}
|
||||
|
||||
this.template_dir = this.app.app_path(base_dir)
|
||||
this.engine = config
|
||||
this.logger.info(`Determined view engine from config: ${config}`)
|
||||
this.logger.info(`Determined base directory for templates: ${this.template_dir}`)
|
||||
|
||||
await this.init_engine()
|
||||
}
|
||||
|
||||
/**
|
||||
* 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]
|
||||
* Render a template file using the configured engine.
|
||||
* @param {string} template_path - the relative path of the template w/in the template dir.
|
||||
* @param {object} [data = {}]
|
||||
* @return Promise<string>
|
||||
*/
|
||||
async render(view: string, args?: any, layout?: string): Promise<string> {
|
||||
this.logger.debug(`Rendering view: ${view}`)
|
||||
return this.handlebars.renderView(view, args, layout)
|
||||
public async template(template_path: string, data: { [key: string]: any } = {}) {
|
||||
const file_path = `${this.template_path(template_path.replace(/:/g, '/'))}${this.get_file_extension()}`
|
||||
|
||||
this.logger.debug(`Rendering template "${template_path}" from file: ${file_path}`)
|
||||
|
||||
// TODO cache this
|
||||
// TODO replace with fs.readFileStr
|
||||
const content = await Deno.readTextFile(file_path)
|
||||
const engine: views.Engine = this.get_engine()
|
||||
|
||||
return engine(
|
||||
content,
|
||||
data,
|
||||
this.get_render_context(),
|
||||
`${template_path}${this.get_file_extension()}`
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a partial view with the given name, using the specified arguments.
|
||||
* @param {string} view
|
||||
* @param [args]
|
||||
* Resolve a path within the template base directory.
|
||||
* @param {...string} parts
|
||||
* @return string
|
||||
*/
|
||||
async partial(view: string, args?: any) {
|
||||
const parts = `${view}.hbs`.split(':')
|
||||
const resolved = this.app.app_path('http', 'views', ...parts)
|
||||
public template_path(...parts: string[]): string {
|
||||
if ( !this.template_dir ) throw new MissingTemplateDirectoryError()
|
||||
return path.resolve(this.template_dir, ...parts)
|
||||
}
|
||||
|
||||
this.logger.debug(`Rendering partial: ${view} from ${resolved}`)
|
||||
return this.handlebars.render(resolved, args)
|
||||
/**
|
||||
* Render a template string using the configured engine.
|
||||
* @param {string} template
|
||||
* @param {object} [data = {}]
|
||||
* @return Promise<string>
|
||||
*/
|
||||
public async render(template: string, data: { [key: string]: any } = {}) {
|
||||
const engine: views.Engine = this.get_engine()
|
||||
return engine(template, data)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the rendering engine for the configured backend.
|
||||
*/
|
||||
public get_engine(): views.Engine {
|
||||
if ( this.engine === Types.Denjuck ) {
|
||||
return views.engineFactory.getDenjuckEngine()
|
||||
} else if ( this.engine === Types.Ejs ) {
|
||||
return views.engineFactory.getEjsEngine()
|
||||
} else if ( this.engine === Types.Handlebars ) {
|
||||
return views.engineFactory.getHandlebarsEngine()
|
||||
}
|
||||
|
||||
throw new MissingViewEngineError()
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file extension expected by the currently configured render engine.
|
||||
*/
|
||||
public get_file_extension(): ViewFileExtension {
|
||||
if ( this.engine === Types.Denjuck ) {
|
||||
return ViewFileExtension.Denjuck
|
||||
} else if ( this.engine === Types.Ejs ) {
|
||||
return ViewFileExtension.Ejs
|
||||
} else if ( this.engine === Types.Handlebars ) {
|
||||
return ViewFileExtension.Handlebars
|
||||
}
|
||||
|
||||
throw new MissingViewEngineError()
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the rendering context to pass to the render engine. This is here
|
||||
* for compatibility with the view_engine library.
|
||||
*/
|
||||
public get_render_context() {
|
||||
if ( !this.template_dir ) throw new MissingTemplateDirectoryError()
|
||||
|
||||
return {
|
||||
viewExt: this.get_file_extension(),
|
||||
viewEngine: this.get_engine(),
|
||||
viewRoot: this.template_dir,
|
||||
useCache: false, // TODO better value here
|
||||
cache: undefined,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the appropriate rendering engine.
|
||||
*/
|
||||
protected async init_engine() {
|
||||
if ( this.engine === Types.Handlebars ) {
|
||||
await this.init_engine_handlebars()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the Handlebars rendering engine by loading and registering partial views
|
||||
* from the configured partials directory, if one exists.
|
||||
*/
|
||||
protected async init_engine_handlebars() {
|
||||
const partials_dir: unknown = this.config.get('app.views.partials_dir')
|
||||
if ( String(partials_dir) ) {
|
||||
const partials_path = this.template_path(String(partials_dir))
|
||||
this.logger.info(`Registering Handlebars partials from: ${partials_path}`)
|
||||
|
||||
for await ( const entry of fs.walk(partials_path) ) {
|
||||
if ( !entry.isFile || !entry.path.endsWith(this.get_file_extension()) ) {
|
||||
if ( entry.isFile ) this.logger.debug(`Skipping file in Handlebars partials path with invalid suffix: ${entry.path}`)
|
||||
continue
|
||||
}
|
||||
|
||||
const content = await Deno.readTextFile(entry.path)
|
||||
|
||||
// Generate the partial name.
|
||||
// This is done by taking the relative path from the partials dir, removing the suffix,
|
||||
// and replacing directory separators with the standard : identifier.
|
||||
let partial_name = entry.path.replace(partials_path, '').slice(0, -1 * (this.get_file_extension()).length)
|
||||
if ( partial_name.startsWith('/') ) partial_name = partial_name.slice(1)
|
||||
partial_name = partial_name.replace(/\//g, ':')
|
||||
|
||||
this.logger.debug(`Registering handlebars partial: ${partial_name}`)
|
||||
handlebars.registerPartial(partial_name, content)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user