UNPKG

boostr

Version:
111 lines 4.35 kB
import { existsSync } from 'fs'; import { join, resolve } from 'path'; import merge from 'lodash/merge.js'; import { throwError } from './utilities.js'; const CONFIG_FILE_NAME = 'boostr.config.mjs'; const PRIVATE_CONFIG_FILE_NAME = 'boostr.config.private.mjs'; const BLACK_HOLE = new Proxy(Object.create(null), { get: (_, key) => (key === Symbol.toPrimitive ? unresolvedToPrimitive : BLACK_HOLE) }); function unresolvedToPrimitive() { return '<UNRESOLVED>'; } export async function loadApplicationConfig(directory, { stage }) { while (true) { const config = await loadConfig(directory, { applicationConfig: BLACK_HOLE, stage }); if (config !== undefined && config.type === 'application') { await _preloadServiceConfigs(config, { stage }); return config; } const parentDirectory = join(directory, '..'); if (parentDirectory === directory) { break; } directory = parentDirectory; } return undefined; } async function _preloadServiceConfigs(applicationConfig, { stage }) { if (applicationConfig.services === undefined) { applicationConfig.services = {}; } Object.defineProperty(applicationConfig, '__preloadedServiceConfigs', { value: {} }); for (const serviceName of Object.keys(applicationConfig.services)) { applicationConfig.__preloadedServiceConfigs[serviceName] = BLACK_HOLE; } for (const serviceName of Object.keys(applicationConfig.services)) { applicationConfig.__preloadedServiceConfigs[serviceName] = await loadServiceConfig(serviceName, { applicationConfig, stage }); } } export async function loadServiceConfig(serviceName, { applicationConfig, stage }) { const rootDirectory = applicationConfig.__directory; const serviceDirectoryRelative = applicationConfig?.services[serviceName]; if (serviceDirectoryRelative === undefined) { throwError(`The specified service is unknown: '${serviceName}'`); } const serviceDirectory = resolve(rootDirectory, serviceDirectoryRelative); if (!existsSync(serviceDirectory)) { throwError(`The '${serviceName}' service references a directory that doesn't exist: ${serviceDirectory}`); } const serviceConfig = await loadConfig(serviceDirectory, { applicationConfig, stage }); if (serviceConfig === undefined) { throwError(`Couldn't find a configuration file for the '${serviceName}' service`); } return serviceConfig; } async function loadConfig(directory, { applicationConfig, stage }) { let config = await _loadConfig(directory, CONFIG_FILE_NAME, { applicationConfig, stage }); const privateConfig = await _loadConfig(directory, PRIVATE_CONFIG_FILE_NAME, { applicationConfig, stage }); if (privateConfig !== undefined) { if (config !== undefined) { merge(config, privateConfig); } else { config = privateConfig; } } if (config === undefined) { return undefined; } if (config.type === undefined) { throwError(`Couldn't find a 'type' property in a configuration (directory: '${directory}')`); } return config; } async function _loadConfig(directory, configFileName, { applicationConfig, stage }) { const file = join(directory, configFileName); if (!existsSync(file)) { return undefined; } let configBuilder; try { configBuilder = (await import(file)).default; } catch (error) { throwError(`An error occurred while loading a configuration file (path: '${file}')\n${error.message}`); } let config; try { config = await configBuilder({ application: applicationConfig, services: applicationConfig?.__preloadedServiceConfigs ?? BLACK_HOLE }); } catch (error) { throwError(`An error occurred while evaluating a configuration file (path: '${file}')\n${error.stack}`); } Object.defineProperty(config, '__directory', { value: directory }); config.environment = { ...applicationConfig?.environment, ...config.environment }; if (config.stages !== undefined) { merge(config, config.stages[stage]); delete config.stages; } return config; } //# sourceMappingURL=config.js.map