@weborigami/origami 0.2.7 → 0.2.9
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 +9 -9
- package/src/builtins.js +0 -4
- package/src/handlers/oridocument.handler.js +12 -1
- package/src/image/resize.js +4 -2
- package/src/origami/regexMatch.js +6 -2
- package/src/site/index.js +4 -2
- package/src/tree/map.js +1 -34
- package/src/tree/parseExtensions.js +44 -0
- package/src/tree/shuffle.js +33 -4
- package/src/deprecated.js +0 -140
- package/src/image/resizeFn.js +0 -17
- package/src/origami/regexMatchFn.js +0 -9
- package/src/tree/ShuffleTransform.js +0 -29
- package/src/tree/mapFn.js +0 -159
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@weborigami/origami",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.9",
|
|
4
4
|
"description": "Web Origami language, CLI, framework, and server",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -13,22 +13,22 @@
|
|
|
13
13
|
"main": "./main.js",
|
|
14
14
|
"types": "./index.ts",
|
|
15
15
|
"devDependencies": {
|
|
16
|
-
"@types/node": "22.
|
|
17
|
-
"typescript": "5.
|
|
16
|
+
"@types/node": "22.13.5",
|
|
17
|
+
"typescript": "5.8.2"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@weborigami/async-tree": "0.2.
|
|
21
|
-
"@weborigami/language": "0.2.
|
|
22
|
-
"@weborigami/types": "0.2.
|
|
20
|
+
"@weborigami/async-tree": "0.2.9",
|
|
21
|
+
"@weborigami/language": "0.2.9",
|
|
22
|
+
"@weborigami/types": "0.2.9",
|
|
23
23
|
"exif-parser": "0.1.12",
|
|
24
24
|
"graphviz-wasm": "3.0.2",
|
|
25
|
-
"highlight.js": "11.11.
|
|
26
|
-
"marked": "15.0.
|
|
25
|
+
"highlight.js": "11.11.1",
|
|
26
|
+
"marked": "15.0.7",
|
|
27
27
|
"marked-gfm-heading-id": "4.1.1",
|
|
28
28
|
"marked-highlight": "2.2.1",
|
|
29
29
|
"marked-smartypants": "1.1.9",
|
|
30
30
|
"sharp": "0.33.5",
|
|
31
|
-
"yaml": "2.
|
|
31
|
+
"yaml": "2.7.0"
|
|
32
32
|
},
|
|
33
33
|
"scripts": {
|
|
34
34
|
"test": "node --test --test-reporter=spec",
|
package/src/builtins.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import * as calc from "./calc/calc.js";
|
|
2
|
-
import deprecated from "./deprecated.js";
|
|
3
2
|
import * as dev from "./dev/dev.js";
|
|
4
3
|
import * as handlers from "./handlers/handlers.js";
|
|
5
4
|
import help from "./help/help.js";
|
|
@@ -46,9 +45,6 @@ export default {
|
|
|
46
45
|
|
|
47
46
|
// Some builtins need to be exposed at top level
|
|
48
47
|
...handlers.default,
|
|
49
|
-
|
|
50
|
-
// Deprecated builtins
|
|
51
|
-
...deprecated,
|
|
52
48
|
};
|
|
53
49
|
|
|
54
50
|
// Handle cases where a builtin name conflicts with a JS reserved word
|
|
@@ -40,6 +40,7 @@ export default {
|
|
|
40
40
|
let text;
|
|
41
41
|
let frontData = null;
|
|
42
42
|
let frontSource = null;
|
|
43
|
+
let offset = 0;
|
|
43
44
|
let extendedParent = parent;
|
|
44
45
|
const parsed = parseFrontMatter(unpacked);
|
|
45
46
|
if (!parsed) {
|
|
@@ -58,11 +59,21 @@ export default {
|
|
|
58
59
|
extendedParent = new ObjectTree(frontData);
|
|
59
60
|
extendedParent.parent = parent;
|
|
60
61
|
}
|
|
62
|
+
|
|
63
|
+
// Determine how many lines the source code is offset by (if any) to
|
|
64
|
+
// account for front matter, plus 2 lines for `---` separators
|
|
65
|
+
offset = (frontText.match(/\r?\n/g) ?? []).length + 2;
|
|
66
|
+
|
|
61
67
|
text = body;
|
|
62
68
|
}
|
|
63
69
|
|
|
64
70
|
// Construct an object to represent the source code
|
|
65
|
-
const bodySource = {
|
|
71
|
+
const bodySource = {
|
|
72
|
+
name: key,
|
|
73
|
+
offset,
|
|
74
|
+
text,
|
|
75
|
+
url,
|
|
76
|
+
};
|
|
66
77
|
|
|
67
78
|
// Compile the source as an Origami template document
|
|
68
79
|
const scopeCaching = frontSource ? false : true;
|
package/src/image/resize.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import sharp from "sharp";
|
|
1
2
|
import assertTreeIsDefined from "../common/assertTreeIsDefined.js";
|
|
2
|
-
import imageResizeFn from "./resizeFn.js";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Resize an image.
|
|
@@ -10,5 +10,7 @@ import imageResizeFn from "./resizeFn.js";
|
|
|
10
10
|
*/
|
|
11
11
|
export default async function resize(input, options) {
|
|
12
12
|
assertTreeIsDefined(this, "image:resize");
|
|
13
|
-
return
|
|
13
|
+
return input instanceof Uint8Array || input instanceof ArrayBuffer
|
|
14
|
+
? sharp(input).rotate().resize(options).toBuffer()
|
|
15
|
+
: undefined;
|
|
14
16
|
}
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
const parsers = {};
|
|
2
2
|
|
|
3
3
|
export default function regexMatch(text, regex) {
|
|
4
|
-
|
|
4
|
+
if (!parsers[regex]) {
|
|
5
|
+
const regexp = new RegExp(regex);
|
|
6
|
+
parsers[regex] = (input) => input.match(regexp)?.groups;
|
|
7
|
+
}
|
|
8
|
+
return parsers[regex](text);
|
|
5
9
|
}
|
package/src/site/index.js
CHANGED
|
@@ -8,8 +8,9 @@ import { getDescriptor } from "../common/utilities.js";
|
|
|
8
8
|
* @typedef {import("@weborigami/async-tree").Treelike} Treelike
|
|
9
9
|
* @this {AsyncTree|null}
|
|
10
10
|
* @param {Treelike} [treelike]
|
|
11
|
+
* @param {string} [basePath]
|
|
11
12
|
*/
|
|
12
|
-
export default async function index(treelike) {
|
|
13
|
+
export default async function index(treelike, basePath) {
|
|
13
14
|
const tree = await getTreeArgument(this, arguments, treelike, "site:index");
|
|
14
15
|
const keys = Array.from(await tree.keys());
|
|
15
16
|
|
|
@@ -21,8 +22,9 @@ export default async function index(treelike) {
|
|
|
21
22
|
const links = [];
|
|
22
23
|
for (const key of filtered) {
|
|
23
24
|
const keyText = String(key);
|
|
25
|
+
const path = basePath ? [basePath, keyText].join("/") : keyText;
|
|
24
26
|
// Simple key.
|
|
25
|
-
const link = ` <li><a href="${
|
|
27
|
+
const link = ` <li><a href="${path}">${keyText}</a></li>`;
|
|
26
28
|
links.push(link);
|
|
27
29
|
}
|
|
28
30
|
|
package/src/tree/map.js
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
} from "@weborigami/async-tree";
|
|
8
8
|
import getTreeArgument from "../common/getTreeArgument.js";
|
|
9
9
|
import { toFunction } from "../common/utilities.js";
|
|
10
|
+
import parseExtensions from "./parseExtensions.js";
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Map a hierarchical tree of keys and values to a new tree of keys and values.
|
|
@@ -142,37 +143,3 @@ function extendKeyFn(keyFn) {
|
|
|
142
143
|
return resultKey;
|
|
143
144
|
};
|
|
144
145
|
}
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Given a string specifying an extension or a mapping of one extension to another,
|
|
148
|
-
* return the source and result extensions.
|
|
149
|
-
*
|
|
150
|
-
* Syntax:
|
|
151
|
-
* foo
|
|
152
|
-
* foo→bar Unicode Rightwards Arrow
|
|
153
|
-
* foo->bar hyphen and greater-than sign
|
|
154
|
-
*
|
|
155
|
-
* @param {string} specifier
|
|
156
|
-
*/
|
|
157
|
-
function parseExtensions(specifier) {
|
|
158
|
-
const lowercase = specifier?.toLowerCase() ?? "";
|
|
159
|
-
const extensionRegex =
|
|
160
|
-
/^((?<sourceExtension>\.?\S*)\s*(→|->)\s*(?<resultExtension>\.?\S*))|(?<extension>\.?\S*)$/;
|
|
161
|
-
const match = lowercase.match(extensionRegex);
|
|
162
|
-
if (!match) {
|
|
163
|
-
// Shouldn't happen because the regex is exhaustive.
|
|
164
|
-
throw new Error(`map: Invalid extension specifier "${specifier}".`);
|
|
165
|
-
}
|
|
166
|
-
// @ts-ignore
|
|
167
|
-
const { extension, resultExtension, sourceExtension } = match.groups;
|
|
168
|
-
if (extension) {
|
|
169
|
-
// foo
|
|
170
|
-
return {
|
|
171
|
-
resultExtension: extension,
|
|
172
|
-
sourceExtension: extension,
|
|
173
|
-
};
|
|
174
|
-
} else {
|
|
175
|
-
// foo→bar
|
|
176
|
-
return { resultExtension, sourceExtension };
|
|
177
|
-
}
|
|
178
|
-
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Given a string specifying an extension or a mapping of one extension to another,
|
|
3
|
+
* return the source and result extensions.
|
|
4
|
+
*
|
|
5
|
+
* Syntax:
|
|
6
|
+
* .foo source and result extension are the same
|
|
7
|
+
* .foo→.bar Unicode Rightwards Arrow
|
|
8
|
+
* .foo→ Unicode Rightwards Arrow, no result extension
|
|
9
|
+
* →.bar Unicode Rightwards Arrow, no source extension
|
|
10
|
+
* .foo->.bar hyphen and greater-than sign
|
|
11
|
+
*
|
|
12
|
+
* @param {string} specifier
|
|
13
|
+
*/
|
|
14
|
+
export default function parseExtensions(specifier) {
|
|
15
|
+
const lowercase = specifier?.toLowerCase() ?? "";
|
|
16
|
+
const extensionRegex =
|
|
17
|
+
/^((?<sourceExtension>\/|\.\S*)?\s*(→|->)\s*(?<resultExtension>\/|\.\S*)?)|(?<extension>\/|\.\S*)$/;
|
|
18
|
+
const match = lowercase.match(extensionRegex);
|
|
19
|
+
if (!match) {
|
|
20
|
+
throw new Error(`Invalid file extension specifier "${specifier}".`);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// @ts-ignore
|
|
24
|
+
let { extension, resultExtension, sourceExtension } = match.groups;
|
|
25
|
+
if (extension) {
|
|
26
|
+
// foo
|
|
27
|
+
return {
|
|
28
|
+
resultExtension: extension,
|
|
29
|
+
sourceExtension: extension,
|
|
30
|
+
};
|
|
31
|
+
} else {
|
|
32
|
+
// foo→bar
|
|
33
|
+
|
|
34
|
+
if (resultExtension === undefined && sourceExtension === undefined) {
|
|
35
|
+
throw new Error(
|
|
36
|
+
`A file extension mapping must indicate a source or result extension: "${specifier}".`
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
resultExtension ??= "";
|
|
41
|
+
sourceExtension ??= "";
|
|
42
|
+
return { resultExtension, sourceExtension };
|
|
43
|
+
}
|
|
44
|
+
}
|
package/src/tree/shuffle.js
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import getTreeArgument from "../common/getTreeArgument.js";
|
|
2
|
-
import { transformObject } from "../common/utilities.js";
|
|
3
|
-
import { default as ShuffleTransform, shuffle } from "./ShuffleTransform.js";
|
|
4
2
|
|
|
5
3
|
/**
|
|
6
4
|
* Return a new tree with the original's keys shuffled
|
|
@@ -10,8 +8,9 @@ import { default as ShuffleTransform, shuffle } from "./ShuffleTransform.js";
|
|
|
10
8
|
*
|
|
11
9
|
* @this {AsyncTree|null}
|
|
12
10
|
* @param {Treelike} [treelike]
|
|
11
|
+
* @param {boolean} [reshuffle]
|
|
13
12
|
*/
|
|
14
|
-
export default async function shuffleTree(treelike) {
|
|
13
|
+
export default async function shuffleTree(treelike, reshuffle = false) {
|
|
15
14
|
// Special case: If the treelike is an array, shuffle it directly. Otherwise
|
|
16
15
|
// we'll end up shuffling the array's indexes, and if this is directly
|
|
17
16
|
// displayed by the ori CLI, this will end up creating a plain object. Even
|
|
@@ -23,6 +22,36 @@ export default async function shuffleTree(treelike) {
|
|
|
23
22
|
shuffle(array);
|
|
24
23
|
return array;
|
|
25
24
|
}
|
|
25
|
+
|
|
26
26
|
const tree = await getTreeArgument(this, arguments, treelike, "tree:shuffle");
|
|
27
|
-
|
|
27
|
+
|
|
28
|
+
let keys;
|
|
29
|
+
return {
|
|
30
|
+
async get(key) {
|
|
31
|
+
return tree.get(key);
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
async keys() {
|
|
35
|
+
if (!keys || reshuffle) {
|
|
36
|
+
keys = Array.from(await tree.keys());
|
|
37
|
+
shuffle(keys);
|
|
38
|
+
}
|
|
39
|
+
return keys;
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/*
|
|
45
|
+
* Shuffle an array.
|
|
46
|
+
*
|
|
47
|
+
* Performs a Fisher-Yates shuffle. From http://sedition.com/perl/javascript-fy.html
|
|
48
|
+
*/
|
|
49
|
+
export function shuffle(array) {
|
|
50
|
+
let i = array.length;
|
|
51
|
+
while (--i >= 0) {
|
|
52
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
53
|
+
const temp = array[i];
|
|
54
|
+
array[i] = array[j];
|
|
55
|
+
array[j] = temp;
|
|
56
|
+
}
|
|
28
57
|
}
|
package/src/deprecated.js
DELETED
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
import { Tree } from "@weborigami/async-tree";
|
|
2
|
-
import * as calc from "./calc/calc.js";
|
|
3
|
-
import * as dev from "./dev/dev.js";
|
|
4
|
-
import * as image from "./image/image.js";
|
|
5
|
-
import js from "./js.js";
|
|
6
|
-
import node from "./node.js";
|
|
7
|
-
import * as origami from "./origami/origami.js";
|
|
8
|
-
import files from "./protocols/files.js";
|
|
9
|
-
import * as site from "./site/site.js";
|
|
10
|
-
import * as text from "./text/text.js";
|
|
11
|
-
import * as tree from "./tree/tree.js";
|
|
12
|
-
|
|
13
|
-
const warningsDisplayedForKeys = new Set();
|
|
14
|
-
|
|
15
|
-
export function command(namespace, newKey, oldKey, fn) {
|
|
16
|
-
const wrappedFn = function (...args) {
|
|
17
|
-
const keys = newKey
|
|
18
|
-
? `"${namespace}${newKey}" or just "${newKey}"`
|
|
19
|
-
: `"${namespace}"`;
|
|
20
|
-
if (!warningsDisplayedForKeys.has(oldKey)) {
|
|
21
|
-
console.warn(
|
|
22
|
-
`ori: Warning: "${oldKey}" is deprecated. Use ${keys} instead.`
|
|
23
|
-
);
|
|
24
|
-
warningsDisplayedForKeys.add(oldKey);
|
|
25
|
-
}
|
|
26
|
-
return fn instanceof Function
|
|
27
|
-
? // @ts-ignore
|
|
28
|
-
fn.call(this, ...args)
|
|
29
|
-
: Tree.traverseOrThrow(fn, ...args);
|
|
30
|
-
};
|
|
31
|
-
if (fn.key) {
|
|
32
|
-
wrappedFn.key = fn.key;
|
|
33
|
-
}
|
|
34
|
-
if (fn.inverseKey) {
|
|
35
|
-
wrappedFn.inverseKey = fn.inverseKey;
|
|
36
|
-
}
|
|
37
|
-
return wrappedFn;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export function commands(namespace, object) {
|
|
41
|
-
const deprecatedEntries = Object.entries(object).map(([key, fn]) => [
|
|
42
|
-
`@${fn.key ?? key}`,
|
|
43
|
-
command(namespace, fn.key ?? key, `@${fn.key ?? key}`, fn),
|
|
44
|
-
]);
|
|
45
|
-
return Object.fromEntries(deprecatedEntries);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export default {
|
|
49
|
-
...commands("calc:", calc),
|
|
50
|
-
...commands("dev:", dev),
|
|
51
|
-
"@false": command("js:", "false", "@false", js.false),
|
|
52
|
-
"@fetch": command("js:", "fetch", "@fetch", js.fetch),
|
|
53
|
-
"@files": command("files:", null, "@files/", files),
|
|
54
|
-
"@image": command("image:", null, "@image/", image),
|
|
55
|
-
"@js": command("js:", null, "@js/", js),
|
|
56
|
-
"@math": command("calc:", null, "@math/", calc),
|
|
57
|
-
"@mdHtml": command("text:", "mdHtml", "@mdHtml", text.mdHtml),
|
|
58
|
-
"@node": command("node:", null, "@node/", node),
|
|
59
|
-
...commands("origami:", origami),
|
|
60
|
-
...commands("site:", site),
|
|
61
|
-
...commands("text:", text),
|
|
62
|
-
...commands("tree:", tree),
|
|
63
|
-
"@tree": command("tree:", null, "@tree/", Tree),
|
|
64
|
-
"@true": command("js:", "true", "@true", js.true),
|
|
65
|
-
|
|
66
|
-
// Renamed commands
|
|
67
|
-
"@clean": command("tree:", "clear", "@clean", tree.clear),
|
|
68
|
-
|
|
69
|
-
// Deprecated commands
|
|
70
|
-
"@deepTakeFn": command(
|
|
71
|
-
"tree:",
|
|
72
|
-
"deepTake",
|
|
73
|
-
"@deepTakeFn",
|
|
74
|
-
(options) =>
|
|
75
|
-
/** @this {any} */
|
|
76
|
-
function (treelike) {
|
|
77
|
-
return tree.deepTake.call(this, treelike, options);
|
|
78
|
-
}
|
|
79
|
-
),
|
|
80
|
-
"@deepMapFn": command(
|
|
81
|
-
"tree:",
|
|
82
|
-
"deepMap",
|
|
83
|
-
"@deepMapFn",
|
|
84
|
-
(options) =>
|
|
85
|
-
/** @this {any} */
|
|
86
|
-
function (treelike) {
|
|
87
|
-
return tree.deepMap.call(this, treelike, options);
|
|
88
|
-
}
|
|
89
|
-
),
|
|
90
|
-
"@groupFn": command(
|
|
91
|
-
"tree:",
|
|
92
|
-
"group",
|
|
93
|
-
"@groupFn",
|
|
94
|
-
(options) =>
|
|
95
|
-
/** @this {any} */
|
|
96
|
-
function (treelike) {
|
|
97
|
-
return tree.group.call(this, treelike, options);
|
|
98
|
-
}
|
|
99
|
-
),
|
|
100
|
-
"@mapFn": command(
|
|
101
|
-
"tree:",
|
|
102
|
-
"map",
|
|
103
|
-
"@mapFn",
|
|
104
|
-
(options) =>
|
|
105
|
-
/** @this {any} */
|
|
106
|
-
function (treelike) {
|
|
107
|
-
return tree.map.call(this, treelike, options);
|
|
108
|
-
}
|
|
109
|
-
),
|
|
110
|
-
"@paginateFn": command(
|
|
111
|
-
"tree:",
|
|
112
|
-
"paginate",
|
|
113
|
-
"@paginateFn",
|
|
114
|
-
(options) =>
|
|
115
|
-
/** @this {any} */
|
|
116
|
-
function (treelike) {
|
|
117
|
-
return tree.paginate.call(this, treelike, options);
|
|
118
|
-
}
|
|
119
|
-
),
|
|
120
|
-
"@sortFn": command(
|
|
121
|
-
"tree:",
|
|
122
|
-
"sort",
|
|
123
|
-
"@sortFn",
|
|
124
|
-
(options) =>
|
|
125
|
-
/** @this {any} */
|
|
126
|
-
function (treelike) {
|
|
127
|
-
return tree.sort.call(this, treelike, options);
|
|
128
|
-
}
|
|
129
|
-
),
|
|
130
|
-
"@takeFn": command(
|
|
131
|
-
"tree:",
|
|
132
|
-
"take",
|
|
133
|
-
"@takeFn",
|
|
134
|
-
(options) =>
|
|
135
|
-
/** @this {any} */
|
|
136
|
-
function (treelike) {
|
|
137
|
-
return tree.take.call(this, treelike, options);
|
|
138
|
-
}
|
|
139
|
-
),
|
|
140
|
-
};
|
package/src/image/resizeFn.js
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import sharp from "sharp";
|
|
2
|
-
import assertTreeIsDefined from "../common/assertTreeIsDefined.js";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Return a function that resizes an image.
|
|
6
|
-
*
|
|
7
|
-
* @this {import("@weborigami/types").AsyncTree|null}
|
|
8
|
-
* @param {import("sharp").ResizeOptions} options
|
|
9
|
-
*/
|
|
10
|
-
export default function imageResizeFn(options) {
|
|
11
|
-
assertTreeIsDefined(this, "image/resizeFn");
|
|
12
|
-
// Include `rotate()` to auto-rotate according to EXIF data.
|
|
13
|
-
return (buffer) =>
|
|
14
|
-
buffer instanceof Uint8Array || buffer instanceof ArrayBuffer
|
|
15
|
-
? sharp(buffer).rotate().resize(options).toBuffer()
|
|
16
|
-
: undefined;
|
|
17
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
|
|
3
|
-
* @typedef {import("../../index.ts").Constructor<AsyncTree>} AsyncTreeConstructor
|
|
4
|
-
* @param {AsyncTreeConstructor} Base
|
|
5
|
-
*/
|
|
6
|
-
export default function ShuffleTransform(Base) {
|
|
7
|
-
return class Shuffle extends Base {
|
|
8
|
-
async keys() {
|
|
9
|
-
const keys = Array.from(await super.keys());
|
|
10
|
-
shuffle(keys);
|
|
11
|
-
return keys;
|
|
12
|
-
}
|
|
13
|
-
};
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/*
|
|
17
|
-
* Shuffle an array.
|
|
18
|
-
*
|
|
19
|
-
* Performs a Fisher-Yates shuffle. From http://sedition.com/perl/javascript-fy.html
|
|
20
|
-
*/
|
|
21
|
-
export function shuffle(array) {
|
|
22
|
-
let i = array.length;
|
|
23
|
-
while (--i >= 0) {
|
|
24
|
-
const j = Math.floor(Math.random() * (i + 1));
|
|
25
|
-
const temp = array[i];
|
|
26
|
-
array[i] = array[j];
|
|
27
|
-
array[j] = temp;
|
|
28
|
-
}
|
|
29
|
-
}
|
package/src/tree/mapFn.js
DELETED
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
cachedKeyFunctions,
|
|
3
|
-
isPlainObject,
|
|
4
|
-
keyFunctionsForExtensions,
|
|
5
|
-
map,
|
|
6
|
-
} from "@weborigami/async-tree";
|
|
7
|
-
import assertTreeIsDefined from "../common/assertTreeIsDefined.js";
|
|
8
|
-
import { toFunction } from "../common/utilities.js";
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Return a function that transforms a tree of keys and values to a new tree of
|
|
12
|
-
* keys and values.
|
|
13
|
-
*
|
|
14
|
-
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
|
|
15
|
-
* @typedef {import("@weborigami/async-tree").Treelike} Treelike
|
|
16
|
-
* @typedef {import("@weborigami/async-tree").ValueKeyFn} ValueKeyFn
|
|
17
|
-
* @typedef {import("./map.js").TreeMapOptions} TreeMapOptions
|
|
18
|
-
*
|
|
19
|
-
* @this {AsyncTree|null}
|
|
20
|
-
* @param {ValueKeyFn|TreeMapOptions} operation
|
|
21
|
-
*/
|
|
22
|
-
export default function mapFnBuiltin(operation) {
|
|
23
|
-
assertTreeIsDefined(this, "map");
|
|
24
|
-
const tree = this;
|
|
25
|
-
|
|
26
|
-
// Identify whether the map instructions take the form of a value function or
|
|
27
|
-
// a dictionary of options.
|
|
28
|
-
/** @type {TreeMapOptions} */
|
|
29
|
-
let options;
|
|
30
|
-
/** @type {ValueKeyFn|undefined} */
|
|
31
|
-
let valueFn;
|
|
32
|
-
if (isPlainObject(operation)) {
|
|
33
|
-
// @ts-ignore
|
|
34
|
-
options = operation;
|
|
35
|
-
if (`value` in options && !options.value) {
|
|
36
|
-
throw new TypeError(`@mapFn: The value function is not defined.`);
|
|
37
|
-
}
|
|
38
|
-
valueFn = options?.value;
|
|
39
|
-
} else if (
|
|
40
|
-
typeof operation === "function" ||
|
|
41
|
-
typeof (/** @type {any} */ (operation)?.unpack) === "function"
|
|
42
|
-
) {
|
|
43
|
-
valueFn = operation;
|
|
44
|
-
options = {};
|
|
45
|
-
} else {
|
|
46
|
-
throw new TypeError(
|
|
47
|
-
`@mapFn: You must specify a value function or options dictionary as the first parameter.`
|
|
48
|
-
);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const { deep, extension, needsSourceValue } = options;
|
|
52
|
-
const description = options.description ?? `@mapFn ${extension ?? ""}`;
|
|
53
|
-
const keyFn = options.key;
|
|
54
|
-
const inverseKeyFn = options.inverseKey;
|
|
55
|
-
|
|
56
|
-
if (extension && (keyFn || inverseKeyFn)) {
|
|
57
|
-
throw new TypeError(
|
|
58
|
-
`@mapFn: You can't specify extensions and also a key or inverseKey function`
|
|
59
|
-
);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
let extendedValueFn;
|
|
63
|
-
if (valueFn) {
|
|
64
|
-
const resolvedValueFn = toFunction(valueFn);
|
|
65
|
-
// Have the value function run in this tree.
|
|
66
|
-
extendedValueFn = resolvedValueFn.bind(tree);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// Extend the key functions to run in this tree.
|
|
70
|
-
let extendedKeyFn;
|
|
71
|
-
let extendedInverseKeyFn;
|
|
72
|
-
if (extension) {
|
|
73
|
-
let { resultExtension, sourceExtension } = parseExtensions(extension);
|
|
74
|
-
const keyFns = keyFunctionsForExtensions({
|
|
75
|
-
resultExtension,
|
|
76
|
-
sourceExtension,
|
|
77
|
-
});
|
|
78
|
-
extendedKeyFn = keyFns.key;
|
|
79
|
-
extendedInverseKeyFn = keyFns.inverseKey;
|
|
80
|
-
} else if (keyFn) {
|
|
81
|
-
const resolvedKeyFn = toFunction(keyFn);
|
|
82
|
-
async function scopedKeyFn(sourceKey, sourceTree) {
|
|
83
|
-
const sourceValue = await sourceTree.get(sourceKey);
|
|
84
|
-
// The key function will be given the source tree, but will run with the
|
|
85
|
-
// scope of this tree.
|
|
86
|
-
const resultKey = await resolvedKeyFn.call(
|
|
87
|
-
tree,
|
|
88
|
-
sourceValue,
|
|
89
|
-
sourceKey,
|
|
90
|
-
sourceTree
|
|
91
|
-
);
|
|
92
|
-
return resultKey;
|
|
93
|
-
}
|
|
94
|
-
const keyFns = cachedKeyFunctions(scopedKeyFn, deep);
|
|
95
|
-
extendedKeyFn = keyFns.key;
|
|
96
|
-
extendedInverseKeyFn = keyFns.inverseKey;
|
|
97
|
-
} else {
|
|
98
|
-
// Use sidecar keyFn/inverseKey functions if the valueFn defines them.
|
|
99
|
-
extendedKeyFn = /** @type {any} */ (valueFn)?.key;
|
|
100
|
-
extendedInverseKeyFn = /** @type {any} */ (valueFn)?.inverseKey;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// const fn = mapFn({
|
|
104
|
-
// deep,
|
|
105
|
-
// description,
|
|
106
|
-
// inverseKey: extendedInverseKeyFn,
|
|
107
|
-
// key: extendedKeyFn,
|
|
108
|
-
// needsSourceValue,
|
|
109
|
-
// value: extendedValueFn,
|
|
110
|
-
// });
|
|
111
|
-
const temp = {
|
|
112
|
-
deep,
|
|
113
|
-
description,
|
|
114
|
-
inverseKey: extendedInverseKeyFn,
|
|
115
|
-
key: extendedKeyFn,
|
|
116
|
-
needsSourceValue,
|
|
117
|
-
value: extendedValueFn,
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
return (treelike) => {
|
|
121
|
-
const mapped = map(treelike, temp);
|
|
122
|
-
mapped.parent = tree;
|
|
123
|
-
return mapped;
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Given a string specifying an extension or a mapping of one extension to another,
|
|
129
|
-
* return the source and result extensions.
|
|
130
|
-
*
|
|
131
|
-
* Syntax:
|
|
132
|
-
* foo
|
|
133
|
-
* foo→bar Unicode Rightwards Arrow
|
|
134
|
-
* foo->bar hyphen and greater-than sign
|
|
135
|
-
*
|
|
136
|
-
* @param {string} specifier
|
|
137
|
-
*/
|
|
138
|
-
function parseExtensions(specifier) {
|
|
139
|
-
const lowercase = specifier?.toLowerCase() ?? "";
|
|
140
|
-
const extensionRegex =
|
|
141
|
-
/^(\.?(?<sourceExtension>\S*)\s*(→|->)\s*\.?(?<resultExtension>\S*))|(\.?(?<extension>\S*))$/;
|
|
142
|
-
const match = lowercase.match(extensionRegex);
|
|
143
|
-
if (!match) {
|
|
144
|
-
// Shouldn't happen because the regex is exhaustive.
|
|
145
|
-
throw new Error(`@mapFn: Invalid extension specifier "${specifier}".`);
|
|
146
|
-
}
|
|
147
|
-
// @ts-ignore
|
|
148
|
-
const { extension, resultExtension, sourceExtension } = match.groups;
|
|
149
|
-
if (sourceExtension || resultExtension) {
|
|
150
|
-
// foo→bar
|
|
151
|
-
return { resultExtension, sourceExtension };
|
|
152
|
-
} else {
|
|
153
|
-
// foo
|
|
154
|
-
return {
|
|
155
|
-
resultExtension: specifier,
|
|
156
|
-
sourceExtension: specifier,
|
|
157
|
-
};
|
|
158
|
-
}
|
|
159
|
-
}
|