@weborigami/async-tree 0.2.8 → 0.2.9

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/package.json CHANGED
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "@weborigami/async-tree",
3
- "version": "0.2.8",
3
+ "version": "0.2.9",
4
4
  "description": "Asynchronous tree drivers based on standard JavaScript classes",
5
5
  "type": "module",
6
6
  "main": "./main.js",
7
7
  "browser": "./browser.js",
8
8
  "types": "./index.ts",
9
9
  "devDependencies": {
10
- "@types/node": "22.10.2",
11
- "typescript": "5.7.2"
10
+ "@types/node": "22.13.5",
11
+ "typescript": "5.8.2"
12
12
  },
13
13
  "dependencies": {
14
- "@weborigami/types": "0.2.8"
14
+ "@weborigami/types": "0.2.9"
15
15
  },
16
16
  "scripts": {
17
17
  "test": "node --test --test-reporter=spec",
package/src/extension.js CHANGED
@@ -65,7 +65,7 @@ export function extname(path) {
65
65
  * See if the key ends with the given extension. If it does, return the base
66
66
  * name without the extension; if it doesn't return null.
67
67
  *
68
- * If the extension is empty, the key must not have an extension to match.
68
+ * If the extension is the empty string, this will match any key.
69
69
  *
70
70
  * If the extension is a slash, then the key must end with a slash for the match
71
71
  * to succeed. Otherwise, a trailing slash in the key is ignored for purposes of
@@ -89,10 +89,7 @@ export function match(key, ext) {
89
89
 
90
90
  // Key matches if it ends with the same extension
91
91
  const normalized = trailingSlash.remove(key);
92
-
93
- if (ext === "") {
94
- return normalized.includes(".") ? null : normalized;
95
- } else if (normalized.endsWith(ext)) {
92
+ if (normalized.endsWith(ext)) {
96
93
  const removed =
97
94
  ext.length > 0 ? normalized.slice(0, -ext.length) : normalized;
98
95
  return trailingSlash.toggle(removed, trailingSlash.has(key));
@@ -6,16 +6,16 @@ const treeToCaches = new Map();
6
6
  * Given a key function, return a new key function and inverse key function that
7
7
  * cache the results of the original.
8
8
  *
9
- * If `skipSubtrees` is true, the inverse key function will skip any source keys
10
- * that are keys for subtrees, returning the source key unmodified.
9
+ * If `deep` is true, the inverse key function will skip any source keys that
10
+ * are keys for subtrees, returning the source key unmodified.
11
11
  *
12
12
  * @typedef {import("../../index.ts").KeyFn} KeyFn
13
13
  *
14
14
  * @param {KeyFn} keyFn
15
- * @param {boolean?} skipSubtrees
15
+ * @param {boolean?} deep
16
16
  * @returns {{ key: KeyFn, inverseKey: KeyFn }}
17
17
  */
18
- export default function cachedKeyFunctions(keyFn, skipSubtrees = false) {
18
+ export default function cachedKeyFunctions(keyFn, deep = false) {
19
19
  return {
20
20
  async inverseKey(resultKey, tree) {
21
21
  const { resultKeyToSourceKey, sourceKeyToResultKey } =
@@ -40,7 +40,7 @@ export default function cachedKeyFunctions(keyFn, skipSubtrees = false) {
40
40
  const computedResultKey = await computeAndCacheResultKey(
41
41
  tree,
42
42
  keyFn,
43
- skipSubtrees,
43
+ deep,
44
44
  sourceKey
45
45
  );
46
46
 
@@ -48,8 +48,8 @@ export default function cachedKeyFunctions(keyFn, skipSubtrees = false) {
48
48
  computedResultKey &&
49
49
  trailingSlash.remove(computedResultKey) === resultKeyWithoutSlash
50
50
  ) {
51
- // Match found, match trailing slash and return
52
- return trailingSlash.toggle(sourceKey, trailingSlash.has(resultKey));
51
+ // Match found
52
+ return matchSlashHandling(computedResultKey, sourceKey, resultKey);
53
53
  }
54
54
  }
55
55
 
@@ -67,7 +67,7 @@ export default function cachedKeyFunctions(keyFn, skipSubtrees = false) {
67
67
  const resultKey = await computeAndCacheResultKey(
68
68
  tree,
69
69
  keyFn,
70
- skipSubtrees,
70
+ deep,
71
71
  sourceKey
72
72
  );
73
73
  return resultKey;
@@ -75,12 +75,12 @@ export default function cachedKeyFunctions(keyFn, skipSubtrees = false) {
75
75
  };
76
76
  }
77
77
 
78
- async function computeAndCacheResultKey(tree, keyFn, skipSubtrees, sourceKey) {
78
+ async function computeAndCacheResultKey(tree, keyFn, deep, sourceKey) {
79
79
  const { resultKeyToSourceKey, sourceKeyToResultKey } =
80
80
  getKeyMapsForTree(tree);
81
81
 
82
82
  const resultKey =
83
- skipSubtrees && trailingSlash.has(sourceKey)
83
+ deep && trailingSlash.has(sourceKey)
84
84
  ? sourceKey
85
85
  : await keyFn(sourceKey, tree);
86
86
 
@@ -104,19 +104,33 @@ function getKeyMapsForTree(tree) {
104
104
  return keyMaps;
105
105
  }
106
106
 
107
- // Search the given key map for the key. Ignore trailing slashes in the search,
108
- // but preserve them in the result.
109
- function searchKeyMap(keyMap, key) {
107
+ // Given the input key passed to a function and the result of that function, and
108
+ // a requested key being searched for, determine whether we should add a slash
109
+ // to the output key.
110
+ function matchSlashHandling(inputKey, outputKey, requestedKey) {
111
+ if (trailingSlash.has(inputKey) !== trailingSlash.has(outputKey)) {
112
+ // The key function toggled the slash on the input key; return output as is
113
+ return outputKey;
114
+ } else {
115
+ // Match the slash handling of the requested key
116
+ return trailingSlash.toggle(outputKey, trailingSlash.has(requestedKey));
117
+ }
118
+ }
119
+
120
+ // Search the given key map for the key. Ignore trailing slashes in the search.
121
+ function searchKeyMap(keyMap, requestedKey) {
110
122
  // Check key as is
111
- let match;
112
- if (keyMap.has(key)) {
113
- match = keyMap.get(key);
123
+ let resultKey;
124
+ let sourceKey = requestedKey;
125
+ if (keyMap.has(sourceKey)) {
126
+ resultKey = keyMap.get(requestedKey);
114
127
  } else {
115
128
  // Check alternative with/without slash
116
- const alternativeKey = trailingSlash.toggle(key, !trailingSlash.has(key));
117
- match = keyMap.get(alternativeKey);
129
+ sourceKey = trailingSlash.toggle(requestedKey);
130
+ resultKey = keyMap.get(sourceKey);
131
+ }
132
+ if (resultKey === undefined) {
133
+ return undefined;
118
134
  }
119
- return match
120
- ? trailingSlash.toggle(match, trailingSlash.has(key))
121
- : undefined;
135
+ return matchSlashHandling(sourceKey, resultKey, requestedKey);
122
136
  }
@@ -19,7 +19,7 @@ describe("extension", () => {
19
19
  assert.equal(match("file.md", ".txt"), null);
20
20
  assert.equal(match("file.md/", ".md"), "file/");
21
21
  assert.equal(match("file", ""), "file");
22
- assert.equal(match("file.md", ""), null);
22
+ assert.equal(match("file.md", ""), "file.md");
23
23
  assert.equal(match("file", "/"), null);
24
24
  assert.equal(match("file/", "/"), "file");
25
25
  });
@@ -2,6 +2,7 @@ import assert from "node:assert";
2
2
  import { describe, test } from "node:test";
3
3
  import { DeepObjectTree, ObjectTree } from "../../src/internal.js";
4
4
  import cachedKeyFunctions from "../../src/operations/cachedKeyFunctions.js";
5
+ import * as trailingSlash from "../../src/trailingSlash.js";
5
6
 
6
7
  describe("cachedKeyFunctions", () => {
7
8
  test("maps keys with caching", async () => {
@@ -74,7 +75,7 @@ describe("cachedKeyFunctions", () => {
74
75
  assert.equal(callCount, 1);
75
76
  });
76
77
 
77
- test("preserves trailing slashes", async () => {
78
+ test("preserves trailing slashes if key function does so", async () => {
78
79
  const tree = new ObjectTree({
79
80
  a: "letter a",
80
81
  });
@@ -87,4 +88,19 @@ describe("cachedKeyFunctions", () => {
87
88
  assert.equal(await inverseKey("_a/", tree), "a/");
88
89
  assert.equal(await inverseKey("_a", tree), "a");
89
90
  });
91
+
92
+ test("if key function toggles slash, defers to key function slash handling", async () => {
93
+ const tree = new ObjectTree({
94
+ a: "letter a",
95
+ });
96
+ const addUnderscoreAndSlash = async (sourceKey) =>
97
+ `_${trailingSlash.remove(sourceKey)}/`;
98
+ const { inverseKey, key } = cachedKeyFunctions(addUnderscoreAndSlash);
99
+
100
+ assert.equal(await inverseKey("_a/", tree), "a");
101
+ assert.equal(await inverseKey("_a", tree), "a");
102
+
103
+ assert.equal(await key("a", tree), "_a/");
104
+ assert.equal(await key("a/", tree), "_a/");
105
+ });
90
106
  });