@weborigami/origami 0.0.35
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/LICENSE +21 -0
- package/ReadMe.md +3 -0
- package/exports/PathTransform.d.ts +5 -0
- package/exports/PathTransform.js +18 -0
- package/exports/buildExports.js +109 -0
- package/exports/exports.js +121 -0
- package/index.ts +25 -0
- package/package.json +40 -0
- package/src/builtins/!.js +21 -0
- package/src/builtins/@apply.js +6 -0
- package/src/builtins/@arrows.js +34 -0
- package/src/builtins/@builtins.js +18 -0
- package/src/builtins/@cache.js +36 -0
- package/src/builtins/@config.js +25 -0
- package/src/builtins/@copy.js +71 -0
- package/src/builtins/@crawl.js +507 -0
- package/src/builtins/@debug.js +89 -0
- package/src/builtins/@document.js +18 -0
- package/src/builtins/@equals.js +6 -0
- package/src/builtins/@explore.js +68 -0
- package/src/builtins/@false.js +1 -0
- package/src/builtins/@files.js +22 -0
- package/src/builtins/@filter.js +23 -0
- package/src/builtins/@globs.js +23 -0
- package/src/builtins/@help.js +49 -0
- package/src/builtins/@http.js +19 -0
- package/src/builtins/@https.js +19 -0
- package/src/builtins/@if.js +27 -0
- package/src/builtins/@image/format.js +5 -0
- package/src/builtins/@image/resize.js +5 -0
- package/src/builtins/@index.js +72 -0
- package/src/builtins/@inherited.js +17 -0
- package/src/builtins/@inline.js +29 -0
- package/src/builtins/@invoke.js +30 -0
- package/src/builtins/@js.js +33 -0
- package/src/builtins/@json.js +22 -0
- package/src/builtins/@loaders/css.js +4 -0
- package/src/builtins/@loaders/htm.js +4 -0
- package/src/builtins/@loaders/html.js +4 -0
- package/src/builtins/@loaders/js.js +14 -0
- package/src/builtins/@loaders/json.js +8 -0
- package/src/builtins/@loaders/md.js +4 -0
- package/src/builtins/@loaders/mjs.js +4 -0
- package/src/builtins/@loaders/ori.js +21 -0
- package/src/builtins/@loaders/orit.js +48 -0
- package/src/builtins/@loaders/txt.js +33 -0
- package/src/builtins/@loaders/xhtml.js +4 -0
- package/src/builtins/@loaders/yaml.js +18 -0
- package/src/builtins/@loaders/yml.js +4 -0
- package/src/builtins/@map.js +182 -0
- package/src/builtins/@match.js +92 -0
- package/src/builtins/@mdHtml.js +45 -0
- package/src/builtins/@new.js +6 -0
- package/src/builtins/@node.js +15 -0
- package/src/builtins/@not.js +6 -0
- package/src/builtins/@or.js +6 -0
- package/src/builtins/@ori.js +83 -0
- package/src/builtins/@pack.js +13 -0
- package/src/builtins/@parse/json.js +7 -0
- package/src/builtins/@parse/yaml.js +9 -0
- package/src/builtins/@project.js +71 -0
- package/src/builtins/@repeat.js +8 -0
- package/src/builtins/@rss.js +49 -0
- package/src/builtins/@scope/extend.js +22 -0
- package/src/builtins/@scope/get.js +25 -0
- package/src/builtins/@scope/invoke.js +22 -0
- package/src/builtins/@scope/set.js +25 -0
- package/src/builtins/@serve.js +74 -0
- package/src/builtins/@shell.js +16 -0
- package/src/builtins/@stdin.js +26 -0
- package/src/builtins/@svg.js +42 -0
- package/src/builtins/@tree/concat.js +21 -0
- package/src/builtins/@tree/count.js +24 -0
- package/src/builtins/@tree/defineds.js +37 -0
- package/src/builtins/@tree/dot.js +201 -0
- package/src/builtins/@tree/exceptions.js +50 -0
- package/src/builtins/@tree/first.js +28 -0
- package/src/builtins/@tree/flowSvg.js +55 -0
- package/src/builtins/@tree/fn.js +34 -0
- package/src/builtins/@tree/from.js +27 -0
- package/src/builtins/@tree/fromJson.js +6 -0
- package/src/builtins/@tree/fromYaml.js +24 -0
- package/src/builtins/@tree/groupBy.js +39 -0
- package/src/builtins/@tree/inners.js +44 -0
- package/src/builtins/@tree/isAsyncTree.js +17 -0
- package/src/builtins/@tree/keys.js +24 -0
- package/src/builtins/@tree/keysJson.js +44 -0
- package/src/builtins/@tree/map.d.ts +19 -0
- package/src/builtins/@tree/merge.js +47 -0
- package/src/builtins/@tree/mergeDeep.js +44 -0
- package/src/builtins/@tree/nextKey.js +29 -0
- package/src/builtins/@tree/parent.js +24 -0
- package/src/builtins/@tree/paths.js +35 -0
- package/src/builtins/@tree/plain.js +22 -0
- package/src/builtins/@tree/previousKey.js +29 -0
- package/src/builtins/@tree/reverse.js +51 -0
- package/src/builtins/@tree/setDeep.js +45 -0
- package/src/builtins/@tree/shuffle.js +31 -0
- package/src/builtins/@tree/sitemap.js +59 -0
- package/src/builtins/@tree/sort.js +25 -0
- package/src/builtins/@tree/sortBy.js +40 -0
- package/src/builtins/@tree/static.js +51 -0
- package/src/builtins/@tree/table.js +74 -0
- package/src/builtins/@tree/take.js +40 -0
- package/src/builtins/@tree/values.js +23 -0
- package/src/builtins/@tree/valuesDeep.js +23 -0
- package/src/builtins/@treeHttp.js +19 -0
- package/src/builtins/@treeHttps.js +19 -0
- package/src/builtins/@true.js +1 -0
- package/src/builtins/@unpack.js +13 -0
- package/src/builtins/@watch.js +108 -0
- package/src/builtins/@with.js +22 -0
- package/src/builtins/@yaml.js +23 -0
- package/src/builtins/~.js +9 -0
- package/src/cli/cli.js +86 -0
- package/src/cli/defaultModuleExport.js +16 -0
- package/src/cli/showUsage.js +86 -0
- package/src/common/CommandModulesTransform.d.ts +5 -0
- package/src/common/CommandModulesTransform.js +37 -0
- package/src/common/ConstantTree.js +17 -0
- package/src/common/ExplorableSiteTransform.d.ts +5 -0
- package/src/common/ExplorableSiteTransform.js +77 -0
- package/src/common/FilterTree.js +60 -0
- package/src/common/GlobTree.js +67 -0
- package/src/common/ShuffleTransform.js +29 -0
- package/src/common/TextDocument.js +57 -0
- package/src/common/addValueKeyToScope.js +30 -0
- package/src/common/arrowFunctionsMap.js +35 -0
- package/src/common/processUnpackedContent.js +39 -0
- package/src/common/serialize.d.ts +8 -0
- package/src/common/serialize.js +138 -0
- package/src/common/utilities.d.ts +7 -0
- package/src/common/utilities.js +132 -0
- package/src/misc/OriCommandTransform.d.ts +5 -0
- package/src/misc/OriCommandTransform.js +54 -0
- package/src/misc/assertScopeIsDefined.js +7 -0
- package/src/misc/explore.orit +241 -0
- package/src/misc/yamlOrigamiTag.js +17 -0
- package/src/server/mediaTypes.js +97 -0
- package/src/server/server.js +258 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Tree } from "@weborigami/async-tree";
|
|
2
|
+
import { Scope } from "@weborigami/language";
|
|
3
|
+
import * as serialize from "../../common/serialize.js";
|
|
4
|
+
import assertScopeIsDefined from "../../misc/assertScopeIsDefined.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
|
|
8
|
+
* @typedef {import("@weborigami/async-tree").Treelike} Treelike
|
|
9
|
+
* @typedef {import("@weborigami/async-tree").StringLike} StringLike
|
|
10
|
+
*
|
|
11
|
+
* @param {StringLike} text
|
|
12
|
+
* @this {AsyncTree|null}
|
|
13
|
+
*/
|
|
14
|
+
export default async function fromYaml(text) {
|
|
15
|
+
assertScopeIsDefined(this);
|
|
16
|
+
let result = text ? serialize.parseYaml(String(text)) : undefined;
|
|
17
|
+
if (this && Tree.isAsyncTree(result)) {
|
|
18
|
+
result = Scope.treeWithScope(result, this);
|
|
19
|
+
}
|
|
20
|
+
return result;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
fromYaml.usage = `fromYaml <text>\tParse text as YAML`;
|
|
24
|
+
fromYaml.documentation = "https://graphorigami.org/cli/builtins.html#fromYaml";
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Tree, groupBy } from "@weborigami/async-tree";
|
|
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";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Return a new tree with the values from the original tree in groups.
|
|
9
|
+
* The groups are determined by the given function.
|
|
10
|
+
*
|
|
11
|
+
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
|
|
12
|
+
* @typedef {import("@weborigami/async-tree").Treelike} Treelike
|
|
13
|
+
* @typedef {import("../../../index.ts").Invocable} Invocable
|
|
14
|
+
*
|
|
15
|
+
* @this {AsyncTree|null}
|
|
16
|
+
* @param {Treelike} treelike
|
|
17
|
+
* @param {Invocable} groupKeyFn
|
|
18
|
+
*/
|
|
19
|
+
export default async function groupByBuiltin(treelike, groupKeyFn) {
|
|
20
|
+
assertScopeIsDefined(this);
|
|
21
|
+
treelike = treelike ?? (await this?.get("@current"));
|
|
22
|
+
const tree = Tree.from(treelike);
|
|
23
|
+
|
|
24
|
+
const fn = toFunction(groupKeyFn);
|
|
25
|
+
const baseScope = Scope.getScope(this);
|
|
26
|
+
async function extendedGroupKeyFn(value, key, tree) {
|
|
27
|
+
const scope = addValueKeyToScope(baseScope, value, key);
|
|
28
|
+
const sortKey = await fn.call(scope, value, key);
|
|
29
|
+
return sortKey;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const grouped = await groupBy(extendedGroupKeyFn)(tree);
|
|
33
|
+
const scoped = Scope.treeWithScope(grouped, this);
|
|
34
|
+
return scoped;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
groupByBuiltin.usage = `groupBy <tree>, [groupKeyFn]\tReturn a new tree with the original's values grouped`;
|
|
38
|
+
groupByBuiltin.documentation =
|
|
39
|
+
"https://graphorigami.org/cli/builtins.html#@group";
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Tree } from "@weborigami/async-tree";
|
|
2
|
+
import { Scope } from "@weborigami/language";
|
|
3
|
+
import assertScopeIsDefined from "../../misc/assertScopeIsDefined.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Return the source nodes of the tree: the nodes with children.
|
|
7
|
+
*
|
|
8
|
+
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
|
|
9
|
+
* @typedef {import("@weborigami/async-tree").Treelike} Treelike
|
|
10
|
+
* @this {AsyncTree|null}
|
|
11
|
+
* @param {Treelike} [treelike]
|
|
12
|
+
*/
|
|
13
|
+
export default async function inners(treelike) {
|
|
14
|
+
assertScopeIsDefined(this);
|
|
15
|
+
treelike = treelike ?? (await this?.get("@current"));
|
|
16
|
+
if (treelike === undefined) {
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
const tree = Tree.from(treelike);
|
|
20
|
+
|
|
21
|
+
/** @type {AsyncTree} */
|
|
22
|
+
let result = {
|
|
23
|
+
async get(key) {
|
|
24
|
+
const value = await tree.get(key);
|
|
25
|
+
return Tree.isAsyncTree(value) ? inners.call(this, value) : undefined;
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
async keys() {
|
|
29
|
+
const subtreeKeys = [];
|
|
30
|
+
for (const key of await tree.keys()) {
|
|
31
|
+
if (await Tree.isKeyForSubtree(tree, key)) {
|
|
32
|
+
subtreeKeys.push(key);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return subtreeKeys;
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
result = Scope.treeWithScope(result, this);
|
|
40
|
+
return result;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
inners.usage = `inners <tree>\tThe source nodes of the tree`;
|
|
44
|
+
inners.documentation = "https://graphorigami.org/cli/builtins.html#inners";
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** @typedef {import("@weborigami/types").AsyncTree} AsyncTree */
|
|
2
|
+
|
|
3
|
+
import { Tree } from "@weborigami/async-tree";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Return true if the value is an async tree.
|
|
7
|
+
*
|
|
8
|
+
* @this {AsyncTree|null}
|
|
9
|
+
* @param {any} value
|
|
10
|
+
*/
|
|
11
|
+
export default function isAsyncTree(value) {
|
|
12
|
+
return Tree.isAsyncTree(value);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
isAsyncTree.usage = `@tree/isAsyncTree <value>\tReturn true for an async tree`;
|
|
16
|
+
isAsyncTree.documentation =
|
|
17
|
+
"https://graphorigami.org/cli/builtins.html#isAsyncTree";
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Tree } from "@weborigami/async-tree";
|
|
2
|
+
import assertScopeIsDefined from "../../misc/assertScopeIsDefined.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Return the top-level keys in the tree as an array.
|
|
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 keys(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);
|
|
19
|
+
const keys = await tree.keys();
|
|
20
|
+
return Array.from(keys);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
keys.usage = `keys <tree>\tThe top-level keys in the tree`;
|
|
24
|
+
keys.documentation = "https://graphorigami.org/cli/builtins.html#keys";
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Tree, keysJson } from "@weborigami/async-tree";
|
|
2
|
+
import { Scope } from "@weborigami/language";
|
|
3
|
+
import { transformObject } from "../../common/utilities.js";
|
|
4
|
+
import assertScopeIsDefined from "../../misc/assertScopeIsDefined.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Expose .keys.json for a tree.
|
|
8
|
+
*
|
|
9
|
+
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
|
|
10
|
+
* @typedef {import("@weborigami/async-tree").Treelike} Treelike
|
|
11
|
+
* @this {AsyncTree|null}
|
|
12
|
+
* @param {Treelike} treelike
|
|
13
|
+
*/
|
|
14
|
+
export default async function treeKeysJson(treelike) {
|
|
15
|
+
assertScopeIsDefined(this);
|
|
16
|
+
treelike = treelike ?? (await this?.get("@current"));
|
|
17
|
+
if (treelike === undefined) {
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
const tree = Tree.from(treelike);
|
|
21
|
+
let result = transformObject(KeysJsonTransform, tree);
|
|
22
|
+
result = Scope.treeWithScope(result, this);
|
|
23
|
+
return result;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function KeysJsonTransform(Base) {
|
|
27
|
+
return class Static extends Base {
|
|
28
|
+
async get(key) {
|
|
29
|
+
let value = await super.get(key);
|
|
30
|
+
if (value === undefined && key === ".keys.json") {
|
|
31
|
+
value = await keysJson.stringify(this);
|
|
32
|
+
} else if (Tree.isAsyncTree(value)) {
|
|
33
|
+
value = transformObject(KeysJsonTransform, value);
|
|
34
|
+
}
|
|
35
|
+
return value;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async keys() {
|
|
39
|
+
const keys = new Set(await super.keys());
|
|
40
|
+
keys.add(".keys.json");
|
|
41
|
+
return keys;
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { KeyFn, Treelike, ValueKeyFn } from "@weborigami/async-tree";
|
|
2
|
+
import { AsyncTree } from "@weborigami/types";
|
|
3
|
+
import { TreelikeTransform } from "../../../index.ts";
|
|
4
|
+
|
|
5
|
+
type TreeMapOptions = {
|
|
6
|
+
deep?: boolean;
|
|
7
|
+
description?: string;
|
|
8
|
+
extensions?: string;
|
|
9
|
+
inverseKeyMap?: KeyFn;
|
|
10
|
+
keyMap?: ValueKeyFn;
|
|
11
|
+
keyName?: string;
|
|
12
|
+
valueMap?: ValueKeyFn;
|
|
13
|
+
valueName?: string
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export default function treeMap(valueMap: ValueKeyFn): TreelikeTransform;
|
|
17
|
+
export default function treeMap(options: TreeMapOptions): TreelikeTransform;
|
|
18
|
+
export default function treeMap(treelike: Treelike, valueMap: ValueKeyFn): AsyncTree;
|
|
19
|
+
export default function treeMap(treelike: Treelike, options: TreeMapOptions): AsyncTree;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { merge } from "@weborigami/async-tree";
|
|
2
|
+
import { Scope } from "@weborigami/language";
|
|
3
|
+
import assertScopeIsDefined from "../../misc/assertScopeIsDefined.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Create a tree that's the result of merging the given trees.
|
|
7
|
+
*
|
|
8
|
+
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
|
|
9
|
+
* @typedef {import("@weborigami/async-tree").Treelike} Treelike
|
|
10
|
+
*
|
|
11
|
+
* @this {AsyncTree|null}
|
|
12
|
+
* @param {(Treelike|null)[]} trees
|
|
13
|
+
*/
|
|
14
|
+
export default async function treeMerge(...trees) {
|
|
15
|
+
assertScopeIsDefined(this);
|
|
16
|
+
|
|
17
|
+
// Filter out null or undefined trees.
|
|
18
|
+
/** @type {Treelike[]}
|
|
19
|
+
* @ts-ignore */
|
|
20
|
+
const filtered = trees.filter((tree) => tree);
|
|
21
|
+
|
|
22
|
+
if (filtered.length === 1) {
|
|
23
|
+
// Only one tree, no need to merge.
|
|
24
|
+
return filtered[0];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// If a tree can take a scope, give it one that includes the other trees and
|
|
28
|
+
// the current scope.
|
|
29
|
+
const scopedTrees = filtered.map((tree) => {
|
|
30
|
+
const otherTrees = filtered.filter((g) => g !== tree);
|
|
31
|
+
const scope = new Scope(...otherTrees, this);
|
|
32
|
+
// Each tree will be included first in its own scope.
|
|
33
|
+
return Scope.treeWithScope(tree, scope);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Merge the trees.
|
|
37
|
+
const result = merge(...scopedTrees);
|
|
38
|
+
|
|
39
|
+
// Give the overall mixed tree a scope that includes the component trees and
|
|
40
|
+
// the current scope.
|
|
41
|
+
/** @type {any} */ (result).scope = new Scope(result, this);
|
|
42
|
+
|
|
43
|
+
return result;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
treeMerge.usage = `@merge <...trees>\tMerge the given trees`;
|
|
47
|
+
treeMerge.documentation = "https://graphorigami.org/cli/builtins.html#@merge";
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { mergeDeep } from "@weborigami/async-tree";
|
|
2
|
+
import { Scope } from "@weborigami/language";
|
|
3
|
+
import assertScopeIsDefined from "../../misc/assertScopeIsDefined.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Create a tree that's the result of deep merging the given trees.
|
|
7
|
+
*
|
|
8
|
+
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
|
|
9
|
+
* @typedef {import("@weborigami/async-tree").Treelike} Treelike
|
|
10
|
+
* @this {AsyncTree|null}
|
|
11
|
+
* @param {Treelike[]} trees
|
|
12
|
+
*/
|
|
13
|
+
export default async function treeMergeDeep(...trees) {
|
|
14
|
+
assertScopeIsDefined(this);
|
|
15
|
+
// Filter out null or undefined trees.
|
|
16
|
+
const filtered = trees.filter((tree) => tree);
|
|
17
|
+
|
|
18
|
+
if (filtered.length === 1) {
|
|
19
|
+
// Only one tree, no need to merge.
|
|
20
|
+
return filtered[0];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// If a tree can take a scope, give it one that includes the other trees and
|
|
24
|
+
// the current scope.
|
|
25
|
+
const scopedTrees = filtered.map((tree) => {
|
|
26
|
+
const otherTrees = filtered.filter((g) => g !== tree);
|
|
27
|
+
const scope = new Scope(...otherTrees, this);
|
|
28
|
+
// Each tree will be included first in its own scope.
|
|
29
|
+
return Scope.treeWithScope(tree, scope);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// Merge the trees.
|
|
33
|
+
const result = mergeDeep(...scopedTrees);
|
|
34
|
+
|
|
35
|
+
// Give the overall mixed tree a scope that includes the component trees and
|
|
36
|
+
// the current scope.
|
|
37
|
+
/** @type {any} */ (result).scope = new Scope(result, this);
|
|
38
|
+
|
|
39
|
+
return result;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
treeMergeDeep.usage = `mergeDeep <...trees>\tMerge the given trees deeply`;
|
|
43
|
+
treeMergeDeep.documentation =
|
|
44
|
+
"https://graphorigami.org/cli/builtins.html#mergeDeep";
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Tree } from "@weborigami/async-tree";
|
|
2
|
+
import assertScopeIsDefined from "../../misc/assertScopeIsDefined.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Returns the key after the indicated key.
|
|
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
|
+
* @param {any} key
|
|
12
|
+
*/
|
|
13
|
+
export default async function nextKey(treelike, key) {
|
|
14
|
+
assertScopeIsDefined(this);
|
|
15
|
+
const tree = Tree.from(treelike);
|
|
16
|
+
let returnNextKey = false;
|
|
17
|
+
for (const treeKey of await tree.keys()) {
|
|
18
|
+
if (returnNextKey) {
|
|
19
|
+
return treeKey;
|
|
20
|
+
}
|
|
21
|
+
if (treeKey === key) {
|
|
22
|
+
returnNextKey = true;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
nextKey.usage = `nextKey <tree>, <key>\tReturns the key after the indicated key`;
|
|
29
|
+
nextKey.documentation = "https://graphorigami.org/cli/builtins.html#nextKey";
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Tree } from "@weborigami/async-tree";
|
|
2
|
+
import assertScopeIsDefined from "../../misc/assertScopeIsDefined.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Returns the parent of the current tree.
|
|
6
|
+
*
|
|
7
|
+
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
|
|
8
|
+
* @typedef {import("@weborigami/async-tree").Treelike} Treelike
|
|
9
|
+
*
|
|
10
|
+
* @this {AsyncTree|null}
|
|
11
|
+
* @param {Treelike} [treelike]
|
|
12
|
+
*/
|
|
13
|
+
export default async function parent(treelike) {
|
|
14
|
+
assertScopeIsDefined(this);
|
|
15
|
+
treelike = treelike ?? (await this?.get("@current"));
|
|
16
|
+
if (treelike === undefined) {
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
const tree = Tree.from(treelike);
|
|
20
|
+
return tree.parent;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
parent.usage = `parent\tThe parent of the current tree`;
|
|
24
|
+
parent.documentation = "https://graphorigami.org/cli/builtins.html#parent";
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Tree } from "@weborigami/async-tree";
|
|
2
|
+
import assertScopeIsDefined from "../../misc/assertScopeIsDefined.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Return an array of paths to the values in the tree.
|
|
6
|
+
*
|
|
7
|
+
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
|
|
8
|
+
* @typedef {import("@weborigami/async-tree").Treelike} Treelike
|
|
9
|
+
*
|
|
10
|
+
* @this {AsyncTree|null}
|
|
11
|
+
* @param {Treelike} [treelike]
|
|
12
|
+
* @param {string} [prefix]
|
|
13
|
+
*/
|
|
14
|
+
export default async function paths(treelike, prefix = "") {
|
|
15
|
+
assertScopeIsDefined(this);
|
|
16
|
+
treelike = treelike ?? (await this?.get("@current"));
|
|
17
|
+
if (treelike === undefined) {
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
const result = [];
|
|
21
|
+
const tree = Tree.from(treelike);
|
|
22
|
+
for (const key of await tree.keys()) {
|
|
23
|
+
const valuePath = prefix ? `${prefix}/${key}` : key;
|
|
24
|
+
const value = await tree.get(key);
|
|
25
|
+
if (await Tree.isAsyncTree(value)) {
|
|
26
|
+
const subPaths = await paths.call(this, value, valuePath);
|
|
27
|
+
result.push(...subPaths);
|
|
28
|
+
} else {
|
|
29
|
+
result.push(valuePath);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return result;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
paths.usage = `paths(tree)\tReturn an array of paths to the values in the tree`;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Tree } from "@weborigami/async-tree";
|
|
2
|
+
import assertScopeIsDefined from "../../misc/assertScopeIsDefined.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Return the interior nodes of the 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 plain(treelike) {
|
|
13
|
+
assertScopeIsDefined(this);
|
|
14
|
+
treelike = treelike ?? (await this?.get("@current"));
|
|
15
|
+
if (treelike === undefined) {
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
return Tree.plain(treelike);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
plain.usage = `plain <tree>\tA plain JavaScript object representation of the tree`;
|
|
22
|
+
plain.documentation = "https://graphorigami.org/cli/builtins.html#plain";
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Tree } from "@weborigami/async-tree";
|
|
2
|
+
import assertScopeIsDefined from "../../misc/assertScopeIsDefined.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Returns the key before the indicated key.
|
|
6
|
+
*
|
|
7
|
+
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
|
|
8
|
+
* @typedef {import("@weborigami/async-tree").Treelike} Treelike
|
|
9
|
+
*
|
|
10
|
+
* @param {Treelike} treelike
|
|
11
|
+
* @param {any} key
|
|
12
|
+
* @this {AsyncTree|null}
|
|
13
|
+
*/
|
|
14
|
+
export default async function previousKey(treelike, key) {
|
|
15
|
+
assertScopeIsDefined(this);
|
|
16
|
+
const tree = Tree.from(treelike);
|
|
17
|
+
let previousKey = undefined;
|
|
18
|
+
for (const treeKey of await tree.keys()) {
|
|
19
|
+
if (treeKey === key) {
|
|
20
|
+
return previousKey;
|
|
21
|
+
}
|
|
22
|
+
previousKey = treeKey;
|
|
23
|
+
}
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
previousKey.usage = `previousKey <tree>, <key>\tReturns the key before the indicated key`;
|
|
28
|
+
previousKey.documentation =
|
|
29
|
+
"https://graphorigami.org/cli/builtins.html#previousKey";
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Tree } from "@weborigami/async-tree";
|
|
2
|
+
import { Scope } from "@weborigami/language";
|
|
3
|
+
import assertScopeIsDefined from "../../misc/assertScopeIsDefined.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Reverse the order of the top-level keys in the tree.
|
|
7
|
+
*
|
|
8
|
+
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
|
|
9
|
+
* @typedef {import("@weborigami/async-tree").Treelike} Treelike
|
|
10
|
+
* @typedef {import("@weborigami/async-tree").PlainObject} PlainObject
|
|
11
|
+
*
|
|
12
|
+
* @this {AsyncTree|null}
|
|
13
|
+
* @param {Treelike} [treelike]
|
|
14
|
+
* @param {PlainObject} [options]
|
|
15
|
+
*/
|
|
16
|
+
export default async function reverse(treelike, options = {}) {
|
|
17
|
+
assertScopeIsDefined(this);
|
|
18
|
+
treelike = treelike ?? (await this?.get("@current"));
|
|
19
|
+
if (treelike === undefined) {
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
const scope = this;
|
|
23
|
+
const tree = Tree.from(treelike);
|
|
24
|
+
const deep = options.deep ?? false;
|
|
25
|
+
|
|
26
|
+
/** @type {AsyncTree} */
|
|
27
|
+
let reversed = {
|
|
28
|
+
async get(key) {
|
|
29
|
+
let value = await tree.get(key);
|
|
30
|
+
|
|
31
|
+
if (deep && Tree.isAsyncTree(value)) {
|
|
32
|
+
value = reverse.call(scope, value, options);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return value;
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
async keys() {
|
|
39
|
+
const keys = Array.from(await tree.keys());
|
|
40
|
+
keys.reverse();
|
|
41
|
+
return keys;
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
reversed = Scope.treeWithScope(reversed, this);
|
|
46
|
+
|
|
47
|
+
return reversed;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
reverse.usage = `reverse <tree>\tReverses the order of the tree's top-level keys`;
|
|
51
|
+
reverse.documentation = "https://graphorigami.org/cli/builtins.html#reverse";
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Tree } from "@weborigami/async-tree";
|
|
2
|
+
|
|
3
|
+
export default async function setDeep(target, source) {
|
|
4
|
+
const targetTree = Tree.from(target);
|
|
5
|
+
const sourceTree = Tree.from(source);
|
|
6
|
+
await applyUpdates(sourceTree, targetTree);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// Apply all updates from the source to the target.
|
|
10
|
+
async function applyUpdates(source, target) {
|
|
11
|
+
// Fire off requests to update all keys, then wait for all of them to finish.
|
|
12
|
+
const promises = [];
|
|
13
|
+
for (const key of await source.keys()) {
|
|
14
|
+
const updateKeyPromise = applyUpdateForKey(source, target, key);
|
|
15
|
+
promises.push(updateKeyPromise);
|
|
16
|
+
}
|
|
17
|
+
await Promise.all(promises);
|
|
18
|
+
|
|
19
|
+
// HACK: Transforms like KeysTransform that maintain caches will need to
|
|
20
|
+
// recalculate things now that updates have been applied. This should be an
|
|
21
|
+
// automatic part of calling set() -- but triggering those changes inside
|
|
22
|
+
// set() produces cases where set() and get() calls can be interleaved. The
|
|
23
|
+
// atomicity of set() needs to be reconsidered. For now, we work around the
|
|
24
|
+
// problem by triggering `onChange` after the updates have been applied.
|
|
25
|
+
target.onChange?.();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Copy the value for the given key from the source to the target.
|
|
29
|
+
async function applyUpdateForKey(source, target, key) {
|
|
30
|
+
const sourceValue = await source.get(key);
|
|
31
|
+
if (Tree.isAsyncTree(sourceValue)) {
|
|
32
|
+
const targetValue = await target.get(key);
|
|
33
|
+
if (Tree.isAsyncTree(targetValue)) {
|
|
34
|
+
// Both source and target are async dictionaries; recurse.
|
|
35
|
+
await applyUpdates(sourceValue, targetValue);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Copy the value from the source to the target.
|
|
41
|
+
await target.set(key, sourceValue);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
setDeep.usage = `setDeep <target>, <source>\tApplies the source tree to the target`;
|
|
45
|
+
setDeep.documentation = "https://graphorigami.org/cli/builtins.html#setDeep";
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Tree } from "@weborigami/async-tree";
|
|
2
|
+
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";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Return a new tree with the original's keys shuffled
|
|
9
|
+
*
|
|
10
|
+
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
|
|
11
|
+
* @typedef {import("@weborigami/async-tree").Treelike} Treelike
|
|
12
|
+
*
|
|
13
|
+
* @this {AsyncTree|null}
|
|
14
|
+
* @param {Treelike} [treelike]
|
|
15
|
+
*/
|
|
16
|
+
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);
|
|
23
|
+
|
|
24
|
+
/** @type {AsyncTree} */
|
|
25
|
+
let shuffled = transformObject(ShuffleTransform, tree);
|
|
26
|
+
shuffled = Scope.treeWithScope(shuffled, this);
|
|
27
|
+
return shuffled;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
shuffle.usage = `shuffle <tree>\tReturn a new tree with the original's keys shuffled`;
|
|
31
|
+
shuffle.documentation = "https://graphorigami.org/cli/builtins.html#shuffle";
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { Tree } from "@weborigami/async-tree";
|
|
2
|
+
import assertScopeIsDefined from "../../misc/assertScopeIsDefined.js";
|
|
3
|
+
import builtins from "../@builtins.js";
|
|
4
|
+
import unpackOrigamiTemplate from "../@loaders/orit.js";
|
|
5
|
+
import paths from "./paths.js";
|
|
6
|
+
|
|
7
|
+
const templateText = `<?xml version="1.0" encoding="UTF-8"?>
|
|
8
|
+
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
9
|
+
{{ @map(=\`
|
|
10
|
+
<url>
|
|
11
|
+
<loc>{{ _ }}</loc>
|
|
12
|
+
</url>
|
|
13
|
+
\`)(_) }}
|
|
14
|
+
</urlset>
|
|
15
|
+
`;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
|
|
19
|
+
* @typedef {import("@weborigami/async-tree").Treelike} Treelike
|
|
20
|
+
* @this {AsyncTree|null}
|
|
21
|
+
* @param {Treelike} treelike
|
|
22
|
+
* @param {string} [baseHref ]
|
|
23
|
+
*/
|
|
24
|
+
export default async function sitemap(treelike, baseHref = "") {
|
|
25
|
+
assertScopeIsDefined(this);
|
|
26
|
+
treelike = treelike ?? (await this?.get("@current"));
|
|
27
|
+
const tree = Tree.from(treelike);
|
|
28
|
+
|
|
29
|
+
// We're only interested in keys that end in .html or with no extension.
|
|
30
|
+
function test(key) {
|
|
31
|
+
return key.endsWith?.(".html") || !key.includes?.(".");
|
|
32
|
+
}
|
|
33
|
+
const filterTree = {
|
|
34
|
+
async keys() {
|
|
35
|
+
const keys = Array.from(await tree.keys());
|
|
36
|
+
return keys.filter((key) => test(key));
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
async get(key) {
|
|
40
|
+
return test(key) ? tree.get(key) : undefined;
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const treePaths = await paths.call(this, filterTree, baseHref);
|
|
45
|
+
|
|
46
|
+
// For simplicity, we assume that HTML pages will end in .html.
|
|
47
|
+
// If the page is named index.html, we remove index.html from
|
|
48
|
+
// the path.
|
|
49
|
+
const htmlPaths = treePaths
|
|
50
|
+
.filter((path) => path.endsWith(".html"))
|
|
51
|
+
.map((path) => (path.endsWith("index.html") ? path.slice(0, -10) : path));
|
|
52
|
+
|
|
53
|
+
const templateFn = await unpackOrigamiTemplate(templateText);
|
|
54
|
+
const templateResult = await templateFn.call(builtins, htmlPaths);
|
|
55
|
+
return String(templateResult);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
sitemap.usage = `@sitemap <tree>\tGenerate a sitemap for a tree`;
|
|
59
|
+
sitemap.documentation = "https://graphorigami.org/cli/builtins.html#@sitemap";
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Tree, sortNatural } from "@weborigami/async-tree";
|
|
2
|
+
import { Scope } from "@weborigami/language";
|
|
3
|
+
import assertScopeIsDefined from "../../misc/assertScopeIsDefined.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Return a new tree with the original's keys sorted in natural sort order.
|
|
7
|
+
*
|
|
8
|
+
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
|
|
9
|
+
* @typedef {import("@weborigami/async-tree").Treelike} Treelike
|
|
10
|
+
* @typedef {import("../../../index.ts").Invocable} Invocable
|
|
11
|
+
*
|
|
12
|
+
* @this {AsyncTree|null}
|
|
13
|
+
* @param {Treelike} [treelike]
|
|
14
|
+
*/
|
|
15
|
+
export default async function sort(treelike) {
|
|
16
|
+
assertScopeIsDefined(this);
|
|
17
|
+
treelike = treelike ?? (await this?.get("@current"));
|
|
18
|
+
const tree = Tree.from(treelike);
|
|
19
|
+
const sorted = sortNatural()(tree);
|
|
20
|
+
const scoped = Scope.treeWithScope(sorted, this);
|
|
21
|
+
return scoped;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
sort.usage = `sort <tree>\tReturn a new tree with the original's keys sorted`;
|
|
25
|
+
sort.documentation = "https://graphorigami.org/cli/builtins.html#@sort";
|