UNPKG

@nestjs/config

Version:

Nest - modern, fast, powerful node.js web framework (@config)

203 lines (202 loc) • 8.8 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; }; var ConfigModule_1; import { Module } from '@nestjs/common'; import { isObject } from '@nestjs/common/utils/shared.utils.js'; import { expand } from 'dotenv-expand'; import * as fs from 'fs'; import { resolve } from 'path'; import { ConfigHostModule } from './config-host.module.js'; import { CONFIGURATION_LOADER, CONFIGURATION_SERVICE_TOKEN, CONFIGURATION_TOKEN, VALIDATED_ENV_LOADER, VALIDATED_ENV_PROPNAME, } from './config.constants.js'; import { ConfigService } from './config.service.js'; import { getDefaultParser } from './utils/index.js'; import { createConfigProvider } from './utils/create-config-factory.util.js'; import { getRegistrationToken } from './utils/get-registration-token.util.js'; import { mergeConfigObject } from './utils/merge-configs.util.js'; import { validateWithStandardSchema } from './utils/validate-with-schema.util.js'; /** * @publicApi */ let ConfigModule = ConfigModule_1 = class ConfigModule { /** * This promise resolves when parser completes loading environment variables. * When "ignoreEnvFile" is set to true, then it will resolve immediately after the * "ConfigModule#forRoot" method is called. */ static get envVariablesLoaded() { return this._envVariablesLoaded; } /** * Loads environment variables based on the "ignoreEnvFile" flag and "envFilePath" value. * Additionally, registers custom configurations globally. * @param options */ static async forRoot(options = {}) { const envFilePaths = Array.isArray(options.envFilePath) ? options.envFilePath : [options.envFilePath || resolve(process.cwd(), '.env')]; const parser = options.parser ?? getDefaultParser(); let validatedEnvConfig = undefined; let config = options.ignoreEnvFile ? {} : this.loadEnvFile(envFilePaths, parser, options); if (!options.ignoreEnvVars && options.validatePredefined !== false) { config = options.override ? { ...process.env, ...config } : { ...config, ...process.env }; } if (options.validate) { const validatedConfig = options.validate(config); validatedEnvConfig = validatedConfig; this.assignVariablesToProcess(validatedConfig, options.override); } else if (options.validationSchema) { const { error, value: validatedConfig } = await validateWithStandardSchema(options.validationSchema, config, options.validationOptions); if (error) { throw new Error(`Config validation error: ${error.message}`); } // Object schemas strip the properties they don't declare (e.g. Zod), so // the undeclared variables are merged back in to keep them available // through process.env and ConfigService. validatedEnvConfig = { ...config, ...validatedConfig }; this.assignVariablesToProcess(validatedEnvConfig, options.override); } else { this.assignVariablesToProcess(config, options.override); } const isConfigToLoad = options.load && options.load.length; const configFactory = await Promise.all(options.load || []); const providers = configFactory .map(factory => createConfigProvider(factory)) .filter(item => item); const configProviderTokens = providers.map(item => item.provide); const configServiceProvider = { provide: ConfigService, useFactory: (configService) => { const untypedConfigService = configService; if (options.cache) { untypedConfigService.isCacheEnabled = true; } if (options.skipProcessEnv) { untypedConfigService.skipProcessEnv = true; } configService.setEnvFilePaths(envFilePaths); configService.setParser(parser); return configService; }, inject: [CONFIGURATION_SERVICE_TOKEN, ...configProviderTokens], }; providers.push(configServiceProvider); if (validatedEnvConfig) { const validatedEnvConfigLoader = { provide: VALIDATED_ENV_LOADER, useFactory: (host) => { host[VALIDATED_ENV_PROPNAME] = validatedEnvConfig; }, inject: [CONFIGURATION_TOKEN], }; providers.push(validatedEnvConfigLoader); } this.environmentVariablesLoadedSignal(); return { module: ConfigModule_1, global: options.isGlobal, providers: isConfigToLoad ? [ ...providers, { provide: CONFIGURATION_LOADER, useFactory: (host, ...configurations) => { configurations.forEach((item, index) => this.mergePartial(host, item, providers[index])); }, inject: [CONFIGURATION_TOKEN, ...configProviderTokens], }, ] : providers, exports: [ConfigService, ...configProviderTokens], }; } /** * Registers configuration object (partial registration). * @param config */ static forFeature(config) { const configProvider = createConfigProvider(config); const serviceProvider = { provide: ConfigService, useFactory: (configService) => configService, inject: [CONFIGURATION_SERVICE_TOKEN, configProvider.provide], }; return { module: ConfigModule_1, providers: [ configProvider, serviceProvider, { provide: CONFIGURATION_LOADER, useFactory: (host, partialConfig) => { this.mergePartial(host, partialConfig, configProvider); }, inject: [CONFIGURATION_TOKEN, configProvider.provide], }, ], exports: [ConfigService, configProvider.provide], }; } static loadEnvFile(envFilePaths, parser, options) { let config = {}; for (const envFilePath of envFilePaths) { if (fs.existsSync(envFilePath)) { config = Object.assign(parser(fs.readFileSync(envFilePath)), config); if (options.expandVariables) { const expandOptions = typeof options.expandVariables === 'object' ? options.expandVariables : {}; config = expand({ ...expandOptions, parsed: config }).parsed || config; } } } return config; } static assignVariablesToProcess(config, override) { if (!isObject(config)) { return; } const keys = override ? Object.keys(config) : Object.keys(config).filter(key => !(key in process.env)); keys.forEach(key => { const value = config[key]; if (typeof value === 'string') { process.env[key] = value; } else if (typeof value === 'boolean' || typeof value === 'number') { process.env[key] = `${value}`; } }); } static mergePartial(host, item, provider) { const factoryRef = provider.useFactory; const token = getRegistrationToken(factoryRef); mergeConfigObject(host, item, token); } }; ConfigModule._envVariablesLoaded = new Promise(resolve => (ConfigModule_1.environmentVariablesLoadedSignal = resolve)); ConfigModule = ConfigModule_1 = __decorate([ Module({ imports: [ConfigHostModule], providers: [ { provide: ConfigService, useExisting: CONFIGURATION_SERVICE_TOKEN, }, ], exports: [ConfigHostModule, ConfigService], }) ], ConfigModule); export { ConfigModule };