@thi.ng/resolve-map 6.1.2 → 7.1.0
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/CHANGELOG.md +29 -1
- package/README.md +35 -8
- package/index.d.ts +35 -7
- package/index.js +55 -23
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
-
- **Last updated**: 2022-
|
|
3
|
+
- **Last updated**: 2022-07-12T19:50:54Z
|
|
4
4
|
- **Generator**: [thi.ng/monopub](https://thi.ng/monopub)
|
|
5
5
|
|
|
6
6
|
All notable changes to this project will be documented in this file.
|
|
@@ -9,6 +9,34 @@ See [Conventional Commits](https://conventionalcommits.org/) for commit guidelin
|
|
|
9
9
|
**Note:** Unlisted _patch_ versions only involve non-code or otherwise excluded changes
|
|
10
10
|
and/or version bumps of transitive dependencies.
|
|
11
11
|
|
|
12
|
+
## [7.1.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/resolve-map@7.1.0) (2022-07-12)
|
|
13
|
+
|
|
14
|
+
#### 🚀 Features
|
|
15
|
+
|
|
16
|
+
- add `onlyFnRefs` option ([a23fc98](https://github.com/thi-ng/umbrella/commit/a23fc98))
|
|
17
|
+
|
|
18
|
+
# [7.0.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/resolve-map@7.0.0) (2022-07-07)
|
|
19
|
+
|
|
20
|
+
#### 🛑 Breaking changes
|
|
21
|
+
|
|
22
|
+
- add ResolveOpts, conditional unwrapping ([a23308b](https://github.com/thi-ng/umbrella/commit/a23308b))
|
|
23
|
+
- BREAKING CHANGE: update resolve() signature, use new `ResolveOpts`
|
|
24
|
+
- this change has only downstream impact on use cases requiring custom
|
|
25
|
+
prefixes to indicate lookup paths
|
|
26
|
+
- add new option to control value unwrapping in final result
|
|
27
|
+
- update docs/readme
|
|
28
|
+
- add new tests
|
|
29
|
+
|
|
30
|
+
## [6.2.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/resolve-map@6.2.0) (2022-07-07)
|
|
31
|
+
|
|
32
|
+
#### 🚀 Features
|
|
33
|
+
|
|
34
|
+
- unwrap all resolved() values in result ([888fa33](https://github.com/thi-ng/umbrella/commit/888fa33))
|
|
35
|
+
- add unwrapResolved() to unwrap any values wrapped via `resolved()`
|
|
36
|
+
- update resolveMap/Array()
|
|
37
|
+
- update doc strings
|
|
38
|
+
- update tests
|
|
39
|
+
|
|
12
40
|
## [6.1.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/resolve-map@6.1.0) (2022-05-23)
|
|
13
41
|
|
|
14
42
|
#### 🚀 Features
|
package/README.md
CHANGED
|
@@ -76,7 +76,7 @@ node --experimental-repl-await
|
|
|
76
76
|
> const resolveMap = await import("@thi.ng/resolve-map");
|
|
77
77
|
```
|
|
78
78
|
|
|
79
|
-
Package sizes (gzipped, pre-treeshake): ESM: 1.
|
|
79
|
+
Package sizes (gzipped, pre-treeshake): ESM: 1.13 KB
|
|
80
80
|
|
|
81
81
|
## Dependencies
|
|
82
82
|
|
|
@@ -237,7 +237,7 @@ resolve({ a: 1, b: { c: "@d", d: "@/a"} })
|
|
|
237
237
|
// { a: 1, b: { c: 1, d: 1 } }
|
|
238
238
|
|
|
239
239
|
// same with custom lookup prefix
|
|
240
|
-
resolve({ a: 1, b: { c: ">>>d", d: ">>>/a"} }, ">>>")
|
|
240
|
+
resolve({ a: 1, b: { c: ">>>d", d: ">>>/a"} }, { prefix: ">>>" })
|
|
241
241
|
// { a: 1, b: { c: 1, d: 1 } }
|
|
242
242
|
```
|
|
243
243
|
|
|
@@ -256,8 +256,8 @@ the 2nd (legacy) form. Also, since ES6 var names can't contain special
|
|
|
256
256
|
characters, destructured keys can ALWAYS only be looked up as siblings
|
|
257
257
|
of the currently processed key.
|
|
258
258
|
|
|
259
|
-
The `resolve` function provided as arg to the user function accepts a
|
|
260
|
-
|
|
259
|
+
The `resolve` function provided as arg to the user function accepts a path
|
|
260
|
+
(**without `@` (or custom) prefix**) to look up any other values in the root
|
|
261
261
|
object.
|
|
262
262
|
|
|
263
263
|
```ts
|
|
@@ -302,10 +302,37 @@ res.e(2);
|
|
|
302
302
|
|
|
303
303
|
Values can be protected from further resolution attempts by wrapping them via
|
|
304
304
|
[`resolved()`](https://docs.thi.ng/umbrella/resolve-map/modules.html#resolved).
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
`.deref()` necessary
|
|
305
|
+
By default, these wrapped values are only used during the resolution phase and
|
|
306
|
+
the final result object/array will only contain the original, unwrapped values.
|
|
307
|
+
Unwrapped values will also be supplied to any lookup functions, no
|
|
308
|
+
[`.deref()`](https://docs.thi.ng/umbrella/api/interfaces/IDeref.html) necessary
|
|
309
|
+
there.
|
|
310
|
+
|
|
311
|
+
```ts
|
|
312
|
+
resolve({ a: 42, b: ({a}) => resolved(a) });
|
|
313
|
+
// { a: 42, b: 42 }
|
|
314
|
+
|
|
315
|
+
const res = resolve({ a: 42, b: ({a}) => resolved(a) }, { unwrap: false });
|
|
316
|
+
// { a: 42, b: Resolved { _value: 42 } }
|
|
317
|
+
|
|
318
|
+
// obtain b's value via .deref()
|
|
319
|
+
res.b.deref()
|
|
320
|
+
// 42
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
Since v7.1.0 a new `onlyFnRefs` option has been added which changes the
|
|
324
|
+
resolution behavior to **not** consider string values for resolution at all
|
|
325
|
+
anymore and instead requires the use of function values to trigger resolution.
|
|
326
|
+
|
|
327
|
+
```ts
|
|
328
|
+
// default behavior
|
|
329
|
+
resolve({ a: "@c", b: ({a}) => a, c: 42 })
|
|
330
|
+
// { a: 42, b: 42, c: 42 }
|
|
331
|
+
|
|
332
|
+
// with option enabled
|
|
333
|
+
resolve({ a: "@c", b: ({a}) => a, c: 42 }, { onlyFnRefs: true })
|
|
334
|
+
// { a: '@c', b: '@c', c: 42 }
|
|
335
|
+
```
|
|
309
336
|
|
|
310
337
|
## Authors
|
|
311
338
|
|
package/index.d.ts
CHANGED
|
@@ -4,6 +4,27 @@ export declare type Unresolved<T> = {
|
|
|
4
4
|
};
|
|
5
5
|
export declare type ResolveFn = (path: string) => any;
|
|
6
6
|
export declare type LookupPath = NumOrString[];
|
|
7
|
+
export interface ResolveOpts {
|
|
8
|
+
/**
|
|
9
|
+
* Prefix for auto-recognizing & interpreting embedded string values as
|
|
10
|
+
* lookup paths (only if {@link ResolveOpts.onlyFnRefs} is false, default)
|
|
11
|
+
*
|
|
12
|
+
* @defaultValue `@`
|
|
13
|
+
*/
|
|
14
|
+
prefix: string;
|
|
15
|
+
/**
|
|
16
|
+
* If true (default), all known values wrapped using {@link resolved} will
|
|
17
|
+
* be unwrapped in the final result.
|
|
18
|
+
*
|
|
19
|
+
* @defaultValue true
|
|
20
|
+
*/
|
|
21
|
+
unwrap: boolean;
|
|
22
|
+
/**
|
|
23
|
+
* If true, only function values (not strings!) will be considered for
|
|
24
|
+
* resolution.
|
|
25
|
+
*/
|
|
26
|
+
onlyFnRefs: boolean;
|
|
27
|
+
}
|
|
7
28
|
/**
|
|
8
29
|
* Visits all key-value pairs or array items in depth-first order, expands any
|
|
9
30
|
* reference values, mutates the original object and returns it. Cyclic
|
|
@@ -19,10 +40,17 @@ export declare type LookupPath = NumOrString[];
|
|
|
19
40
|
* access any parent levels. Absolute refs are always resolved from the root
|
|
20
41
|
* level (the original object passed to this function).
|
|
21
42
|
*
|
|
22
|
-
* Values can be protected from further resolution attempts
|
|
23
|
-
*
|
|
24
|
-
* {@link
|
|
25
|
-
*
|
|
43
|
+
* Values can be protected from (further) resolution attempts in two ways:
|
|
44
|
+
*
|
|
45
|
+
* 1) by wrapping them via {@link resolved}. By default (unless `unwrap` is set
|
|
46
|
+
* to `false`), these wrapped values are only used during the resolution
|
|
47
|
+
* phase and the final result object/array will only contain the original,
|
|
48
|
+
* unwrapped values. In any way, unwrapped values will also be supplied to
|
|
49
|
+
* any lookup functions, no `.deref()` necessary there.
|
|
50
|
+
* 2) Enabling the `onlyFnRefs` option, only function values will be considered
|
|
51
|
+
* for resolution and strings (regardless of prefix) will be ignored.
|
|
52
|
+
*
|
|
53
|
+
* See {@link ResolveOpts} and package readme for further details.
|
|
26
54
|
*
|
|
27
55
|
* @example
|
|
28
56
|
* ```ts
|
|
@@ -88,10 +116,10 @@ export declare type LookupPath = NumOrString[];
|
|
|
88
116
|
* ```
|
|
89
117
|
*
|
|
90
118
|
* @param root -
|
|
91
|
-
* @param
|
|
119
|
+
* @param opts -
|
|
92
120
|
*/
|
|
93
|
-
export declare function resolve<T>(root: Unresolved<T>,
|
|
94
|
-
export declare function resolve<T>(root: Unresolved<T[]>,
|
|
121
|
+
export declare function resolve<T>(root: Unresolved<T>, opts?: Partial<ResolveOpts>): T;
|
|
122
|
+
export declare function resolve<T>(root: Unresolved<T[]>, opts?: Partial<ResolveOpts>): T[];
|
|
95
123
|
/**
|
|
96
124
|
* Takes the path for the current key and a lookup path string. Converts
|
|
97
125
|
* the possibly relative lookup path into its absolute form.
|
package/index.js
CHANGED
|
@@ -7,28 +7,33 @@ import { illegalArgs } from "@thi.ng/errors/illegal-arguments";
|
|
|
7
7
|
import { mutInUnsafe } from "@thi.ng/paths/mut-in";
|
|
8
8
|
import { exists } from "@thi.ng/paths/path";
|
|
9
9
|
const RE_ARGS = /^(function\s+\w+)?\s*\(\{([\w\s,:]+)\}/;
|
|
10
|
-
export function resolve(root,
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
return root;
|
|
10
|
+
export function resolve(root, opts) {
|
|
11
|
+
const $opts = { prefix: "@", unwrap: true, ...opts };
|
|
12
|
+
return isPlainObject(root)
|
|
13
|
+
? resolveMap(root, $opts)
|
|
14
|
+
: isArray(root)
|
|
15
|
+
? resolveArray(root, $opts)
|
|
16
|
+
: root;
|
|
18
17
|
}
|
|
19
|
-
|
|
18
|
+
/** @internal */
|
|
19
|
+
const resolveMap = (obj, opts, root, path = [], resolved = {}, stack = []) => {
|
|
20
20
|
root = root || obj;
|
|
21
21
|
for (let k in obj) {
|
|
22
|
-
_resolve(root, [...path, k], resolved, stack,
|
|
22
|
+
_resolve(root, [...path, k], resolved, stack, opts);
|
|
23
23
|
}
|
|
24
|
-
return
|
|
24
|
+
return !opts.unwrap || path.length
|
|
25
|
+
? obj
|
|
26
|
+
: unwrapResolved(obj, resolved);
|
|
25
27
|
};
|
|
26
|
-
|
|
28
|
+
/** @internal */
|
|
29
|
+
const resolveArray = (arr, opts, root, path = [], resolved = {}, stack = []) => {
|
|
27
30
|
root = root || arr;
|
|
28
31
|
for (let k = 0, n = arr.length; k < n; k++) {
|
|
29
|
-
_resolve(root, [...path, k], resolved, stack,
|
|
32
|
+
_resolve(root, [...path, k], resolved, stack, opts);
|
|
30
33
|
}
|
|
31
|
-
return
|
|
34
|
+
return !opts.unwrap || path.length
|
|
35
|
+
? arr
|
|
36
|
+
: unwrapResolved(arr, resolved);
|
|
32
37
|
};
|
|
33
38
|
/**
|
|
34
39
|
* The actual recursive resolution mechanism. Takes root object, key
|
|
@@ -40,8 +45,10 @@ const resolveArray = (arr, prefix, root, path = [], resolved = {}, stack = []) =
|
|
|
40
45
|
* @param path -
|
|
41
46
|
* @param resolved -
|
|
42
47
|
* @param stack -
|
|
48
|
+
*
|
|
49
|
+
* @internal
|
|
43
50
|
*/
|
|
44
|
-
const _resolve = (root, path, resolved, stack,
|
|
51
|
+
const _resolve = (root, path, resolved, stack, opts) => {
|
|
45
52
|
const pathID = path.join("/");
|
|
46
53
|
if (stack.indexOf(pathID) >= 0) {
|
|
47
54
|
illegalArgs(`cyclic references not allowed: ${pathID}`);
|
|
@@ -56,19 +63,21 @@ const _resolve = (root, path, resolved, stack, prefix) => {
|
|
|
56
63
|
let res = SEMAPHORE;
|
|
57
64
|
stack.push(pathID);
|
|
58
65
|
if (isPlainObject(v)) {
|
|
59
|
-
resolveMap(v,
|
|
66
|
+
resolveMap(v, { ...opts, unwrap: false }, root, path, resolved, stack);
|
|
60
67
|
}
|
|
61
68
|
else if (isArray(v)) {
|
|
62
|
-
resolveArray(v,
|
|
69
|
+
resolveArray(v, { ...opts, unwrap: false }, root, path, resolved, stack);
|
|
63
70
|
}
|
|
64
|
-
else if (
|
|
65
|
-
|
|
71
|
+
else if (!opts.onlyFnRefs &&
|
|
72
|
+
isString(v) &&
|
|
73
|
+
v.startsWith(opts.prefix)) {
|
|
74
|
+
res = _resolve(root, absPath(path, v, opts.prefix.length), resolved, stack, opts);
|
|
66
75
|
}
|
|
67
76
|
else if (isFunction(v)) {
|
|
68
|
-
res = resolveFunction(v, (p) => _resolve(root, absPath(path, p, 0), resolved, stack,
|
|
77
|
+
res = resolveFunction(v, (p) => _resolve(root, absPath(path, p, 0), resolved, stack, opts), pathID, resolved);
|
|
69
78
|
}
|
|
70
79
|
else if (!exists(root, path)) {
|
|
71
|
-
v = resolvePath(root, path, resolved, stack,
|
|
80
|
+
v = resolvePath(root, path, resolved, stack, opts);
|
|
72
81
|
}
|
|
73
82
|
if (res !== SEMAPHORE) {
|
|
74
83
|
mutInUnsafe(root, path, res);
|
|
@@ -95,13 +104,15 @@ const _resolve = (root, path, resolved, stack, prefix) => {
|
|
|
95
104
|
* @param root -
|
|
96
105
|
* @param path -
|
|
97
106
|
* @param resolved -
|
|
107
|
+
*
|
|
108
|
+
* @internal
|
|
98
109
|
*/
|
|
99
|
-
const resolvePath = (root, path, resolved, stack,
|
|
110
|
+
const resolvePath = (root, path, resolved, stack, opts) => {
|
|
100
111
|
// temporarily remove current path to avoid cycle detection
|
|
101
112
|
let pathID = stack.pop();
|
|
102
113
|
let v;
|
|
103
114
|
for (let i = 1, n = path.length; i <= n; i++) {
|
|
104
|
-
v = _resolve(root, path.slice(0, i), resolved, stack,
|
|
115
|
+
v = _resolve(root, path.slice(0, i), resolved, stack, opts);
|
|
105
116
|
}
|
|
106
117
|
// restore
|
|
107
118
|
stack.push(pathID);
|
|
@@ -123,6 +134,8 @@ const resolvePath = (root, path, resolved, stack, prefix) => {
|
|
|
123
134
|
* @param resolve -
|
|
124
135
|
* @param pathID - current base path for marking
|
|
125
136
|
* @param resolved -
|
|
137
|
+
*
|
|
138
|
+
* @internal
|
|
126
139
|
*/
|
|
127
140
|
const resolveFunction = (fn, resolve, pathID, resolved) => {
|
|
128
141
|
const match = RE_ARGS.exec(fn.toString());
|
|
@@ -142,6 +155,7 @@ const resolveFunction = (fn, resolve, pathID, resolved) => {
|
|
|
142
155
|
markResolved(res, pathID, resolved);
|
|
143
156
|
return res;
|
|
144
157
|
};
|
|
158
|
+
/** @internal */
|
|
145
159
|
const markResolved = (v, path, resolved) => {
|
|
146
160
|
resolved[path] = true;
|
|
147
161
|
if (isPlainObject(v)) {
|
|
@@ -151,6 +165,7 @@ const markResolved = (v, path, resolved) => {
|
|
|
151
165
|
markArrayResolved(v, path, resolved);
|
|
152
166
|
}
|
|
153
167
|
};
|
|
168
|
+
/** @internal */
|
|
154
169
|
const markObjResolved = (obj, path, resolved) => {
|
|
155
170
|
let v, p;
|
|
156
171
|
for (let k in obj) {
|
|
@@ -159,6 +174,7 @@ const markObjResolved = (obj, path, resolved) => {
|
|
|
159
174
|
markResolved(v, p, resolved);
|
|
160
175
|
}
|
|
161
176
|
};
|
|
177
|
+
/** @internal */
|
|
162
178
|
const markArrayResolved = (arr, path, resolved) => {
|
|
163
179
|
let v, p;
|
|
164
180
|
for (let i = 0, n = arr.length; i < n; i++) {
|
|
@@ -237,3 +253,19 @@ const getInUnsafe = (obj, path) => {
|
|
|
237
253
|
}
|
|
238
254
|
return [res, isResolved];
|
|
239
255
|
};
|
|
256
|
+
/**
|
|
257
|
+
* Unwraps all known values wrapped using {@link Resolved} in-place.
|
|
258
|
+
*
|
|
259
|
+
* @param root
|
|
260
|
+
* @param resolved
|
|
261
|
+
*
|
|
262
|
+
* @internal
|
|
263
|
+
*/
|
|
264
|
+
const unwrapResolved = (root, resolved) => {
|
|
265
|
+
for (let path in resolved) {
|
|
266
|
+
const $path = path.split("/");
|
|
267
|
+
const val = getInUnsafe(root, $path);
|
|
268
|
+
val[1] && mutInUnsafe(root, $path, val[0]);
|
|
269
|
+
}
|
|
270
|
+
return root;
|
|
271
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thi.ng/resolve-map",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.1.0",
|
|
4
4
|
"description": "DAG resolution of vanilla objects & arrays with internally linked values",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "./index.js",
|
|
@@ -76,5 +76,5 @@
|
|
|
76
76
|
],
|
|
77
77
|
"year": 2018
|
|
78
78
|
},
|
|
79
|
-
"gitHead": "
|
|
79
|
+
"gitHead": "21abd94ba9e13d4636f3e9a85d3a7d4e9ea269ba\n"
|
|
80
80
|
}
|