@weborigami/async-tree 0.0.73 → 0.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/browser.js +2 -11
- package/index.ts +9 -0
- package/main.js +3 -37
- package/package.json +2 -2
- package/shared.js +32 -0
- package/src/Tree.d.ts +2 -2
- package/src/Tree.js +13 -16
- package/src/{BrowserFileTree.js → drivers/BrowserFileTree.js} +3 -3
- package/src/{DeepMapTree.js → drivers/DeepMapTree.js} +1 -1
- package/src/{DeepObjectTree.js → drivers/DeepObjectTree.js} +2 -2
- package/src/{DeferredTree.js → drivers/DeferredTree.js} +5 -3
- package/src/{FileTree.js → drivers/FileTree.js} +3 -3
- package/src/{FunctionTree.js → drivers/FunctionTree.js} +1 -1
- package/src/{MapTree.js → drivers/MapTree.js} +3 -3
- package/src/{ObjectTree.js → drivers/ObjectTree.js} +4 -4
- package/src/{SetTree.js → drivers/SetTree.js} +1 -1
- package/src/{SiteTree.js → drivers/SiteTree.js} +2 -2
- package/src/{calendarTree.js → drivers/calendarTree.js} +1 -1
- package/src/extension.js +140 -0
- package/src/internal.js +3 -2
- package/src/{transforms → operations}/deepReverse.js +1 -1
- package/src/operations/deepTake.js +30 -3
- package/src/operations/group.js +44 -3
- package/src/operations/keyFunctionsForExtensions.js +48 -0
- package/src/operations/map.js +118 -3
- package/src/operations/sort.js +45 -2
- package/src/operations/take.js +19 -2
- package/src/symbols.js +1 -0
- package/test/Tree.test.js +25 -2
- package/test/{BrowserFileTree.test.js → drivers/BrowserFileTree.test.js} +2 -2
- package/test/{DeepMapTree.test.js → drivers/DeepMapTree.test.js} +2 -2
- package/test/{DeepObjectTree.test.js → drivers/DeepObjectTree.test.js} +1 -1
- package/test/{DeferredTree.test.js → drivers/DeferredTree.test.js} +2 -2
- package/test/{ExplorableSiteTree.test.js → drivers/ExplorableSiteTree.test.js} +6 -3
- package/test/{FileTree.test.js → drivers/FileTree.test.js} +6 -5
- package/test/{FunctionTree.test.js → drivers/FunctionTree.test.js} +1 -1
- package/test/{MapTree.test.js → drivers/MapTree.test.js} +2 -2
- package/test/{ObjectTree.test.js → drivers/ObjectTree.test.js} +2 -2
- package/test/{SetTree.test.js → drivers/SetTree.test.js} +2 -2
- package/test/{SiteTree.test.js → drivers/SiteTree.test.js} +1 -1
- package/test/{calendarTree.test.js → drivers/calendarTree.test.js} +5 -5
- package/test/extension.test.js +41 -0
- package/test/{transforms → operations}/cachedKeyFunctions.test.js +1 -1
- package/test/operations/concat.test.js +1 -1
- package/test/{transforms → operations}/deepReverse.test.js +1 -1
- package/test/operations/{deepTakeFn.test.js → deepTake.test.js} +3 -3
- package/test/{transforms/groupFn.test.js → operations/group.test.js} +4 -4
- package/test/{transforms → operations}/invokeFunctions.test.js +1 -1
- package/test/{transforms → operations}/keyFunctionsForExtensions.test.js +21 -12
- package/test/{transforms/mapFn.test.js → operations/map.test.js} +23 -31
- package/test/{transforms → operations}/regExpKeys.test.js +1 -1
- package/test/{transforms → operations}/reverse.test.js +1 -1
- package/test/{transforms/sortFn.test.js → operations/sort.test.js} +6 -7
- package/test/{transforms/takeFn.test.js → operations/take.test.js} +3 -3
- package/src/operations/deepTakeFn.js +0 -43
- package/src/operations/groupFn.js +0 -57
- package/src/transforms/keyFunctionsForExtensions.js +0 -78
- package/src/transforms/mapFn.js +0 -126
- package/src/transforms/sortFn.js +0 -71
- package/src/transforms/takeFn.js +0 -31
- /package/src/{ExplorableSiteTree.js → drivers/ExplorableSiteTree.js} +0 -0
- /package/src/{transforms → operations}/cachedKeyFunctions.js +0 -0
- /package/src/{transforms → operations}/invokeFunctions.js +0 -0
- /package/src/{transforms → operations}/regExpKeys.js +0 -0
- /package/src/{transforms → operations}/reverse.js +0 -0
- /package/test/{fixtures → drivers/fixtures}/markdown/Alice.md +0 -0
- /package/test/{fixtures → drivers/fixtures}/markdown/Bob.md +0 -0
- /package/test/{fixtures → drivers/fixtures}/markdown/Carol.md +0 -0
- /package/test/{fixtures → drivers/fixtures}/markdown/subfolder/README.md +0 -0
package/src/operations/map.js
CHANGED
|
@@ -1,14 +1,129 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Tree } from "../internal.js";
|
|
2
|
+
import * as trailingSlash from "../trailingSlash.js";
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Transform the keys and/or values of a tree.
|
|
5
6
|
*
|
|
6
7
|
* @typedef {import("../../index.ts").KeyFn} KeyFn
|
|
8
|
+
* @typedef {import("../../index.ts").TreeMapOptions} MapOptions
|
|
7
9
|
* @typedef {import("../../index.ts").ValueKeyFn} ValueKeyFn
|
|
8
10
|
*
|
|
9
11
|
* @param {import("../../index.ts").Treelike} treelike
|
|
10
|
-
* @param {
|
|
12
|
+
* @param {MapOptions|ValueKeyFn} options
|
|
11
13
|
*/
|
|
12
14
|
export default function map(treelike, options = {}) {
|
|
13
|
-
|
|
15
|
+
let deep;
|
|
16
|
+
let description;
|
|
17
|
+
let inverseKeyFn;
|
|
18
|
+
let keyFn;
|
|
19
|
+
let needsSourceValue;
|
|
20
|
+
let valueFn;
|
|
21
|
+
|
|
22
|
+
if (!treelike) {
|
|
23
|
+
const error = new TypeError(`map: The tree to map isn't defined.`);
|
|
24
|
+
/** @type {any} */ (error).position = 0;
|
|
25
|
+
throw error;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (typeof options === "function") {
|
|
29
|
+
// Take the single function argument as the valueFn
|
|
30
|
+
valueFn = options;
|
|
31
|
+
} else {
|
|
32
|
+
deep = options.deep;
|
|
33
|
+
description = options.description;
|
|
34
|
+
inverseKeyFn = options.inverseKey;
|
|
35
|
+
keyFn = options.key;
|
|
36
|
+
needsSourceValue = options.needsSourceValue;
|
|
37
|
+
valueFn = options.value;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
deep ??= false;
|
|
41
|
+
description ??= "key/value map";
|
|
42
|
+
// @ts-ignore
|
|
43
|
+
inverseKeyFn ??= valueFn?.inverseKey;
|
|
44
|
+
// @ts-ignore
|
|
45
|
+
keyFn ??= valueFn?.key;
|
|
46
|
+
needsSourceValue ??= true;
|
|
47
|
+
|
|
48
|
+
if ((keyFn && !inverseKeyFn) || (!keyFn && inverseKeyFn)) {
|
|
49
|
+
throw new TypeError(
|
|
50
|
+
`map: You must specify both key and inverseKey functions, or neither.`
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @param {import("@weborigami/types").AsyncTree} tree
|
|
56
|
+
*/
|
|
57
|
+
function mapFn(tree) {
|
|
58
|
+
// The transformed tree is actually an extension of the original tree's
|
|
59
|
+
// prototype chain. This allows the transformed tree to inherit any
|
|
60
|
+
// properties/methods. For example, the `parent` of the transformed tree is
|
|
61
|
+
// the original tree's parent.
|
|
62
|
+
const transformed = Object.create(tree);
|
|
63
|
+
|
|
64
|
+
transformed.description = description;
|
|
65
|
+
|
|
66
|
+
if (keyFn || valueFn) {
|
|
67
|
+
transformed.get = async (resultKey) => {
|
|
68
|
+
// Step 1: Map the result key to the source key.
|
|
69
|
+
const sourceKey = (await inverseKeyFn?.(resultKey, tree)) ?? resultKey;
|
|
70
|
+
|
|
71
|
+
if (sourceKey === undefined) {
|
|
72
|
+
// No source key means no value.
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Step 2: Get the source value.
|
|
77
|
+
let sourceValue;
|
|
78
|
+
if (needsSourceValue) {
|
|
79
|
+
// Normal case: get the value from the source tree.
|
|
80
|
+
sourceValue = await tree.get(sourceKey);
|
|
81
|
+
} else if (deep && trailingSlash.has(sourceKey)) {
|
|
82
|
+
// Only get the source value if it's expected to be a subtree.
|
|
83
|
+
sourceValue = tree;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Step 3: Map the source value to the result value.
|
|
87
|
+
let resultValue;
|
|
88
|
+
if (needsSourceValue && sourceValue === undefined) {
|
|
89
|
+
// No source value means no result value.
|
|
90
|
+
resultValue = undefined;
|
|
91
|
+
} else if (deep && Tree.isAsyncTree(sourceValue)) {
|
|
92
|
+
// Map a subtree.
|
|
93
|
+
resultValue = mapFn(sourceValue);
|
|
94
|
+
} else if (valueFn) {
|
|
95
|
+
// Map a single value.
|
|
96
|
+
resultValue = await valueFn(sourceValue, sourceKey, tree);
|
|
97
|
+
} else {
|
|
98
|
+
// Return source value as is.
|
|
99
|
+
resultValue = sourceValue;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return resultValue;
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (keyFn) {
|
|
107
|
+
transformed.keys = async () => {
|
|
108
|
+
// Apply the keyFn to source keys for leaf values (not subtrees).
|
|
109
|
+
const sourceKeys = Array.from(await tree.keys());
|
|
110
|
+
const mapped = await Promise.all(
|
|
111
|
+
sourceKeys.map(async (sourceKey) =>
|
|
112
|
+
// Deep maps leave source keys for subtrees alone
|
|
113
|
+
deep && trailingSlash.has(sourceKey)
|
|
114
|
+
? sourceKey
|
|
115
|
+
: await keyFn(sourceKey, tree)
|
|
116
|
+
)
|
|
117
|
+
);
|
|
118
|
+
// Filter out any cases where the keyFn returned undefined.
|
|
119
|
+
const resultKeys = mapped.filter((key) => key !== undefined);
|
|
120
|
+
return resultKeys;
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return transformed;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const tree = Tree.from(treelike, { deep });
|
|
128
|
+
return mapFn(tree);
|
|
14
129
|
}
|
package/src/operations/sort.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Tree } from "../internal.js";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Return a new tree with the original's keys sorted. A comparison function can
|
|
@@ -14,5 +14,48 @@ import sortFn from "../transforms/sortFn.js";
|
|
|
14
14
|
* @param {SortOptions} [options]
|
|
15
15
|
*/
|
|
16
16
|
export default function sort(treelike, options) {
|
|
17
|
-
|
|
17
|
+
if (!treelike) {
|
|
18
|
+
const error = new TypeError(`sort: The tree to sort isn't defined.`);
|
|
19
|
+
/** @type {any} */ (error).position = 0;
|
|
20
|
+
throw error;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const sortKey = options?.sortKey;
|
|
24
|
+
let compare = options?.compare;
|
|
25
|
+
|
|
26
|
+
const tree = Tree.from(treelike);
|
|
27
|
+
const transformed = Object.create(tree);
|
|
28
|
+
transformed.keys = async () => {
|
|
29
|
+
const keys = Array.from(await tree.keys());
|
|
30
|
+
|
|
31
|
+
if (sortKey) {
|
|
32
|
+
// Invoke the async sortKey function to get sort keys.
|
|
33
|
+
// Create { key, sortKey } tuples.
|
|
34
|
+
const tuples = await Promise.all(
|
|
35
|
+
keys.map(async (key) => {
|
|
36
|
+
const sort = await sortKey(key, tree);
|
|
37
|
+
if (sort === undefined) {
|
|
38
|
+
throw new Error(
|
|
39
|
+
`sortKey function returned undefined for key ${key}`
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
return { key, sort };
|
|
43
|
+
})
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
// Wrap the comparison function so it applies to sort keys.
|
|
47
|
+
const defaultCompare = (a, b) => (a < b ? -1 : a > b ? 1 : 0);
|
|
48
|
+
const originalCompare = compare ?? defaultCompare;
|
|
49
|
+
// Sort by the sort key.
|
|
50
|
+
tuples.sort((a, b) => originalCompare(a.sort, b.sort));
|
|
51
|
+
// Map back to the original keys.
|
|
52
|
+
const sorted = tuples.map((pair) => pair.key);
|
|
53
|
+
return sorted;
|
|
54
|
+
} else {
|
|
55
|
+
// Use original keys as sort keys.
|
|
56
|
+
// If compare is undefined, this uses default sort order.
|
|
57
|
+
return keys.slice().sort(compare);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
return transformed;
|
|
18
61
|
}
|
package/src/operations/take.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Tree } from "../internal.js";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Returns a new tree with the number of keys limited to the indicated count.
|
|
@@ -7,5 +7,22 @@ import takeFn from "../transforms/takeFn.js";
|
|
|
7
7
|
* @param {number} count
|
|
8
8
|
*/
|
|
9
9
|
export default function take(treelike, count) {
|
|
10
|
-
|
|
10
|
+
if (!treelike) {
|
|
11
|
+
const error = new TypeError(`take: The tree to take from isn't defined.`);
|
|
12
|
+
/** @type {any} */ (error).position = 0;
|
|
13
|
+
throw error;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const tree = Tree.from(treelike);
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
async keys() {
|
|
20
|
+
const keys = Array.from(await tree.keys());
|
|
21
|
+
return keys.slice(0, count);
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
async get(key) {
|
|
25
|
+
return tree.get(key);
|
|
26
|
+
},
|
|
27
|
+
};
|
|
11
28
|
}
|
package/src/symbols.js
CHANGED
package/test/Tree.test.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import { describe, test } from "node:test";
|
|
3
|
-
import MapTree from "../src/MapTree.js";
|
|
3
|
+
import MapTree from "../src/drivers/MapTree.js";
|
|
4
4
|
import { DeepObjectTree, ObjectTree, Tree } from "../src/internal.js";
|
|
5
|
+
import * as symbols from "../src/symbols.js";
|
|
5
6
|
|
|
6
7
|
describe("Tree", () => {
|
|
7
8
|
test("assign applies one tree to another", async () => {
|
|
@@ -108,6 +109,17 @@ describe("Tree", () => {
|
|
|
108
109
|
assert(tree instanceof DeepObjectTree);
|
|
109
110
|
});
|
|
110
111
|
|
|
112
|
+
test("from returns a deep object tree if object has [deep] symbol set", async () => {
|
|
113
|
+
const obj = {
|
|
114
|
+
sub: {
|
|
115
|
+
a: 1,
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
Object.defineProperty(obj, symbols.deep, { value: true });
|
|
119
|
+
const tree = Tree.from(obj);
|
|
120
|
+
assert(tree instanceof DeepObjectTree);
|
|
121
|
+
});
|
|
122
|
+
|
|
111
123
|
test("from() creates a deferred tree if unpack() returns a promise", async () => {
|
|
112
124
|
const obj = new String();
|
|
113
125
|
/** @type {any} */ (obj).unpack = async () => ({
|
|
@@ -179,7 +191,10 @@ describe("Tree", () => {
|
|
|
179
191
|
b: "Bob",
|
|
180
192
|
},
|
|
181
193
|
});
|
|
182
|
-
const mapped = Tree.map(tree,
|
|
194
|
+
const mapped = Tree.map(tree, {
|
|
195
|
+
deep: true,
|
|
196
|
+
value: (value) => value.toUpperCase(),
|
|
197
|
+
});
|
|
183
198
|
assert.deepEqual(await Tree.plain(mapped), {
|
|
184
199
|
a: "ALICE",
|
|
185
200
|
more: {
|
|
@@ -275,6 +290,14 @@ describe("Tree", () => {
|
|
|
275
290
|
assert.deepEqual(await Tree.plain(object), { name: "Alice" });
|
|
276
291
|
});
|
|
277
292
|
|
|
293
|
+
test("plain() coerces TypedArray values to strings", async () => {
|
|
294
|
+
const tree = new ObjectTree({
|
|
295
|
+
a: new TextEncoder().encode("Hello, world."),
|
|
296
|
+
});
|
|
297
|
+
const plain = await Tree.plain(tree);
|
|
298
|
+
assert.equal(plain.a, "Hello, world.");
|
|
299
|
+
});
|
|
300
|
+
|
|
278
301
|
test("remove method removes a value", async () => {
|
|
279
302
|
const fixture = createFixture();
|
|
280
303
|
await Tree.remove(fixture, "Alice.md");
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import { describe, test } from "node:test";
|
|
3
|
-
import BrowserFileTree from "
|
|
4
|
-
import { Tree } from "
|
|
3
|
+
import BrowserFileTree from "../../src/drivers/BrowserFileTree.js";
|
|
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,7 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import { describe, test } from "node:test";
|
|
3
|
-
import DeepMapTree from "
|
|
4
|
-
import { Tree } from "
|
|
3
|
+
import DeepMapTree from "../../src/drivers/DeepMapTree.js";
|
|
4
|
+
import { Tree } from "../../src/internal.js";
|
|
5
5
|
|
|
6
6
|
describe("DeepMapTree", () => {
|
|
7
7
|
test("returns a DeepMapTree for value that's a Map", async () => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import { describe, test } from "node:test";
|
|
3
|
-
import { DeepObjectTree, Tree } from "
|
|
3
|
+
import { DeepObjectTree, Tree } from "../../src/internal.js";
|
|
4
4
|
|
|
5
5
|
describe("DeepObjectTree", () => {
|
|
6
6
|
test("returns an ObjectTree for value that's a plain sub-object or sub-array", async () => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import { describe, test } from "node:test";
|
|
3
|
-
import DeferredTree from "
|
|
4
|
-
import { ObjectTree, Tree } from "
|
|
3
|
+
import DeferredTree from "../../src/drivers/DeferredTree.js";
|
|
4
|
+
import { ObjectTree, Tree } from "../../src/internal.js";
|
|
5
5
|
|
|
6
6
|
describe("DeferredTree", () => {
|
|
7
7
|
test("lazy-loads a treelike object", async () => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import { beforeEach, describe, mock, test } from "node:test";
|
|
3
|
-
import ExplorableSiteTree from "
|
|
4
|
-
import { Tree } from "
|
|
3
|
+
import ExplorableSiteTree from "../../src/drivers/ExplorableSiteTree.js";
|
|
4
|
+
import { Tree } from "../../src/internal.js";
|
|
5
5
|
|
|
6
6
|
const textDecoder = new TextDecoder();
|
|
7
7
|
const textEncoder = new TextEncoder();
|
|
@@ -77,7 +77,10 @@ describe("ExplorableSiteTree", () => {
|
|
|
77
77
|
test("can convert a site to a plain object", async () => {
|
|
78
78
|
const fixture = new ExplorableSiteTree(mockHost);
|
|
79
79
|
// Convert buffers to strings.
|
|
80
|
-
const strings = Tree.map(fixture,
|
|
80
|
+
const strings = Tree.map(fixture, {
|
|
81
|
+
deep: true,
|
|
82
|
+
value: (value) => textDecoder.decode(value),
|
|
83
|
+
});
|
|
81
84
|
assert.deepEqual(await Tree.plain(strings), {
|
|
82
85
|
about: {
|
|
83
86
|
"Alice.html": "Hello, Alice!",
|
|
@@ -3,8 +3,8 @@ import * as fs from "node:fs/promises";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { describe, test } from "node:test";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
|
-
import FileTree from "
|
|
7
|
-
import { ObjectTree, Tree } from "
|
|
6
|
+
import FileTree from "../../src/drivers/FileTree.js";
|
|
7
|
+
import { ObjectTree, 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");
|
|
@@ -132,9 +132,10 @@ describe("FileTree", async () => {
|
|
|
132
132
|
|
|
133
133
|
// Read them back in.
|
|
134
134
|
const actualFiles = await tempFiles.get("folder");
|
|
135
|
-
const strings = Tree.map(actualFiles,
|
|
136
|
-
|
|
137
|
-
|
|
135
|
+
const strings = Tree.map(actualFiles, {
|
|
136
|
+
deep: true,
|
|
137
|
+
value: (buffer) => textDecoder.decode(buffer),
|
|
138
|
+
});
|
|
138
139
|
const plain = await Tree.plain(strings);
|
|
139
140
|
assert.deepEqual(plain, obj);
|
|
140
141
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import { describe, test } from "node:test";
|
|
3
|
-
import FunctionTree from "
|
|
3
|
+
import FunctionTree from "../../src/drivers/FunctionTree.js";
|
|
4
4
|
|
|
5
5
|
describe("FunctionTree", async () => {
|
|
6
6
|
test("can get the keys of the tree", async () => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import { describe, test } from "node:test";
|
|
3
|
-
import MapTree from "
|
|
4
|
-
import * as symbols from "
|
|
3
|
+
import MapTree from "../../src/drivers/MapTree.js";
|
|
4
|
+
import * as symbols from "../../src/symbols.js";
|
|
5
5
|
|
|
6
6
|
describe("MapTree", () => {
|
|
7
7
|
test("can get the keys of the tree", async () => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import { describe, test } from "node:test";
|
|
3
|
-
import { ObjectTree, Tree } from "
|
|
4
|
-
import * as symbols from "
|
|
3
|
+
import { ObjectTree, Tree } from "../../src/internal.js";
|
|
4
|
+
import * as symbols from "../../src/symbols.js";
|
|
5
5
|
|
|
6
6
|
describe("ObjectTree", () => {
|
|
7
7
|
test("can get the keys of the tree", async () => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import { describe, test } from "node:test";
|
|
3
|
-
import SetTree from "
|
|
4
|
-
import { ObjectTree } from "
|
|
3
|
+
import SetTree from "../../src/drivers/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 () => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import { beforeEach, describe, mock, test } from "node:test";
|
|
3
|
-
import SiteTree from "
|
|
3
|
+
import SiteTree from "../../src/drivers/SiteTree.js";
|
|
4
4
|
|
|
5
5
|
const textDecoder = new TextDecoder();
|
|
6
6
|
const textEncoder = new TextEncoder();
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import { describe, test } from "node:test";
|
|
3
|
-
import
|
|
4
|
-
import { toPlainValue } from "
|
|
3
|
+
import calendar from "../../src/drivers/calendarTree.js";
|
|
4
|
+
import { toPlainValue } from "../../src/utilities.js";
|
|
5
5
|
|
|
6
6
|
describe("calendarTree", () => {
|
|
7
7
|
test("without a start or end, returns a tree for today", async () => {
|
|
8
|
-
const tree =
|
|
8
|
+
const tree = calendar({
|
|
9
9
|
value: (year, month, day) => `${year}-${month}-${day}`,
|
|
10
10
|
});
|
|
11
11
|
const plain = await toPlainValue(tree);
|
|
@@ -23,7 +23,7 @@ describe("calendarTree", () => {
|
|
|
23
23
|
});
|
|
24
24
|
|
|
25
25
|
test("returns a tree for a month range", async () => {
|
|
26
|
-
const tree =
|
|
26
|
+
const tree = calendar({
|
|
27
27
|
start: "2025-01",
|
|
28
28
|
end: "2025-02",
|
|
29
29
|
value: (year, month, day) => `${year}-${month}-${day}`,
|
|
@@ -99,7 +99,7 @@ describe("calendarTree", () => {
|
|
|
99
99
|
});
|
|
100
100
|
|
|
101
101
|
test("returns a tree for a day range", async () => {
|
|
102
|
-
const tree =
|
|
102
|
+
const tree = calendar({
|
|
103
103
|
start: "2025-02-27",
|
|
104
104
|
end: "2025-03-02",
|
|
105
105
|
value: (year, month, day) => `${year}-${month}-${day}`,
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
2
|
+
import { describe, test } from "node:test";
|
|
3
|
+
import { extname, match, replace } from "../src/extension.js";
|
|
4
|
+
|
|
5
|
+
describe("extension", () => {
|
|
6
|
+
test("extname", () => {
|
|
7
|
+
assert.equal(extname(".\\"), "");
|
|
8
|
+
assert.equal(extname("..\\"), ".\\");
|
|
9
|
+
assert.equal(extname("file.ext\\"), ".ext\\");
|
|
10
|
+
assert.equal(extname("file.ext\\\\"), ".ext\\\\");
|
|
11
|
+
assert.equal(extname("file\\"), "");
|
|
12
|
+
assert.equal(extname("file\\\\"), "");
|
|
13
|
+
assert.equal(extname("file.\\"), ".\\");
|
|
14
|
+
assert.equal(extname("file.\\\\"), ".\\\\");
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test("match", () => {
|
|
18
|
+
assert.equal(match("file.md", ".md"), "file");
|
|
19
|
+
assert.equal(match("file.md", ".txt"), null);
|
|
20
|
+
assert.equal(match("file.md/", ".md"), "file/");
|
|
21
|
+
assert.equal(match("file", ""), "file");
|
|
22
|
+
assert.equal(match("file", "/"), null);
|
|
23
|
+
assert.equal(match("file/", "/"), "file");
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test("match can handle multi-part extensions", () => {
|
|
27
|
+
assert.equal(match("foo.ori.html", ".ori.html"), "foo");
|
|
28
|
+
assert.equal(match("foo.ori.html", ".html"), "foo.ori");
|
|
29
|
+
assert.equal(match("foo.ori.html", ".txt"), null);
|
|
30
|
+
assert.equal(match("foo.ori.html/", ".ori.html"), "foo/");
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("replace", () => {
|
|
34
|
+
assert.equal(replace("file.md", ".md", ".html"), "file.html");
|
|
35
|
+
assert.equal(replace("file.md", ".txt", ".html"), "file.md");
|
|
36
|
+
assert.equal(replace("file.md/", ".md", ".html"), "file.html/");
|
|
37
|
+
assert.equal(replace("folder/", "", ".html"), "folder.html");
|
|
38
|
+
assert.equal(replace("folder", "/", ".html"), "folder");
|
|
39
|
+
assert.equal(replace("folder/", "/", ".html"), "folder.html");
|
|
40
|
+
});
|
|
41
|
+
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import { describe, test } from "node:test";
|
|
3
3
|
import { DeepObjectTree, ObjectTree } from "../../src/internal.js";
|
|
4
|
-
import cachedKeyFunctions from "../../src/
|
|
4
|
+
import cachedKeyFunctions from "../../src/operations/cachedKeyFunctions.js";
|
|
5
5
|
|
|
6
6
|
describe("cachedKeyFunctions", () => {
|
|
7
7
|
test("maps keys with caching", async () => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import { describe, test } from "node:test";
|
|
3
|
-
import FunctionTree from "../../src/FunctionTree.js";
|
|
3
|
+
import FunctionTree from "../../src/drivers/FunctionTree.js";
|
|
4
4
|
import { Tree } from "../../src/internal.js";
|
|
5
5
|
import concat from "../../src/operations/concat.js";
|
|
6
6
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import { describe, test } from "node:test";
|
|
3
3
|
import { Tree } from "../../src/internal.js";
|
|
4
|
-
import deepReverse from "../../src/
|
|
4
|
+
import deepReverse from "../../src/operations/deepReverse.js";
|
|
5
5
|
|
|
6
6
|
describe("deepReverse", () => {
|
|
7
7
|
test("reverses keys at all levels of a tree", async () => {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import { describe, test } from "node:test";
|
|
3
3
|
import { Tree } from "../../src/internal.js";
|
|
4
|
-
import
|
|
4
|
+
import deepTake from "../../src/operations/deepTake.js";
|
|
5
5
|
|
|
6
|
-
describe("
|
|
6
|
+
describe("deepTake", () => {
|
|
7
7
|
test("traverses deeply and returns a limited number of items", async () => {
|
|
8
8
|
const tree = {
|
|
9
9
|
a: 1,
|
|
@@ -16,7 +16,7 @@ describe("deepTakeFn", () => {
|
|
|
16
16
|
},
|
|
17
17
|
g: 5,
|
|
18
18
|
};
|
|
19
|
-
const result = await
|
|
19
|
+
const result = await deepTake(tree, 4);
|
|
20
20
|
assert.deepEqual(await Tree.plain(result), [1, 2, 3, 4]);
|
|
21
21
|
});
|
|
22
22
|
});
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import { describe, test } from "node:test";
|
|
3
3
|
import { Tree } from "../../src/internal.js";
|
|
4
|
-
import
|
|
4
|
+
import group from "../../src/operations/group.js";
|
|
5
5
|
|
|
6
|
-
describe("
|
|
6
|
+
describe("group transform", () => {
|
|
7
7
|
test("groups an array using a group key function", async () => {
|
|
8
8
|
const fonts = [
|
|
9
9
|
{ name: "Aboreto", tags: ["Sans Serif"] },
|
|
@@ -12,7 +12,7 @@ describe("groupFn transform", () => {
|
|
|
12
12
|
{ name: "Work Sans", tags: ["Grotesque", "Sans Serif"] },
|
|
13
13
|
];
|
|
14
14
|
const tree = Tree.from(fonts);
|
|
15
|
-
const grouped = await
|
|
15
|
+
const grouped = await group(tree, (value, key, tree) => value.tags);
|
|
16
16
|
assert.deepEqual(await Tree.plain(grouped), {
|
|
17
17
|
Geometric: [{ name: "Albert Sans", tags: ["Geometric", "Sans Serif"] }],
|
|
18
18
|
Grotesque: [{ name: "Work Sans", tags: ["Grotesque", "Sans Serif"] }],
|
|
@@ -33,7 +33,7 @@ describe("groupFn transform", () => {
|
|
|
33
33
|
"Work Sans": { tags: ["Grotesque", "Sans Serif"] },
|
|
34
34
|
};
|
|
35
35
|
const tree = Tree.from(fonts);
|
|
36
|
-
const grouped = await
|
|
36
|
+
const grouped = await group(tree, (value, key, tree) => value.tags);
|
|
37
37
|
assert.deepEqual(await Tree.plain(grouped), {
|
|
38
38
|
Geometric: {
|
|
39
39
|
"Albert Sans": { tags: ["Geometric", "Sans Serif"] },
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import { describe, test } from "node:test";
|
|
3
3
|
import { Tree } from "../../src/internal.js";
|
|
4
|
-
import invokeFunctions from "../../src/
|
|
4
|
+
import invokeFunctions from "../../src/operations/invokeFunctions.js";
|
|
5
5
|
|
|
6
6
|
describe("invokeFunctions", () => {
|
|
7
7
|
test("invokes function values, leaves other values as is", async () => {
|