UNPKG

@buka/nestjs-config

Version:
186 lines (185 loc) 8.21 kB
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; import * as R from 'ramda'; import { Logger, Module } from '@nestjs/common'; import { instanceToInstance } from 'class-transformer'; import { validate } from 'class-validator'; import objectPath from 'object-path'; import { dotenvLoader } from './config-loader/dotenv-loader.js'; import { processEnvLoader } from './config-loader/process-env-loader.js'; import { ConfigurableModuleClass, MODULE_OPTIONS_TOKEN } from './config.module-definition.js'; import { MODULE_LOADED_CONFIG_TOKEN, RESET_COLOR } from './constants.js'; import { objectKeysToCamelCase } from './utils/object-keys-to-camel-case.js'; import { toCamelCase } from './utils/to-camel-case.js'; import { deepMergeAll } from './utils/deep-merge-all.js'; import { inspect } from 'util'; import { ConfigurationRegistry } from './configuration-registry.js'; let ConfigModule = class ConfigModule extends ConfigurableModuleClass { static config = null; static providers = new Map(); static async createConfigProvider(options, config, configProvider) { const ConfigProviderClass = configProvider.target; if (this.providers.has(ConfigProviderClass)) return this.providers.get(ConfigProviderClass); const path = toCamelCase(configProvider.path); const subConfig = objectPath.get(config, path); const instance = new ConfigProviderClass(); function set(property, value) { if (value !== undefined) instance[property] = value; } const properties = R.uniq([ ...Object.getOwnPropertyNames(instance), ...ConfigurationRegistry.getProperties(instance), ]); for (const property of properties) { const ck = ConfigurationRegistry.getConfigKey(ConfigProviderClass, property); if (ck.ignore) continue; if (ck.configKey) { const value = objectPath.get(config, ck.configKey); set(property, value); } if (typeof ck.propertyKey === 'symbol') continue; set(property, subConfig && subConfig[toCamelCase(ck.propertyKey)]); } const result = instanceToInstance(instance); const errors = await validate(result); if (errors.length) { const message = errors .map((error) => { let message = `An instance of ${ConfigProviderClass.name} has failed the validation:\n`; for (const constraint in error.constraints) { message += ` - Property: \`${error.property}\`\n`; message += ` Value: ${JSON.stringify(error.value)}\n`; message += ` Constraint: ${constraint}\n`; message += ` Expect: ${error.constraints[constraint]}\n`; } return message; }) .join('\n'); Logger.error(message, '@buka/nestjs-config'); throw new Error(message); } if (options.debug) { Logger.debug(`${ConfigProviderClass.name} initialized${RESET_COLOR}\n${inspect(result, false, null, true)}`, '@buka/nestjs-config'); } this.providers.set(ConfigProviderClass, result); return result; } static async loadConfig(options = {}) { if (this.config !== null) return this.config; const configLoaders = (options.loaders || [processEnvLoader(), '.env']) .map((c) => (typeof c === 'string' ? dotenvLoader(c) : c)); const configs = await Promise.all(configLoaders.map((loader) => loader(options))); const config = objectKeysToCamelCase(deepMergeAll(configs)); this.config = config; return config; } static createConfigProviderFactory(configProvider) { return { provide: configProvider.target, inject: [MODULE_OPTIONS_TOKEN, MODULE_LOADED_CONFIG_TOKEN], useFactory: (options, config) => { const provider = this.providers.get(configProvider.target); if (provider) return provider; return this.createConfigProvider(options, config, configProvider); }, }; } static createLoadedConfigProviderFactory() { return { provide: MODULE_LOADED_CONFIG_TOKEN, inject: [MODULE_OPTIONS_TOKEN], useFactory: async (options) => this.loadConfig(options), }; } /** * Load config and provider before registering the module */ static async preload(options = {}) { const config = await this.loadConfig(options); const configProviders = ConfigurationRegistry.getProviders(); await Promise.all(configProviders.map((provider) => this.createConfigProvider(options, config, provider))); } /** * Get the loaded config */ static get(ConfigProviderClass) { return this.providers.get(ConfigProviderClass); } static async getOrFail(ConfigProviderClass) { const config = await this.get(ConfigProviderClass); if (!config) { throw new Error(`[@buka/nestjs-config] ${ConfigProviderClass.name} Not Founded`); } return config; } static register(options) { const configProviders = ConfigurationRegistry.getProviders(); const dynamicModule = super.register(options); dynamicModule.providers = [ ...(dynamicModule.providers || []), this.createLoadedConfigProviderFactory(), ...configProviders.map((provider) => this.createConfigProviderFactory(provider)), ]; dynamicModule.exports = [ ...(dynamicModule.exports || []), ...configProviders.map((provider) => provider.target), ]; return dynamicModule; } static registerAsync(options) { const configProviders = ConfigurationRegistry.getProviders(); const dynamicModule = super.registerAsync(options); dynamicModule.providers = [ ...(dynamicModule.providers || []), this.createLoadedConfigProviderFactory(), ...configProviders.map((provider) => this.createConfigProviderFactory(provider)), ]; dynamicModule.exports = [ ...(dynamicModule.exports || []), ...configProviders.map((provider) => provider.target), ]; return dynamicModule; } static inject(provider, module, moduleAsyncOptionsOrFactory, optionsFactory) { let moduleAsyncOptions = undefined; let useFactory = (config) => config; if (typeof moduleAsyncOptionsOrFactory === 'function') { useFactory = moduleAsyncOptionsOrFactory; } else if (typeof moduleAsyncOptionsOrFactory === 'object') { moduleAsyncOptions = moduleAsyncOptionsOrFactory; } if (typeof optionsFactory === 'function') { useFactory = optionsFactory; } if ('registerAsync' in module && module.registerAsync) { return module.registerAsync({ ...moduleAsyncOptions, inject: [provider], useFactory, }); } if ('forRootAsync' in module && module.forRootAsync) { return module.forRootAsync({ ...moduleAsyncOptions, inject: [provider], useFactory, }); } throw new TypeError('Invalid module'); } }; ConfigModule = __decorate([ Module({}) ], ConfigModule); export { ConfigModule };