'use strict'; const errors = require('errors'); const express = require('express'); const negotiate = require('express-negotiate'); const path = require('path'); const autoLoad = require('./config/container-autoload'); /** * Determine the appropriate path to a module relative to the 'app' folder * * @private * @param {string} modulePath - the raw path to the module * @return {string} - the normalized path to the module */ function normalizeIncludePath(modulePath) { const basePath = path.resolve(path.join(__dirname, '../')); let includePath = modulePath; // Allow referencing local modules without using a ./ // eg. util/route-loader instead of ./util/route-loader if ( modulePath.includes('/') && ! (modulePath.startsWith('./') || modulePath.includes(__dirname)) ) { includePath = path.join(__dirname, modulePath); } return includePath; } /** * Container for keeping track of dependencies */ class Container { constructor() { const app = express(); let container = new Map(); // Save the base app object container.set('app', app); // Preload some configured modules autoLoad.map((module) => { let moduleMap = module; // Normalize config into [key, value] if (! Array.isArray(module)) { moduleMap = [module, module]; } // Actually require the module moduleMap[1] = require(normalizeIncludePath(moduleMap[1])); container.set.apply(container, moduleMap); }); this._container = container; } /** * Determine if an item exists in the container * * @param {string} name - name of the item * @return {boolean} - whether the item exists in the container */ has(name) { return this._container.has(name); } /** * Return an existing object instance * * @param {string} name - name of the item * @return {Object|undefined} - the item, or undefined if it doesn't exist */ get(name) { if (this.has(name)) { return this._container.get(name); } return this._require(name); } /** * Get a base library instance * * @param {string} name - name of the base module * @returns {*} - the base module */ getBase(name) { return this.get(`base/${name}`); } /** * Get a helper library instance * * @param {string} name - name of the helper module * @return {*} - the helper instance */ getHelper(name) { return this.get(`helpers/${name}`); } /** * Get a model * @param {string} name - the name of the model module * @returns {*} - the model */ getModel(name) { return this.get(`models/${name}`); } /** * Set an object in the container * * @param {string} name - name to associate with object * @param {Object} object - item to keep track of * @return {Container} - the container instance */ set(name, object) { this._container.set(name, object); return this; } /** * Does a native require, relative to the lib folder, * and returns the value * * @private * @param {string} modulePath - name of the module to require * @return {*} - the value returned from require */ _require(modulePath) { // If the value is already saved, just return it if (this.has(modulePath)) { return this.get(modulePath); } // Save the item for later let item = require(normalizeIncludePath(modulePath)); this.set(modulePath, item); return item; } } module.exports = new Container();