read-conf
Version:
reads a config file
394 lines (372 loc) • 9.64 kB
JavaScript
var __is, _is, allTrue, allTypes, getChecker, getKey, getRequired, getTypeError, getTypeNames, isValid, iterate, normalize, realKey, toStr;
allTypes = [String, Number, Array, Object, Function, RegExp, Boolean];
toStr = Object.prototype.toString;
_is = {};
__is = {
Boolean: (bool) => {
return typeof bool === "boolean";
}
};
iterate = (obj, cb) => {
var i, key, keys, len, results;
keys = Object.keys(obj).sort();
results = [];
for (i = 0, len = keys.length; i < len; i++) {
key = keys[i];
results.push(cb(key, obj[key]));
}
return results;
};
getChecker = (name) => {
return (o) => {
return toStr.call(o) === `[object ${name}]`;
};
};
allTypes.forEach(({name}) => {
return __is[name] != null ? __is[name] : __is[name] = _is[name.toLowerCase()] = getChecker(name);
});
normalize = (schema) => {
var k, types, v;
for (k in schema) {
v = schema[k];
if (k === "__strict") {
continue;
}
if (v.type) {
v.types = types = v.type;
} else if ((types = v.types) == null) {
v = schema[k] = {
types: (types = [v])
};
}
if (!Array.isArray(types)) {
v.types = [types];
}
if (!v._normalized) {
v._normalized = true;
if (v.types != null) {
v._types = v.types.map((type) => {
var tmp;
if (!(tmp = __is[type.name])) {
throw new Error(`Schema invalid. ${type} is no Type.`);
}
return tmp;
});
}
}
}
return schema;
};
getRequired = (schema) => {
var required;
required = {};
iterate(schema, (k, v) => {
if (v.required) {
return required[k] = true;
}
});
return required;
};
getTypeNames = (types) => {
return types.map(({name}) => {
return name;
});
};
getTypeError = (obj, schema) => {
var checker, name, str, types;
for (name in __is) {
checker = __is[name];
if (checker(obj)) {
break;
}
}
str = `of type '${name}'. Valid type`;
if ((types = schema.types).length > 1) {
str += "s are: ['" + getTypeNames(types).join("', '") + "']";
} else {
str += ` is: '${types[0].name}'`;
}
return str;
};
isValid = (obj, schema) => {
var types;
if (!(types = schema._types)) {
return true;
}
return types.some((checker) => {
return checker(obj);
});
};
allTrue = (arr) => {
var i, len, val;
for (i = 0, len = arr.length; i < len; i++) {
val = arr[i];
if (val === false) {
return false;
}
}
return true;
};
getKey = (key, newKey) => {
if (!key) {
return newKey;
}
return key + "$" + newKey;
};
realKey = (key) => {
var char, i, lastChar, len, newChar, str;
lastChar = "";
str = "";
for (i = 0, len = key.length; i < len; i++) {
char = key[i];
if (char === "$") {
newChar = ".";
} else if (char === "_" && lastChar === "$") {
newChar = "$";
} else {
newChar = char;
}
lastChar = char;
str += newChar;
}
return str;
};
module.exports = (obj, schema, {isNormalized, ignore = []} = {}) => {
return new Promise((resolve, reject) => {
var addProblem, keys, problems, required, strict, walk;
if (!isNormalized) {
normalize(schema);
}
required = getRequired(schema);
problems = {};
addProblem = (key, msg) => {
return problems[realKey(key)] = msg;
};
walk = (curr, key, currSchema) => {
var i, isStrict, item, len, newKey, newSchema;
delete required[key];
if (~ignore.indexOf(key)) {
return true;
}
if (currSchema == null) {
return false;
}
if (curr == null) {
return true;
}
if (!isValid(curr, currSchema)) {
addProblem(key, getTypeError(curr, currSchema));
}
if (_is.array(curr)) {
newSchema = schema[newKey = getKey(key, "_item")];
if (newSchema != null) {
for (i = 0, len = curr.length; i < len; i++) {
item = curr[i];
walk(item, newKey, newSchema);
}
}
} else if (_is.object(curr)) {
isStrict = currSchema.strict;
iterate(curr, (k, item) => {
if (!walk(item, (newKey = getKey(key, k)), schema[newKey]) && isStrict) {
return addProblem(newKey, "unexpected");
} else if (!isStrict) {
return walk(item, (newKey = getKey(key, "_item")), schema[newKey]);
}
});
}
return true;
};
strict = schema.__strict;
if (strict == null) {
strict = true;
}
walk(obj, "", {
_types: [_is.object],
strict: strict
});
iterate(required, (k) => {
return addProblem(k, "missing");
});
if ((keys = Object.keys(problems)).length > 0) {
return reject(keys.sort().map((key) => {
return `'${key}' is ${problems[key]}`;
}));
} else {
return resolve();
}
});
};
module.exports.getAdder = (schema) => {
normalize(schema);
return (newSchema, merge) => {
var k, k3, ref, v, v2, v3;
ref = normalize(newSchema);
for (k in ref) {
v = ref[k];
if (merge && (v2 = schema[k])) {
for (k3 in v) {
v3 = v[k3];
v2[k3] = v3;
}
} else {
schema[k] = v;
}
}
return schema;
};
};
module.exports.setDefaults = (obj, schema, {concat, ignore = []} = {}) => {
return iterate(schema, (k, v) => {
var def, i, item, len, parent, prop, results, val;
if (((def = v.default) != null) && !~ignore.indexOf(k)) {
({parent, prop} = k.split("$").reduce(((acc, curr) => {
var val;
val = acc.parent = acc.parent[acc.prop];
if (val == null) {
throw new Error(`couldn't set default value on ${realKey(k)}`);
}
acc.prop = curr;
return acc;
}), {
parent: {
1: obj
},
prop: 1
}));
if ((val = parent[prop]) == null) {
return parent[prop] = def;
} else if (concat && Array.isArray(val) && Array.isArray(def)) {
results = [];
for (i = 0, len = def.length; i < len; i++) {
item = def[i];
if (!~val.indexOf(item)) {
results.push(val.push(item));
} else {
results.push(void 0);
}
}
return results;
}
}
});
};
module.exports.toDoc = (schema, type) => {
var concat, getIndent, ignore, indent, indention, keys, lines, parent, push;
lines = ["```js", "module.exports = {"];
indention = " ";
indent = 1;
getIndent = () => {
return indention.repeat(indent);
};
normalize(schema);
delete schema.__strict;
keys = Object.keys(schema);
if (keys.length < 5) {
lines.push("\n // …");
}
({push} = Array.prototype);
concat = (arr) => {
return push.apply(lines, arr);
};
ignore = [];
parent = "";
iterate(schema, (k, v) => {
var children, def, desc, dyn, dynName, dynamic, getTypes, i, ind, len, names, ref, required, tmpval, typeAbove, val;
if (~ignore.indexOf(k)) {
return;
}
// end of block
if (parent && !k.startsWith(parent + "$")) {
parent = "";
indent--;
lines.push("");
lines.push(getIndent() + "},");
}
ind = getIndent();
names = getTypeNames(v.types);
getTypes = (long, schem) => {
var _n, types;
if (schem) {
_n = getTypeNames(schem.types);
} else {
_n = names;
}
types = _n.length === 1 ? _n[0] : "[" + _n.join(", ") + "]";
if (long) {
types = _n.length === 1 ? "type: " + types : "types: " + types;
}
return types;
};
if (v._default == null) {
def = v.default;
}
if (def == null) {
def = null;
}
def = JSON.stringify(def) + ",";
typeAbove = def.length > 20;
if (~names.indexOf("Object") || ~names.indexOf("Array")) {
dynamic = [];
children = keys.filter((key) => {
if (key.startsWith(k + "$")) {
if (key.startsWith(k + "$_item")) {
dynamic.push(key);
return false;
}
return true;
}
return false;
});
if (children.length > 0) {
if (def === "{},") {
def = "{";
typeAbove = true;
parent = k;
indent++;
}
}
}
//else if ~names.indexOf("Object")
// newLine
lines.push("");
// description above
if ((desc = (ref = v.desc) != null ? ref.split("\n") : void 0) != null) {
concat(desc.map((str) => {
return ind + "// " + str;
}));
}
required = v.required ? "(required) " : "";
if (typeAbove) {
// type above
lines.push(ind + "// " + required + getTypes(true));
}
if (v._default) {
// default above when _default is present
lines.push(ind + "// Default: " + v._default);
}
// dynamic above
if ((dynamic != null ? dynamic.length : void 0) > 0) {
for (i = 0, len = dynamic.length; i < len; i++) {
dyn = dynamic[i];
tmpval = schema[dyn];
ignore.push(dyn);
dynName = "$" + realKey(dyn.replace(k + "$_item", "item"));
lines.push(ind + `// ${dynName} (` + getTypes(false, tmpval) + ") " + (tmpval.desc || ""));
}
}
if (parent) {
// value
k = k.replace(parent + "$", "");
}
val = ind + realKey(k) + ": " + def;
if (!typeAbove) {
val += " // " + required + getTypes(false);
}
return lines.push(val);
});
lines.push("\n // …\n");
lines.push("}");
lines.push("```");
return lines.join("\n");
};