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.
lib/src/service/Canonical.ts

126 lines
3.7 KiB

3 years ago
/**
* 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]
3 years ago
}
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 }
}
}