@weborigami/async-tree 0.0.66 → 0.0.67-beta.1

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,6 +1,6 @@
1
1
  {
2
2
  "name": "@weborigami/async-tree",
3
- "version": "0.0.66",
3
+ "version": "0.0.67-beta.1",
4
4
  "description": "Asynchronous tree drivers based on standard JavaScript classes",
5
5
  "type": "module",
6
6
  "main": "./main.js",
@@ -11,7 +11,7 @@
11
11
  "typescript": "5.6.2"
12
12
  },
13
13
  "dependencies": {
14
- "@weborigami/types": "0.0.66"
14
+ "@weborigami/types": "0.0.67-beta.1"
15
15
  },
16
16
  "scripts": {
17
17
  "test": "node --test --test-reporter=spec",
package/src/FileTree.js CHANGED
@@ -190,8 +190,11 @@ export default class FileTree {
190
190
  // Special case: empty object means create an empty directory.
191
191
  await fs.mkdir(destPath, { recursive: true });
192
192
  } else if (Tree.isTreelike(value)) {
193
- // Treat value as a tree and write it out as a subdirectory.
193
+ // Treat value as a subtree and write it out as a subdirectory.
194
194
  const destTree = Reflect.construct(this.constructor, [destPath]);
195
+ // Create the directory here, even if the subtree is empty.
196
+ await fs.mkdir(destPath, { recursive: true });
197
+ // Write out the subtree.
195
198
  await Tree.assign(destTree, value);
196
199
  } else {
197
200
  const typeName = value?.constructor?.name ?? "unknown";
package/src/SiteTree.js CHANGED
@@ -35,14 +35,20 @@ export default class SiteTree {
35
35
  );
36
36
  }
37
37
 
38
- // If the keys ends with a slash, return a tree for the indicated route.
39
- if (trailingSlash.has(key)) {
38
+ // A key with a trailing slash and no extension is for a folder; return a
39
+ // subtree without making a network request.
40
+ if (trailingSlash.has(key) && !key.includes(".")) {
40
41
  const href = new URL(key, this.href).href;
41
42
  const value = Reflect.construct(this.constructor, [href]);
42
43
  setParent(value, this);
43
44
  return value;
44
45
  }
45
46
 
47
+ // HACK: For now we don't allow lookup of Origami extension handlers.
48
+ if (key.endsWith("_handler")) {
49
+ return undefined;
50
+ }
51
+
46
52
  const href = new URL(key, this.href).href;
47
53
 
48
54
  // Fetch the data at the given route.
@@ -60,16 +60,22 @@ export default function treeCache(
60
60
  let value = await source.get(key);
61
61
  if (value !== undefined) {
62
62
  // If a filter is defined, does the key match the filter?
63
- const filterValue = await filter?.get(key);
63
+ const filterValue = filter ? await filter.get(key) : undefined;
64
64
  const filterMatch = !filter || filterValue !== undefined;
65
65
  if (filterMatch) {
66
66
  if (Tree.isAsyncTree(value)) {
67
67
  // Construct merged tree for a tree result.
68
68
  if (cacheValue === undefined) {
69
- // Construct new container in cache
70
- cacheValue = new ObjectTree({});
71
- cacheValue.parent = this;
72
- await cache.set(key, cacheValue);
69
+ // Construct new empty container in cache
70
+ await cache.set(key, {});
71
+ cacheValue = await cache.get(key);
72
+ if (!Tree.isAsyncTree(cacheValue)) {
73
+ // Coerce to tree and then save it back to the cache. This is
74
+ // necessary, e.g., if cache is an ObjectTree; we want the
75
+ // subtree to also be an ObjectTree, not a plain object.
76
+ cacheValue = Tree.from(cacheValue);
77
+ await cache.set(key, cacheValue);
78
+ }
73
79
  }
74
80
  value = treeCache(value, cacheValue, filterValue);
75
81
  } else {
@@ -4,7 +4,7 @@ import path from "node:path";
4
4
  import { describe, test } from "node:test";
5
5
  import { fileURLToPath } from "node:url";
6
6
  import FileTree from "../src/FileTree.js";
7
- import { Tree } from "../src/internal.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");
@@ -81,7 +81,7 @@ describe("FileTree", async () => {
81
81
  await removeTempDirectory();
82
82
  });
83
83
 
84
- test("can create empty subfolder via set()", async () => {
84
+ test("create subfolder via set() with empty object value", async () => {
85
85
  await createTempDirectory();
86
86
 
87
87
  // Write out new, empty folder called "empty".
@@ -98,6 +98,23 @@ describe("FileTree", async () => {
98
98
  await removeTempDirectory();
99
99
  });
100
100
 
101
+ test("create subfolder via set() with empty tree value", async () => {
102
+ await createTempDirectory();
103
+
104
+ // Write out new, empty folder called "empty".
105
+ const tempFiles = new FileTree(tempDirectory);
106
+ await tempFiles.set("empty", new ObjectTree({}));
107
+
108
+ // Verify folder exists and has no contents.
109
+ const folderPath = path.join(tempDirectory, "empty");
110
+ const stats = await fs.stat(folderPath);
111
+ assert(stats.isDirectory());
112
+ const files = await fs.readdir(folderPath);
113
+ assert.deepEqual(files, []);
114
+
115
+ await removeTempDirectory();
116
+ });
117
+
101
118
  test("can write out subfolder via set()", async () => {
102
119
  await createTempDirectory();
103
120