@anolilab/rc
Version:
The runtime configuration loader.
125 lines (121 loc) • 4.28 kB
JavaScript
import { homedir } from 'node:os';
import { cwd, env } from 'node:process';
import { readFileSync, isAccessibleSync } from '@visulima/fs';
import { parseJson, stripJsonComments } from '@visulima/fs/utils';
import { join, dirname } from '@visulima/path';
import { parse } from 'ini';
import { merge } from 'ts-deepmerge';
var __defProp$1 = Object.defineProperty;
var __name$1 = (target, value) => __defProp$1(target, "name", { value, configurable: true });
const isJson = /* @__PURE__ */ __name$1((value) => {
try {
JSON.parse(value);
} catch {
return false;
}
return true;
}, "isJson");
var __defProp = Object.defineProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
/**
* Modified copy of the env function from https://github.com/dominictarr/rc/blob/a97f6adcc37ee1cad06ab7dc9b0bd842bbc5c664/lib/utils.js#L42.
* @license https://github.com/dominictarr/rc/blob/master/LICENSE.APACHE2
* @license https://github.com/dominictarr/rc/blob/master/LICENSE.BSD
* @license https://github.com/dominictarr/rc/blob/master/LICENSE.MIT
* @param prefix
* @param environment
* @returns
*/
const getEnvironment = /* @__PURE__ */ __name((prefix, environment = env) => {
const returnValue = {};
const l = prefix.length;
for (const k in environment) {
if (k.toLowerCase().startsWith(prefix.toLowerCase())) {
const keypath = k.slice(Math.max(0, l)).split("__");
let emptyStringIndex;
while ((emptyStringIndex = keypath.indexOf("")) > -1) {
keypath.splice(emptyStringIndex, 1);
}
let cursor = returnValue;
keypath.forEach((subkey, index) => {
if (!subkey || typeof cursor !== "object") {
return;
}
if (index === keypath.length - 1) {
cursor[subkey] = environment[k];
}
if (cursor[subkey] === void 0) {
cursor[subkey] = {};
}
cursor = cursor[subkey];
});
}
}
return returnValue;
}, "getEnvironment");
const getConfigFiles = /* @__PURE__ */ __name((name, home, internalCwd, stopAt, environmentConfig, optionConfig) => {
const configFiles = /* @__PURE__ */ new Set();
for (const file of [`/etc/${name}/config`, `/etc/${name}rc`]) {
if (isAccessibleSync(file)) {
configFiles.add(file);
}
}
for (const file of [join(home, ".config", name, "config"), join(home, ".config", name), join(home, `.${name}`, "config"), join(home, `.${name}rc`)]) {
if (isAccessibleSync(file)) {
configFiles.add(file);
}
if (isAccessibleSync(`${file}.json`)) {
configFiles.add(`${file}.json`);
}
}
let start = internalCwd;
let endOfLoop = false;
const files = [join(`.${name}`, "config.json"), join(`.${name}`, "config"), join(`.${name}rc.json`), join(`.${name}rc`)];
const traversedFiles = [];
do {
for (const file of files) {
const traverseFile = join(start, file);
if (isAccessibleSync(traverseFile)) {
traversedFiles.push(traverseFile);
}
}
start = dirname(start);
if (endOfLoop) {
break;
}
endOfLoop = dirname(start) === start;
} while (stopAt ? start === stopAt : true);
for (const file of traversedFiles.toReversed()) {
configFiles.add(file);
}
if (typeof environmentConfig === "string" && isAccessibleSync(environmentConfig)) {
configFiles.add(environmentConfig);
}
if (optionConfig && isAccessibleSync(optionConfig)) {
configFiles.add(optionConfig);
}
return [...configFiles];
}, "getConfigFiles");
const rc = /* @__PURE__ */ __name((name, options = {}) => {
options = {
cwd: cwd(),
home: homedir(),
...options
};
const { config: _, ...environment } = getEnvironment(`${name}_`);
const configFiles = getConfigFiles(name, options.home, options.cwd, options.stopAt, env[`${name}_config`], options.config);
const configs = [];
for (const file of configFiles) {
const content = readFileSync(file, { buffer: false });
if (isJson(content)) {
configs.push(parseJson(stripJsonComments(content)));
} else {
configs.push(parse(content));
}
}
if (environment) {
configs.push(environment);
}
return { config: merge(options.defaults ?? {}, ...configs), files: configFiles };
}, "rc");
export { rc };