UNPKG

@nestjs-mod/common

Version:

A collection of utilities for unifying NestJS applications and modules

246 lines 11.9 kB
"use strict"; var DotEnvService_1; Object.defineProperty(exports, "__esModule", { value: true }); exports.DotEnvService = void 0; const tslib_1 = require("tslib"); const common_1 = require("@nestjs/common"); const crypto_1 = require("crypto"); const dotenv_1 = require("dotenv"); const fast_glob_1 = tslib_1.__importDefault(require("fast-glob")); const fs_1 = require("fs"); const path_1 = require("path"); const default_context_name_1 = require("../../../../utils/default-context-name"); const project_utils_configuration_1 = require("../project-utils.configuration"); const gitignore_file_1 = require("./gitignore-file"); const wrap_application_options_service_1 = require("./wrap-application-options.service"); let DotEnvService = DotEnvService_1 = class DotEnvService { constructor(wrapApplicationOptionsService, projectUtilsConfiguration, gitignoreService) { this.wrapApplicationOptionsService = wrapApplicationOptionsService; this.projectUtilsConfiguration = projectUtilsConfiguration; this.gitignoreService = gitignoreService; this.logger = new common_1.Logger(DotEnvService_1.name); } getEnvFilePath() { return this.projectUtilsConfiguration.envFile; } keys(includeHiddenKeys = false) { const modules = Object.entries(this.wrapApplicationOptionsService.modules || {}) .map(([, value]) => value) .flat() .filter((m) => m.getNestModuleMetadata?.()?.moduleCategory) .map((m) => m.moduleSettings); const contextName = (0, default_context_name_1.defaultContextName)(); const keys = [ ...new Set([ ...modules .map((m) => Object.keys(m?.[contextName]?.staticEnvironments?.validations || {}) .filter((key) => !m?.[contextName]?.staticEnvironments?.validations[key]?.propertyValueExtractors.some((e) => e.demoMode)) .map((key) => m?.[contextName]?.staticEnvironments?.validations[key]?.propertyNameFormatters .filter((f) => (f.value === undefined || includeHiddenKeys || (!includeHiddenKeys && !m?.[contextName]?.staticEnvironments?.modelPropertyOptions.find((p) => (p.originalName === key || p.name) && p.hidden))) && f.name === 'dotenv') .map((f) => f.value) .flat()) .flat()) .flat(), ...modules .map((m) => Object.keys(m?.[contextName]?.environments?.validations || {}) .filter((key) => !m?.[contextName]?.environments?.validations[key]?.propertyValueExtractors.some((e) => e.demoMode)) .map((key) => m?.[contextName]?.environments?.validations[key]?.propertyNameFormatters .filter((f) => (f.value === undefined || includeHiddenKeys || (!includeHiddenKeys && !m?.[contextName]?.environments?.modelPropertyOptions.find((p) => (p.originalName === key || p.name) && p.hidden))) && f.name === 'dotenv') .map((f) => f.value) .flat()) .flat()) .flat(), ...modules .map((m) => Object.entries(m?.[contextName]?.featureModuleEnvironments || {}) .map(([, v]) => (v || []) .map((vItem) => Object.keys(vItem?.validations || {}) .filter((key) => !vItem?.validations[key]?.propertyValueExtractors.some((e) => e.demoMode)) .map((key) => vItem?.validations[key]?.propertyNameFormatters .filter((f) => (f.value === undefined || includeHiddenKeys || (!includeHiddenKeys && !vItem?.modelPropertyOptions.find((p) => (p.originalName === key || p.name) && p.hidden))) && f.name === 'dotenv') .map((f) => f.value) .flat())) .flat() .flat()) .flat()) .flat(), ].filter(Boolean)), ]; return keys; } readFile(envFile, updateProcessEnv = true) { const virtualEnv = {}; const loadedEnvJson = (0, dotenv_1.config)({ path: envFile, processEnv: virtualEnv }).parsed || {}; if (updateProcessEnv) { for (const [key, value] of Object.entries(loadedEnvJson)) { if (process.env[key] === undefined && value !== undefined) { process.env[key] = value; } } } return loadedEnvJson; } async writeFile(envFile, data) { const newData = JSON.parse(JSON.stringify(data)); const checksumEnvs = await this.getEnvironmentsFromFilesCheckSum(); const checksumKeys = Object.keys(checksumEnvs.processed); if (checksumKeys.length > 0) { for (const key of checksumKeys) { delete newData[key]; } delete newData['# file checksums']; newData['# file checksums'] = ''; for (const key of checksumKeys) { delete newData[key]; newData[key] = checksumEnvs.processed[key].sha256; } if (this.projectUtilsConfiguration.saveFilesWithCheckSum) { (0, fs_1.writeFileSync)(envFile + '.checksum.json', JSON.stringify(checksumEnvs.processed, null, 4)); } } try { const envContent = Object.entries(newData) .map(([key, value]) => { if (key.trim().startsWith('#')) { return `${key}${value ? value : ''}`; } if (value !== undefined && value !== null && !isNaN(+value)) { return `${key}=${value}`; } if (value && (value.includes('*') || value.includes('!') || value.includes('$') || value.includes(' '))) { if (value.includes("'")) { return `${key}='${value.split("'").join("\\'")}'`; } return `${key}='${value}'`; } return `${key}=${value}`; }) .join('\n'); if (!envFile) { return; } const fileDir = (0, path_1.dirname)(envFile); if (!(0, fs_1.existsSync)(fileDir)) { (0, fs_1.mkdirSync)(fileDir, { recursive: true }); } (0, fs_1.writeFileSync)(envFile, envContent); // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (err) { this.logger.error(err, err.stack); return; } } async getEnvironmentsFromFilesCheckSum() { const processed = {}; for (const [name, rule] of Object.entries(this.projectUtilsConfiguration.filesCheckSumToEnvironments || {})) { const globs = rule.glob; const folders = !Array.isArray(globs) ? rule.folders.map((folder) => (0, path_1.join)(folder, globs)) : globs.map((glob) => rule.folders.map((folder) => (0, path_1.join)(folder, glob))).flat(); const fileList = await (0, fast_glob_1.default)(folders, { dot: true }); const files = fileList .map((filePath) => { const fileContent = rule.prepare ? rule.prepare((0, fs_1.readFileSync)(filePath).toString(), filePath) : (0, fs_1.readFileSync)(filePath).toString(); return { filePath, fileContent, }; }) .sort((a, b) => a.filePath.localeCompare(b.filePath)); processed[name] = { fileList: files.map((fileContent) => fileContent.filePath), sha256: (0, crypto_1.createHash)('sha256') .update(JSON.stringify(files.map((fileContent) => fileContent.fileContent))) .digest('hex'), }; } if (this.projectUtilsConfiguration.prepareProcessedFilesCheckSumToEnvironments) { return { processed: this.projectUtilsConfiguration.prepareProcessedFilesCheckSumToEnvironments(processed) }; } return { processed }; } read(updateProcessEnv = true, ignoreCheckNeededKeys = false) { const envFile = this.getEnvFilePath(); if (!envFile) { return; } try { const neededKeys = this.keys(); const existsEnvJson = this.readFile(envFile, false) || {}; const neededEnvs = { ...(ignoreCheckNeededKeys ? existsEnvJson : {}), ...neededKeys.reduce((all, key) => ({ ...all, [String(key)]: existsEnvJson[key] || '' }), existsEnvJson), }; if (updateProcessEnv) { for (const [key, value] of Object.entries(neededEnvs)) { if (process.env[key] === undefined && value !== undefined) { process.env[key] = value; } } } return neededEnvs; // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (err) { this.logger.error(err, err.stack); return undefined; } } async write(data, ignoreCheckNeededKeys = false) { const envFile = this.getEnvFilePath(); if (!envFile) { return; } try { const neededKeys = this.keys(); const existsEnvJson = this.readFile(envFile, false) || {}; const newEnvJson = { ...(ignoreCheckNeededKeys ? data : {}), ...neededKeys.reduce((all, key) => ({ ...all, [String(key)]: data[key] || existsEnvJson?.[key] || '' }), existsEnvJson), }; await this.writeFile(envFile, newEnvJson); const envExampleFile = envFile.replace('.env', '-example.env').replace('/-example.env', '/example.env'); if ((0, fs_1.existsSync)(envExampleFile)) { const existsExampleEnvJson = this.readFile(envExampleFile, false) || {}; const newEnvJson = [...neededKeys, ...(ignoreCheckNeededKeys ? Object.keys(data) : [])].reduce((all, key) => ({ ...all, [String(key)]: existsExampleEnvJson?.[key] || (key?.trim().startsWith('#') ? '' : ''), }), existsExampleEnvJson); await this.writeFile(envExampleFile, newEnvJson); } else { // eslint-disable-next-line @typescript-eslint/no-explicit-any const newEnvJson = [...neededKeys, ...(ignoreCheckNeededKeys ? Object.keys(data) : [])].reduce((all, key) => ({ ...all, [String(key)]: key?.trim().startsWith('#') ? '' : '' }), {}); await this.writeFile(envExampleFile, newEnvJson); } this.gitignoreService.addGitIgnoreEntry([(0, path_1.basename)(envFile)]); // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (err) { this.logger.error(err, err.stack); return; } } }; exports.DotEnvService = DotEnvService; exports.DotEnvService = DotEnvService = DotEnvService_1 = tslib_1.__decorate([ (0, common_1.Injectable)(), tslib_1.__metadata("design:paramtypes", [wrap_application_options_service_1.WrapApplicationOptionsService, project_utils_configuration_1.ProjectUtilsConfiguration, gitignore_file_1.GitignoreService]) ], DotEnvService); //# sourceMappingURL=dot-env.service.js.map