Skip to content

Commit 393b594

Browse files
author
oldskytree
committed
wip
1 parent 1178c03 commit 393b594

27 files changed

Lines changed: 364 additions & 1 deletion

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
node_modules
2-
build
2+
# build

build/core.d.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import type { MapParser } from './types/map';
2+
import type { OptionParser, OptionParserConfig } from './types/option';
3+
import type { RootParser, RootPrefixes, ConfigParser } from './types/root';
4+
import type { SectionParser, SectionProperties } from './types/section';
5+
import type { DeepPartial } from './types/utils';
6+
declare type Parser<T, R = any> = OptionParser<T, R> | SectionParser<T, R> | MapParser<T, R>;
7+
/**
8+
* Single option
9+
*/
10+
export declare function option<T, S = T, R = any>({ defaultValue, parseCli, parseEnv, validate, map: mapFunc }: OptionParserConfig<T, S, R>): OptionParser<S, R>;
11+
/**
12+
* Object with fixed properties.
13+
* Any unknown property will be reported as error.
14+
*/
15+
export declare function section<T, R = any>(properties: SectionProperties<T, R>): SectionParser<T, R>;
16+
/**
17+
* Object with user-specified keys and values,
18+
* parsed by valueParser.
19+
*/
20+
export declare function map<T extends Record<string, any>, V extends T[string] = T[string], R = any>(valueParser: Parser<V, R>, defaultValue: DeepPartial<Record<string, V>>): MapParser<Record<string, V>, R>;
21+
export declare function root<T>(rootParser: RootParser<T>, { envPrefix, cliPrefix }: RootPrefixes): ConfigParser<T>;
22+
export {};

build/core.js

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
"use strict";
2+
var __importDefault = (this && this.__importDefault) || function (mod) {
3+
return (mod && mod.__esModule) ? mod : { "default": mod };
4+
};
5+
Object.defineProperty(exports, "__esModule", { value: true });
6+
exports.root = exports.map = exports.section = exports.option = void 0;
7+
const lodash_1 = __importDefault(require("lodash"));
8+
const errors_1 = require("./errors");
9+
const lazy_1 = require("./lazy");
10+
const locator_1 = __importDefault(require("./locator"));
11+
/**
12+
* Single option
13+
*/
14+
function option({ defaultValue, parseCli = lodash_1.default.identity, parseEnv = lodash_1.default.identity, validate = lodash_1.default.noop, map: mapFunc = lodash_1.default.identity }) {
15+
const validateFunc = validate;
16+
return (locator, parsed) => {
17+
const config = parsed.root;
18+
const currNode = locator.parent ? lodash_1.default.get(parsed, locator.parent) : config;
19+
let value;
20+
if (locator.cliOption !== undefined) {
21+
value = parseCli(locator.cliOption);
22+
}
23+
else if (locator.envVar !== undefined) {
24+
value = parseEnv(locator.envVar);
25+
}
26+
else if (locator.option !== undefined) {
27+
value = locator.option;
28+
}
29+
else if (defaultValue !== undefined) {
30+
value = lodash_1.default.isFunction(defaultValue)
31+
? defaultValue(config, currNode)
32+
: defaultValue;
33+
}
34+
else {
35+
throw new errors_1.MissingOptionError(locator.name);
36+
}
37+
validateFunc(value, config, currNode);
38+
return mapFunc(value, config, currNode);
39+
};
40+
}
41+
exports.option = option;
42+
/**
43+
* Object with fixed properties.
44+
* Any unknown property will be reported as error.
45+
*/
46+
function section(properties) {
47+
const expectedKeys = lodash_1.default.keys(properties);
48+
return (locator, config) => {
49+
const unknownKeys = lodash_1.default.difference(lodash_1.default.keys(locator.option), expectedKeys);
50+
if (unknownKeys.length > 0) {
51+
throw new errors_1.UnknownKeysError(unknownKeys.map((key) => `${locator.name}.${key}`));
52+
}
53+
const lazyResult = (0, lazy_1.buildLazyObject)(expectedKeys, (key) => {
54+
const parser = properties[key];
55+
return () => parser(locator.nested(key), config);
56+
});
57+
lodash_1.default.set(config, locator.name, lazyResult);
58+
return lazyResult;
59+
};
60+
}
61+
exports.section = section;
62+
/**
63+
* Object with user-specified keys and values,
64+
* parsed by valueParser.
65+
*/
66+
function map(valueParser, defaultValue) {
67+
return (locator, config) => {
68+
if (locator.option === undefined) {
69+
if (!defaultValue) {
70+
return {};
71+
}
72+
locator = locator.resetOption(defaultValue);
73+
}
74+
const optionsToParse = Object.keys(locator.option);
75+
const lazyResult = (0, lazy_1.buildLazyObject)(optionsToParse, (key) => {
76+
return () => valueParser(locator.nested(key), config);
77+
});
78+
lodash_1.default.set(config, locator.name, lazyResult);
79+
return lazyResult;
80+
};
81+
}
82+
exports.map = map;
83+
function root(rootParser, { envPrefix, cliPrefix }) {
84+
return ({ options, env, argv }) => {
85+
const rootLocator = (0, locator_1.default)({ options, env, argv, envPrefix, cliPrefix });
86+
const parsed = rootParser(rootLocator, {});
87+
return (0, lazy_1.forceParsing)(parsed);
88+
};
89+
}
90+
exports.root = root;

build/errors.d.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export declare class MissingOptionError extends Error {
2+
optionName: string;
3+
constructor(optionName: string);
4+
}
5+
export declare class UnknownKeysError extends Error {
6+
keys: Array<string>;
7+
constructor(keys: Array<string>);
8+
}

build/errors.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
"use strict";
2+
Object.defineProperty(exports, "__esModule", { value: true });
3+
exports.UnknownKeysError = exports.MissingOptionError = void 0;
4+
class MissingOptionError extends Error {
5+
constructor(optionName) {
6+
const message = `${optionName} is required`;
7+
super(message);
8+
this.name = 'MissingOptionError';
9+
this.message = message;
10+
this.optionName = optionName;
11+
Error.captureStackTrace(this, MissingOptionError);
12+
}
13+
}
14+
exports.MissingOptionError = MissingOptionError;
15+
class UnknownKeysError extends Error {
16+
constructor(keys) {
17+
const message = `Unknown options: ${keys.join(', ')}`;
18+
super(message);
19+
this.name = 'UnknownKeysError';
20+
this.message = message;
21+
this.keys = keys;
22+
Error.captureStackTrace(this, UnknownKeysError);
23+
}
24+
}
25+
exports.UnknownKeysError = UnknownKeysError;

build/index.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export { root, section, map, option } from './core';
2+
export { MissingOptionError, UnknownKeysError } from './errors';

build/index.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
"use strict";
2+
Object.defineProperty(exports, "__esModule", { value: true });
3+
exports.UnknownKeysError = exports.MissingOptionError = exports.option = exports.map = exports.section = exports.root = void 0;
4+
var core_1 = require("./core");
5+
Object.defineProperty(exports, "root", { enumerable: true, get: function () { return core_1.root; } });
6+
Object.defineProperty(exports, "section", { enumerable: true, get: function () { return core_1.section; } });
7+
Object.defineProperty(exports, "map", { enumerable: true, get: function () { return core_1.map; } });
8+
Object.defineProperty(exports, "option", { enumerable: true, get: function () { return core_1.option; } });
9+
var errors_1 = require("./errors");
10+
Object.defineProperty(exports, "MissingOptionError", { enumerable: true, get: function () { return errors_1.MissingOptionError; } });
11+
Object.defineProperty(exports, "UnknownKeysError", { enumerable: true, get: function () { return errors_1.UnknownKeysError; } });

build/lazy.d.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import type { LazyObject } from './types/lazy';
2+
export declare const isLazy: unique symbol;
3+
export declare function buildLazyObject<T>(keys: Array<keyof T>, getKeyGetter: (key: keyof T) => () => (T[keyof T] | LazyObject<T[keyof T]>)): LazyObject<T>;
4+
export declare function forceParsing<T>(lazyObject: LazyObject<T>): T;

build/lazy.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
"use strict";
2+
var __importDefault = (this && this.__importDefault) || function (mod) {
3+
return (mod && mod.__esModule) ? mod : { "default": mod };
4+
};
5+
Object.defineProperty(exports, "__esModule", { value: true });
6+
exports.forceParsing = exports.buildLazyObject = exports.isLazy = void 0;
7+
const lodash_1 = __importDefault(require("lodash"));
8+
exports.isLazy = Symbol('isLazy');
9+
function buildLazyObject(keys, getKeyGetter) {
10+
const target = {
11+
[exports.isLazy]: true
12+
};
13+
for (const key of keys) {
14+
defineLazy(target, key, getKeyGetter(key));
15+
}
16+
return target;
17+
}
18+
exports.buildLazyObject = buildLazyObject;
19+
function forceParsing(lazyObject) {
20+
return lodash_1.default.cloneDeep(lazyObject);
21+
}
22+
exports.forceParsing = forceParsing;
23+
function defineLazy(object, key, getter) {
24+
let defined = false;
25+
let value;
26+
Object.defineProperty(object, key, {
27+
get() {
28+
if (!defined) {
29+
defined = true;
30+
const val = getter();
31+
if (isLazyObject(val)) {
32+
value = forceParsing(val);
33+
}
34+
else {
35+
value = val;
36+
}
37+
}
38+
return value;
39+
},
40+
enumerable: true
41+
});
42+
}
43+
function isLazyObject(value) {
44+
return lodash_1.default.isObject(value) && hasOwnProperty(value, exports.isLazy) && value[exports.isLazy] === true;
45+
}
46+
function hasOwnProperty(obj, prop) {
47+
return obj.hasOwnProperty(prop);
48+
}

build/locator.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
import type { LocatorArg, Locator } from './types/locator';
2+
export default function <T>({ options, env, argv, envPrefix, cliPrefix }: LocatorArg<T>): Locator<T>;

0 commit comments

Comments
 (0)