@weborigami/async-tree 0.0.73 → 0.2.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 +3 -2
- package/src/Tree.js +26 -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} +25 -5
- 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/operations/concat.js +10 -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
|
@@ -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 () => {
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import { describe, test } from "node:test";
|
|
3
3
|
import { ObjectTree, Tree } from "../../src/internal.js";
|
|
4
|
-
import keyFunctionsForExtensions from "../../src/
|
|
5
|
-
import map from "../../src/
|
|
4
|
+
import keyFunctionsForExtensions from "../../src/operations/keyFunctionsForExtensions.js";
|
|
5
|
+
import map from "../../src/operations/map.js";
|
|
6
6
|
|
|
7
7
|
describe("keyMapsForExtensions", () => {
|
|
8
8
|
test("returns key functions that pass a matching key through", async () => {
|
|
9
9
|
const { inverseKey, key } = keyFunctionsForExtensions({
|
|
10
|
-
sourceExtension: "txt",
|
|
10
|
+
sourceExtension: ".txt",
|
|
11
11
|
});
|
|
12
12
|
assert.equal(await inverseKey("file.txt"), "file.txt");
|
|
13
13
|
assert.equal(await inverseKey("file.txt/"), "file.txt");
|
|
@@ -19,8 +19,8 @@ describe("keyMapsForExtensions", () => {
|
|
|
19
19
|
|
|
20
20
|
test("returns key functions that can map extensions", async () => {
|
|
21
21
|
const { inverseKey, key } = keyFunctionsForExtensions({
|
|
22
|
-
resultExtension: "json",
|
|
23
|
-
sourceExtension: "md",
|
|
22
|
+
resultExtension: ".json",
|
|
23
|
+
sourceExtension: ".md",
|
|
24
24
|
});
|
|
25
25
|
assert.equal(await inverseKey("file.json"), "file.md");
|
|
26
26
|
assert.equal(await inverseKey("file.json/"), "file.md");
|
|
@@ -30,6 +30,17 @@ describe("keyMapsForExtensions", () => {
|
|
|
30
30
|
assert.equal(await key("file.foo"), undefined);
|
|
31
31
|
});
|
|
32
32
|
|
|
33
|
+
test("key functions can handle a slash as an explicit extension", async () => {
|
|
34
|
+
const { inverseKey, key } = keyFunctionsForExtensions({
|
|
35
|
+
resultExtension: ".html",
|
|
36
|
+
sourceExtension: "/",
|
|
37
|
+
});
|
|
38
|
+
assert.equal(await inverseKey("file.html"), "file/");
|
|
39
|
+
assert.equal(await inverseKey("file.html/"), "file/");
|
|
40
|
+
assert.equal(await key("file"), undefined);
|
|
41
|
+
assert.equal(await key("file/"), "file.html");
|
|
42
|
+
});
|
|
43
|
+
|
|
33
44
|
test("works with map to handle keys that end in a given resultExtension", async () => {
|
|
34
45
|
const files = new ObjectTree({
|
|
35
46
|
"file1.txt": "will be mapped",
|
|
@@ -37,14 +48,13 @@ describe("keyMapsForExtensions", () => {
|
|
|
37
48
|
"file3.foo": "won't be mapped",
|
|
38
49
|
});
|
|
39
50
|
const { inverseKey, key } = keyFunctionsForExtensions({
|
|
40
|
-
sourceExtension: "txt",
|
|
51
|
+
sourceExtension: ".txt",
|
|
41
52
|
});
|
|
42
|
-
const
|
|
53
|
+
const fixture = map(files, {
|
|
43
54
|
inverseKey,
|
|
44
55
|
key,
|
|
45
56
|
value: (sourceValue, sourceKey, tree) => sourceValue.toUpperCase(),
|
|
46
57
|
});
|
|
47
|
-
const fixture = transform(files);
|
|
48
58
|
assert.deepEqual(await Tree.plain(fixture), {
|
|
49
59
|
"file1.txt": "WILL BE MAPPED",
|
|
50
60
|
});
|
|
@@ -57,15 +67,14 @@ describe("keyMapsForExtensions", () => {
|
|
|
57
67
|
"file3.foo": "won't be mapped",
|
|
58
68
|
});
|
|
59
69
|
const { inverseKey, key } = keyFunctionsForExtensions({
|
|
60
|
-
resultExtension: "upper",
|
|
61
|
-
sourceExtension: "txt",
|
|
70
|
+
resultExtension: ".upper",
|
|
71
|
+
sourceExtension: ".txt",
|
|
62
72
|
});
|
|
63
|
-
const
|
|
73
|
+
const fixture = map(files, {
|
|
64
74
|
inverseKey,
|
|
65
75
|
key,
|
|
66
76
|
value: (sourceValue, sourceKey, tree) => sourceValue.toUpperCase(),
|
|
67
77
|
});
|
|
68
|
-
const fixture = transform(files);
|
|
69
78
|
assert.deepEqual(await Tree.plain(fixture), {
|
|
70
79
|
"file1.upper": "WILL BE MAPPED",
|
|
71
80
|
});
|
|
@@ -1,17 +1,17 @@
|
|
|
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 { DeepObjectTree, ObjectTree, Tree } from "../../src/internal.js";
|
|
5
|
+
import map from "../../src/operations/map.js";
|
|
5
6
|
import * as trailingSlash from "../../src/trailingSlash.js";
|
|
6
|
-
import mapFn from "../../src/transforms/mapFn.js";
|
|
7
7
|
|
|
8
|
-
describe("
|
|
9
|
-
test("returns identity graph if no key or value", async () => {
|
|
8
|
+
describe("map", () => {
|
|
9
|
+
test("returns identity graph if no key or value function is supplied", async () => {
|
|
10
10
|
const tree = {
|
|
11
11
|
a: "letter a",
|
|
12
12
|
b: "letter b",
|
|
13
13
|
};
|
|
14
|
-
const mapped =
|
|
14
|
+
const mapped = map(tree, {});
|
|
15
15
|
assert.deepEqual(await Tree.plain(mapped), {
|
|
16
16
|
a: "letter a",
|
|
17
17
|
b: "letter b",
|
|
@@ -24,14 +24,13 @@ describe("mapFn", () => {
|
|
|
24
24
|
b: "letter b",
|
|
25
25
|
c: undefined, // Won't be mapped
|
|
26
26
|
});
|
|
27
|
-
const
|
|
27
|
+
const mapped = map(tree, {
|
|
28
28
|
value: (sourceValue, sourceKey, innerTree) => {
|
|
29
29
|
assert(sourceKey === "a" || sourceKey === "b");
|
|
30
30
|
assert.equal(innerTree, tree);
|
|
31
31
|
return sourceValue.toUpperCase();
|
|
32
32
|
},
|
|
33
33
|
});
|
|
34
|
-
const mapped = uppercaseValues(tree);
|
|
35
34
|
assert.deepEqual(await Tree.plain(mapped), {
|
|
36
35
|
a: "LETTER A",
|
|
37
36
|
b: "LETTER B",
|
|
@@ -44,12 +43,11 @@ describe("mapFn", () => {
|
|
|
44
43
|
a: "letter a",
|
|
45
44
|
b: "letter b",
|
|
46
45
|
};
|
|
47
|
-
const uppercaseValues =
|
|
46
|
+
const uppercaseValues = map(tree, (sourceValue, sourceKey, tree) => {
|
|
48
47
|
assert(sourceKey === "a" || sourceKey === "b");
|
|
49
48
|
return sourceValue.toUpperCase();
|
|
50
49
|
});
|
|
51
|
-
|
|
52
|
-
assert.deepEqual(await Tree.plain(mapped), {
|
|
50
|
+
assert.deepEqual(await Tree.plain(uppercaseValues), {
|
|
53
51
|
a: "LETTER A",
|
|
54
52
|
b: "LETTER B",
|
|
55
53
|
});
|
|
@@ -60,12 +58,11 @@ describe("mapFn", () => {
|
|
|
60
58
|
a: "letter a",
|
|
61
59
|
b: "letter b",
|
|
62
60
|
};
|
|
63
|
-
const
|
|
61
|
+
const underscoreKeys = map(tree, {
|
|
64
62
|
key: addUnderscore,
|
|
65
63
|
inverseKey: removeUnderscore,
|
|
66
64
|
});
|
|
67
|
-
|
|
68
|
-
assert.deepEqual(await Tree.plain(mapped), {
|
|
65
|
+
assert.deepEqual(await Tree.plain(underscoreKeys), {
|
|
69
66
|
_a: "letter a",
|
|
70
67
|
_b: "letter b",
|
|
71
68
|
});
|
|
@@ -76,13 +73,12 @@ describe("mapFn", () => {
|
|
|
76
73
|
a: "letter a",
|
|
77
74
|
b: "letter b",
|
|
78
75
|
};
|
|
79
|
-
const underscoreKeysUppercaseValues =
|
|
76
|
+
const underscoreKeysUppercaseValues = map(tree, {
|
|
80
77
|
key: addUnderscore,
|
|
81
78
|
inverseKey: removeUnderscore,
|
|
82
79
|
value: async (sourceValue, sourceKey, tree) => sourceValue.toUpperCase(),
|
|
83
80
|
});
|
|
84
|
-
|
|
85
|
-
assert.deepEqual(await Tree.plain(mapped), {
|
|
81
|
+
assert.deepEqual(await Tree.plain(underscoreKeysUppercaseValues), {
|
|
86
82
|
_a: "LETTER A",
|
|
87
83
|
_b: "LETTER B",
|
|
88
84
|
});
|
|
@@ -95,13 +91,12 @@ describe("mapFn", () => {
|
|
|
95
91
|
b: "letter b",
|
|
96
92
|
},
|
|
97
93
|
};
|
|
98
|
-
const
|
|
94
|
+
const underscoreKeys = map(tree, {
|
|
99
95
|
key: async (sourceKey, tree) => `_${sourceKey}`,
|
|
100
96
|
inverseKey: async (resultKey, tree) => resultKey.slice(1),
|
|
101
97
|
value: async (sourceValue, sourceKey, tree) => sourceKey,
|
|
102
98
|
});
|
|
103
|
-
|
|
104
|
-
assert.deepEqual(await Tree.plain(mapped), {
|
|
99
|
+
assert.deepEqual(await Tree.plain(underscoreKeys), {
|
|
105
100
|
_a: "a",
|
|
106
101
|
_more: "more",
|
|
107
102
|
});
|
|
@@ -115,7 +110,7 @@ describe("mapFn", () => {
|
|
|
115
110
|
a: "letter a",
|
|
116
111
|
b: "letter b",
|
|
117
112
|
};
|
|
118
|
-
const mapped =
|
|
113
|
+
const mapped = map(tree, uppercase);
|
|
119
114
|
assert.deepEqual(await Tree.plain(mapped), {
|
|
120
115
|
_a: "LETTER A",
|
|
121
116
|
_b: "LETTER B",
|
|
@@ -129,12 +124,11 @@ describe("mapFn", () => {
|
|
|
129
124
|
b: "letter b",
|
|
130
125
|
},
|
|
131
126
|
});
|
|
132
|
-
const uppercaseValues =
|
|
127
|
+
const uppercaseValues = map(tree, {
|
|
133
128
|
deep: true,
|
|
134
129
|
value: (sourceValue, sourceKey, tree) => sourceValue.toUpperCase(),
|
|
135
130
|
});
|
|
136
|
-
|
|
137
|
-
assert.deepEqual(await Tree.plain(mapped), {
|
|
131
|
+
assert.deepEqual(await Tree.plain(uppercaseValues), {
|
|
138
132
|
a: "LETTER A",
|
|
139
133
|
more: {
|
|
140
134
|
b: "LETTER B",
|
|
@@ -149,13 +143,12 @@ describe("mapFn", () => {
|
|
|
149
143
|
b: "letter b",
|
|
150
144
|
},
|
|
151
145
|
});
|
|
152
|
-
const underscoreKeys =
|
|
146
|
+
const underscoreKeys = map(tree, {
|
|
153
147
|
deep: true,
|
|
154
148
|
key: addUnderscore,
|
|
155
149
|
inverseKey: removeUnderscore,
|
|
156
150
|
});
|
|
157
|
-
|
|
158
|
-
assert.deepEqual(await Tree.plain(mapped), {
|
|
151
|
+
assert.deepEqual(await Tree.plain(underscoreKeys), {
|
|
159
152
|
_a: "letter a",
|
|
160
153
|
more: {
|
|
161
154
|
_b: "letter b",
|
|
@@ -170,14 +163,13 @@ describe("mapFn", () => {
|
|
|
170
163
|
b: "letter b",
|
|
171
164
|
},
|
|
172
165
|
});
|
|
173
|
-
const underscoreKeysUppercaseValues =
|
|
166
|
+
const underscoreKeysUppercaseValues = map(tree, {
|
|
174
167
|
deep: true,
|
|
175
168
|
key: addUnderscore,
|
|
176
169
|
inverseKey: removeUnderscore,
|
|
177
170
|
value: async (sourceValue, sourceKey, tree) => sourceValue.toUpperCase(),
|
|
178
171
|
});
|
|
179
|
-
|
|
180
|
-
assert.deepEqual(await Tree.plain(mapped), {
|
|
172
|
+
assert.deepEqual(await Tree.plain(underscoreKeysUppercaseValues), {
|
|
181
173
|
_a: "LETTER A",
|
|
182
174
|
more: {
|
|
183
175
|
_b: "LETTER B",
|
|
@@ -190,10 +182,10 @@ describe("mapFn", () => {
|
|
|
190
182
|
const tree = new FunctionTree(() => {
|
|
191
183
|
flag = true;
|
|
192
184
|
}, ["a", "b", "c"]);
|
|
193
|
-
const mapped =
|
|
185
|
+
const mapped = map(tree, {
|
|
194
186
|
needsSourceValue: false,
|
|
195
187
|
value: () => "X",
|
|
196
|
-
})
|
|
188
|
+
});
|
|
197
189
|
assert.deepEqual(await Tree.plain(mapped), {
|
|
198
190
|
a: "X",
|
|
199
191
|
b: "X",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import { describe, test } from "node:test";
|
|
3
3
|
import { DeepObjectTree, Tree } from "../../src/internal.js";
|
|
4
|
-
import regExpKeys from "../../src/
|
|
4
|
+
import regExpKeys from "../../src/operations/regExpKeys.js";
|
|
5
5
|
|
|
6
6
|
describe("regExpKeys", () => {
|
|
7
7
|
test("matches keys using regular expressions", async () => {
|
|
@@ -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 reverse from "../../src/
|
|
4
|
+
import reverse from "../../src/operations/reverse.js";
|
|
5
5
|
|
|
6
6
|
describe("reverse", () => {
|
|
7
7
|
test("reverses a tree's top-level keys", async () => {
|
|
@@ -1,16 +1,16 @@
|
|
|
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 sort from "../../src/operations/sort.js";
|
|
5
5
|
|
|
6
|
-
describe("
|
|
6
|
+
describe("sort", () => {
|
|
7
7
|
test("sorts keys using default sort order", async () => {
|
|
8
8
|
const tree = Tree.from({
|
|
9
9
|
file10: null,
|
|
10
10
|
file1: null,
|
|
11
11
|
file9: null,
|
|
12
12
|
});
|
|
13
|
-
const sorted =
|
|
13
|
+
const sorted = sort(tree);
|
|
14
14
|
assert.deepEqual(Array.from(await sorted.keys()), [
|
|
15
15
|
"file1",
|
|
16
16
|
"file10",
|
|
@@ -26,7 +26,7 @@ describe("sortFn", () => {
|
|
|
26
26
|
});
|
|
27
27
|
// Reverse order
|
|
28
28
|
const compare = (a, b) => (a > b ? -1 : a < b ? 1 : 0);
|
|
29
|
-
const sorted =
|
|
29
|
+
const sorted = sort(tree, { compare });
|
|
30
30
|
assert.deepEqual(Array.from(await sorted.keys()), ["c", "b", "a"]);
|
|
31
31
|
});
|
|
32
32
|
|
|
@@ -36,11 +36,10 @@ describe("sortFn", () => {
|
|
|
36
36
|
Bob: { age: 36 },
|
|
37
37
|
Carol: { age: 42 },
|
|
38
38
|
};
|
|
39
|
-
const
|
|
39
|
+
const sorted = await sort(tree, {
|
|
40
40
|
sortKey: async (key, tree) => Tree.traverse(tree, key, "age"),
|
|
41
41
|
});
|
|
42
|
-
|
|
43
|
-
assert.deepEqual(Array.from(await result.keys()), [
|
|
42
|
+
assert.deepEqual(Array.from(await sorted.keys()), [
|
|
44
43
|
"Bob",
|
|
45
44
|
"Carol",
|
|
46
45
|
"Alice",
|
|
@@ -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 take from "../../src/operations/take.js";
|
|
5
5
|
|
|
6
|
-
describe("
|
|
6
|
+
describe("take", () => {
|
|
7
7
|
test("limits the number of keys to the indicated count", async () => {
|
|
8
8
|
const tree = {
|
|
9
9
|
a: 1,
|
|
@@ -11,7 +11,7 @@ describe("takeFn", () => {
|
|
|
11
11
|
c: 3,
|
|
12
12
|
d: 4,
|
|
13
13
|
};
|
|
14
|
-
const result = await
|
|
14
|
+
const result = await take(tree, 2);
|
|
15
15
|
assert.deepEqual(await Tree.plain(result), {
|
|
16
16
|
a: 1,
|
|
17
17
|
b: 2,
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { Tree } from "../internal.js";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Returns a function that traverses a tree deeply and returns the values of the
|
|
5
|
-
* first `count` keys.
|
|
6
|
-
*
|
|
7
|
-
* @param {number} count
|
|
8
|
-
*/
|
|
9
|
-
export default function deepTakeFn(count) {
|
|
10
|
-
/**
|
|
11
|
-
* @param {import("../../index.ts").Treelike} treelike
|
|
12
|
-
*/
|
|
13
|
-
return async function deepTakeFn(treelike) {
|
|
14
|
-
if (!treelike) {
|
|
15
|
-
const error = new TypeError(`deepTake: The tree isn't defined.`);
|
|
16
|
-
/** @type {any} */ (error).position = 0;
|
|
17
|
-
throw error;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const tree = await Tree.from(treelike, { deep: true });
|
|
21
|
-
const { values } = await traverse(tree, count);
|
|
22
|
-
return Tree.from(values);
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
async function traverse(tree, count) {
|
|
27
|
-
const values = [];
|
|
28
|
-
for (const key of await tree.keys()) {
|
|
29
|
-
if (count <= 0) {
|
|
30
|
-
break;
|
|
31
|
-
}
|
|
32
|
-
let value = await tree.get(key);
|
|
33
|
-
if (Tree.isAsyncTree(value)) {
|
|
34
|
-
const traversed = await traverse(value, count);
|
|
35
|
-
values.push(...traversed.values);
|
|
36
|
-
count = traversed.count;
|
|
37
|
-
} else {
|
|
38
|
-
values.push(value);
|
|
39
|
-
count--;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
return { count, values };
|
|
43
|
-
}
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { ObjectTree, Tree } from "../internal.js";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Given a function that returns a grouping key for a value, returns a transform
|
|
5
|
-
* that applies that grouping function to a tree.
|
|
6
|
-
*
|
|
7
|
-
* @param {import("../../index.ts").ValueKeyFn} groupKeyFn
|
|
8
|
-
*/
|
|
9
|
-
export default function createGroupByTransform(groupKeyFn) {
|
|
10
|
-
/**
|
|
11
|
-
* @type {import("../../index.ts").TreeTransform}
|
|
12
|
-
*/
|
|
13
|
-
return async function groupByTransform(treelike) {
|
|
14
|
-
if (!treelike) {
|
|
15
|
-
const error = new TypeError(`groupBy: The tree to group isn't defined.`);
|
|
16
|
-
/** @type {any} */ (error).position = 0;
|
|
17
|
-
throw error;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const tree = Tree.from(treelike);
|
|
21
|
-
|
|
22
|
-
const keys = Array.from(await tree.keys());
|
|
23
|
-
|
|
24
|
-
// Are all the keys integers?
|
|
25
|
-
const isArray = keys.every((key) => !Number.isNaN(parseInt(key)));
|
|
26
|
-
|
|
27
|
-
const result = {};
|
|
28
|
-
for (const key of await tree.keys()) {
|
|
29
|
-
const value = await tree.get(key);
|
|
30
|
-
|
|
31
|
-
// Get the groups for this value.
|
|
32
|
-
let groups = await groupKeyFn(value, key, tree);
|
|
33
|
-
if (!groups) {
|
|
34
|
-
continue;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
if (!Tree.isTreelike(groups)) {
|
|
38
|
-
// A single value was returned
|
|
39
|
-
groups = [groups];
|
|
40
|
-
}
|
|
41
|
-
groups = Tree.from(groups);
|
|
42
|
-
|
|
43
|
-
// Add the value to each group.
|
|
44
|
-
for (const group of await Tree.values(groups)) {
|
|
45
|
-
if (isArray) {
|
|
46
|
-
result[group] ??= [];
|
|
47
|
-
result[group].push(value);
|
|
48
|
-
} else {
|
|
49
|
-
result[group] ??= {};
|
|
50
|
-
result[group][key] = value;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
return new ObjectTree(result);
|
|
56
|
-
};
|
|
57
|
-
}
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import * as trailingSlash from "../trailingSlash.js";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Given a source resultExtension and a result resultExtension, return a pair of key
|
|
5
|
-
* functions that map between them.
|
|
6
|
-
*
|
|
7
|
-
* The resulting `inverseKey` and `key` functions are compatible with those
|
|
8
|
-
* expected by map and other transforms.
|
|
9
|
-
*
|
|
10
|
-
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
|
|
11
|
-
* @param {{ resultExtension?: string, sourceExtension: string }}
|
|
12
|
-
* options
|
|
13
|
-
*/
|
|
14
|
-
export default function keyFunctionsForExtensions({
|
|
15
|
-
resultExtension,
|
|
16
|
-
sourceExtension,
|
|
17
|
-
}) {
|
|
18
|
-
if (resultExtension === undefined) {
|
|
19
|
-
resultExtension = sourceExtension;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
return {
|
|
23
|
-
async inverseKey(resultKey, tree) {
|
|
24
|
-
// Remove trailing slash so that mapFn won't inadvertently unpack files.
|
|
25
|
-
const baseKey = trailingSlash.remove(resultKey);
|
|
26
|
-
const basename = matchExtension(baseKey, resultExtension);
|
|
27
|
-
return basename ? `${basename}${dotPrefix(sourceExtension)}` : undefined;
|
|
28
|
-
},
|
|
29
|
-
|
|
30
|
-
async key(sourceKey, tree) {
|
|
31
|
-
const hasSlash = trailingSlash.has(sourceKey);
|
|
32
|
-
const baseKey = trailingSlash.remove(sourceKey);
|
|
33
|
-
const basename = matchExtension(baseKey, sourceExtension);
|
|
34
|
-
return basename
|
|
35
|
-
? // Preserve trailing slash
|
|
36
|
-
trailingSlash.toggle(
|
|
37
|
-
`${basename}${dotPrefix(resultExtension)}`,
|
|
38
|
-
hasSlash
|
|
39
|
-
)
|
|
40
|
-
: undefined;
|
|
41
|
-
},
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function dotPrefix(resultExtension) {
|
|
46
|
-
return resultExtension ? `.${resultExtension}` : "";
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* See if the key ends with the given resultExtension. If it does, return the
|
|
51
|
-
* base name without the resultExtension; if it doesn't return null.
|
|
52
|
-
*
|
|
53
|
-
* A trailing slash in the key is ignored for purposes of comparison to comply
|
|
54
|
-
* with the way Origami can unpack files. Example: the keys "data.json" and
|
|
55
|
-
* "data.json/" are treated equally.
|
|
56
|
-
*
|
|
57
|
-
* This uses a different, more general interpretation of "resultExtension" to
|
|
58
|
-
* mean any suffix, rather than Node's interpretation `path.extname`. In
|
|
59
|
-
* particular, this will match an "resultExtension" like ".foo.bar" that
|
|
60
|
-
* contains more than one dot.
|
|
61
|
-
*/
|
|
62
|
-
function matchExtension(key, resultExtension) {
|
|
63
|
-
if (resultExtension) {
|
|
64
|
-
// Key matches if it ends with the same resultExtension
|
|
65
|
-
const dotExtension = dotPrefix(resultExtension);
|
|
66
|
-
if (
|
|
67
|
-
key.length > dotExtension.length &&
|
|
68
|
-
key.toLowerCase().endsWith(dotExtension)
|
|
69
|
-
) {
|
|
70
|
-
return key.substring(0, key.length - dotExtension.length);
|
|
71
|
-
}
|
|
72
|
-
} else if (!key.includes?.(".")) {
|
|
73
|
-
// Key matches if it has no resultExtension
|
|
74
|
-
return key;
|
|
75
|
-
}
|
|
76
|
-
// Didn't match
|
|
77
|
-
return null;
|
|
78
|
-
}
|
package/src/transforms/mapFn.js
DELETED
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
import { Tree } from "../internal.js";
|
|
2
|
-
import * as trailingSlash from "../trailingSlash.js";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Return a transform function that maps the keys and/or values of a tree.
|
|
6
|
-
*
|
|
7
|
-
* @typedef {import("../../index.ts").KeyFn} KeyFn
|
|
8
|
-
* @typedef {import("../../index.ts").ValueKeyFn} ValueKeyFn
|
|
9
|
-
*
|
|
10
|
-
* @param {ValueKeyFn|{ deep?: boolean, description?: string, needsSourceValue?: boolean, inverseKey?: KeyFn, key?: KeyFn, value?: ValueKeyFn }} options
|
|
11
|
-
* @returns {import("../../index.ts").TreeTransform}
|
|
12
|
-
*/
|
|
13
|
-
export default function createMapTransform(options = {}) {
|
|
14
|
-
let deep;
|
|
15
|
-
let description;
|
|
16
|
-
let inverseKeyFn;
|
|
17
|
-
let keyFn;
|
|
18
|
-
let needsSourceValue;
|
|
19
|
-
let valueFn;
|
|
20
|
-
if (typeof options === "function") {
|
|
21
|
-
// Take the single function argument as the valueFn
|
|
22
|
-
valueFn = options;
|
|
23
|
-
} else {
|
|
24
|
-
deep = options.deep;
|
|
25
|
-
description = options.description;
|
|
26
|
-
inverseKeyFn = options.inverseKey;
|
|
27
|
-
keyFn = options.key;
|
|
28
|
-
needsSourceValue = options.needsSourceValue;
|
|
29
|
-
valueFn = options.value;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
deep ??= false;
|
|
33
|
-
description ??= "key/value map";
|
|
34
|
-
// @ts-ignore
|
|
35
|
-
inverseKeyFn ??= valueFn?.inverseKey;
|
|
36
|
-
// @ts-ignore
|
|
37
|
-
keyFn ??= valueFn?.key;
|
|
38
|
-
needsSourceValue ??= true;
|
|
39
|
-
|
|
40
|
-
if ((keyFn && !inverseKeyFn) || (!keyFn && inverseKeyFn)) {
|
|
41
|
-
throw new TypeError(
|
|
42
|
-
`map: You must specify both key and inverseKey functions, or neither.`
|
|
43
|
-
);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* @type {import("../../index.ts").TreeTransform}
|
|
48
|
-
*/
|
|
49
|
-
return function map(treelike) {
|
|
50
|
-
if (!treelike) {
|
|
51
|
-
const error = new TypeError(`map: The tree to map isn't defined.`);
|
|
52
|
-
/** @type {any} */ (error).position = 0;
|
|
53
|
-
throw error;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const tree = Tree.from(treelike, { deep });
|
|
57
|
-
|
|
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 = map(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
|
-
}
|