/**
* @module libflitter/canon/CanonicalUnit
*/
const path = require('path')
const rra = require('recursive-readdir-async')
const Unit = require('../Unit')
const priv = {
// Formats a file path to canonical name.
format_file_name(name) {
const base_dir = process.platform === 'win32' ? this.directory.replace(/\\/g, '/') : this.directory
name = name.replace(base_dir, '')
if ( this.suffix ) {
const suffix_regexp = new RegExp(`${this.suffix}`, 'g')
name = name.replace(suffix_regexp, '')
}
name = name.replace(/\//g, ':').substring(1)
return name
}
}
/**
* Base class for canonical units that load resources from the filesystem
* and name resources based on the file's location within that system.
* @extends module:libflitter/Unit~Unit
*/
class CanonicalUnit extends Unit {
/**
* Defines the services required by this unit.
* The 'canon' service is provided by default.
* @returns {Array<string>}
*/
static get services() {
return [...super.services, 'canon']
}
/**
* Get the name of the service provided by this unit.
* @returns {string}
*/
static get name() {
return this.prototype.canonical_item+'s'
}
/**
* Instantiate the unit.
* @param {string} base_directory - the base search directory
*/
constructor(base_directory) {
super()
/**
* The root directory for this canonical resource's files.
* @type {Promise<void> | Promise<string>}
*/
this.directory = path.resolve(base_directory)
/**
* The file extension of the canonical item files.
* @type {string} - '.js'
*/
this.suffix = '.js'
/**
* The canonical name of the item.
* @type {string} = 'item'
*/
this.canonical_item = 'item'
/**
* Mapping of canonical names to instances for this item.
* @type {object}
*/
this.canonical_items = {}
}
/**
* Initializes the unit. Recursively iterates over the base directory and finds
* all valid files. Loads the instances from those files and initializes them.
* @param {module:libflitter/app/FlitterApp~FlitterApp} app - the Flitter app
* @returns {Promise<void>}
*/
async go(app) {
const files = await rra.list(this.directory)
for ( let key in files ) {
if ( !files.hasOwnProperty(key) ) continue
if ( files[key].fullname && files[key].fullname.endsWith(this.suffix) ) {
const name = priv.format_file_name.bind(this)(files[key].fullname)
const instance = require(files[key].fullname)
if ( app.di().__is_injectable(instance) ) {
app.di().inject(instance)
}
this.canonical_items[name] = await this.init_canonical_file({
app, name, instance
})
}
}
this.canon.register_resource(this.canonical_item, this.get.bind(this))
}
/**
* Prepare a single canonical item and return the value that should be given by the resolver.
* @param {object} info
* @param {module:libflitter/app/FlitterApp} info.app
* @param {string} info.name - the unqualified canonical name
* @param {*} info.instance - the exports from the file
* @returns {Promise<*>}
*/
async init_canonical_file({app, name, instance}) {
return instance
}
/**
* Get the directories provided by this unit.
* @returns {object}
*/
directories() {
return {
[this.canonical_item+'s']: this.directory,
}
}
/**
* Resolve an unqualified canonical name to a registered canonical item.
* @param {string} name
* @returns {object}
*/
get(name) {
const name_parts = name.split('.')
let descending_value = this.canonical_items
name_parts.forEach(part => {
descending_value = descending_value[part]
})
return descending_value
}
}
module.exports = exports = CanonicalUnit