@adpt/cli
Version:
AdaptJS command line interface
193 lines • 6.45 kB
JavaScript
;
/*
* Copyright 2020 Unbounded Systems, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const utils_1 = require("@adpt/utils");
const conf_1 = tslib_1.__importDefault(require("conf"));
const debug_1 = tslib_1.__importDefault(require("debug"));
const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
const p_defer_1 = tslib_1.__importDefault(require("p-defer"));
const path_1 = tslib_1.__importDefault(require("path"));
const config_1 = require("./config");
const get_val_1 = require("./get_val");
// tslint:disable-next-line: no-var-requires
const pjson = require("../../../package.json");
const debug = debug_1.default("adapt:config");
const envPrefix = "ADAPT_CONFIG_";
const defaultConfigFilenames = [
"config.json5",
"config.json",
];
let pConfig;
async function findUserConfigFile(pkgConfig) {
for (const fn of defaultConfigFilenames) {
const filename = path_1.default.join(pkgConfig.configDir, fn);
if (await fs_extra_1.default.pathExists(filename))
return filename;
}
return path_1.default.join(pkgConfig.configDir, defaultConfigFilenames[0]);
}
exports.findUserConfigFile = findUserConfigFile;
async function readUserConfigFile(userConfigFile) {
try {
const val = await utils_1.readJson5(userConfigFile);
if (val == null || !utils_1.isObject(val) || Array.isArray(val)) {
throw new Error(`Does not contain a single object in ` +
`JSON/JSON5 format (actual type=${typeof val})`);
}
return val;
}
catch (err) {
if (err && err.code === "ENOENT")
return {}; // Empty config
return throwConfigFileError(userConfigFile, err);
}
}
function throwConfigFileError(userConfigFile, err, info = "") {
err = utils_1.ensureError(err);
if (info)
info = ` ${info}`;
let msg = `Config file '${userConfigFile}'${info}: `;
switch (err.code) {
case "ENOENT":
msg += "File not found";
break;
case "EACCES":
msg += "Permission denied";
break;
default:
if (err.message) {
msg += err.message.replace(/^Error: /, "");
}
else {
msg += "Unknown error - " + err.toString();
}
}
throw new utils_1.UserError(msg);
}
exports.throwConfigFileError = throwConfigFileError;
const envVarPropTransform = (prop) => envPrefix + prop.toUpperCase();
async function loadUserConfig(userConfigFile) {
const rawConf = await readUserConfigFile(userConfigFile);
const conf = {};
const details = {};
const sources = [
{
sourceType: "Environment",
source: envVarPropTransform,
obj: process.env,
opts: { propTransform: envVarPropTransform },
},
{
sourceType: "File",
source: userConfigFile,
obj: rawConf,
},
{
sourceType: "Default",
source: "Default",
obj: {},
opts: { useDefault: true },
},
];
function getVal(key) {
for (const s of sources) {
const val = get_val_1.getValIfSet(key, s.obj, config_1.userConfigSchema, s.opts);
if (val != null) {
conf[key] = val.parsed;
details[key] = {
parsed: val.parsed,
sourceType: s.sourceType,
source: typeof s.source === "function" ? s.source(key) : s.source,
store: val.store,
valid: true,
};
return;
}
}
}
config_1.userConfigProps.forEach(getVal);
// Find any user config keys we don't understand
const badKeys = Object.keys(rawConf)
.filter((key) => !(key in config_1.userConfigSchema));
badKeys.forEach((key) => {
details[key] = {
parsed: rawConf[key],
sourceType: "File",
source: userConfigFile,
store: rawConf[key],
valid: false,
};
});
// Print warnings for config keys we don't understand
// TODO: This should not be a debug, but rather a log message that doesn't
// display with the default CLI log level.
if (debug.enabled && badKeys.length) {
debug(`The following configuration items are invalid: ${badKeys.join(", ")}`);
}
return {
config: conf,
details,
};
}
exports.loadUserConfig = loadUserConfig;
function createState(configDir, versionCheck = true) {
const state = new conf_1.default({
configName: ".state",
cwd: configDir,
defaults: config_1.cliStateDefaults,
deserialize: utils_1.parseJson5,
serialize: (val) => utils_1.stringifyJson5(val, { space: 2 }) + "\n",
});
if (versionCheck && state.get("version") !== pjson.version) {
state.set({
installed: Date.now(),
version: pjson.version,
});
}
return state;
}
exports.createState = createState;
async function createConfig(pkgConfig) {
const userConfigFile = await findUserConfigFile(pkgConfig);
const user = (await loadUserConfig(userConfigFile)).config;
const state = createState(pkgConfig.configDir);
if (!pConfig)
pConfig = p_defer_1.default();
pConfig.resolve({
state,
package: pkgConfig,
user,
userConfigFile,
});
}
exports.createConfig = createConfig;
function config() {
if (!pConfig)
pConfig = p_defer_1.default();
return pConfig.promise;
}
exports.config = config;
/**
* Exported for testing only
* @internal
*/
function _resetConfig() {
pConfig = undefined;
}
exports._resetConfig = _resetConfig;
//# sourceMappingURL=load.js.map