@weborigami/async-tree 0.0.45 → 0.0.47
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/index.ts +18 -1
- package/main.js +4 -5
- package/package.json +4 -4
- package/src/BrowserFileTree.js +1 -1
- package/src/DeepMapTree.js +1 -1
- package/src/DeepObjectTree.js +1 -2
- package/src/DeferredTree.js +22 -9
- package/src/FileTree.js +24 -20
- package/src/FunctionTree.js +0 -4
- package/src/MapTree.js +1 -1
- package/src/ObjectTree.js +1 -1
- package/src/SetTree.js +1 -1
- package/src/SiteTree.js +1 -6
- package/src/Tree.d.ts +2 -1
- package/src/Tree.js +56 -45
- package/src/internal.js +15 -0
- package/src/keysJson.js +1 -1
- package/src/operations/cache.js +9 -5
- package/src/operations/mergeDeep.js +1 -1
- package/src/symbols.js +1 -0
- package/src/transforms/{cachedKeyMaps.js → cachedKeyFunctions.js} +10 -10
- package/src/transforms/groupBy.js +4 -4
- package/src/transforms/{keyMapsForExtensions.js → keyFunctionsForExtensions.js} +4 -4
- package/src/transforms/map.js +29 -28
- package/src/transforms/regExpKeys.js +3 -2
- package/src/transforms/sort.js +4 -1
- package/src/transforms/sortBy.js +4 -1
- package/src/utilities.d.ts +3 -1
- package/src/utilities.js +37 -9
- package/test/BrowserFileTree.test.js +1 -1
- package/test/DeepObjectTree.test.js +1 -2
- package/test/DeferredTree.test.js +1 -2
- package/test/FileTree.test.js +1 -1
- package/test/FunctionTree.test.js +0 -6
- package/test/ObjectTree.test.js +1 -2
- package/test/SetTree.test.js +1 -1
- package/test/SiteTree.test.js +1 -1
- package/test/Tree.test.js +12 -22
- package/test/operations/cache.test.js +1 -2
- package/test/operations/merge.test.js +1 -1
- package/test/operations/mergeDeep.test.js +1 -2
- package/test/transforms/cachedKeyMaps.test.js +13 -13
- package/test/transforms/groupBy.test.js +1 -1
- package/test/transforms/keyMapsForExtensions.test.js +20 -21
- package/test/transforms/map.test.js +36 -41
- package/test/transforms/regExpKeys.test.js +1 -2
- package/test/transforms/sort.test.js +1 -1
- package/test/transforms/sortBy.test.js +1 -1
- package/test/transforms/sortNatural.test.js +1 -1
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
* Given a source resultExtension and a result resultExtension, return a pair of key
|
|
3
3
|
* functions that map between them.
|
|
4
4
|
*
|
|
5
|
-
* The resulting `
|
|
5
|
+
* The resulting `inverseKey` and `key` functions are compatible with those
|
|
6
6
|
* expected by map and other transforms.
|
|
7
7
|
*
|
|
8
8
|
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
|
|
9
9
|
* @param {{ resultExtension?: string, sourceExtension: string }}
|
|
10
10
|
* options
|
|
11
11
|
*/
|
|
12
|
-
export default function
|
|
12
|
+
export default function keyFunctionsForExtensions({
|
|
13
13
|
resultExtension,
|
|
14
14
|
sourceExtension,
|
|
15
15
|
}) {
|
|
@@ -18,12 +18,12 @@ export default function keyMapsForExtensions({
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
return {
|
|
21
|
-
async
|
|
21
|
+
async inverseKey(resultKey, tree) {
|
|
22
22
|
const basename = matchExtension(resultKey, resultExtension);
|
|
23
23
|
return basename ? `${basename}${dotPrefix(sourceExtension)}` : undefined;
|
|
24
24
|
},
|
|
25
25
|
|
|
26
|
-
async
|
|
26
|
+
async key(sourceKey, tree) {
|
|
27
27
|
const basename = matchExtension(sourceKey, sourceExtension);
|
|
28
28
|
return basename ? `${basename}${dotPrefix(resultExtension)}` : undefined;
|
|
29
29
|
},
|
package/src/transforms/map.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Tree } from "../internal.js";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Return a transform function that maps the keys and/or values of a tree.
|
|
@@ -6,63 +6,64 @@ import * as Tree from "../Tree.js";
|
|
|
6
6
|
* @typedef {import("../../index.ts").KeyFn} KeyFn
|
|
7
7
|
* @typedef {import("../../index.ts").ValueKeyFn} ValueKeyFn
|
|
8
8
|
*
|
|
9
|
-
* @param {ValueKeyFn|{ deep?: boolean, description?: string, needsSourceValue?: boolean,
|
|
9
|
+
* @param {ValueKeyFn|{ deep?: boolean, description?: string, needsSourceValue?: boolean, inverseKey?: KeyFn, key?: KeyFn, value?: ValueKeyFn }} options
|
|
10
10
|
*/
|
|
11
|
-
export default function createMapTransform(options) {
|
|
11
|
+
export default function createMapTransform(options = {}) {
|
|
12
12
|
let deep;
|
|
13
13
|
let description;
|
|
14
|
-
let
|
|
15
|
-
let
|
|
14
|
+
let inverseKeyFn;
|
|
15
|
+
let keyFn;
|
|
16
16
|
let needsSourceValue;
|
|
17
|
-
let
|
|
17
|
+
let valueFn;
|
|
18
18
|
if (typeof options === "function") {
|
|
19
|
-
// Take the single function argument as the
|
|
20
|
-
|
|
19
|
+
// Take the single function argument as the valueFn
|
|
20
|
+
valueFn = options;
|
|
21
21
|
} else {
|
|
22
22
|
deep = options.deep;
|
|
23
23
|
description = options.description;
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
inverseKeyFn = options.inverseKey;
|
|
25
|
+
keyFn = options.key;
|
|
26
26
|
needsSourceValue = options.needsSourceValue;
|
|
27
|
-
|
|
27
|
+
valueFn = options.value;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
deep ??= false;
|
|
31
31
|
description ??= "key/value map";
|
|
32
32
|
// @ts-ignore
|
|
33
|
-
|
|
33
|
+
inverseKeyFn ??= valueFn?.inverseKey;
|
|
34
34
|
// @ts-ignore
|
|
35
|
-
|
|
35
|
+
keyFn ??= valueFn?.key;
|
|
36
36
|
needsSourceValue ??= true;
|
|
37
37
|
|
|
38
|
-
if ((
|
|
38
|
+
if ((keyFn && !inverseKeyFn) || (!keyFn && inverseKeyFn)) {
|
|
39
39
|
throw new TypeError(
|
|
40
|
-
`map: You must specify both
|
|
40
|
+
`map: You must specify both key and inverseKey functions, or neither.`
|
|
41
41
|
);
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
/**
|
|
45
45
|
* @type {import("../../index.ts").TreeTransform}
|
|
46
46
|
*/
|
|
47
|
-
return function map(
|
|
47
|
+
return function map(treelike) {
|
|
48
|
+
const tree = Tree.from(treelike);
|
|
48
49
|
// The transformed tree is actually an extension of the original tree's
|
|
49
50
|
// prototype chain. This allows the transformed tree to inherit any
|
|
50
|
-
// properties/methods
|
|
51
|
-
//
|
|
51
|
+
// properties/methods. For example, the `parent` of the transformed tree is
|
|
52
|
+
// the original tree's parent.
|
|
52
53
|
const transformed = Object.create(tree);
|
|
53
54
|
|
|
54
55
|
transformed.description = description;
|
|
55
56
|
|
|
56
|
-
if (
|
|
57
|
+
if (keyFn || valueFn) {
|
|
57
58
|
transformed.get = async (resultKey) => {
|
|
58
59
|
// Step 1: Map the result key to the source key.
|
|
59
60
|
const isSubtree = deep && (await Tree.isKeyForSubtree(tree, resultKey));
|
|
60
61
|
const sourceKey =
|
|
61
|
-
!isSubtree &&
|
|
62
|
-
? await
|
|
62
|
+
!isSubtree && inverseKeyFn
|
|
63
|
+
? await inverseKeyFn(resultKey, tree)
|
|
63
64
|
: resultKey;
|
|
64
65
|
|
|
65
|
-
if (
|
|
66
|
+
if (sourceKey == null) {
|
|
66
67
|
// No source key means no value.
|
|
67
68
|
return undefined;
|
|
68
69
|
}
|
|
@@ -85,9 +86,9 @@ export default function createMapTransform(options) {
|
|
|
85
86
|
} else if (deep && Tree.isAsyncTree(sourceValue)) {
|
|
86
87
|
// Map a subtree.
|
|
87
88
|
resultValue = map(sourceValue);
|
|
88
|
-
} else if (
|
|
89
|
+
} else if (valueFn) {
|
|
89
90
|
// Map a single value.
|
|
90
|
-
resultValue = await
|
|
91
|
+
resultValue = await valueFn(sourceValue, sourceKey, tree);
|
|
91
92
|
} else {
|
|
92
93
|
// Return source value as is.
|
|
93
94
|
resultValue = sourceValue;
|
|
@@ -97,9 +98,9 @@ export default function createMapTransform(options) {
|
|
|
97
98
|
};
|
|
98
99
|
}
|
|
99
100
|
|
|
100
|
-
if (
|
|
101
|
+
if (keyFn) {
|
|
101
102
|
transformed.keys = async () => {
|
|
102
|
-
// Apply the
|
|
103
|
+
// Apply the keyFn to source keys for leaf values (not subtrees).
|
|
103
104
|
const sourceKeys = Array.from(await tree.keys());
|
|
104
105
|
const mapped = await Promise.all(
|
|
105
106
|
sourceKeys.map(async (sourceKey) => {
|
|
@@ -107,12 +108,12 @@ export default function createMapTransform(options) {
|
|
|
107
108
|
if (deep && (await Tree.isKeyForSubtree(tree, sourceKey))) {
|
|
108
109
|
resultKey = sourceKey;
|
|
109
110
|
} else {
|
|
110
|
-
resultKey = await
|
|
111
|
+
resultKey = await keyFn(sourceKey, tree);
|
|
111
112
|
}
|
|
112
113
|
return resultKey;
|
|
113
114
|
})
|
|
114
115
|
);
|
|
115
|
-
// Filter out any cases where the
|
|
116
|
+
// Filter out any cases where the keyFn returned undefined.
|
|
116
117
|
const resultKeys = mapped.filter((key) => key !== undefined);
|
|
117
118
|
return resultKeys;
|
|
118
119
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Tree } from "../internal.js";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* A tree whose keys are strings interpreted as regular expressions.
|
|
@@ -10,7 +10,8 @@ import * as Tree from "../Tree.js";
|
|
|
10
10
|
*
|
|
11
11
|
* @type {import("../../index.ts").TreeTransform}
|
|
12
12
|
*/
|
|
13
|
-
export default async function regExpKeys(
|
|
13
|
+
export default async function regExpKeys(treelike) {
|
|
14
|
+
const tree = Tree.from(treelike);
|
|
14
15
|
const map = new Map();
|
|
15
16
|
|
|
16
17
|
// We build the output tree first so that we can refer to it when setting
|
package/src/transforms/sort.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { Tree } from "../internal.js";
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Return a transform function that sorts a tree's keys.
|
|
3
5
|
*
|
|
@@ -10,7 +12,8 @@ export default function createSortTransform(compareFn) {
|
|
|
10
12
|
/**
|
|
11
13
|
* @type {import("../../index.ts").TreeTransform}
|
|
12
14
|
*/
|
|
13
|
-
return function sortTransform(
|
|
15
|
+
return function sortTransform(treelike) {
|
|
16
|
+
const tree = Tree.from(treelike);
|
|
14
17
|
const transform = Object.create(tree);
|
|
15
18
|
transform.keys = async () => {
|
|
16
19
|
const keys = Array.from(await tree.keys());
|
package/src/transforms/sortBy.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { Tree } from "../internal.js";
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Return a transform function that sorts a tree's keys.
|
|
3
5
|
*
|
|
@@ -11,7 +13,8 @@ export default function createSortByTransform(sortKeyFn) {
|
|
|
11
13
|
/**
|
|
12
14
|
* @type {import("../../index.ts").TreeTransform}
|
|
13
15
|
*/
|
|
14
|
-
return function sortByTransform(
|
|
16
|
+
return function sortByTransform(treelike) {
|
|
17
|
+
const tree = Tree.from(treelike);
|
|
15
18
|
const transform = Object.create(tree);
|
|
16
19
|
transform.keys = async () => {
|
|
17
20
|
const keysAndSortKeys = [];
|
package/src/utilities.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import { PlainObject, StringLike } from "../index.ts";
|
|
1
|
+
import { Packed, PlainObject, StringLike } from "../index.ts";
|
|
2
2
|
|
|
3
3
|
export function castArrayLike(object: any): any;
|
|
4
4
|
export function getRealmObjectPrototype(object: any): any;
|
|
5
5
|
export const hiddenFileNames: string[];
|
|
6
|
+
export function isPacked(object: any): object is Packed;
|
|
6
7
|
export function isPlainObject(object: any): object is PlainObject;
|
|
8
|
+
export function isUnpackable(object): object is { unpack: () => any };
|
|
7
9
|
export function isStringLike(obj: any): obj is StringLike;
|
|
8
10
|
export function keysFromPath(path: string): string[];
|
|
9
11
|
export const naturalSortCompareFn: (a: string, b: string) => number;
|
package/src/utilities.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
const TypedArray = Object.getPrototypeOf(Uint8Array);
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* If the given plain object has only sequential integer keys, return it as an
|
|
3
5
|
* array. Otherwise return it as is.
|
|
@@ -19,9 +21,6 @@ export function castArrayLike(object) {
|
|
|
19
21
|
return hasKeys ? Object.values(object) : object;
|
|
20
22
|
}
|
|
21
23
|
|
|
22
|
-
// Names of OS-generated files that should not be enumerated
|
|
23
|
-
export const hiddenFileNames = [".DS_Store"];
|
|
24
|
-
|
|
25
24
|
/**
|
|
26
25
|
* Return the Object prototype at the root of the object's prototype chain.
|
|
27
26
|
*
|
|
@@ -38,6 +37,27 @@ export function getRealmObjectPrototype(object) {
|
|
|
38
37
|
return proto;
|
|
39
38
|
}
|
|
40
39
|
|
|
40
|
+
// Names of OS-generated files that should not be enumerated
|
|
41
|
+
export const hiddenFileNames = [".DS_Store"];
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Return true if the object is in a packed form (or can be readily packed into
|
|
45
|
+
* a form) that can be given to fs.writeFile or response.write().
|
|
46
|
+
*
|
|
47
|
+
* @param {any} object
|
|
48
|
+
* @returns {object is import("../index.ts").Packed}
|
|
49
|
+
*/
|
|
50
|
+
export function isPacked(object) {
|
|
51
|
+
return (
|
|
52
|
+
typeof object === "string" ||
|
|
53
|
+
object instanceof ArrayBuffer ||
|
|
54
|
+
object instanceof Buffer ||
|
|
55
|
+
object instanceof ReadableStream ||
|
|
56
|
+
object instanceof String ||
|
|
57
|
+
object instanceof TypedArray
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
41
61
|
/**
|
|
42
62
|
* Return true if the object is a plain JavaScript object created by `{}`,
|
|
43
63
|
* `new Object()`, or `Object.create(null)`.
|
|
@@ -46,6 +66,7 @@ export function getRealmObjectPrototype(object) {
|
|
|
46
66
|
* `Module`) as plain objects.
|
|
47
67
|
*
|
|
48
68
|
* @param {any} object
|
|
69
|
+
* @returns {object is import("../index.ts").PlainObject}
|
|
49
70
|
*/
|
|
50
71
|
export function isPlainObject(object) {
|
|
51
72
|
// From https://stackoverflow.com/q/51722354/76472
|
|
@@ -67,15 +88,15 @@ export function isPlainObject(object) {
|
|
|
67
88
|
* Return true if the object is a string or object with a non-trival `toString`
|
|
68
89
|
* method.
|
|
69
90
|
*
|
|
70
|
-
* @param {any}
|
|
71
|
-
* @returns {obj is import("
|
|
91
|
+
* @param {any} object
|
|
92
|
+
* @returns {obj is import("../index.ts").StringLike}
|
|
72
93
|
*/
|
|
73
|
-
export function isStringLike(
|
|
74
|
-
if (typeof
|
|
94
|
+
export function isStringLike(object) {
|
|
95
|
+
if (typeof object === "string") {
|
|
75
96
|
return true;
|
|
76
|
-
} else if (
|
|
97
|
+
} else if (object?.toString === undefined) {
|
|
77
98
|
return false;
|
|
78
|
-
} else if (
|
|
99
|
+
} else if (object.toString === getRealmObjectPrototype(object).toString) {
|
|
79
100
|
// The stupid Object.prototype.toString implementation always returns
|
|
80
101
|
// "[object Object]", so if that's the only toString method the object has,
|
|
81
102
|
// we return false.
|
|
@@ -85,6 +106,13 @@ export function isStringLike(obj) {
|
|
|
85
106
|
}
|
|
86
107
|
}
|
|
87
108
|
|
|
109
|
+
export function isUnpackable(object) {
|
|
110
|
+
return (
|
|
111
|
+
isPacked(object) &&
|
|
112
|
+
typeof (/** @type {any} */ (object).unpack) === "function"
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
88
116
|
/**
|
|
89
117
|
* Given a path like "/foo/bar/baz", return an array of keys like ["foo", "bar",
|
|
90
118
|
* "baz"].
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import { describe, test } from "node:test";
|
|
3
3
|
import BrowserFileTree from "../src/BrowserFileTree.js";
|
|
4
|
-
import
|
|
4
|
+
import { Tree } from "../src/internal.js";
|
|
5
5
|
|
|
6
6
|
// Skip these tests if we're not in a browser.
|
|
7
7
|
const isBrowser = typeof window !== "undefined";
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import { describe, test } from "node:test";
|
|
3
|
-
import DeepObjectTree from "../src/
|
|
4
|
-
import * as Tree from "../src/Tree.js";
|
|
3
|
+
import { DeepObjectTree, Tree } from "../src/internal.js";
|
|
5
4
|
|
|
6
5
|
describe("DeepObjectTree", () => {
|
|
7
6
|
test("returns an ObjectTree for value that's a plain sub-object or sub-array", async () => {
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import { describe, test } from "node:test";
|
|
3
3
|
import DeferredTree from "../src/DeferredTree.js";
|
|
4
|
-
import ObjectTree from "../src/
|
|
5
|
-
import * as Tree from "../src/Tree.js";
|
|
4
|
+
import { ObjectTree, Tree } from "../src/internal.js";
|
|
6
5
|
|
|
7
6
|
describe("DeferredTree", () => {
|
|
8
7
|
test("lazy-loads a treelike object", async () => {
|
package/test/FileTree.test.js
CHANGED
|
@@ -4,7 +4,7 @@ import path from "node:path";
|
|
|
4
4
|
import { describe, test } from "node:test";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import FileTree from "../src/FileTree.js";
|
|
7
|
-
import
|
|
7
|
+
import { Tree } from "../src/internal.js";
|
|
8
8
|
|
|
9
9
|
const dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
10
10
|
const tempDirectory = path.join(dirname, "fixtures/temp");
|
|
@@ -18,12 +18,6 @@ describe("FunctionTree", async () => {
|
|
|
18
18
|
assert.equal(alice, "Hello, **Alice**.");
|
|
19
19
|
});
|
|
20
20
|
|
|
21
|
-
test("unpacking the tree returns the function itself", async () => {
|
|
22
|
-
const fn = () => true;
|
|
23
|
-
const fixture = new FunctionTree(fn);
|
|
24
|
-
assert.equal(await fixture.unpack(), fn);
|
|
25
|
-
});
|
|
26
|
-
|
|
27
21
|
test("getting a value from function with multiple arguments curries the function", async () => {
|
|
28
22
|
const fixture = new FunctionTree((a, b, c) => a + b + c);
|
|
29
23
|
const fnA = await fixture.get(1);
|
package/test/ObjectTree.test.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import { describe, test } from "node:test";
|
|
3
|
-
import ObjectTree from "../src/
|
|
4
|
-
import * as Tree from "../src/Tree.js";
|
|
3
|
+
import { ObjectTree, Tree } from "../src/internal.js";
|
|
5
4
|
|
|
6
5
|
describe("ObjectTree", () => {
|
|
7
6
|
test("can get the keys of the tree", async () => {
|
package/test/SetTree.test.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import { describe, test } from "node:test";
|
|
3
|
-
import ObjectTree from "../src/ObjectTree.js";
|
|
4
3
|
import SetTree from "../src/SetTree.js";
|
|
4
|
+
import { ObjectTree } from "../src/internal.js";
|
|
5
5
|
|
|
6
6
|
describe("SetTree", () => {
|
|
7
7
|
test("can get the keys of the tree", async () => {
|
package/test/SiteTree.test.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import { beforeEach, describe, mock, test } from "node:test";
|
|
3
3
|
import SiteTree from "../src/SiteTree.js";
|
|
4
|
-
import
|
|
4
|
+
import { Tree } from "../src/internal.js";
|
|
5
5
|
|
|
6
6
|
const mockHost = "https://mock";
|
|
7
7
|
|
package/test/Tree.test.js
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import { describe, test } from "node:test";
|
|
3
|
-
import { DeepObjectTree } from "../main.js";
|
|
4
3
|
import MapTree from "../src/MapTree.js";
|
|
5
|
-
import ObjectTree from "../src/
|
|
6
|
-
import * as Tree from "../src/Tree.js";
|
|
4
|
+
import { DeepObjectTree, ObjectTree, Tree } from "../src/internal.js";
|
|
7
5
|
|
|
8
6
|
describe("Tree", () => {
|
|
9
7
|
test("assign applies one tree to another", async () => {
|
|
@@ -89,13 +87,10 @@ describe("Tree", () => {
|
|
|
89
87
|
});
|
|
90
88
|
|
|
91
89
|
test("from() uses an object's unpack() method if defined", async () => {
|
|
92
|
-
const obj =
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
};
|
|
97
|
-
},
|
|
98
|
-
};
|
|
90
|
+
const obj = new String();
|
|
91
|
+
/** @type {any} */ (obj).unpack = () => ({
|
|
92
|
+
a: "Hello, a.",
|
|
93
|
+
});
|
|
99
94
|
const tree = Tree.from(obj);
|
|
100
95
|
assert.deepEqual(await Tree.plain(tree), {
|
|
101
96
|
a: "Hello, a.",
|
|
@@ -103,13 +98,10 @@ describe("Tree", () => {
|
|
|
103
98
|
});
|
|
104
99
|
|
|
105
100
|
test("from() creates a deferred tree if unpack() returns a promise", async () => {
|
|
106
|
-
const obj =
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
};
|
|
111
|
-
},
|
|
112
|
-
};
|
|
101
|
+
const obj = new String();
|
|
102
|
+
/** @type {any} */ (obj).unpack = async () => ({
|
|
103
|
+
a: "Hello, a.",
|
|
104
|
+
});
|
|
113
105
|
const tree = Tree.from(obj);
|
|
114
106
|
assert.deepEqual(await Tree.plain(tree), {
|
|
115
107
|
a: "Hello, a.",
|
|
@@ -299,12 +291,10 @@ describe("Tree", () => {
|
|
|
299
291
|
});
|
|
300
292
|
|
|
301
293
|
test("traversing a final empty string can unpack the last value", async () => {
|
|
294
|
+
const unpackable = new String();
|
|
295
|
+
/** @type {any} */ (unpackable).unpack = () => "Content";
|
|
302
296
|
const tree = {
|
|
303
|
-
unpackable
|
|
304
|
-
unpack() {
|
|
305
|
-
return "Content";
|
|
306
|
-
},
|
|
307
|
-
},
|
|
297
|
+
unpackable,
|
|
308
298
|
};
|
|
309
299
|
const result = await Tree.traverse(tree, "unpackable", "");
|
|
310
300
|
assert.equal(result, "Content");
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import { describe, test } from "node:test";
|
|
3
|
-
import ObjectTree from "../../src/
|
|
4
|
-
import * as Tree from "../../src/Tree.js";
|
|
3
|
+
import { ObjectTree, Tree } from "../../src/internal.js";
|
|
5
4
|
import cache from "../../src/operations/cache.js";
|
|
6
5
|
|
|
7
6
|
describe("cache", () => {
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import { describe, test } from "node:test";
|
|
3
|
-
import DeepObjectTree from "../../src/
|
|
4
|
-
import * as Tree from "../../src/Tree.js";
|
|
3
|
+
import { DeepObjectTree, Tree } from "../../src/internal.js";
|
|
5
4
|
import mergeDeep from "../../src/operations/mergeDeep.js";
|
|
6
5
|
|
|
7
6
|
describe("mergeDeep", () => {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import { describe, test } from "node:test";
|
|
3
|
-
import ObjectTree from "../../src/
|
|
4
|
-
import
|
|
3
|
+
import { ObjectTree } from "../../src/internal.js";
|
|
4
|
+
import cachedKeyFunctions from "../../src/transforms/cachedKeyFunctions.js";
|
|
5
5
|
|
|
6
|
-
describe("
|
|
6
|
+
describe("cachedKeyFunctions", () => {
|
|
7
7
|
test("maps keys with caching", async () => {
|
|
8
8
|
const tree = new ObjectTree({
|
|
9
9
|
a: "letter a",
|
|
@@ -16,26 +16,26 @@ describe("cachedKeyMaps", () => {
|
|
|
16
16
|
return `_${sourceKey}`;
|
|
17
17
|
};
|
|
18
18
|
|
|
19
|
-
const {
|
|
19
|
+
const { inverseKey, key } = cachedKeyFunctions(underscoreKeys);
|
|
20
20
|
|
|
21
|
-
assert.equal(await
|
|
21
|
+
assert.equal(await inverseKey("_a", tree), "a"); // Cache miss
|
|
22
22
|
assert.equal(callCount, 1);
|
|
23
|
-
assert.equal(await
|
|
23
|
+
assert.equal(await inverseKey("_a", tree), "a");
|
|
24
24
|
assert.equal(callCount, 1);
|
|
25
|
-
assert.equal(await
|
|
25
|
+
assert.equal(await inverseKey("_b", tree), "b"); // Cache miss
|
|
26
26
|
assert.equal(callCount, 2);
|
|
27
27
|
|
|
28
|
-
assert.equal(await
|
|
29
|
-
assert.equal(await
|
|
30
|
-
assert.equal(await
|
|
28
|
+
assert.equal(await key("a", tree), "_a");
|
|
29
|
+
assert.equal(await key("a", tree), "_a");
|
|
30
|
+
assert.equal(await key("b", tree), "_b");
|
|
31
31
|
assert.equal(callCount, 2);
|
|
32
32
|
|
|
33
33
|
// `c` isn't in tree, so we should get undefined.
|
|
34
|
-
assert.equal(await
|
|
34
|
+
assert.equal(await inverseKey("_c", tree), undefined);
|
|
35
35
|
// But key mapping is still possible.
|
|
36
|
-
assert.equal(await
|
|
36
|
+
assert.equal(await key("c", tree), "_c");
|
|
37
37
|
// And now we have a cache hit.
|
|
38
|
-
assert.equal(await
|
|
38
|
+
assert.equal(await inverseKey("_c", tree), "c");
|
|
39
39
|
assert.equal(callCount, 3);
|
|
40
40
|
});
|
|
41
41
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import { describe, test } from "node:test";
|
|
3
|
-
import
|
|
3
|
+
import { Tree } from "../../src/internal.js";
|
|
4
4
|
import groupBy from "../../src/transforms/groupBy.js";
|
|
5
5
|
|
|
6
6
|
describe("groupBy transform", () => {
|
|
@@ -1,30 +1,29 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import { describe, test } from "node:test";
|
|
3
|
-
import ObjectTree from "../../src/
|
|
4
|
-
import
|
|
5
|
-
import keyMapsForExtensions from "../../src/transforms/keyMapsForExtensions.js";
|
|
3
|
+
import { ObjectTree, Tree } from "../../src/internal.js";
|
|
4
|
+
import keyFunctionsForExtensions from "../../src/transforms/keyFunctionsForExtensions.js";
|
|
6
5
|
import map from "../../src/transforms/map.js";
|
|
7
6
|
|
|
8
7
|
describe("keyMapsForExtensions", () => {
|
|
9
8
|
test("returns key functions that pass a matching key through", async () => {
|
|
10
|
-
const {
|
|
9
|
+
const { inverseKey, key } = keyFunctionsForExtensions({
|
|
11
10
|
sourceExtension: "txt",
|
|
12
11
|
});
|
|
13
|
-
assert.equal(await
|
|
14
|
-
assert.equal(await
|
|
15
|
-
assert.equal(await
|
|
16
|
-
assert.equal(await
|
|
12
|
+
assert.equal(await inverseKey("file.txt"), "file.txt");
|
|
13
|
+
assert.equal(await key("file.txt"), "file.txt");
|
|
14
|
+
assert.equal(await inverseKey("file.foo"), undefined);
|
|
15
|
+
assert.equal(await key("file.foo"), undefined);
|
|
17
16
|
});
|
|
18
17
|
|
|
19
18
|
test("returns key functions that can map extensions", async () => {
|
|
20
|
-
const {
|
|
19
|
+
const { inverseKey, key } = keyFunctionsForExtensions({
|
|
21
20
|
resultExtension: "html",
|
|
22
21
|
sourceExtension: "md",
|
|
23
22
|
});
|
|
24
|
-
assert.equal(await
|
|
25
|
-
assert.equal(await
|
|
26
|
-
assert.equal(await
|
|
27
|
-
assert.equal(await
|
|
23
|
+
assert.equal(await inverseKey("file.html"), "file.md");
|
|
24
|
+
assert.equal(await key("file.md"), "file.html");
|
|
25
|
+
assert.equal(await inverseKey("file.foo"), undefined);
|
|
26
|
+
assert.equal(await key("file.foo"), undefined);
|
|
28
27
|
});
|
|
29
28
|
|
|
30
29
|
test("works with map to handle keys that end in a given resultExtension", async () => {
|
|
@@ -33,13 +32,13 @@ describe("keyMapsForExtensions", () => {
|
|
|
33
32
|
file2: "won't be mapped",
|
|
34
33
|
"file3.foo": "won't be mapped",
|
|
35
34
|
});
|
|
36
|
-
const {
|
|
35
|
+
const { inverseKey, key } = keyFunctionsForExtensions({
|
|
37
36
|
sourceExtension: "txt",
|
|
38
37
|
});
|
|
39
38
|
const transform = map({
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
inverseKey,
|
|
40
|
+
key,
|
|
41
|
+
value: (sourceValue, sourceKey, tree) => sourceValue.toUpperCase(),
|
|
43
42
|
});
|
|
44
43
|
const fixture = transform(files);
|
|
45
44
|
assert.deepEqual(await Tree.plain(fixture), {
|
|
@@ -53,14 +52,14 @@ describe("keyMapsForExtensions", () => {
|
|
|
53
52
|
file2: "won't be mapped",
|
|
54
53
|
"file3.foo": "won't be mapped",
|
|
55
54
|
});
|
|
56
|
-
const {
|
|
55
|
+
const { inverseKey, key } = keyFunctionsForExtensions({
|
|
57
56
|
resultExtension: "upper",
|
|
58
57
|
sourceExtension: "txt",
|
|
59
58
|
});
|
|
60
59
|
const transform = map({
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
60
|
+
inverseKey,
|
|
61
|
+
key,
|
|
62
|
+
value: (sourceValue, sourceKey, tree) => sourceValue.toUpperCase(),
|
|
64
63
|
});
|
|
65
64
|
const fixture = transform(files);
|
|
66
65
|
assert.deepEqual(await Tree.plain(fixture), {
|