add universal path classes, refactor view engine to use them
This commit is contained in:
parent
90ded11fae
commit
380d50be43
90
lib/src/support/UniversalPath.ts
Normal file
90
lib/src/support/UniversalPath.ts
Normal file
@ -0,0 +1,90 @@
|
||||
import { path as deno_path } from '../external/std.ts'
|
||||
|
||||
export enum UniversalPathPrefix {
|
||||
HTTP = 'http://',
|
||||
HTTPS = 'https://',
|
||||
Local = 'file://',
|
||||
}
|
||||
|
||||
export type PathLike = string | UniversalPath
|
||||
|
||||
export function universal_path(...parts: PathLike[]): UniversalPath {
|
||||
let [main, ...concats] = parts
|
||||
if ( !(main instanceof UniversalPath) ) main = new UniversalPath(main)
|
||||
return main.concat(...concats)
|
||||
}
|
||||
|
||||
export class UniversalPath {
|
||||
protected _prefix!: UniversalPathPrefix
|
||||
protected _local!: string
|
||||
|
||||
constructor(
|
||||
protected readonly initial: string,
|
||||
) {
|
||||
this.set_prefix()
|
||||
this.set_local()
|
||||
}
|
||||
|
||||
protected set_prefix() {
|
||||
if ( this.initial.toLowerCase().startsWith('http://') ) {
|
||||
this._prefix = UniversalPathPrefix.HTTP
|
||||
} else if ( this.initial.toLowerCase().startsWith('https://') ) {
|
||||
this._prefix = UniversalPathPrefix.HTTPS
|
||||
} else {
|
||||
this._prefix = UniversalPathPrefix.Local
|
||||
}
|
||||
}
|
||||
|
||||
protected set_local() {
|
||||
this._local = this.initial
|
||||
if ( this.initial.toLowerCase().startsWith(this._prefix) ) {
|
||||
this._local = this._local.slice(this._prefix.length)
|
||||
}
|
||||
|
||||
if ( this._prefix === UniversalPathPrefix.Local && !this._local.startsWith('/') ) {
|
||||
this._local = deno_path.resolve(this._local)
|
||||
}
|
||||
}
|
||||
|
||||
get prefix() {
|
||||
return this._prefix
|
||||
}
|
||||
|
||||
get is_local() {
|
||||
return this._prefix === UniversalPathPrefix.Local
|
||||
}
|
||||
|
||||
get is_remote() {
|
||||
return this._prefix !== UniversalPathPrefix.Local
|
||||
}
|
||||
|
||||
get unqualified() {
|
||||
return this._local
|
||||
}
|
||||
|
||||
get to_local() {
|
||||
if ( this.is_local ) {
|
||||
return this._local
|
||||
} else {
|
||||
return `${this.prefix}${this._local}`
|
||||
}
|
||||
}
|
||||
|
||||
get to_remote() {
|
||||
return `${this.prefix}${this._local}`
|
||||
}
|
||||
|
||||
public concat(...paths: PathLike[]): UniversalPath {
|
||||
const resolved = deno_path.join(this.unqualified, ...(paths.map(p => typeof p === 'string' ? p : p.unqualified)))
|
||||
return new UniversalPath(`${this.prefix}${resolved}`)
|
||||
}
|
||||
|
||||
public append(path: PathLike): this {
|
||||
this._local += String(path)
|
||||
return this
|
||||
}
|
||||
|
||||
toString() {
|
||||
return `${this.prefix}${this._local}`
|
||||
}
|
||||
}
|
@ -5,7 +5,8 @@ import {views, handlebars} from '../external/http.ts'
|
||||
import {Unit} from '../lifecycle/decorators.ts'
|
||||
import {Logging} from '../service/logging/Logging.ts'
|
||||
import {ConfigError} from '../error/ConfigError.ts'
|
||||
import {path, fs} from '../external/std.ts'
|
||||
import {fs} from '../external/std.ts'
|
||||
import {PathLike, universal_path, UniversalPath} from '../support/UniversalPath.ts'
|
||||
|
||||
/**
|
||||
* Error thrown when an action requiring a view engine is attempted, but no view
|
||||
@ -35,7 +36,7 @@ export class MissingTemplateDirectoryError extends Error {
|
||||
@Unit()
|
||||
export default class ViewEngine extends LifecycleUnit {
|
||||
protected engine?: Types
|
||||
protected template_dir?: string
|
||||
protected template_dir?: UniversalPath
|
||||
|
||||
constructor(
|
||||
protected readonly config: Config,
|
||||
@ -56,7 +57,7 @@ export default class ViewEngine extends LifecycleUnit {
|
||||
throw new ConfigError('app.views.base_dir', base_dir)
|
||||
}
|
||||
|
||||
this.template_dir = this.app.app_path(base_dir)
|
||||
this.template_dir = universal_path(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}`)
|
||||
@ -71,16 +72,14 @@ export default class ViewEngine extends LifecycleUnit {
|
||||
* @return Promise<string>
|
||||
*/
|
||||
public async template(template_path: string, data: { [key: string]: any } = {}) {
|
||||
let file_path = `${this.template_path(template_path.replace(/:/g, '/'))}${this.get_file_extension()}`
|
||||
if ( file_path.startsWith('file://') ) {
|
||||
file_path = file_path.slice(7)
|
||||
}
|
||||
const template_name = template_path.replace(/:/g, '/')
|
||||
const file_path = this.template_path(template_name).append(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 content = await Deno.readTextFile(file_path.to_local)
|
||||
const engine: views.Engine = this.get_engine()
|
||||
|
||||
return engine(
|
||||
@ -96,17 +95,9 @@ export default class ViewEngine extends LifecycleUnit {
|
||||
* @param {...string} parts
|
||||
* @return string
|
||||
*/
|
||||
public template_path(...parts: string[]): string {
|
||||
public template_path(...parts: PathLike[]): UniversalPath {
|
||||
if ( !this.template_dir ) throw new MissingTemplateDirectoryError()
|
||||
let template_dir = this.template_dir
|
||||
if ( template_dir.startsWith('file://') ) template_dir = template_dir.slice(7)
|
||||
|
||||
const resolved_path = path.resolve(template_dir, ...parts)
|
||||
if ( resolved_path.startsWith('/') ) {
|
||||
return `file://${resolved_path}`
|
||||
} else {
|
||||
return resolved_path
|
||||
}
|
||||
return this.template_dir.concat(...parts)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -156,13 +147,11 @@ export default class ViewEngine extends LifecycleUnit {
|
||||
*/
|
||||
public get_render_context() {
|
||||
if ( !this.template_dir ) throw new MissingTemplateDirectoryError()
|
||||
let template_dir = this.template_dir
|
||||
if ( template_dir.startsWith('file://') ) template_dir = template_dir.slice(7)
|
||||
|
||||
return {
|
||||
viewExt: this.get_file_extension(),
|
||||
viewEngine: this.get_engine(),
|
||||
viewRoot: template_dir,
|
||||
viewRoot: this.template_dir.to_local,
|
||||
useCache: false, // TODO better value here
|
||||
cache: undefined,
|
||||
}
|
||||
@ -184,14 +173,10 @@ export default class ViewEngine extends LifecycleUnit {
|
||||
protected async init_engine_handlebars() {
|
||||
const partials_dir: unknown = this.config.get('app.views.partials_dir')
|
||||
if ( String(partials_dir) ) {
|
||||
let partials_path = this.template_path(String(partials_dir))
|
||||
const partials_path = this.template_path(String(partials_dir))
|
||||
this.logger.info(`Registering Handlebars partials from: ${partials_path}`)
|
||||
|
||||
if ( partials_path.startsWith('file://') ) {
|
||||
partials_path = partials_path.slice(7)
|
||||
}
|
||||
|
||||
for await ( const entry of fs.walk(partials_path) ) {
|
||||
for await ( const entry of fs.walk(partials_path.to_local) ) {
|
||||
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
|
||||
|
Loading…
Reference in New Issue
Block a user