UNPKG

@nivinjoseph/n-config

Version:
219 lines 9.08 kB
import { given } from "@nivinjoseph/n-defensive"; import { ApplicationException } from "@nivinjoseph/n-exception"; import "@nivinjoseph/n-ext"; import { TypeHelper } from "@nivinjoseph/n-util"; let config = {}; const parseProcessDotEnv = () => { // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition const obj = process.env || {}; // we need to remove useless values from obj const uselessValue = "[object Object]"; Object.keys(obj).forEach(t => { if (obj[t] === uselessValue) delete obj[t]; }); // console.log("parseProcessDotEnv", JSON.stringify(obj)); return obj; }; const parseCommandLineArgs = () => { const obj = {}; const args = process.argv; if (args.length <= 2) return obj; for (let i = 2; i < args.length; i++) { const arg = args[i].trim(); if (!arg.contains("=")) continue; const parts = arg.split("="); if (parts.length !== 2) continue; const key = parts[0].trim(); const value = parts[1].trim(); if (key.isEmptyOrWhiteSpace() || value.isEmptyOrWhiteSpace()) continue; const boolVal = value.toLowerCase(); if (boolVal === "true" || boolVal === "false") { obj[key] = boolVal === "true"; continue; } try { // const numVal = value.contains(".") ? Number.parseFloat(value) : Number.parseInt(value); // if (!Number.isNaN(numVal)) // { // obj[key] = numVal; // continue; // } const parsed = +value; if (!isNaN(parsed) && isFinite(parsed)) { obj[key] = parsed; continue; } } catch (error) { // suppress parse error? } const strVal = value; obj[key] = strVal; } // console.log("parseCommandLineArgs", JSON.stringify(obj)); return obj; }; if (typeof window !== "undefined" && typeof document !== "undefined") { const conf = APP_CONFIG; if (conf && typeof conf === "object") config = Object.assign(config, conf); if (window.config != null && typeof window.config === "string") config = Object.assign(config, JSON.parse(window.config.hexDecode())); } else { const fs = await import("node:fs"); const path = await import("node:path"); const parsePackageDotJson = () => { // eslint-disable-next-line @typescript-eslint/no-unsafe-call const packageDotJsonPath = path.resolve(process.cwd(), "package.json"); const obj = {}; // eslint-disable-next-line @typescript-eslint/no-unsafe-call if (!fs.existsSync(packageDotJsonPath)) return obj; // eslint-disable-next-line @typescript-eslint/no-unsafe-call const json = fs.readFileSync(packageDotJsonPath, "utf8"); // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (json != null && !json.toString().isEmptyOrWhiteSpace()) { const parsed = JSON.parse(json.toString()); obj.package = { name: parsed.getValue("name"), description: parsed.getValue("description"), version: parsed.getValue("version") }; } // console.log("parsePackageDotJson", JSON.stringify(obj)); return obj; }; const parseConfigDotJson = () => { // eslint-disable-next-line @typescript-eslint/no-unsafe-call const configDotJsonPath = path.resolve(process.cwd(), "config.json"); let obj = {}; // eslint-disable-next-line @typescript-eslint/no-unsafe-call if (!fs.existsSync(configDotJsonPath)) return obj; // eslint-disable-next-line @typescript-eslint/no-unsafe-call const json = fs.readFileSync(configDotJsonPath, "utf8"); // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (json != null && !json.toString().isEmptyOrWhiteSpace()) obj = JSON.parse(json.toString()); // console.log("parseConfigDotJson", JSON.stringify(obj)); return obj; }; /* BORROWED FROM https://github.com/motdotla/dotenv/blob/master/lib/main.js * Parses a string or buffer into an object * @param {(string|Buffer)} src - source to be parsed * @returns {Object} keys and values from src */ const parseDotEnv = () => { // eslint-disable-next-line @typescript-eslint/no-unsafe-call const dotEnvPath = path.resolve(process.cwd(), ".env"); const obj = {}; // eslint-disable-next-line @typescript-eslint/no-unsafe-call if (!fs.existsSync(dotEnvPath)) return obj; // eslint-disable-next-line @typescript-eslint/no-unsafe-call const src = fs.readFileSync(dotEnvPath, "utf8"); src.toString().split("\n").forEach((line) => { // matching "KEY' and 'VAL' in 'KEY=VAL' // eslint-disable-next-line no-useless-escape const keyValueArr = line.match(/^\s*([\w\.\-]+)\s*=\s*(.*)?\s*$/); // matched? if (keyValueArr != null) { const key = keyValueArr[1]; // default undefined or missing values to empty string let value = keyValueArr[2] || ""; // expand newlines in quoted values const len = value ? value.length : 0; if (len > 0 && value.startsWith(`"`) && value.charAt(len - 1) === `"`) { value = value.replace(/\\n/gm, "\n"); } // remove any surrounding quotes and extra spaces value = value.replace(/(^['"]|['"]$)/g, "").trim(); obj[key] = value; } }); // console.log("parseDotEnv", JSON.stringify(obj)); return obj; }; const mergedConfig = Object.assign(config, parsePackageDotJson(), parseConfigDotJson()); [ ...Object.entries(parseDotEnv()), ...Object.entries(parseProcessDotEnv()), ...Object.entries(parseCommandLineArgs()) ].forEach((entry) => mergedConfig.setValue(entry[0], entry[1])); config = mergedConfig; } export class ConfigurationManager { constructor() { } static async initializeProviders(providers) { given(providers, "providers").ensureHasValue().ensureIsArray().ensure(t => t.isNotEmpty); const providedConfig = (await Promise.all(providers.map(t => t.provide()))) .reduce((acc, t) => Object.assign(acc, t), {}); [ ...Object.entries(providedConfig), ...Object.entries(parseProcessDotEnv()), ...Object.entries(parseCommandLineArgs()) ].forEach((entry) => config.setValue(entry[0], entry[1])); } static getConfig(key) { given(key, "key").ensureHasValue().ensureIsString().ensure(t => !t.isEmptyOrWhiteSpace()); key = key.trim(); if (key === "*") { return JSON.parse(JSON.stringify(config)); } else if (key.startsWith("*") && key.endsWith("*")) { key = key.substring(1, key.length - 1); return Object.entries(config).reduce((acc, entry) => { if (entry[0].contains(key)) acc[entry[0]] = entry[1]; return acc; }, {}); } else if (key.startsWith("*")) { key = key.substring(1); return Object.entries(config).reduce((acc, entry) => { if (entry[0].endsWith(key)) acc[entry[0]] = entry[1]; return acc; }, {}); } else if (key.endsWith("*")) { key = key.substring(0, key.length - 1); return Object.entries(config).reduce((acc, entry) => { if (entry[0].startsWith(key)) acc[entry[0]] = entry[1]; return acc; }, {}); } else return config.getValue(key); } static requireConfig(key) { const value = ConfigurationManager.getConfig(key); if (value == null || (typeof value === "string" && value.isEmptyOrWhiteSpace())) throw new ApplicationException(`Required config '${key}' not found`); return value; } static requireStringConfig(key) { const value = ConfigurationManager.requireConfig(key); return value.toString(); } static requireNumberConfig(key) { const value = TypeHelper.parseNumber(ConfigurationManager.requireConfig(key)); if (value == null) throw new ApplicationException(`Required number config '${key}' not found`); return value; } static requireBooleanConfig(key) { const value = TypeHelper.parseBoolean(ConfigurationManager.requireConfig(key)); if (value == null) throw new ApplicationException(`Required boolean config '${key}' not found`); return value; } } //# sourceMappingURL=index.js.map