@wener/utils 1.1.3 → 1.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -0
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/system/index.js +1 -1
- package/dist/system/index.js.map +1 -1
- package/lib/index.js +8 -1
- package/lib/index.js.map +1 -1
- package/lib/io/isBuffer.js.map +1 -1
- package/lib/logging/createChildLogger.js +18 -0
- package/lib/logging/createChildLogger.js.map +1 -0
- package/lib/logging/createNoopLogger.js +14 -0
- package/lib/logging/createNoopLogger.js.map +1 -0
- package/lib/logging/createWriteLogger.js +13 -0
- package/lib/logging/createWriteLogger.js.map +1 -0
- package/lib/modules/isModule.js +6 -0
- package/lib/modules/isModule.js.map +1 -0
- package/lib/modules/parseModuleId.js.map +1 -1
- package/lib/objects/get.js +14 -0
- package/lib/objects/get.js.map +1 -0
- package/lib/objects/parseObjectPath.js +27 -0
- package/lib/objects/parseObjectPath.js.map +1 -0
- package/lib/objects/set.js +36 -0
- package/lib/objects/set.js.map +1 -0
- package/lib/strings/renderTemplate.js +23 -0
- package/lib/strings/renderTemplate.js.map +1 -0
- package/package.json +13 -6
- package/src/index.ts +13 -1
- package/src/io/isBuffer.ts +5 -1
- package/src/logging/Logger.ts +19 -0
- package/src/logging/createChildLogger.ts +16 -0
- package/src/logging/createNoopLogger.ts +13 -0
- package/src/logging/createWriteLogger.ts +15 -0
- package/src/logging/logger.test.ts +34 -0
- package/src/modules/isModule.ts +10 -0
- package/src/modules/parseModuleId.ts +5 -0
- package/src/objects/get.test.ts +116 -0
- package/src/objects/get.ts +49 -0
- package/src/objects/parseObjectPath.test.ts +18 -0
- package/src/objects/parseObjectPath.ts +40 -0
- package/src/objects/set.test.ts +405 -0
- package/src/objects/set.ts +48 -0
- package/src/strings/renderTemplate.test.ts +25 -0
- package/src/strings/renderTemplate.ts +31 -0
- package/tsconfig.json +0 -4
- package/lib/strings/templates.js +0 -8
- package/lib/strings/templates.js.map +0 -1
- package/src/strings/templates.ts +0 -13
package/src/index.ts
CHANGED
|
@@ -7,6 +7,11 @@ export {
|
|
|
7
7
|
type MaybeArray,
|
|
8
8
|
} from './arrays/MaybeArray';
|
|
9
9
|
|
|
10
|
+
// object
|
|
11
|
+
export { get } from './objects/get';
|
|
12
|
+
export { set } from './objects/set';
|
|
13
|
+
export { parseObjectPath } from './objects/parseObjectPath';
|
|
14
|
+
|
|
10
15
|
// async
|
|
11
16
|
export { createLazyPromise, type LazyPromise } from './asyncs/LazyPromise';
|
|
12
17
|
export { setAsyncInterval, clearAsyncInterval } from './asyncs/AsyncInterval';
|
|
@@ -26,10 +31,17 @@ export { dequal } from './validations/dequal';
|
|
|
26
31
|
|
|
27
32
|
// modules
|
|
28
33
|
export { parseModuleId, type ParsedModuleId } from './modules/parseModuleId';
|
|
34
|
+
export { isModule } from './modules/isModule';
|
|
35
|
+
|
|
36
|
+
// logging
|
|
37
|
+
export { type Logger, type LogLevel } from './logging/Logger';
|
|
38
|
+
export { createWriteLogger } from './logging/createWriteLogger';
|
|
39
|
+
export { createNoopLogger } from './logging/createNoopLogger';
|
|
40
|
+
export { createChildLogger } from './logging/createChildLogger';
|
|
29
41
|
|
|
30
42
|
// strings
|
|
31
43
|
export { pascalCase, camelCase } from './strings/camelCase';
|
|
32
|
-
export {
|
|
44
|
+
export { renderTemplate } from './strings/renderTemplate';
|
|
33
45
|
|
|
34
46
|
export { createRandom } from './maths/random';
|
|
35
47
|
export { isBuffer } from './io/isBuffer';
|
package/src/io/isBuffer.ts
CHANGED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface Logger {
|
|
2
|
+
trace(...data: any[]): void;
|
|
3
|
+
|
|
4
|
+
debug(...data: any[]): void;
|
|
5
|
+
|
|
6
|
+
info(...data: any[]): void;
|
|
7
|
+
|
|
8
|
+
warn(...data: any[]): void;
|
|
9
|
+
|
|
10
|
+
error(...data: any[]): void;
|
|
11
|
+
|
|
12
|
+
child?: (o: object) => Logger;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface LoggerWithChild extends Logger {
|
|
16
|
+
child: (o: object) => LoggerWithChild;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type LogLevel = 'error' | 'warn' | 'info' | 'debug' | 'trace';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Logger, LoggerWithChild } from './Logger';
|
|
2
|
+
import { createWriteLogger } from './createWriteLogger';
|
|
3
|
+
|
|
4
|
+
export function createChildLogger(l: Logger, ctx: object): LoggerWithChild {
|
|
5
|
+
if (l.child) {
|
|
6
|
+
return l.child(ctx) as LoggerWithChild;
|
|
7
|
+
}
|
|
8
|
+
return createWriteLogger((o) => {
|
|
9
|
+
const { level, values, ...c } = o;
|
|
10
|
+
if (Object.keys(c).length) {
|
|
11
|
+
l[level](c, ...values);
|
|
12
|
+
} else {
|
|
13
|
+
l[level](...values);
|
|
14
|
+
}
|
|
15
|
+
}, ctx);
|
|
16
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { LoggerWithChild } from './Logger';
|
|
2
|
+
|
|
3
|
+
export function createNoopLogger(): LoggerWithChild {
|
|
4
|
+
const noop = (..._: any[]) => void 0;
|
|
5
|
+
return {
|
|
6
|
+
trace: noop,
|
|
7
|
+
debug: noop,
|
|
8
|
+
info: noop,
|
|
9
|
+
warn: noop,
|
|
10
|
+
error: noop,
|
|
11
|
+
child: () => createNoopLogger(),
|
|
12
|
+
};
|
|
13
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { LoggerWithChild, LogLevel } from './Logger';
|
|
2
|
+
|
|
3
|
+
export function createWriteLogger(
|
|
4
|
+
write: (o: { level: LogLevel; values: any[] } & Record<string | symbol, any>) => void,
|
|
5
|
+
context: object = {},
|
|
6
|
+
): LoggerWithChild {
|
|
7
|
+
return {
|
|
8
|
+
trace: (...values) => write({ ...context, level: 'trace', values }),
|
|
9
|
+
debug: (...values) => write({ ...context, level: 'debug', values }),
|
|
10
|
+
info: (...values) => write({ ...context, level: 'info', values }),
|
|
11
|
+
warn: (...values) => write({ ...context, level: 'warn', values }),
|
|
12
|
+
error: (...values) => write({ ...context, level: 'error', values }),
|
|
13
|
+
child: (ctx) => createWriteLogger(write, { ...context, ...ctx }),
|
|
14
|
+
};
|
|
15
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import test from 'ava';
|
|
2
|
+
import { createChildLogger } from './createChildLogger';
|
|
3
|
+
import { createWriteLogger } from './createWriteLogger';
|
|
4
|
+
|
|
5
|
+
test('logger', (t) => {
|
|
6
|
+
{
|
|
7
|
+
let logs: any[] = [];
|
|
8
|
+
const base = createWriteLogger((o) => logs.push(o));
|
|
9
|
+
let l = createChildLogger(base, { c: 'test' });
|
|
10
|
+
l.info('hello');
|
|
11
|
+
t.deepEqual(logs.shift(), { level: 'info', values: ['hello'], c: 'test' });
|
|
12
|
+
l.child({ m: 1 }).trace('trace');
|
|
13
|
+
t.deepEqual(logs.shift(), { level: 'trace', values: ['trace'], c: 'test', m: 1 });
|
|
14
|
+
}
|
|
15
|
+
createChildLogger(console, { c: 'test' }).info('hello');
|
|
16
|
+
{
|
|
17
|
+
let pass = 0;
|
|
18
|
+
let l = createWriteLogger(
|
|
19
|
+
(o) => {
|
|
20
|
+
pass++;
|
|
21
|
+
t.log(`${o.level}: [${[o.m, o.c].filter(Boolean).join('.') || 'default'}]`, ...o.values);
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
m: 'Root',
|
|
25
|
+
},
|
|
26
|
+
);
|
|
27
|
+
l.info('nice');
|
|
28
|
+
t.is(pass, 1);
|
|
29
|
+
l.child({}).info('nice 2');
|
|
30
|
+
t.is(pass, 2);
|
|
31
|
+
createChildLogger(l, { m: 'Child' }).info('nice 3');
|
|
32
|
+
t.is(pass, 3);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
@@ -17,6 +17,11 @@ export type ParsedModuleId = {
|
|
|
17
17
|
}
|
|
18
18
|
);
|
|
19
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Parse NPM module id
|
|
22
|
+
*
|
|
23
|
+
* parseModuleId('@wener/reaction@latest/index.js') // => { id: '@wener/reaction@latest', name: '@wener/reaction', version: 'latest', range: 'latest', pkg: 'reaction', path: '/index.js', scoped: true, org: 'wener' }
|
|
24
|
+
*/
|
|
20
25
|
export function parseModuleId(s: string): ParsedModuleId | undefined {
|
|
21
26
|
const groups = s.match(regModuleId)?.groups;
|
|
22
27
|
if (!groups) {
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import test from 'ava';
|
|
2
|
+
import { expectType } from 'tsd';
|
|
3
|
+
import { get } from './get';
|
|
4
|
+
import { parseObjectPath } from './parseObjectPath';
|
|
5
|
+
|
|
6
|
+
test('get typed', (t) => {
|
|
7
|
+
interface TestClass {
|
|
8
|
+
normal: string;
|
|
9
|
+
nested: {
|
|
10
|
+
a: number;
|
|
11
|
+
b: {
|
|
12
|
+
c: boolean;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
arr: number[];
|
|
16
|
+
nestedArr: {
|
|
17
|
+
sum: number;
|
|
18
|
+
other: null;
|
|
19
|
+
}[];
|
|
20
|
+
deep: {
|
|
21
|
+
arr: string[];
|
|
22
|
+
};
|
|
23
|
+
deeplvl1: {
|
|
24
|
+
deeplvl2: {
|
|
25
|
+
deeplvl3: {
|
|
26
|
+
deeplvl4: {
|
|
27
|
+
value: RegExp;
|
|
28
|
+
};
|
|
29
|
+
}[];
|
|
30
|
+
};
|
|
31
|
+
}[];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const obj = {} as TestClass;
|
|
35
|
+
|
|
36
|
+
expectType<number>(get(obj, 'nested.a', null));
|
|
37
|
+
expectType<string>(get(obj, 'normal', null));
|
|
38
|
+
expectType<number>(get(obj, 'nested.a'));
|
|
39
|
+
expectType<boolean>(get(obj, 'nested.b.c'));
|
|
40
|
+
expectType<number[]>(get(obj, 'arr'));
|
|
41
|
+
expectType<number>(get(obj, 'arr[13]'));
|
|
42
|
+
expectType<number>(get(obj, 'arr.13'));
|
|
43
|
+
expectType<null>(get(obj, 'nestedArr[3].other'));
|
|
44
|
+
expectType<string[]>(get(obj, 'deep.deep'));
|
|
45
|
+
expectType<string>(get(obj, 'deep.arr[333]'));
|
|
46
|
+
expectType<number>(get(obj, 'deep.arr[333].length'));
|
|
47
|
+
expectType<boolean>(get(obj, 'nested["b"]["c"]'));
|
|
48
|
+
expectType<never>(get(obj, ''));
|
|
49
|
+
expectType<number>(get(obj, '', 3));
|
|
50
|
+
expectType<never>(get(obj, 'nested.asdfasdf'));
|
|
51
|
+
expectType<RegExp>(get(obj, 'deeplvl1[1].deeplvl2.deeplvl3[88].deeplvl4.value'));
|
|
52
|
+
expectType<never>(get(obj, 'deeplvl1[1].deeplvl2.deeplvl1[88].deeplvl4.value'));
|
|
53
|
+
expectType<TestClass>(get(obj, 'deeplvl1[1].deeplvl2.deeplvl1[88].deeplvl4.value', obj));
|
|
54
|
+
expectType<string>(get(obj, 'nested["dd"]', ''));
|
|
55
|
+
t.pass();
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('get', (t) => {
|
|
59
|
+
let obj = {
|
|
60
|
+
undef: undefined,
|
|
61
|
+
zero: 0,
|
|
62
|
+
one: 1,
|
|
63
|
+
n: null,
|
|
64
|
+
f: false,
|
|
65
|
+
a: {
|
|
66
|
+
two: 2,
|
|
67
|
+
b: {
|
|
68
|
+
three: 3,
|
|
69
|
+
c: {
|
|
70
|
+
four: 4,
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
arr: [5, [6, { v: 7 }]],
|
|
75
|
+
} as const;
|
|
76
|
+
|
|
77
|
+
function check(path: string, value: any, def?: any) {
|
|
78
|
+
const out = get(obj, path, def);
|
|
79
|
+
t.is(out, value, 'get(obj, "' + path + '") should be ' + value + ', got ' + out);
|
|
80
|
+
if (path) {
|
|
81
|
+
const arr = parseObjectPath(path);
|
|
82
|
+
t.is(get(obj, arr, def), value, `get(obj,${JSON.stringify(arr)}, ${def})`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
check('', undefined);
|
|
87
|
+
check('one', obj.one);
|
|
88
|
+
check('one.two', undefined);
|
|
89
|
+
check('a', obj.a);
|
|
90
|
+
check('a.two', obj.a.two);
|
|
91
|
+
check('a.b', obj.a.b);
|
|
92
|
+
check('a.b.three', obj.a.b.three);
|
|
93
|
+
check('a.b.c', obj.a.b.c);
|
|
94
|
+
check('a.b.c.four', obj.a.b.c.four);
|
|
95
|
+
check('n', obj.n);
|
|
96
|
+
check('n.badkey', undefined);
|
|
97
|
+
check('f', false);
|
|
98
|
+
check('f.badkey', undefined);
|
|
99
|
+
|
|
100
|
+
check('', 'foo', 'foo');
|
|
101
|
+
check('undef', 'foo', 'foo');
|
|
102
|
+
check('n', null, 'foo');
|
|
103
|
+
check('n.badkey', 'foo', 'foo');
|
|
104
|
+
check('zero', 0, 'foo');
|
|
105
|
+
check('a.badkey', 'foo', 'foo');
|
|
106
|
+
check('a.badkey.anotherbadkey', 'foo', 'foo');
|
|
107
|
+
check('f', false, 'foo');
|
|
108
|
+
check('f.badkey', 'foo', 'foo');
|
|
109
|
+
|
|
110
|
+
check('arr.0', 5);
|
|
111
|
+
check('arr.1.0', 6);
|
|
112
|
+
check('arr.1.1.v', 7);
|
|
113
|
+
check('arr[0]', 5);
|
|
114
|
+
check('arr[1][0]', 6);
|
|
115
|
+
check('arr[1][1][v]', 7);
|
|
116
|
+
});
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { ObjectKey, parseObjectPath } from './parseObjectPath';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* get by path
|
|
5
|
+
*
|
|
6
|
+
* {@link https://github.com/developit/dlv dlv}
|
|
7
|
+
*/
|
|
8
|
+
export function get<O extends object, P extends ObjectKey, OrElse extends unknown>(
|
|
9
|
+
obj: O,
|
|
10
|
+
key: P | P[],
|
|
11
|
+
def?: OrElse,
|
|
12
|
+
): ResolveObjectPathType<O, P, OrElse> {
|
|
13
|
+
const undef = undefined;
|
|
14
|
+
const path = parseObjectPath(key);
|
|
15
|
+
let out: any = obj;
|
|
16
|
+
for (const i of path) {
|
|
17
|
+
out = out ? out[i] : undef;
|
|
18
|
+
}
|
|
19
|
+
return out === undef ? def : out;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* It tries to resolve the path of the given object, otherwise it will return OrElse
|
|
24
|
+
*
|
|
25
|
+
* {@link https://github.com/Pouja/typescript-deep-path-safe typescript-deep-path-safe}
|
|
26
|
+
*/
|
|
27
|
+
export type ResolveObjectPathType<
|
|
28
|
+
ObjectType,
|
|
29
|
+
Path extends string | symbol | number,
|
|
30
|
+
OrElse,
|
|
31
|
+
> = Path extends keyof ObjectType
|
|
32
|
+
? ObjectType[Path]
|
|
33
|
+
: Path extends `${infer LeftSide}.${infer RightSide}`
|
|
34
|
+
? LeftSide extends keyof ObjectType
|
|
35
|
+
? ResolveObjectPathType<ObjectType[LeftSide], RightSide, OrElse>
|
|
36
|
+
: Path extends `${infer LeftSide}[${number}].${infer RightSide}`
|
|
37
|
+
? LeftSide extends keyof ObjectType
|
|
38
|
+
? ObjectType[LeftSide] extends Array<infer U>
|
|
39
|
+
? ResolveObjectPathType<U, RightSide, OrElse>
|
|
40
|
+
: OrElse
|
|
41
|
+
: OrElse
|
|
42
|
+
: OrElse
|
|
43
|
+
: Path extends `${infer LeftSide}[${number}]`
|
|
44
|
+
? LeftSide extends keyof ObjectType
|
|
45
|
+
? ObjectType[LeftSide] extends Array<infer U>
|
|
46
|
+
? U
|
|
47
|
+
: OrElse
|
|
48
|
+
: OrElse
|
|
49
|
+
: OrElse;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import test from 'ava';
|
|
2
|
+
import { parseObjectPath } from './parseObjectPath';
|
|
3
|
+
|
|
4
|
+
test('parseObjectPath', (t) => {
|
|
5
|
+
for (const [k, v] of [
|
|
6
|
+
['a.b.c', ['a', 'b', 'c']],
|
|
7
|
+
['a[1].c', ['a', '1', 'c']],
|
|
8
|
+
['a.1.c', ['a', '1', 'c']],
|
|
9
|
+
['a[b].c', ['a', 'b', 'c']],
|
|
10
|
+
['arr[1][0]', ['arr', '1', '0']],
|
|
11
|
+
['arr.2', ['arr', '2']],
|
|
12
|
+
['arr[2]', ['arr', '2']],
|
|
13
|
+
[Symbol.toStringTag, [Symbol.toStringTag]],
|
|
14
|
+
[[Symbol.toStringTag], [Symbol.toStringTag]],
|
|
15
|
+
]) {
|
|
16
|
+
t.deepEqual(parseObjectPath(k), v);
|
|
17
|
+
}
|
|
18
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export type ObjectKey = string | symbol | number;
|
|
2
|
+
export type ObjectPath = Array<ObjectKey>;
|
|
3
|
+
export type ObjectPathLike = ObjectKey | ObjectPath;
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Parse object path
|
|
7
|
+
*
|
|
8
|
+
* parseObjectPath('a.b.c') // => ['a', 'b', 'c']
|
|
9
|
+
* parseObjectPath('a[0].b') // => ['a', 0, 'b']
|
|
10
|
+
* parseObjectPath('a[0][1]') // => ['a', 0, 1]
|
|
11
|
+
*
|
|
12
|
+
*/
|
|
13
|
+
export function parseObjectPath(s: ObjectPathLike): ObjectPath {
|
|
14
|
+
if (typeof s !== 'string') {
|
|
15
|
+
return Array.isArray(s) ? s : [s];
|
|
16
|
+
}
|
|
17
|
+
const parts = s.split('.');
|
|
18
|
+
// fast path
|
|
19
|
+
if (!s.includes('[')) {
|
|
20
|
+
return parts;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const result = [];
|
|
24
|
+
for (const part of parts) {
|
|
25
|
+
if (!part.endsWith(']')) {
|
|
26
|
+
result.push(part);
|
|
27
|
+
} else {
|
|
28
|
+
// a[0][1]
|
|
29
|
+
// try parseInt can extend to support a[-1] to use .at access
|
|
30
|
+
const s = part.split('[');
|
|
31
|
+
for (let sub of s) {
|
|
32
|
+
if (sub.endsWith(']')) {
|
|
33
|
+
sub = sub.slice(0, -1);
|
|
34
|
+
}
|
|
35
|
+
result.push(sub);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return result;
|
|
40
|
+
}
|