@weborigami/async-tree 0.3.3 → 0.3.4-jse.5

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,13 +1,13 @@
1
1
  {
2
2
  "name": "@weborigami/async-tree",
3
- "version": "0.3.3",
3
+ "version": "0.3.4-jse.5",
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
  "dependencies": {
10
- "@weborigami/types": "0.3.3"
10
+ "@weborigami/types": "0.3.4-jse.5"
11
11
  },
12
12
  "devDependencies": {
13
13
  "@types/node": "22.13.13",
package/shared.js CHANGED
@@ -14,7 +14,6 @@ export * as jsonKeys from "./src/jsonKeys.js";
14
14
  export { default as addNextPrevious } from "./src/operations/addNextPrevious.js";
15
15
  export { default as cache } from "./src/operations/cache.js";
16
16
  export { default as cachedKeyFunctions } from "./src/operations/cachedKeyFunctions.js";
17
- export { default as concat } from "./src/operations/concat.js";
18
17
  export { default as deepMerge } from "./src/operations/deepMerge.js";
19
18
  export { default as deepReverse } from "./src/operations/deepReverse.js";
20
19
  export { default as deepTake } from "./src/operations/deepTake.js";
@@ -34,6 +33,7 @@ export { default as reverse } from "./src/operations/reverse.js";
34
33
  export { default as scope } from "./src/operations/scope.js";
35
34
  export { default as sort } from "./src/operations/sort.js";
36
35
  export { default as take } from "./src/operations/take.js";
36
+ export { default as text } from "./src/operations/text.js";
37
37
  export * as symbols from "./src/symbols.js";
38
38
  export * as trailingSlash from "./src/trailingSlash.js";
39
39
  export { default as TraverseError } from "./src/TraverseError.js";
package/src/Tree.d.ts CHANGED
@@ -21,4 +21,4 @@ export function toFunction(tree: Treelike): Function;
21
21
  export function traverse(tree: Treelike, ...keys: any[]): Promise<any>;
22
22
  export function traverseOrThrow(tree: Treelike, ...keys: any[]): Promise<any>;
23
23
  export function traversePath(tree: Treelike, path: string): Promise<any>;
24
- export function values(AsyncTree: AsyncTree): Promise<IterableIterator<any>>;
24
+ export function values(tree: Treelike): Promise<IterableIterator<any>>;
package/src/Tree.js CHANGED
@@ -26,8 +26,6 @@ import {
26
26
  * @typedef {import("@weborigami/types").AsyncTree} AsyncTree
27
27
  */
28
28
 
29
- const treeModule = this;
30
-
31
29
  /**
32
30
  * Apply the key/values pairs from the source tree to the target tree.
33
31
  *
@@ -362,8 +360,8 @@ export async function remove(tree, key) {
362
360
  */
363
361
  export function root(tree) {
364
362
  let current = from(tree);
365
- while (current.parent) {
366
- current = current.parent;
363
+ while (current.parent || current[symbols.parent]) {
364
+ current = current.parent || current[symbols.parent];
367
365
  }
368
366
  return current;
369
367
  }
@@ -416,7 +414,7 @@ export async function traverseOrThrow(treelike, ...keys) {
416
414
 
417
415
  // If traversal operation was called with a `this` context, use that as the
418
416
  // target for function calls.
419
- const target = this === treeModule ? undefined : this;
417
+ const target = this;
420
418
 
421
419
  // Process all the keys.
422
420
  const remainingKeys = keys.slice();
@@ -473,9 +471,10 @@ export async function traversePath(tree, path) {
473
471
  /**
474
472
  * Return the values in the specific node of the tree.
475
473
  *
476
- * @param {AsyncTree} tree
474
+ * @param {Treelike} treelike
477
475
  */
478
- export async function values(tree) {
476
+ export async function values(treelike) {
477
+ const tree = from(treelike);
479
478
  const keys = Array.from(await tree.keys());
480
479
  const promises = keys.map(async (key) => tree.get(key));
481
480
  return Promise.all(promises);
@@ -92,7 +92,12 @@ export default class FileTree {
92
92
  value = Uint8Array.from(buffer);
93
93
  }
94
94
 
95
- setParent(value, this);
95
+ const parent =
96
+ key === ".."
97
+ ? // Special case: ".." parent is the grandparent (if it exists)
98
+ this.parent?.parent
99
+ : this;
100
+ setParent(value, parent);
96
101
  return value;
97
102
  }
98
103
 
@@ -25,7 +25,7 @@ export default function limitConcurrency(fn, maxConcurrency) {
25
25
  // Join the queue
26
26
  queue.push({
27
27
  promise: resultPromise,
28
- resolve: turnWithResolvers.resolve,
28
+ unblock: turnWithResolvers.resolve,
29
29
  });
30
30
 
31
31
  if (activeCallPool.size >= maxConcurrency) {
@@ -39,13 +39,14 @@ export default function limitConcurrency(fn, maxConcurrency) {
39
39
  return resultPromise;
40
40
  };
41
41
 
42
- // If there are calls in the queue and the active pool is not full, resolve
43
- // the next call in the queue and add it to the active pool.
42
+ // If there are calls in the queue and the active pool is not full, start
43
+ // the next call in the queue.
44
44
  function next() {
45
45
  if (queue.length > 0 && activeCallPool.size < maxConcurrency) {
46
- const { promise, resolve } = queue.shift();
46
+ const { promise, unblock } = queue.shift();
47
47
  activeCallPool.add(promise);
48
- resolve();
48
+ // Resolve the turn promise to allow the call to proceed
49
+ unblock();
49
50
  }
50
51
  }
51
52
  }
@@ -1,25 +1,29 @@
1
- import { Tree } from "../internal.js";
2
- import { toString } from "../utilities.js";
3
- import concat from "./concat.js";
1
+ import { assertIsTreelike, toString } from "../utilities.js";
2
+ import deepValuesIterator from "./deepValuesIterator.js";
4
3
 
5
4
  /**
6
- * A tagged template literal function that concatenate the deep text values in a
7
- * tree. Any treelike values will be concatenated using `concat`.
5
+ * Concatenate the deep text values in a tree.
8
6
  *
9
- * @param {TemplateStringsArray} strings
10
- * @param {...any} values
7
+ * @param {import("../../index.ts").Treelike} treelike
11
8
  */
12
- export default async function deepText(strings, ...values) {
13
- // Convert all the values to strings
14
- const valueTexts = await Promise.all(
15
- values.map((value) =>
16
- Tree.isTreelike(value) ? concat(value) : toString(value)
17
- )
18
- );
19
- // Splice all the strings together
20
- let result = strings[0];
21
- for (let i = 0; i < valueTexts.length; i++) {
22
- result += valueTexts[i] + strings[i + 1];
9
+ export default async function deepText(treelike) {
10
+ assertIsTreelike(treelike, "text");
11
+
12
+ const strings = [];
13
+ for await (const value of deepValuesIterator(treelike, { expand: true })) {
14
+ let string;
15
+ if (value === null) {
16
+ string = "null";
17
+ } else if (value === undefined) {
18
+ string = "undefined";
19
+ } else {
20
+ string = toString(value);
21
+ }
22
+ if (value === null || value === undefined) {
23
+ const message = `Warning: a template encountered a ${string} value. To locate where this happened, build your project and search your build output for the text "${string}".`;
24
+ console.warn(message);
25
+ }
26
+ strings.push(string);
23
27
  }
24
- return result;
28
+ return strings.join("");
25
29
  }
@@ -16,7 +16,17 @@ import * as trailingSlash from "../trailingSlash.js";
16
16
  * @returns {AsyncTree & { description: string, trees: AsyncTree[]}}
17
17
  */
18
18
  export default function merge(...sources) {
19
- const trees = sources.map((treelike) => Tree.from(treelike));
19
+ const trees = sources
20
+ .filter((source) => source)
21
+ .map((treelike) => Tree.from(treelike));
22
+
23
+ if (trees.length === 0) {
24
+ throw new TypeError("merge: all trees are null or undefined");
25
+ } else if (trees.length === 1) {
26
+ // Only one tree, no need to merge
27
+ return trees[0];
28
+ }
29
+
20
30
  return {
21
31
  description: "merge",
22
32
 
@@ -0,0 +1,25 @@
1
+ import { Tree } from "../internal.js";
2
+ import { toString } from "../utilities.js";
3
+ import deepText from "./deepText.js";
4
+
5
+ /**
6
+ * A tagged template literal function that concatenate the deep text values in a
7
+ * tree. Any treelike values will be concatenated using `deepText`.
8
+ *
9
+ * @param {TemplateStringsArray} strings
10
+ * @param {...any} values
11
+ */
12
+ export default async function text(strings, ...values) {
13
+ // Convert all the values to strings
14
+ const valueTexts = await Promise.all(
15
+ values.map((value) =>
16
+ Tree.isTreelike(value) ? deepText(value) : toString(value)
17
+ )
18
+ );
19
+ // Splice all the strings together
20
+ let result = strings[0];
21
+ for (let i = 0; i < valueTexts.length; i++) {
22
+ result += valueTexts[i] + strings[i + 1];
23
+ }
24
+ return result;
25
+ }
package/src/utilities.js CHANGED
@@ -279,17 +279,22 @@ export function setParent(child, parent) {
279
279
  child.parent = parent;
280
280
  }
281
281
  } else if (Object.isExtensible(child) && !child[symbols.parent]) {
282
- // Add parent reference as a symbol to avoid polluting the object. This
283
- // reference will be used if the object is later used as a tree. We set
284
- // `enumerable` to false even thought this makes no practical difference
285
- // (symbols are never enumerated) because it can provide a hint in the
286
- // debugger that the property is for internal use.
287
- Object.defineProperty(child, symbols.parent, {
288
- configurable: true,
289
- enumerable: false,
290
- value: parent,
291
- writable: true,
292
- });
282
+ try {
283
+ // Add parent reference as a symbol to avoid polluting the object. This
284
+ // reference will be used if the object is later used as a tree. We set
285
+ // `enumerable` to false even thought this makes no practical difference
286
+ // (symbols are never enumerated) because it can provide a hint in the
287
+ // debugger that the property is for internal use.
288
+ Object.defineProperty(child, symbols.parent, {
289
+ configurable: true,
290
+ enumerable: false,
291
+ value: parent,
292
+ writable: true,
293
+ });
294
+ } catch (error) {
295
+ // Ignore exceptions. Some esoteric objects don't allow adding properties.
296
+ // We can still treat them as trees, but they won't have a parent.
297
+ }
293
298
  }
294
299
  }
295
300
 
@@ -1,12 +1,34 @@
1
1
  import assert from "node:assert";
2
2
  import { describe, test } from "node:test";
3
+ import FunctionTree from "../../src/drivers/FunctionTree.js";
4
+ import { Tree } from "../../src/internal.js";
3
5
  import deepText from "../../src/operations/deepText.js";
4
6
 
5
7
  describe("deepText", () => {
6
- test("joins strings and values together", async () => {
7
- const array = [1, 2, 3];
8
- const object = { person1: "Alice", person2: "Bob" };
9
- const result = await deepText`a ${array} b ${object} c`;
10
- assert.equal(result, "a 123 b AliceBob c");
8
+ test("concatenates deep tree values", async () => {
9
+ const tree = Tree.from({
10
+ a: "A",
11
+ b: "B",
12
+ c: "C",
13
+ more: {
14
+ d: "D",
15
+ e: "E",
16
+ },
17
+ });
18
+ const result = await deepText(tree);
19
+ assert.equal(result, "ABCDE");
20
+ });
21
+
22
+ test("concatenates deep tree-like values", async () => {
23
+ const letters = ["a", "b", "c"];
24
+ const specimens = new FunctionTree(
25
+ (letter) => ({
26
+ lowercase: letter,
27
+ uppercase: letter.toUpperCase(),
28
+ }),
29
+ letters
30
+ );
31
+ const result = await deepText(specimens);
32
+ assert.equal(result, "aAbBcC");
11
33
  });
12
34
  });
@@ -0,0 +1,12 @@
1
+ import assert from "node:assert";
2
+ import { describe, test } from "node:test";
3
+ import text from "../../src/operations/text.js";
4
+
5
+ describe("text template literal function", () => {
6
+ test("joins strings and values together", async () => {
7
+ const array = [1, 2, 3];
8
+ const object = { person1: "Alice", person2: "Bob" };
9
+ const result = await text`a ${array} b ${object} c`;
10
+ assert.equal(result, "a 123 b AliceBob c");
11
+ });
12
+ });
@@ -1,29 +0,0 @@
1
- import { assertIsTreelike, toString } from "../utilities.js";
2
- import deepValuesIterator from "./deepValuesIterator.js";
3
-
4
- /**
5
- * Concatenate the deep text values in a tree.
6
- *
7
- * @param {import("../../index.ts").Treelike} treelike
8
- */
9
- export default async function concat(treelike) {
10
- assertIsTreelike(treelike, "concat");
11
-
12
- const strings = [];
13
- for await (const value of deepValuesIterator(treelike, { expand: true })) {
14
- let string;
15
- if (value === null) {
16
- string = "null";
17
- } else if (value === undefined) {
18
- string = "undefined";
19
- } else {
20
- string = toString(value);
21
- }
22
- if (value === null || value === undefined) {
23
- const message = `Warning: Origami template encountered a ${string} value. To locate where this happened, build your project and search your build output for the text "${string}".`;
24
- console.warn(message);
25
- }
26
- strings.push(string);
27
- }
28
- return strings.join("");
29
- }
@@ -1,34 +0,0 @@
1
- import assert from "node:assert";
2
- import { describe, test } from "node:test";
3
- import FunctionTree from "../../src/drivers/FunctionTree.js";
4
- import { Tree } from "../../src/internal.js";
5
- import concat from "../../src/operations/concat.js";
6
-
7
- describe("concat", () => {
8
- test("concatenates deep tree values", async () => {
9
- const tree = Tree.from({
10
- a: "A",
11
- b: "B",
12
- c: "C",
13
- more: {
14
- d: "D",
15
- e: "E",
16
- },
17
- });
18
- const result = await concat.call(null, tree);
19
- assert.equal(result, "ABCDE");
20
- });
21
-
22
- test("concatenates deep tree-like values", async () => {
23
- const letters = ["a", "b", "c"];
24
- const specimens = new FunctionTree(
25
- (letter) => ({
26
- lowercase: letter,
27
- uppercase: letter.toUpperCase(),
28
- }),
29
- letters
30
- );
31
- const result = await concat.call(null, specimens);
32
- assert.equal(result, "aAbBcC");
33
- });
34
- });