@weborigami/async-tree 0.0.65 → 0.0.66-beta.2

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.
Files changed (45) hide show
  1. package/main.js +2 -0
  2. package/package.json +4 -4
  3. package/src/BrowserFileTree.js +28 -6
  4. package/src/DeepMapTree.js +2 -2
  5. package/src/DeepObjectTree.js +6 -9
  6. package/src/FileTree.js +14 -10
  7. package/src/MapTree.js +27 -4
  8. package/src/ObjectTree.js +53 -6
  9. package/src/OpenSiteTree.js +41 -0
  10. package/src/SetTree.js +0 -6
  11. package/src/SiteTree.js +24 -85
  12. package/src/Tree.d.ts +1 -2
  13. package/src/Tree.js +40 -52
  14. package/src/jsonKeys.js +4 -37
  15. package/src/operations/cache.js +0 -4
  16. package/src/operations/deepMerge.js +7 -10
  17. package/src/operations/merge.js +7 -10
  18. package/src/trailingSlash.js +54 -0
  19. package/src/transforms/cachedKeyFunctions.js +72 -34
  20. package/src/transforms/keyFunctionsForExtensions.js +24 -10
  21. package/src/transforms/mapFn.js +11 -17
  22. package/src/transforms/regExpKeys.js +17 -12
  23. package/src/utilities.js +34 -6
  24. package/test/BrowserFileTree.test.js +28 -5
  25. package/test/DeepMapTree.test.js +17 -0
  26. package/test/DeepObjectTree.test.js +17 -7
  27. package/test/FileTree.test.js +14 -7
  28. package/test/MapTree.test.js +21 -0
  29. package/test/ObjectTree.test.js +16 -12
  30. package/test/OpenSiteTree.test.js +113 -0
  31. package/test/SiteTree.test.js +14 -49
  32. package/test/Tree.test.js +19 -39
  33. package/test/browser/assert.js +9 -0
  34. package/test/browser/index.html +4 -4
  35. package/test/calendarTree.test.js +1 -1
  36. package/test/fixtures/markdown/subfolder/README.md +1 -0
  37. package/test/jsonKeys.test.js +0 -9
  38. package/test/operations/cache.test.js +1 -1
  39. package/test/operations/merge.test.js +20 -1
  40. package/test/trailingSlash.test.js +36 -0
  41. package/test/transforms/cachedKeyFunctions.test.js +90 -0
  42. package/test/transforms/keyFunctionsForExtensions.test.js +7 -3
  43. package/test/transforms/mapFn.test.js +29 -20
  44. package/test/utilities.test.js +6 -2
  45. package/test/transforms/cachedKeyMaps.test.js +0 -41
package/test/Tree.test.js CHANGED
@@ -31,7 +31,8 @@ describe("Tree", () => {
31
31
  const result = await Tree.assign(target, source);
32
32
 
33
33
  assert.equal(result, target);
34
- assert.deepEqual(await Tree.plain(target), {
34
+ const plain = await Tree.plain(target);
35
+ assert.deepEqual(plain, {
35
36
  a: 4,
36
37
  c: 5,
37
38
  more: {
@@ -171,17 +172,6 @@ describe("Tree", () => {
171
172
  assert(Tree.isTreelike(new Set()));
172
173
  });
173
174
 
174
- test("isKeyForSubtree() returns true if the key is for a subtree", async () => {
175
- const tree = new DeepObjectTree({
176
- a: 1,
177
- more: {
178
- b: 2,
179
- },
180
- });
181
- assert(!(await Tree.isKeyForSubtree(tree, "a")));
182
- assert(await Tree.isKeyForSubtree(tree, "more"));
183
- });
184
-
185
175
  test("map() maps values", async () => {
186
176
  const tree = new DeepObjectTree({
187
177
  a: "Alice",
@@ -228,18 +218,26 @@ describe("Tree", () => {
228
218
  });
229
219
 
230
220
  test("plain() produces a plain object version of a tree", async () => {
231
- const original = {
221
+ const tree = new ObjectTree({
232
222
  a: 1,
233
- b: 2,
234
- c: 3,
235
- more: {
236
- d: 4,
237
- e: 5,
223
+ // Slashes should be normalized
224
+ "sub1/": {
225
+ b: 2,
238
226
  },
239
- };
240
- const tree = new ObjectTree(original);
227
+ sub2: {
228
+ c: 3,
229
+ },
230
+ });
241
231
  const plain = await Tree.plain(tree);
242
- assert.deepEqual(plain, original);
232
+ assert.deepEqual(plain, {
233
+ a: 1,
234
+ sub1: {
235
+ b: 2,
236
+ },
237
+ sub2: {
238
+ c: 3,
239
+ },
240
+ });
243
241
  });
244
242
 
245
243
  test("plain() produces an array for an array-like tree", async () => {
@@ -335,24 +333,6 @@ describe("Tree", () => {
335
333
  assert.equal(await Tree.traverse(tree, "a", "b", "c"), "Hello");
336
334
  });
337
335
 
338
- test("traversing a final empty string can unpack the last value", async () => {
339
- const unpackable = new String();
340
- /** @type {any} */ (unpackable).unpack = () => "Content";
341
- const tree = {
342
- unpackable,
343
- };
344
- const result = await Tree.traverse(tree, "unpackable", "");
345
- assert.equal(result, "Content");
346
- });
347
-
348
- test("traversing a final empty string returns a value as is if it's not unpackable", async () => {
349
- const tree = new ObjectTree({
350
- a: "Hello",
351
- });
352
- const result = await Tree.traverse(tree, "a", "");
353
- assert.equal(result, "Hello");
354
- });
355
-
356
336
  test("traversePath() traverses a slash-separated path", async () => {
357
337
  const tree = new ObjectTree({
358
338
  a: {
@@ -43,3 +43,12 @@ assert.deepEqual = (actual, expected) => {
43
43
 
44
44
  throw new Error(`Expected ${expected} but got ${actual}`);
45
45
  };
46
+
47
+ assert.rejects = async (promise) => {
48
+ try {
49
+ await promise;
50
+ throw new Error("Expected promise to reject but it resolved");
51
+ } catch (error) {
52
+ return;
53
+ }
54
+ };
@@ -22,13 +22,13 @@
22
22
  <script type="module" src="../Tree.test.js"></script>
23
23
  <script type="module" src="../operations/cache.test.js"></script>
24
24
  <script type="module" src="../operations/merge.test.js"></script>
25
- <script type="module" src="../operations/mergeDeep.test.js"></script>
26
- <script type="module" src="../transforms/cachedKeyFns.test.js"></script>
25
+ <script type="module" src="../operations/deepMerge.test.js"></script>
26
+ <script type="module" src="../transforms/cachedKeyMaps.test.js"></script>
27
27
  <script
28
28
  type="module"
29
- src="../transforms/keyMapsForExtensions.test.js"
29
+ src="../transforms/keyFunctionsForExtensions.test.js"
30
30
  ></script>
31
- <script type="module" src="../transforms/map.test.js"></script>
31
+ <script type="module" src="../transforms/mapFn.test.js"></script>
32
32
  <script type="module" src="../utilities.test.js"></script>
33
33
  </head>
34
34
  <body></body>
@@ -1,7 +1,7 @@
1
1
  import assert from "node:assert";
2
2
  import { describe, test } from "node:test";
3
- import { toPlainValue } from "../main.js";
4
3
  import calendarTree from "../src/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 () => {
@@ -0,0 +1 @@
1
+ This file exists to force the creation of the parent folder.
@@ -4,15 +4,6 @@ import { describe, test } from "node:test";
4
4
  import * as jsonKeys from "../src/jsonKeys.js";
5
5
 
6
6
  describe("jsonKeys", () => {
7
- test("parses JSON Keys", async () => {
8
- const json = '["index.html","about/"]';
9
- const parsed = jsonKeys.parse(json);
10
- assert.deepStrictEqual(parsed, {
11
- "index.html": false,
12
- about: true,
13
- });
14
- });
15
-
16
7
  test("stringifies JSON Keys", async () => {
17
8
  const tree = new DeepObjectTree({
18
9
  about: {},
@@ -19,7 +19,7 @@ describe("cache", () => {
19
19
  );
20
20
 
21
21
  const keys = [...(await fixture.keys())];
22
- assert.deepEqual(keys, ["a", "b", "c", "more"]);
22
+ assert.deepEqual(keys, ["a", "b", "c", "more/"]);
23
23
 
24
24
  assert.equal(await objectCache.get("a"), undefined);
25
25
  assert.equal(await fixture.get("a"), 1);
@@ -1,6 +1,6 @@
1
1
  import assert from "node:assert";
2
2
  import { describe, test } from "node:test";
3
- import { Tree } from "../../src/internal.js";
3
+ import { DeepObjectTree, ObjectTree, Tree } from "../../src/internal.js";
4
4
  import merge from "../../src/operations/merge.js";
5
5
  import * as symbols from "../../src/symbols.js";
6
6
 
@@ -42,4 +42,23 @@ describe("merge", () => {
42
42
  const b = await fixture.get("b");
43
43
  assert.equal(b[symbols.parent], fixture);
44
44
  });
45
+
46
+ test("subtree can overwrite a leaf node", async () => {
47
+ const fixture = merge(
48
+ new ObjectTree({
49
+ a: 1,
50
+ }),
51
+ new DeepObjectTree({
52
+ a: {
53
+ b: 2,
54
+ },
55
+ })
56
+ );
57
+ assert.deepEqual([...(await fixture.keys())], ["a/"]);
58
+ assert.deepEqual(await Tree.plain(fixture), {
59
+ a: {
60
+ b: 2,
61
+ },
62
+ });
63
+ });
45
64
  });
@@ -0,0 +1,36 @@
1
+ import assert from "node:assert";
2
+ import { describe, test } from "node:test";
3
+ import { add, has, remove, toggle } from "../src/trailingSlash.js";
4
+
5
+ describe("trailingSlash", () => {
6
+ test("add adds a trailing slash to a string key for a truthy value", () => {
7
+ assert.equal(add("key"), "key/");
8
+ assert.equal(add("key/"), "key/");
9
+ assert.equal(add(1), 1);
10
+ });
11
+
12
+ test("has returns true if a string key has a trailing slash", () => {
13
+ assert.equal(has("key/"), true);
14
+ assert.equal(has("key"), false);
15
+ assert.equal(has(1), false);
16
+ });
17
+
18
+ test("remove removes a trailing slash from a string key", () => {
19
+ assert.equal(remove("key/"), "key");
20
+ assert.equal(remove("key"), "key");
21
+ assert.equal(remove(1), 1);
22
+ });
23
+
24
+ test("toggle removes a slash if present, adds one if not", () => {
25
+ assert.equal(toggle("key/"), "key");
26
+ assert.equal(toggle("key"), "key/");
27
+ assert.equal(toggle(1), 1);
28
+ });
29
+
30
+ test("toggle can force toggling on or off", () => {
31
+ assert.equal(toggle("key/", false), "key");
32
+ assert.equal(toggle("key/", true), "key/");
33
+ assert.equal(toggle("key", false), "key");
34
+ assert.equal(toggle("key", true), "key/");
35
+ });
36
+ });
@@ -0,0 +1,90 @@
1
+ import assert from "node:assert";
2
+ import { describe, test } from "node:test";
3
+ import { DeepObjectTree, ObjectTree } from "../../src/internal.js";
4
+ import cachedKeyFunctions from "../../src/transforms/cachedKeyFunctions.js";
5
+
6
+ describe("cachedKeyFunctions", () => {
7
+ test("maps keys with caching", async () => {
8
+ const tree = new ObjectTree({
9
+ a: "letter a",
10
+ b: "letter b",
11
+ });
12
+
13
+ let callCount = 0;
14
+ const addUnderscore = async (sourceKey, tree) => {
15
+ callCount++;
16
+ return `_${sourceKey}`;
17
+ };
18
+
19
+ const { inverseKey, key } = cachedKeyFunctions(addUnderscore);
20
+
21
+ assert.equal(await inverseKey("_a", tree), "a"); // Cache miss
22
+ assert.equal(callCount, 1);
23
+ assert.equal(await inverseKey("_a", tree), "a");
24
+ assert.equal(callCount, 1);
25
+ assert.equal(await inverseKey("_b", tree), "b"); // Cache miss
26
+ assert.equal(callCount, 2);
27
+
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
+ assert.equal(callCount, 2);
32
+
33
+ // `c` isn't in tree, so we should get undefined.
34
+ assert.equal(await inverseKey("_c", tree), undefined);
35
+ // But key mapping is still possible.
36
+ assert.equal(await key("c", tree), "_c");
37
+ // And now we have a cache hit.
38
+ assert.equal(await inverseKey("_c", tree), "c");
39
+ assert.equal(callCount, 3);
40
+ });
41
+
42
+ test("maps keys with caching and deep option", async () => {
43
+ const tree = new DeepObjectTree({
44
+ a: "letter a",
45
+ b: {
46
+ c: "letter c",
47
+ },
48
+ });
49
+
50
+ let callCount = 0;
51
+ const addUnderscore = async (sourceKey, tree) => {
52
+ callCount++;
53
+ return `_${sourceKey}`;
54
+ };
55
+
56
+ const { inverseKey, key } = cachedKeyFunctions(addUnderscore, true);
57
+
58
+ assert.equal(await inverseKey("_a", tree), "a"); // Cache miss
59
+ assert.equal(await inverseKey("_a", tree), "a");
60
+ assert.equal(callCount, 1);
61
+
62
+ // Subtree key left alone
63
+ assert.equal(await inverseKey("_b", tree), undefined);
64
+ assert.equal(await inverseKey("b", tree), "b");
65
+ assert.equal(await inverseKey("b/", tree), "b/");
66
+ assert.equal(callCount, 1);
67
+
68
+ assert.equal(await key("a", tree), "_a");
69
+ assert.equal(await key("a", tree), "_a");
70
+ assert.equal(callCount, 1);
71
+
72
+ assert.equal(await key("b/", tree), "b/");
73
+ assert.equal(await key("b", tree), "b");
74
+ assert.equal(callCount, 1);
75
+ });
76
+
77
+ test("preserves trailing slashes", async () => {
78
+ const tree = new ObjectTree({
79
+ a: "letter a",
80
+ });
81
+ const addUnderscore = async (sourceKey) => `_${sourceKey}`;
82
+ const { inverseKey, key } = cachedKeyFunctions(addUnderscore);
83
+
84
+ assert.equal(await key("a/", tree), "_a/");
85
+ assert.equal(await key("a", tree), "_a");
86
+
87
+ assert.equal(await inverseKey("_a/", tree), "a/");
88
+ assert.equal(await inverseKey("_a", tree), "a");
89
+ });
90
+ });
@@ -10,18 +10,22 @@ describe("keyMapsForExtensions", () => {
10
10
  sourceExtension: "txt",
11
11
  });
12
12
  assert.equal(await inverseKey("file.txt"), "file.txt");
13
+ assert.equal(await inverseKey("file.txt/"), "file.txt");
13
14
  assert.equal(await key("file.txt"), "file.txt");
15
+ assert.equal(await key("file.txt/"), "file.txt/");
14
16
  assert.equal(await inverseKey("file.foo"), undefined);
15
17
  assert.equal(await key("file.foo"), undefined);
16
18
  });
17
19
 
18
20
  test("returns key functions that can map extensions", async () => {
19
21
  const { inverseKey, key } = keyFunctionsForExtensions({
20
- resultExtension: "html",
22
+ resultExtension: "json",
21
23
  sourceExtension: "md",
22
24
  });
23
- assert.equal(await inverseKey("file.html"), "file.md");
24
- assert.equal(await key("file.md"), "file.html");
25
+ assert.equal(await inverseKey("file.json"), "file.md");
26
+ assert.equal(await inverseKey("file.json/"), "file.md");
27
+ assert.equal(await key("file.md"), "file.json");
28
+ assert.equal(await key("file.md/"), "file.json/");
25
29
  assert.equal(await inverseKey("file.foo"), undefined);
26
30
  assert.equal(await key("file.foo"), undefined);
27
31
  });
@@ -2,6 +2,7 @@ import assert from "node:assert";
2
2
  import { describe, test } from "node:test";
3
3
  import FunctionTree from "../../src/FunctionTree.js";
4
4
  import { DeepObjectTree, ObjectTree, Tree } from "../../src/internal.js";
5
+ import * as trailingSlash from "../../src/trailingSlash.js";
5
6
  import mapFn from "../../src/transforms/mapFn.js";
6
7
 
7
8
  describe("mapFn", () => {
@@ -59,11 +60,11 @@ describe("mapFn", () => {
59
60
  a: "letter a",
60
61
  b: "letter b",
61
62
  };
62
- const doubleKeys = mapFn({
63
- key: async (sourceKey, tree) => `_${sourceKey}`,
64
- inverseKey: async (resultKey, tree) => resultKey.slice(1),
63
+ const underscopeKeys = mapFn({
64
+ key: addUnderscore,
65
+ inverseKey: removeUnderscore,
65
66
  });
66
- const mapped = doubleKeys(tree);
67
+ const mapped = underscopeKeys(tree);
67
68
  assert.deepEqual(await Tree.plain(mapped), {
68
69
  _a: "letter a",
69
70
  _b: "letter b",
@@ -75,12 +76,12 @@ describe("mapFn", () => {
75
76
  a: "letter a",
76
77
  b: "letter b",
77
78
  };
78
- const doubleKeysUppercaseValues = mapFn({
79
- key: async (sourceKey, tree) => `_${sourceKey}`,
80
- inverseKey: async (resultKey, tree) => resultKey.slice(1),
79
+ const underscoreKeysUppercaseValues = mapFn({
80
+ key: addUnderscore,
81
+ inverseKey: removeUnderscore,
81
82
  value: async (sourceValue, sourceKey, tree) => sourceValue.toUpperCase(),
82
83
  });
83
- const mapped = doubleKeysUppercaseValues(tree);
84
+ const mapped = underscoreKeysUppercaseValues(tree);
84
85
  assert.deepEqual(await Tree.plain(mapped), {
85
86
  _a: "LETTER A",
86
87
  _b: "LETTER B",
@@ -94,12 +95,12 @@ describe("mapFn", () => {
94
95
  b: "letter b",
95
96
  },
96
97
  };
97
- const doubleKeys = mapFn({
98
+ const underscopeKeys = mapFn({
98
99
  key: async (sourceKey, tree) => `_${sourceKey}`,
99
100
  inverseKey: async (resultKey, tree) => resultKey.slice(1),
100
101
  value: async (sourceValue, sourceKey, tree) => sourceKey,
101
102
  });
102
- const mapped = doubleKeys(tree);
103
+ const mapped = underscopeKeys(tree);
103
104
  assert.deepEqual(await Tree.plain(mapped), {
104
105
  _a: "a",
105
106
  _more: "more",
@@ -108,8 +109,8 @@ describe("mapFn", () => {
108
109
 
109
110
  test("value can provide a default key and inverse key functions", async () => {
110
111
  const uppercase = (s) => s.toUpperCase();
111
- uppercase.key = (sourceKey) => `_${sourceKey}`;
112
- uppercase.inverseKey = (resultKey) => resultKey.slice(1);
112
+ uppercase.key = addUnderscore;
113
+ uppercase.inverseKey = removeUnderscore;
113
114
  const tree = {
114
115
  a: "letter a",
115
116
  b: "letter b",
@@ -148,12 +149,12 @@ describe("mapFn", () => {
148
149
  b: "letter b",
149
150
  },
150
151
  });
151
- const doubleKeys = mapFn({
152
+ const underscoreKeys = mapFn({
152
153
  deep: true,
153
- key: async (sourceKey, tree) => `_${sourceKey}`,
154
- inverseKey: async (resultKey, tree) => resultKey.slice(1),
154
+ key: addUnderscore,
155
+ inverseKey: removeUnderscore,
155
156
  });
156
- const mapped = doubleKeys(tree);
157
+ const mapped = underscoreKeys(tree);
157
158
  assert.deepEqual(await Tree.plain(mapped), {
158
159
  _a: "letter a",
159
160
  more: {
@@ -169,13 +170,13 @@ describe("mapFn", () => {
169
170
  b: "letter b",
170
171
  },
171
172
  });
172
- const doubleKeysUppercaseValues = mapFn({
173
+ const underscoreKeysUppercaseValues = mapFn({
173
174
  deep: true,
174
- key: async (sourceKey, tree) => `_${sourceKey}`,
175
- inverseKey: async (resultKey, tree) => resultKey.slice(1),
175
+ key: addUnderscore,
176
+ inverseKey: removeUnderscore,
176
177
  value: async (sourceValue, sourceKey, tree) => sourceValue.toUpperCase(),
177
178
  });
178
- const mapped = doubleKeysUppercaseValues(tree);
179
+ const mapped = underscoreKeysUppercaseValues(tree);
179
180
  assert.deepEqual(await Tree.plain(mapped), {
180
181
  _a: "LETTER A",
181
182
  more: {
@@ -201,3 +202,11 @@ describe("mapFn", () => {
201
202
  assert(!flag);
202
203
  });
203
204
  });
205
+
206
+ function addUnderscore(key) {
207
+ return `_${key}`;
208
+ }
209
+
210
+ function removeUnderscore(key) {
211
+ return trailingSlash.has(key) ? key : key.slice(1);
212
+ }
@@ -41,8 +41,12 @@ describe("utilities", () => {
41
41
  });
42
42
 
43
43
  test("keysFromPath() returns the keys from a slash-separated path", () => {
44
- assert.deepEqual(utilities.keysFromPath("a/b/c"), ["a", "b", "c"]);
45
- assert.deepEqual(utilities.keysFromPath("foo/"), ["foo", ""]);
44
+ assert.deepEqual(utilities.keysFromPath(""), []);
45
+ assert.deepEqual(utilities.keysFromPath("/"), []);
46
+ assert.deepEqual(utilities.keysFromPath("a/b/c"), ["a/", "b/", "c"]);
47
+ assert.deepEqual(utilities.keysFromPath("a/b/c/"), ["a/", "b/", "c/"]);
48
+ assert.deepEqual(utilities.keysFromPath("/foo/"), ["foo/"]);
49
+ assert.deepEqual(utilities.keysFromPath("a///b"), ["a/", "b"]);
46
50
  });
47
51
 
48
52
  test("naturalOrder compares strings in natural order", () => {
@@ -1,41 +0,0 @@
1
- import assert from "node:assert";
2
- import { describe, test } from "node:test";
3
- import { ObjectTree } from "../../src/internal.js";
4
- import cachedKeyFunctions from "../../src/transforms/cachedKeyFunctions.js";
5
-
6
- describe("cachedKeyFunctions", () => {
7
- test("maps keys with caching", async () => {
8
- const tree = new ObjectTree({
9
- a: "letter a",
10
- b: "letter b",
11
- });
12
-
13
- let callCount = 0;
14
- const underscoreKeys = async (sourceKey, tree) => {
15
- callCount++;
16
- return `_${sourceKey}`;
17
- };
18
-
19
- const { inverseKey, key } = cachedKeyFunctions(underscoreKeys);
20
-
21
- assert.equal(await inverseKey("_a", tree), "a"); // Cache miss
22
- assert.equal(callCount, 1);
23
- assert.equal(await inverseKey("_a", tree), "a");
24
- assert.equal(callCount, 1);
25
- assert.equal(await inverseKey("_b", tree), "b"); // Cache miss
26
- assert.equal(callCount, 2);
27
-
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
- assert.equal(callCount, 2);
32
-
33
- // `c` isn't in tree, so we should get undefined.
34
- assert.equal(await inverseKey("_c", tree), undefined);
35
- // But key mapping is still possible.
36
- assert.equal(await key("c", tree), "_c");
37
- // And now we have a cache hit.
38
- assert.equal(await inverseKey("_c", tree), "c");
39
- assert.equal(callCount, 3);
40
- });
41
- });