@weborigami/origami 0.0.40 → 0.0.42

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 (90) hide show
  1. package/exports/exports.js +33 -34
  2. package/package.json +4 -4
  3. package/src/builtins/@arrows.js +2 -11
  4. package/src/builtins/@builtins.js +2 -5
  5. package/src/builtins/@cache.js +1 -1
  6. package/src/builtins/{@tree/concat.js → @concat.js} +3 -3
  7. package/src/builtins/@copy.js +2 -2
  8. package/src/builtins/{@tree/count.js → @count.js} +3 -9
  9. package/src/builtins/@crawl.js +1 -1
  10. package/src/builtins/@debug.js +2 -8
  11. package/src/builtins/{@tree/defineds.js → @defineds.js} +3 -7
  12. package/src/builtins/@document.js +1 -1
  13. package/src/builtins/{@tree/exceptions.js → @exceptions.js} +7 -8
  14. package/src/builtins/@explore.js +2 -5
  15. package/src/builtins/@files.js +1 -1
  16. package/src/builtins/@filter.js +1 -1
  17. package/src/builtins/{@tree/first.js → @first.js} +3 -9
  18. package/src/builtins/{@tree/fn.js → @fnTree.js} +13 -7
  19. package/src/builtins/@globs.js +1 -1
  20. package/src/builtins/{@tree/groupBy.js → @groupBy.js} +7 -9
  21. package/src/builtins/@help.js +1 -1
  22. package/src/builtins/@http.js +1 -1
  23. package/src/builtins/@https.js +1 -1
  24. package/src/builtins/@if.js +1 -1
  25. package/src/builtins/@image/format.js +36 -2
  26. package/src/builtins/@image/resize.js +30 -2
  27. package/src/builtins/@index.js +2 -8
  28. package/src/builtins/@inherited.js +1 -1
  29. package/src/builtins/@inline.js +1 -1
  30. package/src/builtins/{@tree/inners.js → @inners.js} +3 -8
  31. package/src/builtins/@invoke.js +8 -0
  32. package/src/builtins/{@tree/isAsyncTree.js → @isAsyncTree.js} +1 -1
  33. package/src/builtins/@json.js +7 -1
  34. package/src/builtins/{@tree/keys.js → @keys.js} +3 -9
  35. package/src/builtins/{@tree/keysJson.js → @keysJson.js} +3 -8
  36. package/src/builtins/@loaders/ori.js +18 -18
  37. package/src/builtins/@map.js +17 -19
  38. package/src/builtins/@match.js +1 -1
  39. package/src/builtins/{@tree/merge.js → @merge.js} +2 -2
  40. package/src/builtins/{@tree/mergeDeep.js → @mergeDeep.js} +3 -3
  41. package/src/builtins/@once.js +1 -1
  42. package/src/builtins/@ori.js +1 -1
  43. package/src/builtins/@pack.js +1 -1
  44. package/src/builtins/{@tree/parent.js → @parent.js} +3 -9
  45. package/src/builtins/{@tree/paths.js → @paths.js} +3 -8
  46. package/src/builtins/@perf.js +18 -0
  47. package/src/builtins/{@tree/plain.js → @plain.js} +4 -8
  48. package/src/builtins/@project.js +1 -1
  49. package/src/builtins/@redirect.js +8 -0
  50. package/src/builtins/{@tree/reverse.js → @reverse.js} +3 -8
  51. package/src/builtins/@rss.js +1 -1
  52. package/src/builtins/@scope/extend.js +6 -6
  53. package/src/builtins/@scope/get.js +1 -1
  54. package/src/builtins/@scope/set.js +4 -4
  55. package/src/builtins/@serve.js +1 -1
  56. package/src/builtins/{@tree/setDeep.js → @setDeep.js} +1 -1
  57. package/src/builtins/{@tree/shuffle.js → @shuffle.js} +5 -11
  58. package/src/builtins/{@tree/sitemap.js → @sitemap.js} +5 -8
  59. package/src/builtins/{@tree/sort.js → @sort.js} +5 -7
  60. package/src/builtins/{@tree/sortBy.js → @sortBy.js} +7 -9
  61. package/src/builtins/{@tree/static.js → @static.js} +5 -10
  62. package/src/builtins/@svg.js +3 -9
  63. package/src/builtins/{@tree/table.js → @table.js} +3 -8
  64. package/src/builtins/{@tree/take.js → @take.js} +3 -9
  65. package/src/builtins/@tree.js +19 -0
  66. package/src/builtins/@treeHttp.js +1 -1
  67. package/src/builtins/@treeHttps.js +1 -1
  68. package/src/builtins/@unpack.js +1 -1
  69. package/src/builtins/{@tree/values.js → @values.js} +3 -8
  70. package/src/builtins/{@tree/valuesDeep.js → @valuesDeep.js} +4 -8
  71. package/src/builtins/@watch.js +3 -8
  72. package/src/builtins/@with.js +1 -1
  73. package/src/builtins/@yaml.js +7 -1
  74. package/src/builtins/{@tree/map.d.ts → map.d.ts} +1 -3
  75. package/src/cli/cli.js +2 -15
  76. package/src/common/ExplorableSiteTransform.js +6 -4
  77. package/src/common/addValueKeyToScope.js +3 -11
  78. package/src/common/processUnpackedContent.js +2 -7
  79. package/src/misc/assertScopeIsDefined.js +2 -2
  80. package/src/misc/getTreeArgument.js +51 -0
  81. package/src/{builtins/@tree/dot.js → misc/treeDot.js} +2 -11
  82. package/src/server/constructResponse.js +126 -0
  83. package/src/server/mediaTypes.js +0 -16
  84. package/src/server/server.js +62 -131
  85. package/src/builtins/@tree/flowSvg.js +0 -55
  86. package/src/builtins/@tree/from.js +0 -27
  87. package/src/builtins/@tree/fromJson.js +0 -6
  88. package/src/builtins/@tree/fromYaml.js +0 -24
  89. package/src/builtins/@tree/nextKey.js +0 -29
  90. package/src/builtins/@tree/previousKey.js +0 -29
@@ -1,8 +1,7 @@
1
- import { Tree } from "@weborigami/async-tree";
2
1
  import { Scope } from "@weborigami/language";
3
- import ShuffleTransform from "../../common/ShuffleTransform.js";
4
- import { transformObject } from "../../common/utilities.js";
5
- import assertScopeIsDefined from "../../misc/assertScopeIsDefined.js";
2
+ import ShuffleTransform from "../common/ShuffleTransform.js";
3
+ import { transformObject } from "../common/utilities.js";
4
+ import getTreeArgument from "../misc/getTreeArgument.js";
6
5
 
7
6
  /**
8
7
  * Return a new tree with the original's keys shuffled
@@ -14,12 +13,7 @@ import assertScopeIsDefined from "../../misc/assertScopeIsDefined.js";
14
13
  * @param {Treelike} [treelike]
15
14
  */
16
15
  export default async function shuffle(treelike) {
17
- assertScopeIsDefined(this);
18
- treelike = treelike ?? (await this?.get("@current"));
19
- if (treelike === undefined) {
20
- return undefined;
21
- }
22
- const tree = Tree.from(treelike);
16
+ const tree = await getTreeArgument(this, arguments, treelike, "@shuffle");
23
17
 
24
18
  /** @type {AsyncTree} */
25
19
  let shuffled = transformObject(ShuffleTransform, tree);
@@ -27,5 +21,5 @@ export default async function shuffle(treelike) {
27
21
  return shuffled;
28
22
  }
29
23
 
30
- shuffle.usage = `shuffle <tree>\tReturn a new tree with the original's keys shuffled`;
24
+ shuffle.usage = `@shuffle <tree>\tReturn a new tree with the original's keys shuffled`;
31
25
  shuffle.documentation = "https://weborigami.org/cli/builtins.html#shuffle";
@@ -1,8 +1,7 @@
1
- import { Tree } from "@weborigami/async-tree";
2
- import assertScopeIsDefined from "../../misc/assertScopeIsDefined.js";
3
- import builtins from "../@builtins.js";
4
- import unpackOrigamiExpression from "../@loaders/ori.js";
5
- import paths from "./paths.js";
1
+ import getTreeArgument from "../misc/getTreeArgument.js";
2
+ import builtins from "./@builtins.js";
3
+ import unpackOrigamiExpression from "./@loaders/ori.js";
4
+ import paths from "./@paths.js";
6
5
 
7
6
  const templateText = `=\`<?xml version="1.0" encoding="UTF-8"?>
8
7
  <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
@@ -23,9 +22,7 @@ const templateText = `=\`<?xml version="1.0" encoding="UTF-8"?>
23
22
  * @param {string} [baseHref ]
24
23
  */
25
24
  export default async function sitemap(treelike, baseHref = "") {
26
- assertScopeIsDefined(this);
27
- treelike = treelike ?? (await this?.get("@current"));
28
- const tree = Tree.from(treelike);
25
+ const tree = await getTreeArgument(this, arguments, treelike, "@sitemap");
29
26
 
30
27
  // We're only interested in keys that end in .html or with no extension.
31
28
  function test(key) {
@@ -1,25 +1,23 @@
1
- import { Tree, sortNatural } from "@weborigami/async-tree";
1
+ import { sortNatural } from "@weborigami/async-tree";
2
2
  import { Scope } from "@weborigami/language";
3
- import assertScopeIsDefined from "../../misc/assertScopeIsDefined.js";
3
+ import getTreeArgument from "../misc/getTreeArgument.js";
4
4
 
5
5
  /**
6
6
  * Return a new tree with the original's keys sorted in natural sort order.
7
7
  *
8
8
  * @typedef {import("@weborigami/types").AsyncTree} AsyncTree
9
9
  * @typedef {import("@weborigami/async-tree").Treelike} Treelike
10
- * @typedef {import("../../../index.ts").Invocable} Invocable
10
+ * @typedef {import("../../index.ts").Invocable} Invocable
11
11
  *
12
12
  * @this {AsyncTree|null}
13
13
  * @param {Treelike} [treelike]
14
14
  */
15
15
  export default async function sort(treelike) {
16
- assertScopeIsDefined(this);
17
- treelike = treelike ?? (await this?.get("@current"));
18
- const tree = Tree.from(treelike);
16
+ const tree = await getTreeArgument(this, arguments, treelike, "@sort");
19
17
  const sorted = sortNatural()(tree);
20
18
  const scoped = Scope.treeWithScope(sorted, this);
21
19
  return scoped;
22
20
  }
23
21
 
24
- sort.usage = `sort <tree>\tReturn a new tree with the original's keys sorted`;
22
+ sort.usage = `@sort <tree>\tReturn a new tree with the original's keys sorted`;
25
23
  sort.documentation = "https://weborigami.org/cli/builtins.html#@sort";
@@ -1,8 +1,8 @@
1
- import { Tree, sortBy } from "@weborigami/async-tree";
1
+ import { sortBy } from "@weborigami/async-tree";
2
2
  import { Scope } from "@weborigami/language";
3
- import addValueKeyToScope from "../../common/addValueKeyToScope.js";
4
- import { toFunction } from "../../common/utilities.js";
5
- import assertScopeIsDefined from "../../misc/assertScopeIsDefined.js";
3
+ import addValueKeyToScope from "../common/addValueKeyToScope.js";
4
+ import { toFunction } from "../common/utilities.js";
5
+ import getTreeArgument from "../misc/getTreeArgument.js";
6
6
 
7
7
  /**
8
8
  * Return a new tree with the original's keys sorted using the given function to
@@ -10,16 +10,14 @@ import assertScopeIsDefined from "../../misc/assertScopeIsDefined.js";
10
10
  *
11
11
  * @typedef {import("@weborigami/types").AsyncTree} AsyncTree
12
12
  * @typedef {import("@weborigami/async-tree").Treelike} Treelike
13
- * @typedef {import("../../../index.ts").Invocable} Invocable
13
+ * @typedef {import("../../index.ts").Invocable} Invocable
14
14
  *
15
15
  * @this {AsyncTree|null}
16
16
  * @param {Treelike} treelike
17
17
  * @param {Invocable} sortKeyFn
18
18
  */
19
19
  export default async function sortByBuiltin(treelike, sortKeyFn) {
20
- assertScopeIsDefined(this);
21
- treelike = treelike ?? (await this?.get("@current"));
22
- const tree = Tree.from(treelike);
20
+ const tree = await getTreeArgument(this, arguments, treelike, "@sortBy");
23
21
 
24
22
  const fn = toFunction(sortKeyFn);
25
23
  const baseScope = Scope.getScope(this);
@@ -35,5 +33,5 @@ export default async function sortByBuiltin(treelike, sortKeyFn) {
35
33
  return scoped;
36
34
  }
37
35
 
38
- sortByBuiltin.usage = `sortBy <tree>, [sortKeyFn]\tReturn a new tree with the original's keys sorted`;
36
+ sortByBuiltin.usage = `@sortBy <tree>, [sortKeyFn]\tReturn a new tree with the original's keys sorted`;
39
37
  sortByBuiltin.documentation = "https://weborigami.org/cli/builtins.html#@sort";
@@ -1,8 +1,8 @@
1
1
  import { Tree, keysJson } from "@weborigami/async-tree";
2
2
  import { Scope } from "@weborigami/language";
3
- import { transformObject } from "../../common/utilities.js";
4
- import assertScopeIsDefined from "../../misc/assertScopeIsDefined.js";
5
- import index from "../@index.js";
3
+ import { transformObject } from "../common/utilities.js";
4
+ import getTreeArgument from "../misc/getTreeArgument.js";
5
+ import index from "./@index.js";
6
6
 
7
7
  /**
8
8
  * Expose common static keys (index.html, .keys.json) for a tree.
@@ -13,12 +13,7 @@ import index from "../@index.js";
13
13
  * @param {Treelike} treelike
14
14
  */
15
15
  export default async function staticTree(treelike) {
16
- assertScopeIsDefined(this);
17
- treelike = treelike ?? (await this?.get("@current"));
18
- if (treelike === undefined) {
19
- return undefined;
20
- }
21
- const tree = Tree.from(treelike);
16
+ const tree = await getTreeArgument(this, arguments, treelike, "@static");
22
17
  let result = transformObject(StaticTransform, tree);
23
18
  result = Scope.treeWithScope(result, this);
24
19
  return result;
@@ -47,5 +42,5 @@ function StaticTransform(Base) {
47
42
  };
48
43
  }
49
44
 
50
- staticTree.usage = `static <tree>\tAdd keys for generating common static files`;
45
+ staticTree.usage = `@static <tree>\tAdd keys for generating common static files`;
51
46
  staticTree.documentation = "https://weborigami.org/cli/builtins.html#static";
@@ -1,7 +1,6 @@
1
- import { Tree } from "@weborigami/async-tree";
2
1
  import graphviz from "graphviz-wasm";
3
- import assertScopeIsDefined from "../misc/assertScopeIsDefined.js";
4
- import dot from "./@tree/dot.js";
2
+ import getTreeArgument from "../misc/getTreeArgument.js";
3
+ import dot from "../misc/treeDot.js";
5
4
 
6
5
  let graphvizLoaded = false;
7
6
 
@@ -17,16 +16,11 @@ let graphvizLoaded = false;
17
16
  * @param {PlainObject} [options]
18
17
  */
19
18
  export default async function svg(treelike, options = {}) {
20
- assertScopeIsDefined(this);
21
19
  if (!graphvizLoaded) {
22
20
  await graphviz.loadWASM();
23
21
  graphvizLoaded = true;
24
22
  }
25
- treelike = treelike ?? (await this?.get("@current"));
26
- if (treelike === undefined) {
27
- return undefined;
28
- }
29
- const tree = Tree.from(treelike);
23
+ const tree = await getTreeArgument(this, arguments, treelike, "@svg");
30
24
  const dotText = await dot.call(this, tree, options);
31
25
  if (dotText === undefined) {
32
26
  return undefined;
@@ -1,5 +1,5 @@
1
1
  import { Tree } from "@weborigami/async-tree";
2
- import assertScopeIsDefined from "../../misc/assertScopeIsDefined.js";
2
+ import getTreeArgument from "../misc/getTreeArgument.js";
3
3
 
4
4
  /**
5
5
  * @typedef {import("@weborigami/types").AsyncTree} AsyncTree
@@ -8,12 +8,7 @@ import assertScopeIsDefined from "../../misc/assertScopeIsDefined.js";
8
8
  * @param {Treelike} treelike
9
9
  */
10
10
  export default async function table(treelike) {
11
- assertScopeIsDefined(this);
12
- treelike = treelike ?? (await this?.get("@current"));
13
- if (treelike === undefined) {
14
- return undefined;
15
- }
16
- const tree = Tree.from(treelike);
11
+ const tree = await getTreeArgument(this, arguments, treelike, "@table");
17
12
  const firstValue = await valueForFirstKey(tree);
18
13
  if (Tree.isAsyncTree(firstValue)) {
19
14
  return fullTable(tree, firstValue);
@@ -70,5 +65,5 @@ async function valueForFirstKey(tree) {
70
65
  }
71
66
 
72
67
  table.usage =
73
- "table <tree>\tFormat the tree's top level as a tab-delimited table";
68
+ "@table <tree>\tFormat the tree's top level as a tab-delimited table";
74
69
  table.documentation = "https://weborigami.org/cli/builtins.html#table";
@@ -1,6 +1,5 @@
1
- import { Tree } from "@weborigami/async-tree";
2
1
  import { Scope } from "@weborigami/language";
3
- import assertScopeIsDefined from "../../misc/assertScopeIsDefined.js";
2
+ import getTreeArgument from "../misc/getTreeArgument.js";
4
3
 
5
4
  /**
6
5
  * Given a tree, take the first n items from it.
@@ -12,12 +11,7 @@ import assertScopeIsDefined from "../../misc/assertScopeIsDefined.js";
12
11
  * @param {number} n
13
12
  */
14
13
  export default async function take(treelike, n) {
15
- assertScopeIsDefined(this);
16
- treelike = treelike ?? (await this?.get("@current"));
17
- if (treelike === undefined) {
18
- return undefined;
19
- }
20
- const tree = Tree.from(treelike);
14
+ const tree = await getTreeArgument(this, arguments, treelike, "@take");
21
15
 
22
16
  /** @type {AsyncTree} */
23
17
  let takeTree = {
@@ -36,5 +30,5 @@ export default async function take(treelike, n) {
36
30
  return takeTree;
37
31
  }
38
32
 
39
- take.usage = `take tree, n\tReturn the first n items from tree`;
33
+ take.usage = `@take tree, n\tReturn the first n items from tree`;
40
34
  take.documentation = "https://weborigami.org/cli/builtins.html#take";
@@ -0,0 +1,19 @@
1
+ import { Scope } from "@weborigami/language";
2
+ import getTreeArgument from "../misc/getTreeArgument.js";
3
+
4
+ /**
5
+ * Cast the indicated treelike to a tree.
6
+ *
7
+ * @typedef {import("@weborigami/types").AsyncTree} AsyncTree
8
+ * @typedef {import("@weborigami/async-tree").Treelike} Treelike
9
+ * @this {AsyncTree|null}
10
+ * @param {Treelike} [treelike]
11
+ */
12
+ export default async function tree(treelike) {
13
+ let tree = await getTreeArgument(this, arguments, treelike, "@tree");
14
+ tree = Scope.treeWithScope(tree, this);
15
+ return tree;
16
+ }
17
+
18
+ tree.usage = `@tree <treelike>\tConvert JSON, YAML, function, or plain object to a tree`;
19
+ tree.documentation = "https://weborigami.org/cli/builtins.html#tree";
@@ -11,7 +11,7 @@ import assertScopeIsDefined from "../misc/assertScopeIsDefined.js";
11
11
  * @param {...string|Symbol} keys
12
12
  */
13
13
  export default function treeHttp(host, ...keys) {
14
- assertScopeIsDefined(this);
14
+ assertScopeIsDefined(this, "treeHttp");
15
15
  return ops.treeHttp.call(this, host, ...keys);
16
16
  }
17
17
 
@@ -11,7 +11,7 @@ import assertScopeIsDefined from "../misc/assertScopeIsDefined.js";
11
11
  * @param {...string|Symbol} keys
12
12
  */
13
13
  export default function treeHttps(host, ...keys) {
14
- assertScopeIsDefined(this);
14
+ assertScopeIsDefined(this, "treeHttps");
15
15
  return ops.treeHttps.call(this, host, ...keys);
16
16
  }
17
17
 
@@ -8,6 +8,6 @@ import assertScopeIsDefined from "../misc/assertScopeIsDefined.js";
8
8
  * @returns
9
9
  */
10
10
  export default function unpack(obj) {
11
- assertScopeIsDefined(this);
11
+ assertScopeIsDefined(this, "unpack");
12
12
  return obj?.unpack?.();
13
13
  }
@@ -1,5 +1,5 @@
1
1
  import { Tree } from "@weborigami/async-tree";
2
- import assertScopeIsDefined from "../../misc/assertScopeIsDefined.js";
2
+ import getTreeArgument from "../misc/getTreeArgument.js";
3
3
 
4
4
  /**
5
5
  * Return the interior nodes of the tree.
@@ -10,14 +10,9 @@ import assertScopeIsDefined from "../../misc/assertScopeIsDefined.js";
10
10
  * @param {Treelike} [treelike]
11
11
  */
12
12
  export default async function values(treelike) {
13
- assertScopeIsDefined(this);
14
- treelike = treelike ?? (await this?.get("@current"));
15
- if (treelike === undefined) {
16
- return undefined;
17
- }
18
- const tree = Tree.from(treelike);
13
+ const tree = await getTreeArgument(this, arguments, treelike, "@values");
19
14
  return Tree.values(tree);
20
15
  }
21
16
 
22
- values.usage = `values <tree>\tThe top-level values in the tree`;
17
+ values.usage = `@values <tree>\tThe top-level values in the tree`;
23
18
  values.documentation = "https://weborigami.org/cli/builtins.html#values";
@@ -1,5 +1,5 @@
1
1
  import { Tree } from "@weborigami/async-tree";
2
- import assertScopeIsDefined from "../../misc/assertScopeIsDefined.js";
2
+ import getTreeArgument from "../misc/getTreeArgument.js";
3
3
 
4
4
  /**
5
5
  * Return the in-order exterior values of a tree as a flat array.
@@ -10,14 +10,10 @@ import assertScopeIsDefined from "../../misc/assertScopeIsDefined.js";
10
10
  * @param {Treelike} [treelike]
11
11
  */
12
12
  export default async function valuesDeep(treelike) {
13
- assertScopeIsDefined(this);
14
- treelike = treelike ?? (await this?.get("@current"));
15
- if (treelike === undefined) {
16
- return undefined;
17
- }
18
- return Tree.mapReduce(treelike, null, async (values) => values.flat());
13
+ const tree = await getTreeArgument(this, arguments, treelike, "@valuesDeep");
14
+ return Tree.mapReduce(tree, null, async (values) => values.flat());
19
15
  }
20
16
 
21
- valuesDeep.usage = `valuesDeep <tree>\tThe in-order tree values as a flat array`;
17
+ valuesDeep.usage = `@valuesDeep <tree>\tThe in-order tree values as a flat array`;
22
18
  valuesDeep.documentation =
23
19
  "https://weborigami.org/cli/builtins.html#valuesDeep";
@@ -1,7 +1,7 @@
1
1
  import { Tree } from "@weborigami/async-tree";
2
2
  import { Scope } from "@weborigami/language";
3
3
  import ConstantTree from "../common/ConstantTree.js";
4
- import assertScopeIsDefined from "../misc/assertScopeIsDefined.js";
4
+ import getTreeArgument from "../misc/getTreeArgument.js";
5
5
 
6
6
  /**
7
7
  * Let a tree (e.g., of files) respond to changes.
@@ -15,15 +15,10 @@ import assertScopeIsDefined from "../misc/assertScopeIsDefined.js";
15
15
  * @param {Invocable} [fn]
16
16
  */
17
17
  export default async function watch(treelike, fn) {
18
- assertScopeIsDefined(this);
19
- treelike = treelike ?? (await this?.get("@current"));
20
- if (treelike === undefined) {
21
- return undefined;
22
- }
18
+ /** @type {any} */
19
+ const container = await getTreeArgument(this, arguments, treelike, "@watch");
23
20
 
24
21
  // Watch the indicated tree.
25
- /** @type {any} */
26
- const container = Tree.from(treelike);
27
22
  await /** @type {any} */ (container).watch?.();
28
23
 
29
24
  // // Watch trees in scope.
@@ -14,7 +14,7 @@ import assertScopeIsDefined from "../misc/assertScopeIsDefined.js";
14
14
  * @param {Invocable} invocable
15
15
  */
16
16
  export default function withTree(treelike, invocable) {
17
- assertScopeIsDefined(this);
17
+ assertScopeIsDefined(this, "with");
18
18
  const tree = Tree.from(treelike);
19
19
  const fn = toFunction(invocable);
20
20
  const scope = new Scope(tree, this);
@@ -10,7 +10,13 @@ import assertScopeIsDefined from "../misc/assertScopeIsDefined.js";
10
10
  * @param {any} [obj]
11
11
  */
12
12
  export default async function toYaml(obj) {
13
- assertScopeIsDefined(this);
13
+ assertScopeIsDefined(this, "yaml");
14
+ // A fragment of the logic from getTreeArgument.js
15
+ if (arguments.length > 0 && obj === undefined) {
16
+ throw new Error(
17
+ "An Origami function was called with an initial argument, but its value is undefined."
18
+ );
19
+ }
14
20
  obj = obj ?? (await this?.get("@current"));
15
21
  if (obj === undefined) {
16
22
  return undefined;
@@ -1,6 +1,6 @@
1
1
  import { KeyFn, Treelike, ValueKeyFn } from "@weborigami/async-tree";
2
2
  import { AsyncTree } from "@weborigami/types";
3
- import { TreelikeTransform } from "../../../index.ts";
3
+ import { TreelikeTransform } from "../../index.ts";
4
4
 
5
5
  type TreeMapOptions = {
6
6
  deep?: boolean;
@@ -8,9 +8,7 @@ type TreeMapOptions = {
8
8
  extensions?: string;
9
9
  inverseKeyMap?: KeyFn;
10
10
  keyMap?: ValueKeyFn;
11
- keyName?: string;
12
11
  valueMap?: ValueKeyFn;
13
- valueName?: string
14
12
  };
15
13
 
16
14
  export default function treeMap(valueMap: ValueKeyFn): TreelikeTransform;
package/src/cli/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import { ObjectTree, Tree } from "@weborigami/async-tree";
4
- import { Scope } from "@weborigami/language";
4
+ import { Scope, formatError } from "@weborigami/language";
5
5
  import path from "node:path";
6
6
  import process, { stdout } from "node:process";
7
7
  import ori from "../builtins/@ori.js";
@@ -75,19 +75,6 @@ while (args[0] === "") {
75
75
  try {
76
76
  await main(...args);
77
77
  } catch (/** @type {any} */ error) {
78
- // Work up to the root cause, displaying intermediate messages as we go up.
79
- if (!error.cause && !error.stack) {
80
- console.error(error.message);
81
- } else {
82
- while (error.cause) {
83
- console.error(error.message);
84
- error = error.cause;
85
- }
86
- }
87
- if (error.stack) {
88
- // Display stack trace for root cause, under the theory that that's the most
89
- // useful place to look for the problem.
90
- console.error(error.stack);
91
- }
78
+ console.error(formatError(error));
92
79
  process.exitCode = 1;
93
80
  }
@@ -37,10 +37,12 @@ export default function ExplorableSiteTransform(Base) {
37
37
  if (value === undefined) {
38
38
  // The tree doesn't have the key; try the defaults.
39
39
  const scope = Scope.getScope(this);
40
- if (key === "index.html") {
41
- value = await index.call(scope, this);
42
- } else if (key === ".keys.json") {
43
- value = await keysJson.stringify(this);
40
+ if (scope) {
41
+ if (key === "index.html") {
42
+ value = await index.call(scope, this);
43
+ } else if (key === ".keys.json") {
44
+ value = await keysJson.stringify(this);
45
+ }
44
46
  }
45
47
  }
46
48
 
@@ -11,20 +11,12 @@ import { Scope } from "@weborigami/language";
11
11
  * @param {AsyncTree|null} scope
12
12
  * @param {any} value
13
13
  * @param {any} key
14
- * @param {string} [valueName]
15
- * @param {string} [keyName]
16
14
  */
17
- export default function addValueKeyToScope(
18
- scope,
19
- value,
20
- key,
21
- valueName = "_",
22
- keyName = "@key"
23
- ) {
15
+ export default function addValueKeyToScope(scope, value, key) {
24
16
  // Add the key and value to the scope as ambients.
25
17
  const ambients = {
26
- [keyName]: key,
27
- [valueName]: value,
18
+ "@key": key,
19
+ _: value,
28
20
  };
29
21
  return new Scope(ambients, scope);
30
22
  }
@@ -24,13 +24,8 @@ export default function processUnpackedContent(content, parent, inputDocument) {
24
24
  ? new Scope(inputDocument, parentScope)
25
25
  : parentScope;
26
26
 
27
- /** @this {AsyncTree|null} */
28
- async function extendScope(input, ...rest) {
29
- return fn.call(extendedScope, input, ...rest);
30
- }
31
-
32
- extendScope.code = fn.code;
33
- return extendScope;
27
+ const boundFn = fn.bind(extendedScope);
28
+ return boundFn;
34
29
  } else if (
35
30
  Tree.isAsyncTree(content) &&
36
31
  !(/** @type {any} */ (content).scope)
@@ -1,7 +1,7 @@
1
- export default function assertScopeIsDefined(scope) {
1
+ export default function assertScopeIsDefined(scope, methodName) {
2
2
  if (scope === undefined) {
3
3
  throw new Error(
4
- "Tree methods must be called a scope. If you don't want to pass a scope, invoke with: <methodName>.call(null)"
4
+ `${methodName} must be called with a scope. If you don't want to pass a scope, invoke with: ${methodName}.call(null)`
5
5
  );
6
6
  }
7
7
  }
@@ -0,0 +1,51 @@
1
+ import { Tree } from "@weborigami/async-tree";
2
+ import { isTreelike } from "@weborigami/async-tree/src/Tree.js";
3
+ import assertScopeIsDefined from "./assertScopeIsDefined.js";
4
+
5
+ /**
6
+ * Many Origami built-in functions accept an optional treelike object as their
7
+ * first argument. If no tree is supplied, then the current context for the
8
+ * Origami command is used as the tree.
9
+ *
10
+ * So the argument is optional -- but if supplied, it must be defined. The
11
+ * caller should pass its `arguments` object to this function so that the actual
12
+ * number of supplied arguments can be checked.
13
+ *
14
+ * @typedef {import("@weborigami/types").AsyncTree} AsyncTree
15
+ * @typedef {import("@weborigami/async-tree").Treelike} Treelike
16
+ *
17
+ * @param {AsyncTree|null} scope
18
+ * @param {IArguments} args
19
+ * @param {Treelike|undefined} treelike
20
+ * @param {string} methodName
21
+ * @returns {Promise<AsyncTree>}
22
+ */
23
+ export default async function getTreeArgument(
24
+ scope,
25
+ args,
26
+ treelike,
27
+ methodName
28
+ ) {
29
+ assertScopeIsDefined(scope);
30
+
31
+ if (treelike !== undefined) {
32
+ if (isTreelike(treelike)) {
33
+ return Tree.from(treelike);
34
+ }
35
+ throw new Error(
36
+ `${methodName}: The first argument must be a tree, like an array, object, or files.`
37
+ );
38
+ }
39
+
40
+ if (args.length === 0) {
41
+ if (!scope) {
42
+ // Should never happen because assertScopeIsDefined throws an exception.
43
+ throw new Error(
44
+ `${methodName} was called with no tree argument and no scope.`
45
+ );
46
+ }
47
+ return scope.get("@current");
48
+ }
49
+
50
+ throw new Error(`${methodName}: The first argument was undefined.`);
51
+ }
@@ -1,11 +1,7 @@
1
1
  import { Tree, isPlainObject, isStringLike } from "@weborigami/async-tree";
2
2
  import { extname } from "@weborigami/language";
3
- import * as serialize from "../../common/serialize.js";
4
- import {
5
- hasNonPrintableCharacters,
6
- keySymbol,
7
- } from "../../common/utilities.js";
8
- import assertScopeIsDefined from "../../misc/assertScopeIsDefined.js";
3
+ import * as serialize from "../common/serialize.js";
4
+ import { hasNonPrintableCharacters, keySymbol } from "../common/utilities.js";
9
5
 
10
6
  /**
11
7
  * Render a tree in DOT format.
@@ -19,11 +15,6 @@ import assertScopeIsDefined from "../../misc/assertScopeIsDefined.js";
19
15
  * @param {PlainObject} [options]
20
16
  */
21
17
  export default async function dot(treelike, options = {}) {
22
- assertScopeIsDefined(this);
23
- treelike = treelike ?? (await this?.get("@current"));
24
- if (treelike === undefined) {
25
- return undefined;
26
- }
27
18
  const tree = Tree.from(treelike);
28
19
  const rootLabel = tree[keySymbol] ?? "";
29
20
  const treeArcs = await statements(tree, "", rootLabel, options);