@weborigami/origami 0.0.48 → 0.0.50
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/exports/buildExports.js +2 -2
- package/exports/exports.js +29 -13
- package/index.ts +0 -3
- package/package.json +10 -10
- package/src/builtins/@addNextPrevious.js +58 -0
- package/src/builtins/{@arrows.js → @arrowsMap.js} +7 -7
- package/src/builtins/@changes.js +46 -0
- package/src/builtins/@clean.js +19 -0
- package/src/builtins/@constructor.js +17 -0
- package/src/builtins/@crawl.js +2 -2
- package/src/builtins/@debug.js +17 -19
- package/src/builtins/@deepMap.js +19 -0
- package/src/builtins/@deepMapFn.js +25 -0
- package/src/builtins/{@mergeDeep.js → @deepMerge.js} +7 -7
- package/src/builtins/@deepTake.js +21 -0
- package/src/builtins/@deepTakeFn.js +22 -0
- package/src/builtins/{@valuesDeep.js → @deepValues.js} +6 -5
- package/src/builtins/@document.js +1 -2
- package/src/builtins/@files.js +14 -1
- package/src/builtins/@group.js +20 -0
- package/src/builtins/@groupFn.js +30 -0
- package/src/builtins/@if.js +2 -1
- package/src/builtins/@image/format.js +10 -31
- package/src/builtins/@image/formatFn.js +15 -0
- package/src/builtins/@image/resize.js +7 -28
- package/src/builtins/@image/resizeFn.js +14 -0
- package/src/builtins/@inline.js +8 -2
- package/src/builtins/@invoke.js +1 -1
- package/src/builtins/@json.js +5 -1
- package/src/builtins/@jsonParse.js +9 -0
- package/src/builtins/@map.js +10 -170
- package/src/builtins/@mapFn.js +143 -0
- package/src/builtins/@mdHtml.js +2 -0
- package/src/builtins/@mdTree.js +69 -0
- package/src/builtins/@naturalOrder.js +1 -0
- package/src/builtins/@ori.js +1 -1
- package/src/builtins/@paginate.js +18 -0
- package/src/builtins/@paginateFn.js +61 -0
- package/src/builtins/@perf.js +1 -1
- package/src/builtins/@redirect.js +10 -1
- package/src/builtins/@regexParse.js +5 -0
- package/src/builtins/@regexParseFn.js +9 -0
- package/src/builtins/@rss.js +8 -4
- package/src/builtins/@sitemap.js +4 -4
- package/src/builtins/@slug.js +15 -0
- package/src/builtins/@sort.js +10 -7
- package/src/builtins/@sortFn.js +58 -0
- package/src/builtins/@take.js +3 -17
- package/src/builtins/@takeFn.js +21 -0
- package/src/builtins/@tree.js +2 -14
- package/src/builtins/@yaml.js +4 -0
- package/src/builtins/@yamlParse.js +10 -0
- package/src/builtins/map.d.ts +6 -7
- package/src/common/ExplorableSiteTransform.js +16 -10
- package/src/common/ShuffleTransform.js +3 -3
- package/src/common/{arrowFunctionsMap.js → arrowsMapFn.js} +3 -3
- package/src/common/documentObject.js +18 -9
- package/src/common/serialize.js +1 -10
- package/src/common/utilities.js +5 -2
- package/src/misc/OriCommandTransform.js +2 -7
- package/src/misc/explore.ori +7 -7
- package/src/server/constructResponse.js +3 -9
- package/src/server/server.js +5 -2
- package/src/builtins/@apply.js +0 -6
- package/src/builtins/@groupBy.js +0 -37
- package/src/builtins/@isAsyncTree.js +0 -17
- package/src/builtins/@mapDeep.js +0 -22
- package/src/builtins/@new.js +0 -6
- package/src/builtins/@parse/json.js +0 -6
- package/src/builtins/@parse/yaml.js +0 -8
- package/src/builtins/@sortBy.js +0 -37
- package/src/builtins/@with.js +0 -22
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import getTreeArgument from "../misc/getTreeArgument.js";
|
|
2
|
+
import paginateFn from "./@paginateFn.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Return a new grouping of the treelike's values into chunks of the specified
|
|
6
|
+
* size.
|
|
7
|
+
*
|
|
8
|
+
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
|
|
9
|
+
* @typedef {import("@weborigami/async-tree").Treelike} Treelike
|
|
10
|
+
*
|
|
11
|
+
* @this {AsyncTree|null}
|
|
12
|
+
* @param {Treelike} [treelike]
|
|
13
|
+
* @param {number} [size=10]
|
|
14
|
+
*/
|
|
15
|
+
export default async function paginate(treelike, size = 10) {
|
|
16
|
+
const tree = await getTreeArgument(this, arguments, treelike, "@count");
|
|
17
|
+
return paginateFn.call(this, size)(tree);
|
|
18
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { Tree } from "@weborigami/async-tree";
|
|
2
|
+
import { Scope } from "@weborigami/language";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Return a new grouping of the treelike's values into "pages" of the specified
|
|
6
|
+
* size.
|
|
7
|
+
*
|
|
8
|
+
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
|
|
9
|
+
* @typedef {import("@weborigami/async-tree").Treelike} Treelike
|
|
10
|
+
*
|
|
11
|
+
* @this {AsyncTree|null}
|
|
12
|
+
* @param {number} [size=10]
|
|
13
|
+
*/
|
|
14
|
+
export default function paginateFn(size = 10) {
|
|
15
|
+
const scope = this;
|
|
16
|
+
/**
|
|
17
|
+
* @param {Treelike} [treelike]
|
|
18
|
+
*/
|
|
19
|
+
return async function (treelike) {
|
|
20
|
+
const tree = Tree.from(treelike);
|
|
21
|
+
const keys = Array.from(await tree.keys());
|
|
22
|
+
const pageCount = Math.ceil(keys.length / size);
|
|
23
|
+
|
|
24
|
+
const result = {
|
|
25
|
+
async get(pageKey) {
|
|
26
|
+
// Note: page numbers are 1-based.
|
|
27
|
+
const pageNumber = Number(pageKey);
|
|
28
|
+
if (Number.isNaN(pageNumber)) {
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
const nextPage = pageNumber + 1 <= pageCount ? pageNumber + 1 : null;
|
|
32
|
+
const previousPage = pageNumber - 1 >= 1 ? pageNumber - 1 : null;
|
|
33
|
+
const items = {};
|
|
34
|
+
for (
|
|
35
|
+
let index = (pageNumber - 1) * size;
|
|
36
|
+
index < Math.min(keys.length, pageNumber * size);
|
|
37
|
+
index++
|
|
38
|
+
) {
|
|
39
|
+
const key = keys[index];
|
|
40
|
+
items[key] = await tree.get(keys[index]);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
items,
|
|
45
|
+
nextPage,
|
|
46
|
+
pageCount,
|
|
47
|
+
pageNumber,
|
|
48
|
+
previousPage,
|
|
49
|
+
};
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
async keys() {
|
|
53
|
+
// Return an array from 1..totalPages
|
|
54
|
+
return Array.from({ length: pageCount }, (_, index) => index + 1);
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const scoped = Scope.treeWithScope(result, scope);
|
|
59
|
+
return scoped;
|
|
60
|
+
};
|
|
61
|
+
}
|
package/src/builtins/@perf.js
CHANGED
|
@@ -7,7 +7,7 @@ import assertScopeIsDefined from "../misc/assertScopeIsDefined.js";
|
|
|
7
7
|
* @this {import("@weborigami/types").AsyncTree|null}
|
|
8
8
|
* @param {Function} fn
|
|
9
9
|
*/
|
|
10
|
-
export default async function perf(fn, count =
|
|
10
|
+
export default async function perf(fn, count = 1) {
|
|
11
11
|
assertScopeIsDefined(this, "perf");
|
|
12
12
|
const start = performance.now();
|
|
13
13
|
for (let i = 0; i < count; i++) {
|
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
export default function redirect(url, options = { permanent: false }) {
|
|
2
|
-
|
|
2
|
+
const response = new Response("ok", {
|
|
3
3
|
headers: {
|
|
4
4
|
Location: url,
|
|
5
5
|
},
|
|
6
6
|
status: options.permanent ? 301 : 307,
|
|
7
7
|
});
|
|
8
|
+
/** @type {any} */ (response).pack = () => `<!DOCTYPE html>
|
|
9
|
+
<html>
|
|
10
|
+
<head>
|
|
11
|
+
<meta charset="utf-8" />
|
|
12
|
+
<meta http-equiv="refresh" content="0;url=${url}" />
|
|
13
|
+
</head>
|
|
14
|
+
</html>
|
|
15
|
+
`;
|
|
16
|
+
return response;
|
|
8
17
|
}
|
package/src/builtins/@rss.js
CHANGED
|
@@ -24,7 +24,7 @@ export default async function rss(jsonFeedTree) {
|
|
|
24
24
|
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
|
25
25
|
<channel>
|
|
26
26
|
<atom:link href="${rssUrl}" rel="self" type="application/rss+xml"/>
|
|
27
|
-
<title
|
|
27
|
+
<title><![CDATA[${title}]]></title>
|
|
28
28
|
<link>${home_page_url}</link>
|
|
29
29
|
<description>${description}</description>
|
|
30
30
|
${itemsRss}</channel>
|
|
@@ -33,15 +33,19 @@ ${itemsRss}</channel>
|
|
|
33
33
|
|
|
34
34
|
function itemRss(jsonFeedItem) {
|
|
35
35
|
const { content_html, date_published, id, title, url } = jsonFeedItem;
|
|
36
|
-
// RSS wants dates in RFC-822.
|
|
37
|
-
const date =
|
|
36
|
+
// RSS wants dates in RFC-822, essentially the same as ISO 8601.
|
|
37
|
+
const date =
|
|
38
|
+
date_published instanceof Date
|
|
39
|
+
? date_published.toUTCString()
|
|
40
|
+
: date_published;
|
|
38
41
|
const dateElement = date ? ` <pubDate>${date}</pubDate>\n` : "";
|
|
39
42
|
const guidElement = id ? ` <guid>${id}</guid>\n` : "";
|
|
40
43
|
const contentElement = content_html
|
|
41
44
|
? ` <description><![CDATA[${content_html}]]></description>\n`
|
|
42
45
|
: "";
|
|
46
|
+
const titleElement = `<![CDATA[${title}]]>`;
|
|
43
47
|
return ` <item>
|
|
44
|
-
${dateElement} <title>${
|
|
48
|
+
${dateElement} <title>${titleElement}</title>
|
|
45
49
|
<link>${url}</link>
|
|
46
50
|
${guidElement}${contentElement} </item>
|
|
47
51
|
`;
|
package/src/builtins/@sitemap.js
CHANGED
|
@@ -3,13 +3,13 @@ import builtins from "./@builtins.js";
|
|
|
3
3
|
import paths from "./@paths.js";
|
|
4
4
|
import fileTypeOrigami from "./ori_handler.js";
|
|
5
5
|
|
|
6
|
-
const templateText =
|
|
6
|
+
const templateText = `(urls) => \`<?xml version="1.0" encoding="UTF-8"?>
|
|
7
7
|
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
8
|
-
\${ @map(
|
|
8
|
+
\${ @map(urls, (url) => \`
|
|
9
9
|
<url>
|
|
10
|
-
<loc>\${
|
|
10
|
+
<loc>\${ url }</loc>
|
|
11
11
|
</url>
|
|
12
|
-
\`)
|
|
12
|
+
\`) }
|
|
13
13
|
</urlset>
|
|
14
14
|
\`
|
|
15
15
|
`;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export default function slug(filename) {
|
|
2
|
+
let slug = filename.toLowerCase();
|
|
3
|
+
|
|
4
|
+
// Convert spaces to dashes
|
|
5
|
+
slug = slug.replace(/\s+/g, "-");
|
|
6
|
+
|
|
7
|
+
// Remove special characters except dashes, letters, numbers, and periods.
|
|
8
|
+
slug = slug.replace(/[^\w\-\.]/g, "");
|
|
9
|
+
|
|
10
|
+
// Trim leading or trailing dashes.
|
|
11
|
+
slug = slug.replace(/^-+/, "");
|
|
12
|
+
slug = slug.replace(/-+$/, "");
|
|
13
|
+
|
|
14
|
+
return slug;
|
|
15
|
+
}
|
package/src/builtins/@sort.js
CHANGED
|
@@ -1,20 +1,23 @@
|
|
|
1
|
-
import { sortNatural } from "@weborigami/async-tree";
|
|
2
1
|
import getTreeArgument from "../misc/getTreeArgument.js";
|
|
2
|
+
import sortFn from "./@sortFn.js";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* Return a new tree with the original's keys sorted
|
|
5
|
+
* Return a new tree with the original's keys sorted.
|
|
6
6
|
*
|
|
7
7
|
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
|
|
8
8
|
* @typedef {import("@weborigami/async-tree").Treelike} Treelike
|
|
9
|
-
* @typedef {import("
|
|
9
|
+
* @typedef {import("@weborigami/async-tree").ValueKeyFn} ValueKeyFn
|
|
10
|
+
* @typedef {{ compare?: (a: any, b: any) => number, sortKey?: ValueKeyFn }}
|
|
11
|
+
* SortOptions
|
|
10
12
|
*
|
|
11
13
|
* @this {AsyncTree|null}
|
|
12
14
|
* @param {Treelike} [treelike]
|
|
15
|
+
* @param {ValueKeyFn|SortOptions} [options]
|
|
13
16
|
*/
|
|
14
|
-
export default async function
|
|
17
|
+
export default async function sortBuiltin(treelike, options) {
|
|
15
18
|
const tree = await getTreeArgument(this, arguments, treelike, "@sort");
|
|
16
|
-
return
|
|
19
|
+
return sortFn.call(this, options)(tree);
|
|
17
20
|
}
|
|
18
21
|
|
|
19
|
-
|
|
20
|
-
|
|
22
|
+
sortBuiltin.usage = `@sort <tree>, [options]\tReturn a new tree with the original's keys sorted`;
|
|
23
|
+
sortBuiltin.documentation = "https://weborigami.org/builtins/@sort.html";
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { sortFn } from "@weborigami/async-tree";
|
|
2
|
+
import { Scope } from "@weborigami/language";
|
|
3
|
+
import { toFunction } from "../common/utilities.js";
|
|
4
|
+
import assertScopeIsDefined from "../misc/assertScopeIsDefined.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Return a transform function that sorts a tree's keys using a comparison
|
|
8
|
+
* function.
|
|
9
|
+
*
|
|
10
|
+
* If the `options` include a `sortKey` function, that will be invoked for each
|
|
11
|
+
* key in the tree to produce a sort key. If no `sortKey` function is provided,
|
|
12
|
+
* the original keys will be used as sort keys.
|
|
13
|
+
*
|
|
14
|
+
* If the `options` include a `compare` function, that will be used to compare
|
|
15
|
+
* sort keys.
|
|
16
|
+
*
|
|
17
|
+
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
|
|
18
|
+
* @typedef {import("@weborigami/async-tree").ValueKeyFn} ValueKeyFn
|
|
19
|
+
* @typedef {{ compare?: (a: any, b: any) => number, sortKey?: ValueKeyFn }}
|
|
20
|
+
* SortOptions
|
|
21
|
+
*
|
|
22
|
+
* @this {AsyncTree|null}
|
|
23
|
+
* @param {ValueKeyFn|SortOptions} [options]
|
|
24
|
+
*/
|
|
25
|
+
export default function sortFnBuiltin(options) {
|
|
26
|
+
assertScopeIsDefined(this);
|
|
27
|
+
const scope = this;
|
|
28
|
+
|
|
29
|
+
if (typeof options === "function") {
|
|
30
|
+
// Take the function as the `sortKey` option
|
|
31
|
+
options = { sortKey: options };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const compare = options?.compare;
|
|
35
|
+
let extendedSortKeyFn;
|
|
36
|
+
if (options?.sortKey) {
|
|
37
|
+
const originalSortKey = toFunction(options?.sortKey);
|
|
38
|
+
extendedSortKeyFn = async (key, tree) => {
|
|
39
|
+
const value = await tree.get(key);
|
|
40
|
+
const sortKey = await originalSortKey.call(scope, value, key);
|
|
41
|
+
return sortKey;
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const fn = sortFn({
|
|
46
|
+
compare,
|
|
47
|
+
sortKey: extendedSortKeyFn,
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
return (treelike) => {
|
|
51
|
+
const sorted = fn(treelike);
|
|
52
|
+
const scoped = Scope.treeWithScope(sorted, scope);
|
|
53
|
+
return scoped;
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
sortFnBuiltin.usage = `@sortFn [sortFn]\tReturn a function that sorts a tree`;
|
|
58
|
+
sortFnBuiltin.documentation = "https://weborigami.org/builtins/@sortFn.html";
|
package/src/builtins/@take.js
CHANGED
|
@@ -1,33 +1,19 @@
|
|
|
1
|
-
import { Scope } from "@weborigami/language";
|
|
2
1
|
import getTreeArgument from "../misc/getTreeArgument.js";
|
|
2
|
+
import takeFn from "./@takeFn.js";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Given a tree, take the first n items from it.
|
|
6
6
|
*
|
|
7
7
|
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
|
|
8
8
|
* @typedef {import("@weborigami/async-tree").Treelike} Treelike
|
|
9
|
+
*
|
|
9
10
|
* @this {AsyncTree|null}
|
|
10
11
|
* @param {Treelike} treelike
|
|
11
12
|
* @param {number} n
|
|
12
13
|
*/
|
|
13
14
|
export default async function take(treelike, n) {
|
|
14
15
|
const tree = await getTreeArgument(this, arguments, treelike, "@take");
|
|
15
|
-
|
|
16
|
-
/** @type {AsyncTree} */
|
|
17
|
-
let takeTree = {
|
|
18
|
-
async keys() {
|
|
19
|
-
const keys = Array.from(await tree.keys());
|
|
20
|
-
return keys.slice(0, n);
|
|
21
|
-
},
|
|
22
|
-
|
|
23
|
-
async get(key) {
|
|
24
|
-
return tree.get(key);
|
|
25
|
-
},
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
takeTree = Scope.treeWithScope(takeTree, this);
|
|
29
|
-
|
|
30
|
-
return takeTree;
|
|
16
|
+
return takeFn.call(this, n)(tree);
|
|
31
17
|
}
|
|
32
18
|
|
|
33
19
|
take.usage = `@take tree, n\tReturn the first n items from tree`;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { takeFn } from "@weborigami/async-tree";
|
|
2
|
+
import { Scope } from "@weborigami/language";
|
|
3
|
+
import assertScopeIsDefined from "../misc/assertScopeIsDefined.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Limit the number of keys to the indicated count.
|
|
7
|
+
*
|
|
8
|
+
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
|
|
9
|
+
*
|
|
10
|
+
* @this {AsyncTree|null}
|
|
11
|
+
* @param {number} count
|
|
12
|
+
*/
|
|
13
|
+
export default function takeFnBuiltin(count) {
|
|
14
|
+
assertScopeIsDefined(this, "takeFn");
|
|
15
|
+
const scope = this;
|
|
16
|
+
return (treelike) => {
|
|
17
|
+
const taken = takeFn(count)(treelike);
|
|
18
|
+
const scoped = Scope.treeWithScope(taken, scope);
|
|
19
|
+
return scoped;
|
|
20
|
+
};
|
|
21
|
+
}
|
package/src/builtins/@tree.js
CHANGED
|
@@ -1,16 +1,4 @@
|
|
|
1
|
-
import getTreeArgument from "../misc/getTreeArgument.js";
|
|
2
|
-
|
|
3
1
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
|
|
7
|
-
* @typedef {import("@weborigami/async-tree").Treelike} Treelike
|
|
8
|
-
* @this {AsyncTree|null}
|
|
9
|
-
* @param {Treelike} [treelike]
|
|
2
|
+
* Tree utilities functions
|
|
10
3
|
*/
|
|
11
|
-
export default
|
|
12
|
-
return getTreeArgument(this, arguments, treelike, "@tree");
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
tree.usage = `@tree <treelike>\tConvert JSON, YAML, function, or plain object to a tree`;
|
|
16
|
-
tree.documentation = "https://weborigami.org/cli/builtins.html#tree";
|
|
4
|
+
export { Tree as default } from "@weborigami/async-tree";
|
package/src/builtins/@yaml.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/** @typedef {import("@weborigami/types").AsyncTree} AsyncTree */
|
|
2
|
+
import { isUnpackable } from "@weborigami/async-tree";
|
|
2
3
|
import YAML from "yaml";
|
|
3
4
|
import * as serialize from "../common/serialize.js";
|
|
4
5
|
import assertScopeIsDefined from "../misc/assertScopeIsDefined.js";
|
|
@@ -21,6 +22,9 @@ export default async function toYaml(obj) {
|
|
|
21
22
|
if (obj === undefined) {
|
|
22
23
|
return undefined;
|
|
23
24
|
}
|
|
25
|
+
if (isUnpackable(obj)) {
|
|
26
|
+
obj = await obj.unpack();
|
|
27
|
+
}
|
|
24
28
|
const value = await serialize.toJsonValue(obj);
|
|
25
29
|
return YAML.stringify(value);
|
|
26
30
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import * as serialize from "../common/serialize.js";
|
|
2
|
+
import { toString } from "../common/utilities.js";
|
|
3
|
+
|
|
4
|
+
export default async function yamlParse(input) {
|
|
5
|
+
const text = toString(input);
|
|
6
|
+
return text ? serialize.parseYaml(text) : undefined;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
yamlParse.usage = `@yamlParse <text>\tParse text as YAML`;
|
|
10
|
+
yamlParse.documentation = "https://weborigami.org/builtins/@yamlParse.html";
|
package/src/builtins/map.d.ts
CHANGED
|
@@ -1,17 +1,16 @@
|
|
|
1
|
-
import { KeyFn, Treelike, ValueKeyFn } from "@weborigami/async-tree";
|
|
1
|
+
import { KeyFn, Treelike, TreeTransform, ValueKeyFn } from "@weborigami/async-tree";
|
|
2
2
|
import { AsyncTree } from "@weborigami/types";
|
|
3
|
-
import { TreelikeTransform } from "../../index.ts";
|
|
4
3
|
|
|
5
4
|
type TreeMapOptions = {
|
|
6
5
|
deep?: boolean;
|
|
7
6
|
description?: string;
|
|
8
|
-
|
|
7
|
+
extension?: string;
|
|
9
8
|
inverseKey?: KeyFn;
|
|
10
9
|
key?: ValueKeyFn;
|
|
10
|
+
needsSourceValue?: boolean;
|
|
11
11
|
value?: ValueKeyFn;
|
|
12
12
|
};
|
|
13
13
|
|
|
14
|
-
export default function treeMap(
|
|
15
|
-
export default function treeMap(options: TreeMapOptions):
|
|
16
|
-
export default function treeMap(
|
|
17
|
-
export default function treeMap(treelike: Treelike, options: TreeMapOptions): AsyncTree;
|
|
14
|
+
export default function treeMap(options: ValueKeyFn | TreeMapOptions): TreeTransform;
|
|
15
|
+
export default function treeMap(treelike: Treelike, options: ValueKeyFn | TreeMapOptions): AsyncTree;
|
|
16
|
+
export default function treeMap(param1: Treelike | ValueKeyFn | TreeMapOptions, param2?: ValueKeyFn | TreeMapOptions): AsyncTree | TreeTransform;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Tree, keysJson } from "@weborigami/async-tree";
|
|
2
2
|
import { Scope } from "@weborigami/language";
|
|
3
3
|
import index from "../builtins/@index.js";
|
|
4
|
-
import { transformObject } from "../common/utilities.js";
|
|
4
|
+
import { isTransformApplied, transformObject } from "../common/utilities.js";
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Wraps a tree (typically a SiteTree) to turn a standard site into an
|
|
@@ -33,10 +33,10 @@ export default function ExplorableSiteTransform(Base) {
|
|
|
33
33
|
|
|
34
34
|
// Ask the tree if it has the key.
|
|
35
35
|
let value = await super.get(key);
|
|
36
|
+
const scope = Scope.getScope(this);
|
|
36
37
|
|
|
37
38
|
if (value === undefined) {
|
|
38
39
|
// The tree doesn't have the key; try the defaults.
|
|
39
|
-
const scope = Scope.getScope(this);
|
|
40
40
|
if (scope) {
|
|
41
41
|
if (key === "index.html") {
|
|
42
42
|
value = await index.call(scope, this);
|
|
@@ -46,15 +46,19 @@ export default function ExplorableSiteTransform(Base) {
|
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
// Ensure this transform is applied to any explorable result. This lets
|
|
50
|
-
// the user browse into data and explorable trees of types other than the
|
|
51
|
-
// current class.
|
|
52
49
|
if (Tree.isAsyncTree(value)) {
|
|
53
|
-
|
|
54
|
-
|
|
50
|
+
// Ensure this transform is applied to any tree result so the user
|
|
51
|
+
// browse into data and trees of classes other than the current class.
|
|
52
|
+
if (!isTransformApplied(ExplorableSiteTransform, value)) {
|
|
53
|
+
value = transformObject(ExplorableSiteTransform, value);
|
|
54
|
+
}
|
|
55
55
|
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
if (key.endsWith?.("/")) {
|
|
57
|
+
// Instead of return the tree directly, return an index for it.
|
|
58
|
+
value = await index.call(scope, value);
|
|
59
|
+
}
|
|
60
|
+
} else if (value?.unpack) {
|
|
61
|
+
// If the value isn't a tree, but has a tree attached via an `unpack`
|
|
58
62
|
// method, wrap the unpack method to add this transform.
|
|
59
63
|
const original = value.unpack.bind(value);
|
|
60
64
|
value.unpack = async () => {
|
|
@@ -68,7 +72,9 @@ export default function ExplorableSiteTransform(Base) {
|
|
|
68
72
|
const scope = Scope.getScope(this);
|
|
69
73
|
tree = Scope.treeWithScope(tree, scope);
|
|
70
74
|
}
|
|
71
|
-
|
|
75
|
+
if (!isTransformApplied(ExplorableSiteTransform, tree)) {
|
|
76
|
+
tree = transformObject(ExplorableSiteTransform, tree);
|
|
77
|
+
}
|
|
72
78
|
return tree;
|
|
73
79
|
};
|
|
74
80
|
}
|
|
@@ -19,10 +19,10 @@ export default function ShuffleTransform(Base) {
|
|
|
19
19
|
* Performs a Fisher-Yates shuffle. From http://sedition.com/perl/javascript-fy.html
|
|
20
20
|
*/
|
|
21
21
|
function shuffle(array) {
|
|
22
|
-
|
|
22
|
+
let i = array.length;
|
|
23
23
|
while (--i >= 0) {
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
25
|
+
const temp = array[i];
|
|
26
26
|
array[i] = array[j];
|
|
27
27
|
array[j] = temp;
|
|
28
28
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { cachedKeyFunctions,
|
|
1
|
+
import { cachedKeyFunctions, mapFn } from "@weborigami/async-tree";
|
|
2
2
|
import { toFunction } from "./utilities.js";
|
|
3
3
|
|
|
4
|
-
export default function
|
|
4
|
+
export default function arrowsMapFn() {
|
|
5
5
|
const deep = true;
|
|
6
|
-
return
|
|
6
|
+
return mapFn({
|
|
7
7
|
deep,
|
|
8
8
|
description: "arrowFunctions",
|
|
9
9
|
value: valueFn,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { isPlainObject } from "@weborigami/async-tree";
|
|
2
|
-
import txtHandler from "../builtins/txt_handler.js";
|
|
1
|
+
import { isPlainObject, isUnpackable } from "@weborigami/async-tree";
|
|
2
|
+
// import txtHandler from "../builtins/txt_handler.js";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* In Origami, a text document object is any object with a `@text` property and
|
|
@@ -12,9 +12,15 @@ import txtHandler from "../builtins/txt_handler.js";
|
|
|
12
12
|
* @param {StringLike|PlainObject} input
|
|
13
13
|
* @param {any} [data]
|
|
14
14
|
*/
|
|
15
|
-
export default function documentObject(input, data) {
|
|
15
|
+
export default async function documentObject(input, data) {
|
|
16
16
|
let text;
|
|
17
17
|
let inputData;
|
|
18
|
+
|
|
19
|
+
if (isUnpackable(input)) {
|
|
20
|
+
// Unpack the input first, might already be a document object.
|
|
21
|
+
input = await input.unpack();
|
|
22
|
+
}
|
|
23
|
+
|
|
18
24
|
if (isPlainObject(input)) {
|
|
19
25
|
text = input["@text"];
|
|
20
26
|
inputData = input;
|
|
@@ -22,12 +28,15 @@ export default function documentObject(input, data) {
|
|
|
22
28
|
text = String(input);
|
|
23
29
|
inputData = null;
|
|
24
30
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
+
// TODO: Either restore this code, or move responsibility for packing a
|
|
32
|
+
// document to HandleExtensionsTransform set().
|
|
33
|
+
// const base = {
|
|
34
|
+
// pack() {
|
|
35
|
+
// return txtHandler.pack(this);
|
|
36
|
+
// },
|
|
37
|
+
// };
|
|
38
|
+
// const result = Object.create(base);
|
|
39
|
+
const result = {};
|
|
31
40
|
Object.assign(result, inputData, data, { "@text": text });
|
|
32
41
|
return result;
|
|
33
42
|
}
|
package/src/common/serialize.js
CHANGED
|
@@ -5,12 +5,7 @@
|
|
|
5
5
|
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import {
|
|
9
|
-
Tree,
|
|
10
|
-
isPlainObject,
|
|
11
|
-
isStringLike,
|
|
12
|
-
isUnpackable,
|
|
13
|
-
} from "@weborigami/async-tree";
|
|
8
|
+
import { Tree, isPlainObject, isStringLike } from "@weborigami/async-tree";
|
|
14
9
|
import { OrigamiTree } from "@weborigami/language";
|
|
15
10
|
import * as YAMLModule from "yaml";
|
|
16
11
|
import yamlOrigamiTag from "../misc/yamlOrigamiTag.js";
|
|
@@ -145,10 +140,6 @@ export async function toValue(input, jsonValuesOnly = false) {
|
|
|
145
140
|
} else {
|
|
146
141
|
return input;
|
|
147
142
|
}
|
|
148
|
-
} else if (isUnpackable(input)) {
|
|
149
|
-
// Unpack first, then convert to JSON value.
|
|
150
|
-
const unpacked = await input.unpack();
|
|
151
|
-
return toValue(unpacked);
|
|
152
143
|
} else if (isStringLike(input) && !(input instanceof Array)) {
|
|
153
144
|
return String(input);
|
|
154
145
|
} else if (Tree.isTreelike(input)) {
|
package/src/common/utilities.js
CHANGED
|
@@ -45,10 +45,13 @@ export const keySymbol = Symbol("key");
|
|
|
45
45
|
* @param {string} resultExtension
|
|
46
46
|
*/
|
|
47
47
|
export function replaceExtension(key, sourceExtension, resultExtension) {
|
|
48
|
-
if (!key
|
|
48
|
+
if (!key) {
|
|
49
|
+
return undefined;
|
|
50
|
+
} else if (!key.endsWith(sourceExtension)) {
|
|
49
51
|
return key;
|
|
52
|
+
} else {
|
|
53
|
+
return key.slice(0, -sourceExtension.length) + resultExtension;
|
|
50
54
|
}
|
|
51
|
-
return key.slice(0, -sourceExtension.length) + resultExtension;
|
|
52
55
|
}
|
|
53
56
|
|
|
54
57
|
/**
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/** @typedef {import("@weborigami/types").AsyncTree} AsyncTree */
|
|
2
|
-
import { ObjectTree
|
|
2
|
+
import { ObjectTree } from "@weborigami/async-tree";
|
|
3
3
|
import { Scope } from "@weborigami/language";
|
|
4
4
|
import ori from "../builtins/@ori.js";
|
|
5
|
-
import { keySymbol
|
|
5
|
+
import { keySymbol } from "../common/utilities.js";
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Add support for commands prefixed with `!`.
|
|
@@ -34,11 +34,6 @@ export default function OriCommandTransform(Base) {
|
|
|
34
34
|
const extendedScope = new Scope(ambientsTree, Scope.getScope(this));
|
|
35
35
|
const source = key.slice(1).trim();
|
|
36
36
|
value = await ori.call(extendedScope, source, { formatResult: false });
|
|
37
|
-
|
|
38
|
-
// Ensure this transform is applied to any subtree.
|
|
39
|
-
if (Tree.isAsyncTree(value)) {
|
|
40
|
-
value = transformObject(OriCommandTransform, value);
|
|
41
|
-
}
|
|
42
37
|
}
|
|
43
38
|
|
|
44
39
|
return value;
|
package/src/misc/explore.ori
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
(scope) => `<!DOCTYPE html>
|
|
2
2
|
<html lang="en">
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="utf-8" />
|
|
@@ -16,16 +16,16 @@
|
|
|
16
16
|
<button id="buttonSVG">SVG</button>
|
|
17
17
|
<button id="buttonYAML">YAML</button>
|
|
18
18
|
</div>
|
|
19
|
-
{
|
|
19
|
+
${ @map(scope, (scopeTree) => `
|
|
20
20
|
<ul>
|
|
21
|
-
<h2>${
|
|
22
|
-
{
|
|
21
|
+
<h2>${ scopeTree/name }</h2>
|
|
22
|
+
${ @map(scopeTree/keys, (key) => `
|
|
23
23
|
<li>
|
|
24
|
-
<a href="./!@explore/${
|
|
24
|
+
<a href="./!@explore/${ key }" target="frame">${ key }</a>
|
|
25
25
|
</li>
|
|
26
|
-
`)
|
|
26
|
+
`) }
|
|
27
27
|
</ul>
|
|
28
|
-
`)
|
|
28
|
+
`) }
|
|
29
29
|
</nav>
|
|
30
30
|
<iframe id="frame" name="frame"></iframe>
|
|
31
31
|
</body>
|