You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
126 lines
3.7 KiB
126 lines
3.7 KiB
/**
|
|
* Base type for a canonical definition.
|
|
*/
|
|
import {Canon} from "./Canon";
|
|
import {universalPath, UniversalPath} from "@extollo/util";
|
|
import {Logging} from "./Logging";
|
|
import {Inject} from "@extollo/di";
|
|
import * as nodePath from 'path'
|
|
import {Unit} from "../lifecycle/Unit";
|
|
|
|
export interface CanonicalDefinition {
|
|
canonicalName: string,
|
|
originalName: string,
|
|
imported: any,
|
|
}
|
|
|
|
/**
|
|
* Base type for a canonical name reference.
|
|
*/
|
|
export interface CanonicalReference {
|
|
resource?: string,
|
|
item: string,
|
|
particular?: string,
|
|
}
|
|
|
|
export abstract class Canonical<T> extends Unit {
|
|
@Inject()
|
|
protected readonly logging!: Logging
|
|
|
|
@Inject()
|
|
protected readonly canon!: Canon
|
|
|
|
/**
|
|
* The base path directory where the canonical definitions reside.
|
|
* @type string
|
|
*/
|
|
protected appPath: string[] = []
|
|
|
|
/**
|
|
* The file suffix of files in the base path that should be loaded.
|
|
* @type string
|
|
*/
|
|
protected suffix: string = '.js'
|
|
|
|
/**
|
|
* The singular, programmatic name of one of these canonical items.
|
|
* @example middleware
|
|
* @type string
|
|
*/
|
|
protected canonicalItem: string = ''
|
|
|
|
/**
|
|
* Object mapping canonical names to loaded file references.
|
|
* @type object
|
|
*/
|
|
protected loadedItems: { [key: string]: T } = {}
|
|
|
|
/**
|
|
* Resolve a canonical reference from its string form to a CanonicalReference.
|
|
* @param {string} reference
|
|
* @return CanonicalReference
|
|
*/
|
|
public static resolve(reference: string): CanonicalReference {
|
|
const rscParts = reference.split('::')
|
|
const resource = rscParts.length > 1 ? rscParts[0] + 's' : undefined
|
|
const rsc_less = rscParts.length > 1 ? rscParts[1] : rscParts[0]
|
|
const prtParts = rsc_less.split('.')
|
|
const item = prtParts[0]
|
|
const particular = prtParts.length > 1 ? prtParts.slice(1).join('.') : undefined
|
|
|
|
return {
|
|
resource, item, particular
|
|
}
|
|
}
|
|
|
|
public all(): string[] {
|
|
return Object.keys(this.loadedItems)
|
|
}
|
|
|
|
public get path(): UniversalPath {
|
|
return this.app().appPath(...this.appPath)
|
|
}
|
|
|
|
public get canonicalItems() {
|
|
return `${this.canonicalItem}s`
|
|
}
|
|
|
|
public get(key: string): T | undefined {
|
|
return this.loadedItems[key]
|
|
}
|
|
|
|
public async up() {
|
|
for await ( const entry of this.path.walk() ) {
|
|
if ( !entry.endsWith(this.suffix) ) {
|
|
this.logging.debug(`Skipping file with invalid suffix: ${entry}`)
|
|
continue
|
|
}
|
|
|
|
const definition = await this.buildCanonicalDefinition(entry)
|
|
this.logging.verbose(`Registering canonical ${this.canonicalItem} "${definition.canonicalName}" from ${entry}`)
|
|
this.loadedItems[definition.canonicalName] = await this.initCanonicalItem(definition)
|
|
}
|
|
|
|
this.canon.registerCanonical(this)
|
|
}
|
|
|
|
public async initCanonicalItem(definition: CanonicalDefinition): Promise<T> {
|
|
return definition.imported.default ?? definition.imported[definition.canonicalName]
|
|
}
|
|
|
|
protected async buildCanonicalDefinition(filePath: string): Promise<CanonicalDefinition> {
|
|
const originalName = filePath.replace(this.path.toLocal, '').substr(1)
|
|
const pathRegex = new RegExp(nodePath.sep, 'g')
|
|
const canonicalName = originalName.replace(pathRegex, ':')
|
|
.split('').reverse().join('')
|
|
.substr(this.suffix.length)
|
|
.split('').reverse().join('')
|
|
|
|
const fullUniversalPath = universalPath(filePath)
|
|
this.logging.verbose(`Importing from: ${fullUniversalPath}`)
|
|
|
|
const imported = await import(fullUniversalPath.toLocal)
|
|
return { canonicalName, originalName, imported }
|
|
}
|
|
}
|