UNPKG

actionhero

Version:

The reusable, scalable, and quick node.js API server for stateless and stateful applications

194 lines (193 loc) 8.79 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.rebuildConfig = exports.config = void 0; exports.buildConfig = buildConfig; const fs = require("fs"); const path = require("path"); const utils_1 = require("./utils"); const ensureNoTsHeaderOrSpecFiles_1 = require("./utils/ensureNoTsHeaderOrSpecFiles"); const env_1 = require("./../classes/process/env"); const id_1 = require("./../classes/process/id"); const actionheroVersion_1 = require("./../classes/process/actionheroVersion"); const typescript_1 = require("./../classes/process/typescript"); const projectRoot_1 = require("./../classes/process/projectRoot"); const safeGlob_1 = require("./utils/safeGlob"); function buildConfig() { var _a; const configPaths = []; let config = { process: { env: env_1.env, id: id_1.id, typescript: typescript_1.typescript, projectRoot: projectRoot_1.projectRoot, actionheroVersion: actionheroVersion_1.actionheroVersion, }, }; // We support multiple configuration paths as follows: // // 1. Use the project 'config' folder, if it exists. // 2. "actionhero --config=PATH1 --config=PATH2 --config=PATH3,PATH4" // 3. "ACTIONHERO_CONFIG=PATH1,PATH2 npm start" // 4. "ACTIONHERO_CONFIG_OVERRIDES" (stringified JSON) can partially override any of the config objects loaded from the above // // Note that if --config or ACTIONHERO_CONFIG are used, they _overwrite_ the use of the default "config" folder. If // you wish to use both, you need to re-specify "config", e.g. "--config=config,local-config". Also, note that // specifying multiple --config options on the command line does exactly the same thing as using one parameter with // comma separators, however the environment variable method only supports the comma-delimited syntax. function addConfigPath(pathToCheck, alreadySplit) { if (typeof pathToCheck === "string") { if (!alreadySplit) { addConfigPath(pathToCheck.split(","), true); } else { if (pathToCheck.charAt(0) !== "/") { pathToCheck = path.resolve(projectRoot_1.projectRoot, pathToCheck); } if (fs.existsSync(pathToCheck)) { configPaths.push(pathToCheck); } } } else if (Array.isArray(pathToCheck)) { pathToCheck.map((entry) => { addConfigPath(entry, alreadySplit); }); } } [(_a = utils_1.utils.argv.config) === null || _a === void 0 ? void 0 : _a.toString(), process.env.ACTIONHERO_CONFIG].map((entry) => { addConfigPath(entry, false); }); if (configPaths.length < 1 && typescript_1.typescript) { addConfigPath("src/config", false); } if (configPaths.length < 1) { addConfigPath("dist/config", false); } if (configPaths.length < 1) { throw new Error(configPaths + "No config directory found in this project. Did you compile your typescript project?"); } const loadConfigFile = (f) => { const localConfig = require(f); if (f.includes("routes.js") || f.includes("routes.ts")) { // We don't want to merge in routes from Actionhero core unless we are running core directly // Routes can be loaded by plugins via `registerRoute` if (f.includes(`${path.sep}node_modules${path.sep}actionhero${path.sep}`)) { return; } let localRoutes = { routes: {} }; if (localConfig.DEFAULT) { // @ts-ignore localRoutes = utils_1.utils.hashMerge(localRoutes, localConfig.DEFAULT, config); } if (localConfig[env_1.env]) { // @ts-ignore localRoutes = utils_1.utils.hashMerge(localRoutes, localConfig[env_1.env], config); } Object.keys(localRoutes.routes).forEach((v) => { if (config.routes && config.routes[v]) { config.routes[v].push(...localRoutes.routes[v]); } else { if (!config.routes) config.routes = {}; config.routes[v] = localRoutes.routes[v]; } }); } else { if (localConfig.DEFAULT) { config = utils_1.utils.hashMerge(config, localConfig.DEFAULT, config); } if (localConfig[env_1.env]) { config = utils_1.utils.hashMerge(config, localConfig[env_1.env], config); } } }; const loadConfigDirectory = (configPath, watch) => { const configFiles = (0, ensureNoTsHeaderOrSpecFiles_1.ensureNoTsHeaderOrSpecFiles)((0, safeGlob_1.safeGlobSync)(path.join(configPath, "**", "**/*(*.js|*.ts)"))); let loadRetries = 0; let loadErrors = {}; for (let i = 0, limit = configFiles.length; i < limit; i++) { const f = configFiles[i]; try { // attempt configuration file load loadConfigFile(f); // configuration file load success: clear retries and // errors since progress has been made loadRetries = 0; loadErrors = {}; } catch (error) { // error loading configuration, abort if all remaining // configuration files have been tried and failed // indicating inability to progress loadErrors[f] = { error: error, msg: error.toString() }; if (++loadRetries === limit - i) { Object.keys(loadErrors).forEach((e) => { console.log(loadErrors[e].error.stack); console.log(""); delete loadErrors[e].error; }); throw new Error("Unable to load configurations, errors: " + JSON.stringify(loadErrors)); } // adjust configuration files list: remove and push // failed configuration to the end of the list and // continue with next file at same index configFiles.push(configFiles.splice(i--, 1)[0]); continue; } } // We load the config twice. Utilize configuration files load order that succeeded on the first pass. // This is to allow 'literal' values to be loaded whenever possible, and then for references to be resolved configFiles.forEach(loadConfigFile); // Remove duplicate routes since we might be loading from multiple config directories, also we load every // config directory twice. if (config.routes) { Object.keys(config.routes).forEach((v) => { config.routes[v] = config.routes[v].filter((route, index, self) => index === self.findIndex((r) => r.path === route.path && r.action === route.action && r.apiVersion === route.apiVersion && r.matchTrailingPathParts === route.matchTrailingPathParts && r.dir === route.dir)); }); } }; // load the default config of actionhero loadConfigDirectory(path.join(__dirname, "/../config"), false); // load the project specific config configPaths.map((p) => loadConfigDirectory(p, false)); if (process.env.ACTIONHERO_CONFIG_OVERRIDES) { try { config = utils_1.utils.hashMerge(config, JSON.parse(process.env.ACTIONHERO_CONFIG_OVERRIDES)); } catch (error) { throw new Error(`could not parse ACTIONHERO_CONFIG_OVERRIDES: ${error}`); } } if (utils_1.utils.argv.ACTIONHERO_CONFIG_OVERRIDES) { try { config = utils_1.utils.hashMerge(config, JSON.parse(utils_1.utils.argv.ACTIONHERO_CONFIG_OVERRIDES.toString())); } catch (error) { throw new Error(`could not parse ACTIONHERO_CONFIG_OVERRIDES: ${error}`); } } return config; } exports.config = buildConfig(); /** * Rebuild Actionhero's `config` object. Useful when Environment variables effecting the config may have changed. */ const rebuildConfig = () => { (0, env_1.recalculateEnv)(); (0, actionheroVersion_1.recalculateActionheroVersion)(); (0, id_1.recalcuateId)(); (0, projectRoot_1.recalculateProjectRoot)(); (0, typescript_1.recalculateIsTypescript)(); exports.config = buildConfig(); }; exports.rebuildConfig = rebuildConfig;