@weborigami/origami 0.6.0 → 0.6.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/origami",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "description": "Web Origami language, CLI, framework, and server",
5
5
  "type": "module",
6
6
  "repository": {
@@ -17,9 +17,9 @@
17
17
  "typescript": "5.9.3"
18
18
  },
19
19
  "dependencies": {
20
- "@weborigami/async-tree": "0.6.0",
20
+ "@weborigami/async-tree": "0.6.1",
21
21
  "@weborigami/json-feed-to-rss": "1.0.0",
22
- "@weborigami/language": "0.6.0",
22
+ "@weborigami/language": "0.6.1",
23
23
  "css-tree": "3.1.0",
24
24
  "graphviz-wasm": "3.0.2",
25
25
  "highlight.js": "11.11.1",
@@ -5,12 +5,7 @@
5
5
  * @typedef {import("@weborigami/async-tree").SyncOrAsyncMap} SyncOrAsyncMap
6
6
  */
7
7
 
8
- import {
9
- castArraylike,
10
- SyncMap,
11
- toPlainValue,
12
- trailingSlash,
13
- } from "@weborigami/async-tree";
8
+ import { castArraylike, toPlainValue } from "@weborigami/async-tree";
14
9
  import * as YAMLModule from "yaml";
15
10
 
16
11
  // The "yaml" package doesn't seem to provide a default export that the browser can
@@ -26,10 +21,9 @@ export function parseYaml(text) {
26
21
  return YAML.parse(text);
27
22
  }
28
23
 
29
- function reduceToMap(values, keys, map) {
30
- // Normalize slashes in keys.
31
- keys = keys.map(trailingSlash.remove);
32
- return castArraylike(keys, values, (entries) => new SyncMap(entries));
24
+ function reduceToMap(map) {
25
+ // createFn parameter returns as map as is
26
+ return castArraylike(map, (result) => result);
33
27
  }
34
28
 
35
29
  /**
package/src/dev/copy.js CHANGED
@@ -1,7 +1,11 @@
1
- import { getTreeArgument, Tree } from "@weborigami/async-tree";
1
+ import {
2
+ AsyncMap,
3
+ getTreeArgument,
4
+ SyncMap,
5
+ Tree,
6
+ } from "@weborigami/async-tree";
2
7
  import { formatError } from "@weborigami/language";
3
8
  import process, { stdout } from "node:process";
4
- import { transformObject } from "../common/utilities.js";
5
9
 
6
10
  /**
7
11
  * @typedef {import("@weborigami/async-tree").Maplike} Maplike
@@ -13,75 +17,87 @@ export default async function copy(source, target) {
13
17
  const sourceTree = await getTreeArgument(source, "copy", { position: 0 });
14
18
  let targetTree = await getTreeArgument(target, "copy", { position: 1 });
15
19
 
20
+ let progressTree;
16
21
  if (stdout.isTTY) {
17
- targetTree =
18
- targetTree instanceof Map
19
- ? transformObject(SyncProgressTransform, targetTree)
20
- : transformObject(AsyncProgressTransform, targetTree);
21
- copyRoot = targetTree;
22
- countFiles = 0;
23
- countCopied = 0;
22
+ progressTree = showSetProgress(targetTree, {
23
+ copied: 0,
24
+ total: 0,
25
+ });
26
+ } else {
27
+ progressTree = targetTree;
24
28
  }
25
29
 
26
- await Tree.assign(targetTree, sourceTree);
30
+ await Tree.assign(progressTree, sourceTree);
27
31
 
28
32
  if (stdout.isTTY) {
29
33
  process.stdout.clearLine(0);
30
34
  process.stdout.cursorTo(0);
31
- copyRoot = null;
32
- countFiles = null;
33
- countCopied = null;
34
35
  }
35
36
  }
36
37
 
37
- let countFiles;
38
- let countCopied;
39
- let copyRoot;
38
+ // Wrap the source tree to show progress on set() operations. Handle both sync
39
+ // and async trees. All child trees will share the same counts object.
40
+ function showSetProgress(source, counts) {
41
+ function showProgress() {
42
+ process.stdout.clearLine(0);
43
+ process.stdout.cursorTo(0);
44
+ process.stdout.write(`Copied ${counts.copied} of ${counts.total}`);
45
+ }
40
46
 
41
- function AsyncProgressTransform(Base) {
42
- return class Progress extends Base {
43
- async set(...args) {
44
- countFiles++;
45
- copyRoot.showProgress();
46
- let result;
47
- try {
48
- result = await super.set(...args);
49
- countCopied++;
50
- } catch (/** @type {any} */ error) {
51
- console.error(formatError(error));
52
- }
53
- copyRoot.showProgress();
54
- return result;
55
- }
47
+ const isSync = source instanceof Map;
48
+ const MapClass = isSync ? SyncMap : AsyncMap;
49
+ const iteratorKey = isSync ? Symbol.iterator : Symbol.asyncIterator;
56
50
 
57
- showProgress() {
58
- process.stdout.clearLine(0);
59
- process.stdout.cursorTo(0);
60
- process.stdout.write(`Copied ${countCopied} of ${countFiles}`);
61
- }
62
- };
63
- }
51
+ const progressTree = Object.assign(new MapClass(), {
52
+ delete: source.delete.bind(source),
53
+ keys: source.keys.bind(source),
54
+ [iteratorKey]: source[iteratorKey].bind(source),
55
+
56
+ // Wrap get() to apply progress tracking
57
+ get(key) {
58
+ return awaitIfPromise(source.get(key), (value) => {
59
+ return Tree.isMap(value) ? showSetProgress(value, counts) : value;
60
+ });
61
+ },
64
62
 
65
- function SyncProgressTransform(Base) {
66
- return class Progress extends Base {
67
- set(...args) {
68
- countFiles++;
69
- copyRoot.showProgress();
70
- let result;
63
+ // Wrap set() to show progress
64
+ set(key, value) {
65
+ counts.total++;
66
+ showProgress();
71
67
  try {
72
- result = super.set(...args);
73
- countCopied++;
68
+ const setResult = source.set(key, value);
69
+ return awaitIfPromise(setResult, () => {
70
+ counts.copied++;
71
+ showProgress();
72
+ return progressTree;
73
+ });
74
74
  } catch (/** @type {any} */ error) {
75
75
  console.error(formatError(error));
76
+ return progressTree;
76
77
  }
77
- copyRoot.showProgress();
78
- return result;
79
- }
78
+ },
79
+ });
80
+
81
+ if (typeof source.child === "function") {
82
+ // @ts-ignore
83
+ progressTree.child = async function (key) {
84
+ counts.total++;
85
+ showProgress();
86
+ const childResult = source.child(key);
87
+ return awaitIfPromise(childResult, (child) => {
88
+ counts.copied++;
89
+ showProgress();
90
+ return showSetProgress(child, counts);
91
+ });
92
+ };
93
+ }
94
+
95
+ return progressTree;
96
+ }
80
97
 
81
- showProgress() {
82
- process.stdout.clearLine(0);
83
- process.stdout.cursorTo(0);
84
- process.stdout.write(`Copied ${countCopied} of ${countFiles}`);
85
- }
86
- };
98
+ // Helper function that awaits a value if it's a Promise, then gives it to the
99
+ // function; otherwise calls the function directly. This helps us write code
100
+ // that can handle both sync and async values.
101
+ function awaitIfPromise(value, fn) {
102
+ return value instanceof Promise ? value.then(fn) : fn(value);
87
103
  }
@@ -1,5 +1,5 @@
1
1
  import {
2
- DeepObjectMap,
2
+ ObjectMap,
3
3
  Tree,
4
4
  getTreeArgument,
5
5
  keysFromPath,
@@ -53,7 +53,7 @@ export default async function crawlBuiltin(maplike, baseHref) {
53
53
  // for something already, that's better than a function that will get that
54
54
  // value.
55
55
  const result = Tree.deepMerge(
56
- new DeepObjectMap(cache),
56
+ new ObjectMap(cache, { deep: true }),
57
57
  await Tree.invokeFunctions(resources)
58
58
  );
59
59
  return result;
package/src/dev/help.yaml CHANGED
@@ -178,6 +178,9 @@ Tree:
178
178
  calendar:
179
179
  args: (options)
180
180
  description: Return a tree structure for years/months/days
181
+ child:
182
+ args: (tree, key)
183
+ description: Returns the indicated child node, creating it if necessary
181
184
  clear:
182
185
  args: (map)
183
186
  description: Remove all values from the map
@@ -235,6 +238,9 @@ Tree:
235
238
  inners:
236
239
  args: (tree)
237
240
  description: The tree's interior nodes
241
+ invokeFunctions:
242
+ args: (map)
243
+ description: Getting a map value invokes it if it's a function
238
244
  isMap:
239
245
  args: (object)
240
246
  description: True if object is a map
@@ -283,6 +289,9 @@ Tree:
283
289
  plain:
284
290
  args: (tree)
285
291
  description: Render the tree as a plain JavaScript object
292
+ reduce:
293
+ args: (tree, reduceFn)
294
+ description: Reduce the tree to a single value
286
295
  regExpKeys:
287
296
  args: (tree)
288
297
  description: A tree whose keys are regular expression strings
@@ -295,6 +304,9 @@ Tree:
295
304
  scope:
296
305
  args: (tree)
297
306
  description: A merged view of the tree and its ancestors
307
+ set:
308
+ args: (map, key, value)
309
+ description: Set the value for the key in the map
298
310
  shuffle:
299
311
  args: (map)
300
312
  description: Shuffle the keys of the map
@@ -41,7 +41,9 @@ function jsonKeysMap(source) {
41
41
 
42
42
  source: source,
43
43
 
44
- trailingSlashKeys: source.trailingSlashKeys,
44
+ get trailingSlashKeys() {
45
+ return source.trailingSlashKeys;
46
+ },
45
47
  });
46
48
  return result;
47
49
  }
@@ -48,6 +48,17 @@ export default async function ori(expression, options = {}) {
48
48
  // Execute
49
49
  let result = await fn();
50
50
 
51
+ // if (result === undefined) {
52
+ // // Was the code a path traversal?
53
+ // const wasTraversal =
54
+ // fn.code[0] === ops.unpack ||
55
+ // (fn.code[0] instanceof Array && fn.code[0][0] === ops.scope);
56
+ // if (wasTraversal) {
57
+ // // Yes, probably an error
58
+ // console.warn(`ori: warning: undefined ${highlightError(expression)}`);
59
+ // }
60
+ // }
61
+
51
62
  // If result was a function, execute it.
52
63
  if (typeof result === "function") {
53
64
  result = await result();
@@ -14,7 +14,7 @@ const templateText = `(urls) => \`<?xml version="1.0" encoding="UTF-8"?>
14
14
  * @typedef {import("@weborigami/async-tree").Maplike} Maplike
15
15
  *
16
16
  * @param {Maplike} maplike
17
- * @param {{ assumeSlashes?: boolean, base?: string }} options
17
+ * @param {{ base?: string }} options
18
18
  * @returns {Promise<string>}
19
19
  */
20
20
  export default async function sitemap(maplike, options = {}) {
@@ -5,5 +5,8 @@
5
5
  * @param {any} obj
6
6
  */
7
7
  export default function unpack(obj) {
8
- return obj?.unpack?.() ?? obj;
8
+ if (obj == null) {
9
+ throw new ReferenceError("Cannot unpack null or undefined value");
10
+ }
11
+ return obj.unpack?.() ?? obj;
9
12
  }