@nestjs-mod/common
Version:
A collection of utilities for unifying NestJS applications and modules
246 lines • 11.9 kB
JavaScript
;
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