@weborigami/language 0.6.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/main.js +1 -1
- package/package.json +2 -2
- package/src/compiler/optimize.js +3 -1
- package/src/compiler/parserHelpers.js +1 -7
- package/src/handlers/getSource.js +36 -0
- package/src/handlers/ori_handler.js +5 -20
- package/src/handlers/oridocument_handler.js +5 -28
- package/src/handlers/yaml_handler.js +75 -3
- package/src/runtime/errors.js +9 -2
- package/src/runtime/evaluate.js +1 -1
- package/src/runtime/handleExtension.js +0 -5
- package/test/compiler/parse.test.js +13 -19
- package/test/handlers/yaml_handler.test.js +31 -1
- package/test/runtime/handleExtension.test.js +0 -7
- package/test/runtime/ops.test.js +16 -10
package/main.js
CHANGED
|
@@ -8,7 +8,7 @@ export { default as jsGlobals } from "./src/project/jsGlobals.js";
|
|
|
8
8
|
export { default as projectGlobals } from "./src/project/projectGlobals.js";
|
|
9
9
|
export { default as projectRoot } from "./src/project/projectRoot.js";
|
|
10
10
|
export * as Protocols from "./src/protocols/protocols.js";
|
|
11
|
-
export { formatError } from "./src/runtime/errors.js";
|
|
11
|
+
export { formatError, highlightError } from "./src/runtime/errors.js";
|
|
12
12
|
export { default as evaluate } from "./src/runtime/evaluate.js";
|
|
13
13
|
export { default as EventTargetMixin } from "./src/runtime/EventTargetMixin.js";
|
|
14
14
|
export * as expressionFunction from "./src/runtime/expressionFunction.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@weborigami/language",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "Web Origami expression language compiler and runtime",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./main.js",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"typescript": "5.9.3"
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@weborigami/async-tree": "0.6.
|
|
14
|
+
"@weborigami/async-tree": "0.6.1",
|
|
15
15
|
"exif-parser": "0.1.12",
|
|
16
16
|
"watcher": "2.3.1",
|
|
17
17
|
"yaml": "2.8.1"
|
package/src/compiler/optimize.js
CHANGED
|
@@ -2,7 +2,7 @@ import { pathFromKeys, trailingSlash } from "@weborigami/async-tree";
|
|
|
2
2
|
import jsGlobals from "../project/jsGlobals.js";
|
|
3
3
|
import { entryKey } from "../runtime/expressionObject.js";
|
|
4
4
|
import { ops } from "../runtime/internal.js";
|
|
5
|
-
import { annotate, markers } from "./parserHelpers.js";
|
|
5
|
+
import { annotate, markers, spanLocations } from "./parserHelpers.js";
|
|
6
6
|
|
|
7
7
|
export const REFERENCE_PARAM = 1;
|
|
8
8
|
export const REFERENCE_INHERITED = 2;
|
|
@@ -334,6 +334,8 @@ function resolvePath(code, globals, parent, locals, cache) {
|
|
|
334
334
|
result[0][0] === ops.inherited);
|
|
335
335
|
if (extendResult) {
|
|
336
336
|
result.push(...tail);
|
|
337
|
+
result.location = spanLocations(args);
|
|
338
|
+
result.source = code.source;
|
|
337
339
|
} else {
|
|
338
340
|
result = annotate([result, ...tail], code.location);
|
|
339
341
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { trailingSlash } from "@weborigami/async-tree";
|
|
2
1
|
import * as YAMLModule from "yaml";
|
|
3
2
|
import codeFragment from "../runtime/codeFragment.js";
|
|
4
3
|
import * as ops from "../runtime/ops.js";
|
|
@@ -380,11 +379,6 @@ export function makePath(keys) {
|
|
|
380
379
|
const location = spanLocations(code);
|
|
381
380
|
code = annotate(code, location);
|
|
382
381
|
|
|
383
|
-
// Last key has trailing slash implies unpack operation
|
|
384
|
-
if (trailingSlash.has(args.at(-1)[1])) {
|
|
385
|
-
code = annotate([ops.unpack, code], location);
|
|
386
|
-
}
|
|
387
|
-
|
|
388
382
|
return code;
|
|
389
383
|
}
|
|
390
384
|
|
|
@@ -518,7 +512,7 @@ export function makeYamlObject(text, location) {
|
|
|
518
512
|
|
|
519
513
|
// Create a locations that spans those in the array. This assumes the locations
|
|
520
514
|
// are in order and non-overlapping.
|
|
521
|
-
function spanLocations(code) {
|
|
515
|
+
export function spanLocations(code) {
|
|
522
516
|
const first = code.find((item) => item.location).location;
|
|
523
517
|
const last = code[code.findLastIndex((item) => item.location)].location;
|
|
524
518
|
return {
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { getParent, toString } from "@weborigami/async-tree";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Given packed source text and a handler's options, return a source
|
|
5
|
+
* object that can be passed to the compiler.
|
|
6
|
+
*/
|
|
7
|
+
export default function getSource(packed, options = {}) {
|
|
8
|
+
const parent = getParent(packed, options);
|
|
9
|
+
|
|
10
|
+
// Try to determine a URL for error messages
|
|
11
|
+
const sourceName = options.key;
|
|
12
|
+
let url;
|
|
13
|
+
if (sourceName) {
|
|
14
|
+
if (/** @type {any} */ (parent)?.url) {
|
|
15
|
+
let parentHref = /** @type {any} */ (parent).url.href;
|
|
16
|
+
if (!parentHref.endsWith("/")) {
|
|
17
|
+
parentHref += "/";
|
|
18
|
+
}
|
|
19
|
+
url = new URL(sourceName, parentHref);
|
|
20
|
+
} else if (/** @type {any} */ (parent)?.path) {
|
|
21
|
+
let parentHref = new URL(/** @type {any} */ (parent).path, "file:///")
|
|
22
|
+
.href;
|
|
23
|
+
if (!parentHref.endsWith("/")) {
|
|
24
|
+
parentHref += "/";
|
|
25
|
+
}
|
|
26
|
+
url = new URL(sourceName, parentHref);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const source = {
|
|
31
|
+
text: toString(packed),
|
|
32
|
+
name: options.key,
|
|
33
|
+
url,
|
|
34
|
+
};
|
|
35
|
+
return source;
|
|
36
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { getParent, setParent
|
|
1
|
+
import { getParent, setParent } from "@weborigami/async-tree";
|
|
2
2
|
import * as compile from "../compiler/compile.js";
|
|
3
3
|
import projectGlobals from "../project/projectGlobals.js";
|
|
4
|
+
import getSource from "./getSource.js";
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* An Origami expression file
|
|
@@ -13,34 +14,18 @@ export default {
|
|
|
13
14
|
/** @type {import("@weborigami/async-tree").UnpackFunction} */
|
|
14
15
|
async unpack(packed, options = {}) {
|
|
15
16
|
const parent = getParent(packed, options);
|
|
17
|
+
const source = getSource(packed, options);
|
|
16
18
|
|
|
17
|
-
//
|
|
18
|
-
const sourceName = options.key;
|
|
19
|
-
let url;
|
|
20
|
-
if (sourceName && /** @type {any} */ (parent)?.url) {
|
|
21
|
-
let parentHref = /** @type {any} */ (parent).url.href;
|
|
22
|
-
if (!parentHref.endsWith("/")) {
|
|
23
|
-
parentHref += "/";
|
|
24
|
-
}
|
|
25
|
-
url = new URL(sourceName, parentHref);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const source = {
|
|
29
|
-
text: toString(packed),
|
|
30
|
-
name: options.key,
|
|
31
|
-
url,
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
// Compile the source code as an Origami program and evaluate it.
|
|
19
|
+
// Compile the source code as an Origami program
|
|
35
20
|
const compiler = options.compiler ?? compile.program;
|
|
36
21
|
const globals = options.globals ?? (await projectGlobals());
|
|
37
|
-
|
|
38
22
|
const fn = compiler(source, {
|
|
39
23
|
globals,
|
|
40
24
|
mode: "program",
|
|
41
25
|
parent,
|
|
42
26
|
});
|
|
43
27
|
|
|
28
|
+
// Evaluate the program
|
|
44
29
|
const result = await fn();
|
|
45
30
|
|
|
46
31
|
if (parent) {
|
|
@@ -1,11 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
extension,
|
|
3
|
-
getParent,
|
|
4
|
-
toString,
|
|
5
|
-
trailingSlash,
|
|
6
|
-
} from "@weborigami/async-tree";
|
|
1
|
+
import { extension, getParent, trailingSlash } from "@weborigami/async-tree";
|
|
7
2
|
import * as compile from "../compiler/compile.js";
|
|
8
3
|
import projectGlobals from "../project/projectGlobals.js";
|
|
4
|
+
import getSource from "./getSource.js";
|
|
9
5
|
|
|
10
6
|
/**
|
|
11
7
|
* An Origami template document: a plain text file that contains Origami
|
|
@@ -17,30 +13,10 @@ export default {
|
|
|
17
13
|
/** @type {import("@weborigami/async-tree").UnpackFunction} */
|
|
18
14
|
async unpack(packed, options = {}) {
|
|
19
15
|
const parent = getParent(packed, options);
|
|
16
|
+
const source = getSource(packed, options);
|
|
20
17
|
|
|
21
|
-
//
|
|
22
|
-
const text = toString(packed);
|
|
23
|
-
|
|
24
|
-
// See if we can construct a URL to use in error messages
|
|
25
|
-
const key = options.key;
|
|
26
|
-
let url;
|
|
27
|
-
if (key && /** @type {any} */ (parent)?.url) {
|
|
28
|
-
let parentHref = /** @type {any} */ (parent).url.href;
|
|
29
|
-
if (!parentHref.endsWith("/")) {
|
|
30
|
-
parentHref += "/";
|
|
31
|
-
}
|
|
32
|
-
url = new URL(key, parentHref);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// Compile the text as an Origami template document
|
|
36
|
-
const source = {
|
|
37
|
-
name: key,
|
|
38
|
-
text,
|
|
39
|
-
url,
|
|
40
|
-
};
|
|
41
|
-
|
|
18
|
+
// Compile the source code as an Origami template document
|
|
42
19
|
const globals = options.globals ?? (await projectGlobals());
|
|
43
|
-
|
|
44
20
|
const defineFn = compile.templateDocument(source, {
|
|
45
21
|
front: options.front,
|
|
46
22
|
globals,
|
|
@@ -51,6 +27,7 @@ export default {
|
|
|
51
27
|
// Invoke the definition to get back the template function
|
|
52
28
|
const result = await defineFn();
|
|
53
29
|
|
|
30
|
+
const key = options.key;
|
|
54
31
|
const resultExtension = key ? extension.extname(key) : null;
|
|
55
32
|
if (resultExtension && Object.isExtensible(result)) {
|
|
56
33
|
// Add sidecar function so this template can be used in a map.
|
|
@@ -1,5 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
getParent,
|
|
3
|
+
isUnpackable,
|
|
4
|
+
symbols,
|
|
5
|
+
toString,
|
|
6
|
+
} from "@weborigami/async-tree";
|
|
2
7
|
import * as YAMLModule from "yaml";
|
|
8
|
+
import * as compile from "../compiler/compile.js";
|
|
9
|
+
import projectGlobals from "../project/projectGlobals.js";
|
|
10
|
+
import * as expressionFunction from "../runtime/expressionFunction.js";
|
|
11
|
+
import getSource from "./getSource.js";
|
|
3
12
|
|
|
4
13
|
// The "yaml" package doesn't seem to provide a default export that the browser can
|
|
5
14
|
// recognize, so we have to handle two ways to accommodate Node and the browser.
|
|
@@ -16,12 +25,20 @@ export default {
|
|
|
16
25
|
mediaType: "application/yaml",
|
|
17
26
|
|
|
18
27
|
/** @type {import("@weborigami/async-tree").UnpackFunction} */
|
|
19
|
-
unpack(packed) {
|
|
28
|
+
async unpack(packed, options = {}) {
|
|
20
29
|
const yaml = toString(packed);
|
|
21
30
|
if (!yaml) {
|
|
22
31
|
throw new Error("Tried to parse something as YAML but it wasn't text.");
|
|
23
32
|
}
|
|
24
|
-
const
|
|
33
|
+
const parent = getParent(packed, options);
|
|
34
|
+
const oriCallTag = await oriCallTagForParent(parent, options);
|
|
35
|
+
const oriTag = await oriTagForParent(parent, options);
|
|
36
|
+
// YAML parser is sync, but top-level !ori or !ori.call tags will return a
|
|
37
|
+
// promise.
|
|
38
|
+
// @ts-ignore TypeScript complains customTags isn't valid here but it is.
|
|
39
|
+
const data = await YAML.parse(yaml, {
|
|
40
|
+
customTags: [oriCallTag, oriTag],
|
|
41
|
+
});
|
|
25
42
|
if (data && typeof data === "object" && Object.isExtensible(data)) {
|
|
26
43
|
Object.defineProperty(data, symbols.deep, {
|
|
27
44
|
enumerable: false,
|
|
@@ -31,3 +48,58 @@ export default {
|
|
|
31
48
|
return data;
|
|
32
49
|
},
|
|
33
50
|
};
|
|
51
|
+
|
|
52
|
+
async function oriCallTagForParent(parent, options) {
|
|
53
|
+
const globals = await projectGlobals();
|
|
54
|
+
return {
|
|
55
|
+
collection: "seq",
|
|
56
|
+
|
|
57
|
+
tag: "!ori.call",
|
|
58
|
+
|
|
59
|
+
identify: (value) => false,
|
|
60
|
+
|
|
61
|
+
async resolve(value) {
|
|
62
|
+
/** @type {any[]} */
|
|
63
|
+
const args = typeof value?.toJSON === "function" ? value.toJSON() : value;
|
|
64
|
+
|
|
65
|
+
// First arg is Origami source
|
|
66
|
+
const text = args.shift();
|
|
67
|
+
const source = getSource(text, options);
|
|
68
|
+
|
|
69
|
+
const codeFn = compile.expression(source, {
|
|
70
|
+
globals,
|
|
71
|
+
parent,
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// Evaluate the code to get a function
|
|
75
|
+
let fn = await codeFn.call(parent);
|
|
76
|
+
|
|
77
|
+
// Call the function with the rest of the args
|
|
78
|
+
if (isUnpackable(fn)) {
|
|
79
|
+
fn = await fn.unpack();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return fn.call(null, ...args);
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Define the !ori tag for YAML parsing. This will run in the context of the
|
|
88
|
+
// supplied parent.
|
|
89
|
+
async function oriTagForParent(parent, options) {
|
|
90
|
+
const globals = await projectGlobals();
|
|
91
|
+
return {
|
|
92
|
+
identify: expressionFunction.isExpressionFunction,
|
|
93
|
+
|
|
94
|
+
resolve(text) {
|
|
95
|
+
const source = getSource(text, options);
|
|
96
|
+
const fn = compile.expression(source, {
|
|
97
|
+
globals,
|
|
98
|
+
parent,
|
|
99
|
+
});
|
|
100
|
+
return fn.call(parent);
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
tag: "!ori",
|
|
104
|
+
};
|
|
105
|
+
}
|
package/src/runtime/errors.js
CHANGED
|
@@ -95,7 +95,9 @@ export function formatError(error) {
|
|
|
95
95
|
error.message === "A null or undefined value can't be traversed"
|
|
96
96
|
) {
|
|
97
97
|
// Provide more meaningful message for TraverseError
|
|
98
|
-
line = `TraverseError: This part of the path is null or undefined: ${
|
|
98
|
+
line = `TraverseError: This part of the path is null or undefined: ${highlightError(
|
|
99
|
+
fragment
|
|
100
|
+
)}`;
|
|
99
101
|
fragmentInMessage = true;
|
|
100
102
|
}
|
|
101
103
|
if (message) {
|
|
@@ -110,7 +112,7 @@ export function formatError(error) {
|
|
|
110
112
|
// Add location
|
|
111
113
|
if (location) {
|
|
112
114
|
if (!fragmentInMessage) {
|
|
113
|
-
message += `\nevaluating: ${fragment}`;
|
|
115
|
+
message += `\nevaluating: ${highlightError(fragment)}`;
|
|
114
116
|
}
|
|
115
117
|
message += lineInfo(location);
|
|
116
118
|
}
|
|
@@ -130,6 +132,11 @@ export async function formatScopeTypos(scope, key) {
|
|
|
130
132
|
return `Maybe you meant ${list}?`;
|
|
131
133
|
}
|
|
132
134
|
|
|
135
|
+
export function highlightError(text) {
|
|
136
|
+
// ANSI escape sequence to highlight text in red
|
|
137
|
+
return `\x1b[31m${text}\x1b[0m`;
|
|
138
|
+
}
|
|
139
|
+
|
|
133
140
|
export function maybeOrigamiSourceCode(text) {
|
|
134
141
|
return origamiSourceSignals.some((signal) => text.includes(signal));
|
|
135
142
|
}
|
package/src/runtime/evaluate.js
CHANGED
|
@@ -36,7 +36,7 @@ export default async function evaluate(code, state = {}) {
|
|
|
36
36
|
const error = ReferenceError(
|
|
37
37
|
`${codeFragment(code[0].location)} is not defined`
|
|
38
38
|
);
|
|
39
|
-
/** @type {any} */ (error).location = code.location;
|
|
39
|
+
/** @type {any} */ (error).location = code[0].location;
|
|
40
40
|
throw error;
|
|
41
41
|
}
|
|
42
42
|
|
|
@@ -41,11 +41,6 @@ export default async function handleExtension(value, key, parent) {
|
|
|
41
41
|
handler = await handler.unpack();
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
if (hasSlash && handler.unpack) {
|
|
45
|
-
// Key like `data.json/` ends in slash -- unpack immediately
|
|
46
|
-
return handler.unpack(value, { key, parent });
|
|
47
|
-
}
|
|
48
|
-
|
|
49
44
|
// If the value is a primitive, box it so we can attach data to it.
|
|
50
45
|
value = box(value);
|
|
51
46
|
|
|
@@ -233,8 +233,8 @@ describe("Origami parser", () => {
|
|
|
233
233
|
|
|
234
234
|
test("with paths", () => {
|
|
235
235
|
assertParse("callExpression", "tree/", [
|
|
236
|
-
|
|
237
|
-
[markers.
|
|
236
|
+
markers.traverse,
|
|
237
|
+
[markers.reference, "tree/"],
|
|
238
238
|
]);
|
|
239
239
|
assertParse("callExpression", "tree/foo/bar", [
|
|
240
240
|
markers.traverse,
|
|
@@ -243,13 +243,10 @@ describe("Origami parser", () => {
|
|
|
243
243
|
[ops.literal, "bar"],
|
|
244
244
|
]);
|
|
245
245
|
assertParse("callExpression", "tree/foo/bar/", [
|
|
246
|
-
|
|
247
|
-
[
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
[ops.literal, "foo/"],
|
|
251
|
-
[ops.literal, "bar/"],
|
|
252
|
-
],
|
|
246
|
+
markers.traverse,
|
|
247
|
+
[markers.reference, "tree/"],
|
|
248
|
+
[ops.literal, "foo/"],
|
|
249
|
+
[ops.literal, "bar/"],
|
|
253
250
|
]);
|
|
254
251
|
// Consecutive slahes in a path are removed
|
|
255
252
|
assertParse("callExpression", "tree//key", [
|
|
@@ -1032,7 +1029,7 @@ Body`,
|
|
|
1032
1029
|
]);
|
|
1033
1030
|
assertParse("objectEntry", "folder/", [
|
|
1034
1031
|
"folder/",
|
|
1035
|
-
[
|
|
1032
|
+
[markers.traverse, [markers.reference, "folder/"]],
|
|
1036
1033
|
]);
|
|
1037
1034
|
assertParse("objectEntry", "path/to/file.txt", [
|
|
1038
1035
|
"file.txt",
|
|
@@ -1202,8 +1199,8 @@ Body`,
|
|
|
1202
1199
|
[markers.reference, "tree"],
|
|
1203
1200
|
]);
|
|
1204
1201
|
assertParse("pathLiteral", "tree/", [
|
|
1205
|
-
|
|
1206
|
-
[markers.
|
|
1202
|
+
markers.traverse,
|
|
1203
|
+
[markers.reference, "tree/"],
|
|
1207
1204
|
]);
|
|
1208
1205
|
assertParse("pathLiteral", "month/12", [
|
|
1209
1206
|
markers.traverse,
|
|
@@ -1211,13 +1208,10 @@ Body`,
|
|
|
1211
1208
|
[ops.literal, "12"],
|
|
1212
1209
|
]);
|
|
1213
1210
|
assertParse("pathLiteral", "a/b/c/", [
|
|
1214
|
-
|
|
1215
|
-
[
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
[ops.literal, "b/"],
|
|
1219
|
-
[ops.literal, "c/"],
|
|
1220
|
-
],
|
|
1211
|
+
markers.traverse,
|
|
1212
|
+
[markers.reference, "a/"],
|
|
1213
|
+
[ops.literal, "b/"],
|
|
1214
|
+
[ops.literal, "c/"],
|
|
1221
1215
|
]);
|
|
1222
1216
|
assertParse("pathLiteral", "~/.cshrc", [
|
|
1223
1217
|
markers.traverse,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Tree } from "@weborigami/async-tree";
|
|
1
2
|
import assert from "node:assert";
|
|
2
3
|
import { describe, test } from "node:test";
|
|
3
4
|
import yaml_handler from "../../src/handlers/yaml_handler.js";
|
|
@@ -8,10 +9,39 @@ describe(".yaml handler", () => {
|
|
|
8
9
|
a: 1
|
|
9
10
|
b: 2
|
|
10
11
|
`;
|
|
11
|
-
const data = yaml_handler.unpack(text);
|
|
12
|
+
const data = await yaml_handler.unpack(text);
|
|
12
13
|
assert.deepEqual(data, {
|
|
13
14
|
a: 1,
|
|
14
15
|
b: 2,
|
|
15
16
|
});
|
|
16
17
|
});
|
|
18
|
+
|
|
19
|
+
test("defines !ori tag for Origami expressions", async () => {
|
|
20
|
+
const text = `
|
|
21
|
+
message: Hello
|
|
22
|
+
answer: !ori 1 + 1
|
|
23
|
+
`;
|
|
24
|
+
const data = await yaml_handler.unpack(text);
|
|
25
|
+
const plain = await Tree.plain(data);
|
|
26
|
+
assert.deepEqual(plain, {
|
|
27
|
+
message: "Hello",
|
|
28
|
+
answer: 2,
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test("defines !ori.call tag for Origami function invocation", async () => {
|
|
33
|
+
const text = `
|
|
34
|
+
message: Hello
|
|
35
|
+
answer: !ori.call
|
|
36
|
+
- (a, b) => a + b
|
|
37
|
+
- 2
|
|
38
|
+
- 3
|
|
39
|
+
`;
|
|
40
|
+
const data = await yaml_handler.unpack(text);
|
|
41
|
+
const plain = await Tree.plain(data);
|
|
42
|
+
assert.deepEqual(plain, {
|
|
43
|
+
message: "Hello",
|
|
44
|
+
answer: 5,
|
|
45
|
+
});
|
|
46
|
+
});
|
|
17
47
|
});
|
|
@@ -15,13 +15,6 @@ describe("handleExtension", () => {
|
|
|
15
15
|
const data = await withHandler.unpack();
|
|
16
16
|
assert.deepEqual(data, { bar: 2 });
|
|
17
17
|
});
|
|
18
|
-
|
|
19
|
-
test("immediately unpacks if key ends in slash", async () => {
|
|
20
|
-
const fixture = createFixture();
|
|
21
|
-
const jsonFile = await fixture.get("bar.json");
|
|
22
|
-
const data = await handleExtension(jsonFile, "bar.json/", fixture);
|
|
23
|
-
assert.deepEqual(data, { bar: 2 });
|
|
24
|
-
});
|
|
25
18
|
});
|
|
26
19
|
|
|
27
20
|
function createFixture() {
|
package/test/runtime/ops.test.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ObjectMap, Tree } from "@weborigami/async-tree";
|
|
2
2
|
import assert from "node:assert";
|
|
3
3
|
import { describe, test } from "node:test";
|
|
4
4
|
|
|
@@ -156,11 +156,14 @@ describe("ops", () => {
|
|
|
156
156
|
});
|
|
157
157
|
|
|
158
158
|
test("ops.inherited walks up the object parent chain", async () => {
|
|
159
|
-
const tree = new
|
|
160
|
-
|
|
161
|
-
|
|
159
|
+
const tree = new ObjectMap(
|
|
160
|
+
{
|
|
161
|
+
a: {
|
|
162
|
+
b: {},
|
|
163
|
+
},
|
|
162
164
|
},
|
|
163
|
-
|
|
165
|
+
{ deep: true }
|
|
166
|
+
);
|
|
164
167
|
const b = await Tree.traverse(tree, "a", "b");
|
|
165
168
|
assert.equal(await ops.inherited(2, { object: b }), tree);
|
|
166
169
|
});
|
|
@@ -368,12 +371,15 @@ describe("ops", () => {
|
|
|
368
371
|
|
|
369
372
|
describe("ops.scope", () => {
|
|
370
373
|
test("returns the scope of the given tree", async () => {
|
|
371
|
-
const tree = new
|
|
372
|
-
|
|
373
|
-
|
|
374
|
+
const tree = new ObjectMap(
|
|
375
|
+
{
|
|
376
|
+
a: {
|
|
377
|
+
b: {},
|
|
378
|
+
},
|
|
379
|
+
c: 1,
|
|
374
380
|
},
|
|
375
|
-
|
|
376
|
-
|
|
381
|
+
{ deep: true }
|
|
382
|
+
);
|
|
377
383
|
const a = await tree.get("a");
|
|
378
384
|
const b = await a.get("b");
|
|
379
385
|
const scope = await ops.scope(b);
|