@weborigami/language 0.5.5 → 0.5.7
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/index.ts +16 -6
- package/main.js +9 -4
- package/package.json +4 -3
- package/src/compiler/compile.js +10 -4
- package/src/compiler/optimize.js +115 -97
- package/src/compiler/origami.pegjs +1 -4
- package/src/compiler/parse.js +568 -588
- package/src/compiler/parserHelpers.js +2 -2
- package/src/handlers/css_handler.js +7 -0
- package/src/handlers/csv_handler.js +129 -0
- package/src/handlers/handlers.js +33 -0
- package/src/handlers/htm_handler.js +2 -0
- package/src/handlers/html_handler.js +7 -0
- package/src/handlers/jpeg_handler.js +62 -0
- package/src/handlers/jpg_handler.js +2 -0
- package/src/handlers/js_handler.js +51 -0
- package/src/handlers/json_handler.js +26 -0
- package/src/handlers/md_handler.js +7 -0
- package/src/handlers/mjs_handler.js +2 -0
- package/src/handlers/ori_handler.js +52 -0
- package/src/handlers/oridocument_handler.js +77 -0
- package/src/handlers/parseFrontMatter.js +16 -0
- package/src/handlers/ts_handler.js +1 -0
- package/src/handlers/txt_handler.js +108 -0
- package/src/handlers/wasm_handler.js +15 -0
- package/src/handlers/xhtml_handler.js +2 -0
- package/src/handlers/yaml_handler.js +33 -0
- package/src/handlers/yml_handler.js +2 -0
- package/src/project/builtins.js +5 -0
- package/src/project/coreGlobals.js +17 -0
- package/src/{runtime → project}/jsGlobals.js +3 -1
- package/src/project/projectConfig.js +36 -0
- package/src/project/projectGlobals.js +19 -0
- package/src/project/projectRoot.js +59 -0
- package/src/protocols/constructHref.js +20 -0
- package/src/protocols/constructSiteTree.js +26 -0
- package/src/protocols/explore.js +14 -0
- package/src/protocols/fetchAndHandleExtension.js +25 -0
- package/src/protocols/files.js +26 -0
- package/src/protocols/http.js +15 -0
- package/src/protocols/https.js +15 -0
- package/src/protocols/httpstree.js +14 -0
- package/src/protocols/httptree.js +14 -0
- package/src/protocols/node.js +13 -0
- package/src/protocols/package.js +67 -0
- package/src/protocols/protocolGlobals.js +12 -0
- package/src/protocols/protocols.js +8 -0
- package/src/runtime/EventTargetMixin.js +1 -1
- package/src/runtime/HandleExtensionsTransform.js +3 -12
- package/src/runtime/ImportModulesMixin.js +4 -10
- package/src/runtime/InvokeFunctionsTransform.js +1 -1
- package/src/runtime/evaluate.js +15 -8
- package/src/runtime/expressionFunction.js +5 -7
- package/src/runtime/expressionObject.js +10 -20
- package/src/runtime/functionResultsMap.js +1 -3
- package/src/runtime/{handlers.js → handleExtension.js} +13 -11
- package/src/runtime/mergeTrees.js +1 -8
- package/src/runtime/ops.js +83 -90
- package/test/compiler/compile.test.js +20 -19
- package/test/compiler/optimize.test.js +60 -25
- package/test/compiler/parse.test.js +4 -4
- package/test/generator/oriEval.js +4 -5
- package/test/handlers/csv.handler.test.js +36 -0
- package/test/handlers/fixtures/add.wasm +0 -0
- package/test/handlers/fixtures/exif.jpeg +0 -0
- package/test/handlers/fixtures/frontMatter.md +5 -0
- package/test/handlers/fixtures/list.js +4 -0
- package/test/handlers/fixtures/multiple.js +4 -0
- package/test/handlers/fixtures/obj.js +3 -0
- package/test/handlers/fixtures/site.ori +5 -0
- package/test/handlers/fixtures/string.js +1 -0
- package/test/handlers/fixtures/tag.yaml +5 -0
- package/test/handlers/fixtures/test.ori +9 -0
- package/test/handlers/jpeg.handler.test.js +18 -0
- package/test/handlers/js.handler.test.js +46 -0
- package/test/handlers/json.handler.test.js +14 -0
- package/test/handlers/ori.handler.test.js +87 -0
- package/test/handlers/oridocument.handler.test.js +68 -0
- package/test/handlers/txt.handler.test.js +41 -0
- package/test/handlers/wasm.handler.test.js +20 -0
- package/test/handlers/yaml.handler.test.js +17 -0
- package/test/project/fixtures/withConfig/config.ori +4 -0
- package/test/project/fixtures/withConfig/subfolder/greet.js +1 -0
- package/test/project/fixtures/withPackageJson/package.json +0 -0
- package/test/project/jsGlobals.test.js +21 -0
- package/test/project/projectConfig.test.js +28 -0
- package/test/project/projectRoot.test.js +40 -0
- package/test/protocols/package.test.js +11 -0
- package/test/runtime/evaluate.test.js +26 -42
- package/test/runtime/expressionObject.test.js +16 -20
- package/test/runtime/{handlers.test.js → handleExtension.test.js} +4 -20
- package/test/runtime/jsGlobals.test.js +4 -6
- package/test/runtime/mergeTrees.test.js +2 -4
- package/test/runtime/ops.test.js +66 -68
- package/src/runtime/getHandlers.js +0 -10
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
2
|
+
import { describe, test } from "node:test";
|
|
3
|
+
import yaml_handler from "../../src/handlers/yaml_handler.js";
|
|
4
|
+
|
|
5
|
+
describe(".yaml handler", () => {
|
|
6
|
+
test("loads input as a YAML file", async () => {
|
|
7
|
+
const text = `
|
|
8
|
+
a: 1
|
|
9
|
+
b: 2
|
|
10
|
+
`;
|
|
11
|
+
const data = yaml_handler.unpack(text);
|
|
12
|
+
assert.deepEqual(data, {
|
|
13
|
+
a: 1,
|
|
14
|
+
b: 2,
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default (name) => `Hello, ${name}!`;
|
|
File without changes
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
2
|
+
import { describe, test } from "node:test";
|
|
3
|
+
import jsGlobals from "../../src/project/jsGlobals.js";
|
|
4
|
+
|
|
5
|
+
describe("jsGlobals", () => {
|
|
6
|
+
test("can invoke static methods", async () => {
|
|
7
|
+
const { Promise } = jsGlobals;
|
|
8
|
+
const { all } = Promise;
|
|
9
|
+
const result = (
|
|
10
|
+
await all(["fruit", "computer", "park"].map((item) => `Apple ${item}`))
|
|
11
|
+
).join(", ");
|
|
12
|
+
assert.equal(result, "Apple fruit, Apple computer, Apple park");
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test("can invoke a method on a static method", () => {
|
|
16
|
+
const { Math } = jsGlobals;
|
|
17
|
+
const a = [1, 3, 2];
|
|
18
|
+
const b = Math.max.apply(null, a);
|
|
19
|
+
assert.equal(b, 3);
|
|
20
|
+
});
|
|
21
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
2
|
+
import { describe, test } from "node:test";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import projectConfig from "../../src/project/projectConfig.js";
|
|
5
|
+
|
|
6
|
+
describe("projectConfig", () => {
|
|
7
|
+
test("finds Origami configuration file", async () => {
|
|
8
|
+
// Find the folder that represents the project root.
|
|
9
|
+
const projectUrl = new URL("fixtures/withConfig/", import.meta.url);
|
|
10
|
+
// Find subfolder inside project root.
|
|
11
|
+
const subfolderUrl = new URL("./subfolder/", projectUrl);
|
|
12
|
+
const subfolderPath = fileURLToPath(subfolderUrl);
|
|
13
|
+
|
|
14
|
+
const config = await projectConfig(subfolderPath);
|
|
15
|
+
assert.equal(config.message, "Hello");
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test("defaults to an empty object", async () => {
|
|
19
|
+
// Find the folder that represents the project root.
|
|
20
|
+
const projectUrl = new URL("fixtures/withPackageJson/", import.meta.url);
|
|
21
|
+
// Find subfolder inside project root.
|
|
22
|
+
const subfolderUrl = new URL("./subfolder/", projectUrl);
|
|
23
|
+
const subfolderPath = fileURLToPath(subfolderUrl);
|
|
24
|
+
|
|
25
|
+
const config = await projectConfig(subfolderPath);
|
|
26
|
+
assert.deepEqual(config, {});
|
|
27
|
+
});
|
|
28
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { describe, test } from "node:test";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import projectRoot from "../../src/project/projectRoot.js";
|
|
6
|
+
|
|
7
|
+
describe("projectRoot", () => {
|
|
8
|
+
test("finds Origami configuration file", async () => {
|
|
9
|
+
// Find the folder that represents the project root.
|
|
10
|
+
const projectUrl = new URL("fixtures/withConfig/", import.meta.url);
|
|
11
|
+
// Find subfolder inside project root.
|
|
12
|
+
const subfolderUrl = new URL("./subfolder/", projectUrl);
|
|
13
|
+
const subfolderPath = fileURLToPath(subfolderUrl);
|
|
14
|
+
|
|
15
|
+
const root = await projectRoot(subfolderPath);
|
|
16
|
+
|
|
17
|
+
// Get result path, it'll need a trailing slash to compare.
|
|
18
|
+
const resultPath = root.path + path.sep;
|
|
19
|
+
assert.equal(resultPath, fileURLToPath(projectUrl));
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test("finds package.json file", async () => {
|
|
23
|
+
// Find the folder that represents the project root.
|
|
24
|
+
const projectUrl = new URL("fixtures/withPackageJson/", import.meta.url);
|
|
25
|
+
// Find subfolder inside project root.
|
|
26
|
+
const subfolderUrl = new URL("./subfolder/", projectUrl);
|
|
27
|
+
const subfolderPath = fileURLToPath(subfolderUrl);
|
|
28
|
+
|
|
29
|
+
const root = await projectRoot(subfolderPath);
|
|
30
|
+
|
|
31
|
+
// Get result path, it'll need a trailing slash to compare.
|
|
32
|
+
const resultPath = root.path + path.sep;
|
|
33
|
+
assert.equal(resultPath, fileURLToPath(projectUrl));
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test("defaults to current working directory", async () => {
|
|
37
|
+
const root = await projectRoot("/");
|
|
38
|
+
assert.equal(root.path, process.cwd());
|
|
39
|
+
});
|
|
40
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
2
|
+
import { describe, test } from "node:test";
|
|
3
|
+
import packageProtocol from "../../src/protocols/package.js";
|
|
4
|
+
|
|
5
|
+
describe("package: protocol", () => {
|
|
6
|
+
test("returns a package's main export(s)", async () => {
|
|
7
|
+
const result = await packageProtocol("@weborigami", "async-tree");
|
|
8
|
+
const { toString } = result;
|
|
9
|
+
assert.equal(toString(123), "123");
|
|
10
|
+
});
|
|
11
|
+
});
|
|
@@ -1,57 +1,41 @@
|
|
|
1
|
-
import { ObjectTree } from "@weborigami/async-tree";
|
|
2
1
|
import assert from "node:assert";
|
|
3
2
|
import { describe, test } from "node:test";
|
|
4
|
-
import * as ops from "../../src/runtime/ops.js";
|
|
5
3
|
|
|
4
|
+
import { ObjectTree } from "@weborigami/async-tree";
|
|
6
5
|
import evaluate from "../../src/runtime/evaluate.js";
|
|
7
6
|
import { createCode } from "../compiler/codeHelpers.js";
|
|
8
7
|
|
|
9
8
|
describe("evaluate", () => {
|
|
10
|
-
test("can retrieve values from scope", async () => {
|
|
11
|
-
const code = createCode([[ops.scope], "message"]);
|
|
12
|
-
const parent = new ObjectTree({
|
|
13
|
-
message: "Hello",
|
|
14
|
-
});
|
|
15
|
-
const tree = new ObjectTree({});
|
|
16
|
-
tree.parent = parent;
|
|
17
|
-
const result = await evaluate.call(tree, code);
|
|
18
|
-
assert.equal(result, "Hello");
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
test("can invoke functions in scope", async () => {
|
|
22
|
-
// Match the array representation of code generated by the parser.
|
|
23
|
-
const code = createCode([
|
|
24
|
-
[[ops.scope], "greet"],
|
|
25
|
-
[[ops.scope], "name"],
|
|
26
|
-
]);
|
|
27
|
-
|
|
28
|
-
const tree = new ObjectTree({
|
|
29
|
-
async greet(name) {
|
|
30
|
-
return `Hello ${name}`;
|
|
31
|
-
},
|
|
32
|
-
name: "world",
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
const result = await evaluate.call(tree, code);
|
|
36
|
-
assert.equal(result, "Hello world");
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
test("passes context to invoked functions", async () => {
|
|
40
|
-
const code = createCode([[ops.scope], "fn"]);
|
|
41
|
-
const tree = new ObjectTree({
|
|
42
|
-
async fn() {
|
|
43
|
-
assert.equal(this, tree);
|
|
44
|
-
},
|
|
45
|
-
});
|
|
46
|
-
await evaluate.call(tree, code);
|
|
47
|
-
});
|
|
48
|
-
|
|
49
9
|
test("if object in function position isn't a function, can unpack it", async () => {
|
|
50
10
|
const fn = (...args) => args.join(",");
|
|
51
11
|
const packed = new String();
|
|
52
12
|
/** @type {any} */ (packed).unpack = async () => fn;
|
|
53
13
|
const code = createCode([packed, "a", "b", "c"]);
|
|
54
|
-
const result = await evaluate
|
|
14
|
+
const result = await evaluate(code);
|
|
55
15
|
assert.equal(result, "a,b,c");
|
|
56
16
|
});
|
|
17
|
+
|
|
18
|
+
test("if function has needsState, it gets the state", async () => {
|
|
19
|
+
const fn = (state) => {
|
|
20
|
+
return state;
|
|
21
|
+
};
|
|
22
|
+
fn.needsState = true;
|
|
23
|
+
const state = {};
|
|
24
|
+
const code = createCode([fn]);
|
|
25
|
+
const result = await evaluate(code, state);
|
|
26
|
+
assert.equal(result, state);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test("if function has containerAsTarget, it gets bound to state.container", async () => {
|
|
30
|
+
/** @this {import("@weborigami/types").AsyncTree} */
|
|
31
|
+
const fn = function () {
|
|
32
|
+
return this;
|
|
33
|
+
};
|
|
34
|
+
fn.containerAsTarget = true;
|
|
35
|
+
const container = new ObjectTree({});
|
|
36
|
+
const state = { container };
|
|
37
|
+
const code = createCode([fn]);
|
|
38
|
+
const result = await evaluate(code, state);
|
|
39
|
+
assert.equal(result, container);
|
|
40
|
+
});
|
|
57
41
|
});
|
|
@@ -7,59 +7,55 @@ import { ops } from "../../src/runtime/internal.js";
|
|
|
7
7
|
|
|
8
8
|
describe("expressionObject", () => {
|
|
9
9
|
test("can instantiate an object", async () => {
|
|
10
|
-
const
|
|
10
|
+
const container = new ObjectTree({
|
|
11
11
|
upper: (s) => s.toUpperCase(),
|
|
12
12
|
});
|
|
13
13
|
|
|
14
14
|
const entries = [
|
|
15
|
-
["hello", [[[ops.scope], "upper"], "hello"]],
|
|
16
|
-
["world", [[[ops.scope], "upper"], "world"]],
|
|
15
|
+
["hello", [[[ops.scope, container], "upper"], "hello"]],
|
|
16
|
+
["world", [[[ops.scope, container], "upper"], "world"]],
|
|
17
17
|
];
|
|
18
|
+
const context = new ObjectTree({});
|
|
18
19
|
|
|
19
|
-
const object = await expressionObject(entries,
|
|
20
|
+
const object = await expressionObject(entries, { object: context });
|
|
20
21
|
assert.equal(await object.hello, "HELLO");
|
|
21
22
|
assert.equal(await object.world, "WORLD");
|
|
22
|
-
assert.equal(object[symbols.parent],
|
|
23
|
+
assert.equal(object[symbols.parent], context);
|
|
23
24
|
});
|
|
24
25
|
|
|
25
26
|
test("can define a property getter", async () => {
|
|
26
27
|
let count = 0;
|
|
27
28
|
const increment = () => count++;
|
|
28
29
|
const entries = [["count", [ops.getter, [increment]]]];
|
|
29
|
-
const object = await expressionObject(entries
|
|
30
|
+
const object = await expressionObject(entries);
|
|
30
31
|
assert.equal(await object.count, 0);
|
|
31
32
|
assert.equal(await object.count, 1);
|
|
32
33
|
});
|
|
33
34
|
|
|
34
35
|
test("treats a getter for a primitive value as a regular property", async () => {
|
|
35
36
|
const entries = [["name", [ops.getter, "world"]]];
|
|
36
|
-
const object = await expressionObject(entries
|
|
37
|
+
const object = await expressionObject(entries);
|
|
37
38
|
assert.equal(object.name, "world");
|
|
38
39
|
});
|
|
39
40
|
|
|
40
41
|
test("can instantiate an Origami tree", async () => {
|
|
41
42
|
const entries = [
|
|
42
43
|
["name", "world"],
|
|
43
|
-
["message", [ops.concat, "Hello, ", [[ops.
|
|
44
|
+
["message", [ops.concat, "Hello, ", [[ops.inherited, 0], "name"], "!"]],
|
|
44
45
|
];
|
|
45
|
-
const
|
|
46
|
-
const object = await expressionObject(entries,
|
|
46
|
+
const context = new ObjectTree({});
|
|
47
|
+
const object = await expressionObject(entries, { object: context });
|
|
47
48
|
assert.deepEqual(await Tree.plain(object), {
|
|
48
49
|
name: "world",
|
|
49
50
|
message: "Hello, world!",
|
|
50
51
|
});
|
|
51
|
-
assert.equal(object[symbols.parent],
|
|
52
|
+
assert.equal(object[symbols.parent], context);
|
|
52
53
|
});
|
|
53
54
|
|
|
54
55
|
test("returned object values can be unpacked", async () => {
|
|
55
56
|
const entries = [["data.json", `{ "a": 1 }`]];
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
"json.handler": {
|
|
59
|
-
unpack: JSON.parse,
|
|
60
|
-
},
|
|
61
|
-
};
|
|
62
|
-
const result = await expressionObject(entries, parent);
|
|
57
|
+
const context = new ObjectTree({});
|
|
58
|
+
const result = await expressionObject(entries, { object: context });
|
|
63
59
|
const dataJson = await result["data.json"];
|
|
64
60
|
const json = await dataJson.unpack();
|
|
65
61
|
assert.deepEqual(json, { a: 1 });
|
|
@@ -70,7 +66,7 @@ describe("expressionObject", () => {
|
|
|
70
66
|
["(hidden)", "shh"],
|
|
71
67
|
["visible", "hey"],
|
|
72
68
|
];
|
|
73
|
-
const object = await expressionObject(entries
|
|
69
|
+
const object = await expressionObject(entries);
|
|
74
70
|
assert.deepEqual(Object.keys(object), ["visible"]);
|
|
75
71
|
assert.equal(object["hidden"], "shh");
|
|
76
72
|
});
|
|
@@ -84,7 +80,7 @@ describe("expressionObject", () => {
|
|
|
84
80
|
// Immediate treelike value, should have a slash
|
|
85
81
|
["object", [ops.object, ["b", [ops.literal, 2]]]],
|
|
86
82
|
];
|
|
87
|
-
const object = await expressionObject(entries
|
|
83
|
+
const object = await expressionObject(entries);
|
|
88
84
|
assert.deepEqual(object[symbols.keys](), [
|
|
89
85
|
"getter/",
|
|
90
86
|
"hasSlash/",
|
|
@@ -1,27 +1,16 @@
|
|
|
1
1
|
import { ObjectTree } from "@weborigami/async-tree";
|
|
2
2
|
import assert from "node:assert";
|
|
3
3
|
import { describe, test } from "node:test";
|
|
4
|
-
import
|
|
4
|
+
import handleExtension from "../../src/runtime/handleExtension.js";
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
"json.handler": {
|
|
8
|
-
unpack: (buffer) => JSON.parse(String(buffer)),
|
|
9
|
-
},
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
describe("handlers", () => {
|
|
6
|
+
describe("handleExtension", () => {
|
|
13
7
|
test("attaches an unpack method to a value with an extension", async () => {
|
|
14
8
|
const fixture = createFixture();
|
|
15
9
|
const numberValue = await fixture.get("foo");
|
|
16
10
|
assert(typeof numberValue === "number");
|
|
17
11
|
assert.equal(numberValue, 1);
|
|
18
12
|
const jsonFile = await fixture.get("bar.json");
|
|
19
|
-
const withHandler = await handleExtension(
|
|
20
|
-
fixture,
|
|
21
|
-
jsonFile,
|
|
22
|
-
"bar.json",
|
|
23
|
-
handlers
|
|
24
|
-
);
|
|
13
|
+
const withHandler = await handleExtension(jsonFile, "bar.json", fixture);
|
|
25
14
|
assert.equal(String(withHandler), `{ "bar": 2 }`);
|
|
26
15
|
const data = await withHandler.unpack();
|
|
27
16
|
assert.deepEqual(data, { bar: 2 });
|
|
@@ -30,12 +19,7 @@ describe("handlers", () => {
|
|
|
30
19
|
test("immediately unpacks if key ends in slash", async () => {
|
|
31
20
|
const fixture = createFixture();
|
|
32
21
|
const jsonFile = await fixture.get("bar.json");
|
|
33
|
-
const data = await handleExtension(
|
|
34
|
-
fixture,
|
|
35
|
-
jsonFile,
|
|
36
|
-
"bar.json/",
|
|
37
|
-
handlers
|
|
38
|
-
);
|
|
22
|
+
const data = await handleExtension(jsonFile, "bar.json/", fixture);
|
|
39
23
|
assert.deepEqual(data, { bar: 2 });
|
|
40
24
|
});
|
|
41
25
|
});
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import { describe, test } from "node:test";
|
|
3
|
-
import jsGlobals from "../../src/
|
|
3
|
+
import jsGlobals from "../../src/project/jsGlobals.js";
|
|
4
4
|
|
|
5
5
|
describe("jsGlobals", () => {
|
|
6
|
-
test("wraps static methods to
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const promise = fixture.resolve.call(target, "hi");
|
|
10
|
-
const value = await promise;
|
|
6
|
+
test("wraps static methods to bind them to defining object", async () => {
|
|
7
|
+
const all = jsGlobals.Promise.all;
|
|
8
|
+
const value = await all([Promise.resolve("hi")]);
|
|
11
9
|
assert.equal(value, "hi");
|
|
12
10
|
});
|
|
13
11
|
|
|
@@ -7,8 +7,7 @@ describe("mergeTrees", () => {
|
|
|
7
7
|
test("if all arguments are plain objects, result is a plain object", async () => {
|
|
8
8
|
let calledFoo = false;
|
|
9
9
|
let calledBar = false;
|
|
10
|
-
const result = await mergeTrees
|
|
11
|
-
null,
|
|
10
|
+
const result = await mergeTrees(
|
|
12
11
|
{
|
|
13
12
|
a: 1,
|
|
14
13
|
b: 2,
|
|
@@ -42,8 +41,7 @@ describe("mergeTrees", () => {
|
|
|
42
41
|
});
|
|
43
42
|
|
|
44
43
|
test("merges heterogenous arguments as trees", async () => {
|
|
45
|
-
const tree = await mergeTrees
|
|
46
|
-
null,
|
|
44
|
+
const tree = await mergeTrees(
|
|
47
45
|
new ObjectTree({
|
|
48
46
|
a: 1,
|
|
49
47
|
b: 2,
|
package/test/runtime/ops.test.js
CHANGED
|
@@ -21,7 +21,7 @@ describe("ops", () => {
|
|
|
21
21
|
|
|
22
22
|
test("ops.array creates an array", async () => {
|
|
23
23
|
const code = createCode([ops.array, 1, 2, 3]);
|
|
24
|
-
const result = await evaluate
|
|
24
|
+
const result = await evaluate(code);
|
|
25
25
|
assert.deepEqual(result, [1, 2, 3]);
|
|
26
26
|
});
|
|
27
27
|
|
|
@@ -44,23 +44,22 @@ describe("ops", () => {
|
|
|
44
44
|
|
|
45
45
|
test("ops.comma returns the last value", async () => {
|
|
46
46
|
const code = createCode([ops.comma, 1, 2, 3]);
|
|
47
|
-
const result = await evaluate
|
|
47
|
+
const result = await evaluate(code);
|
|
48
48
|
assert.strictEqual(result, 3);
|
|
49
49
|
});
|
|
50
50
|
|
|
51
51
|
test("ops.concat concatenates tree value text", async () => {
|
|
52
|
-
const
|
|
52
|
+
const container = {
|
|
53
53
|
name: "world",
|
|
54
|
-
}
|
|
55
|
-
|
|
54
|
+
};
|
|
56
55
|
const code = createCode([
|
|
57
56
|
ops.concat,
|
|
58
57
|
"Hello, ",
|
|
59
|
-
[[ops.scope], "name"],
|
|
58
|
+
[[ops.scope, container], "name"],
|
|
60
59
|
".",
|
|
61
60
|
]);
|
|
62
61
|
|
|
63
|
-
const result = await evaluate
|
|
62
|
+
const result = await evaluate(code);
|
|
64
63
|
assert.strictEqual(result, "Hello, world.");
|
|
65
64
|
});
|
|
66
65
|
|
|
@@ -78,20 +77,6 @@ describe("ops", () => {
|
|
|
78
77
|
assert.equal(await ops.construct(String, "hello"), "hello");
|
|
79
78
|
});
|
|
80
79
|
|
|
81
|
-
test("ops.context", async () => {
|
|
82
|
-
const tree = new DeepObjectTree({
|
|
83
|
-
a: {
|
|
84
|
-
b: {
|
|
85
|
-
c: {},
|
|
86
|
-
},
|
|
87
|
-
},
|
|
88
|
-
});
|
|
89
|
-
const b = await Tree.traverse(tree, "a", "b");
|
|
90
|
-
const c = await b.get("c");
|
|
91
|
-
assert.equal(ops.context.call(c), c);
|
|
92
|
-
assert.equal(ops.context.call(c, 1), b);
|
|
93
|
-
});
|
|
94
|
-
|
|
95
80
|
test("ops.division divides two numbers", async () => {
|
|
96
81
|
assert.strictEqual(ops.division(12, 2), 6);
|
|
97
82
|
assert.strictEqual(ops.division(3, 2), 1.5);
|
|
@@ -114,23 +99,27 @@ describe("ops", () => {
|
|
|
114
99
|
|
|
115
100
|
test("ops.cache evaluates code and cache its result", async () => {
|
|
116
101
|
let count = 0;
|
|
117
|
-
const
|
|
102
|
+
const container = {
|
|
118
103
|
group: {
|
|
119
104
|
get count() {
|
|
120
105
|
// Use promise to test async behavior
|
|
121
106
|
return Promise.resolve(++count);
|
|
122
107
|
},
|
|
123
108
|
},
|
|
124
|
-
}
|
|
109
|
+
};
|
|
125
110
|
const code = createCode([
|
|
126
111
|
ops.cache,
|
|
127
112
|
{},
|
|
128
113
|
"group/count",
|
|
129
|
-
[
|
|
114
|
+
[
|
|
115
|
+
[ops.scope, container],
|
|
116
|
+
[ops.literal, "group"],
|
|
117
|
+
[ops.literal, "count"],
|
|
118
|
+
],
|
|
130
119
|
]);
|
|
131
|
-
const result = await evaluate
|
|
120
|
+
const result = await evaluate(code);
|
|
132
121
|
assert.strictEqual(result, 1);
|
|
133
|
-
const result2 = await evaluate
|
|
122
|
+
const result2 = await evaluate(code);
|
|
134
123
|
assert.strictEqual(result2, 1);
|
|
135
124
|
});
|
|
136
125
|
|
|
@@ -166,22 +155,36 @@ describe("ops", () => {
|
|
|
166
155
|
assert(ops.greaterThanOrEqual("ab", "aa"));
|
|
167
156
|
});
|
|
168
157
|
|
|
158
|
+
test("ops.inherited walks up the object parent chain", async () => {
|
|
159
|
+
const tree = new DeepObjectTree({
|
|
160
|
+
a: {
|
|
161
|
+
b: {},
|
|
162
|
+
},
|
|
163
|
+
});
|
|
164
|
+
const b = await Tree.traverse(tree, "a", "b");
|
|
165
|
+
assert.equal(await ops.inherited(2, { object: b }), tree);
|
|
166
|
+
});
|
|
167
|
+
|
|
169
168
|
test("ops.lambda defines a function with no inputs", async () => {
|
|
170
169
|
const code = createCode([ops.lambda, [], [ops.literal, "result"]]);
|
|
171
|
-
const fn = await evaluate
|
|
172
|
-
const result = await fn
|
|
170
|
+
const fn = await evaluate(code);
|
|
171
|
+
const result = await fn();
|
|
173
172
|
assert.strictEqual(result, "result");
|
|
174
173
|
});
|
|
175
174
|
|
|
176
175
|
test("ops.lambda defines a function with underscore input", async () => {
|
|
177
|
-
const
|
|
176
|
+
const container = {
|
|
178
177
|
message: "Hello",
|
|
179
|
-
}
|
|
178
|
+
};
|
|
180
179
|
|
|
181
|
-
const code = createCode([
|
|
180
|
+
const code = createCode([
|
|
181
|
+
ops.lambda,
|
|
182
|
+
["_"],
|
|
183
|
+
[[ops.scope, container], "message"],
|
|
184
|
+
]);
|
|
182
185
|
|
|
183
|
-
const fn = await evaluate
|
|
184
|
-
const result = await fn
|
|
186
|
+
const fn = await evaluate(code);
|
|
187
|
+
const result = await fn();
|
|
185
188
|
assert.strictEqual(result, "Hello");
|
|
186
189
|
});
|
|
187
190
|
|
|
@@ -192,9 +195,9 @@ describe("ops", () => {
|
|
|
192
195
|
[ops.literal, "a"],
|
|
193
196
|
[ops.literal, "b"],
|
|
194
197
|
],
|
|
195
|
-
[ops.concat, [[ops.
|
|
198
|
+
[ops.concat, [[ops.params, 0], "b"], [[ops.params, 0], "a"]],
|
|
196
199
|
]);
|
|
197
|
-
const fn = await evaluate
|
|
200
|
+
const fn = await evaluate(code);
|
|
198
201
|
const result = await fn("x", "y");
|
|
199
202
|
assert.strictEqual(result, "yx");
|
|
200
203
|
});
|
|
@@ -249,27 +252,27 @@ describe("ops", () => {
|
|
|
249
252
|
// ...more
|
|
250
253
|
// c: a
|
|
251
254
|
// }
|
|
252
|
-
const
|
|
255
|
+
const container = {
|
|
253
256
|
more: { b: 2 },
|
|
254
|
-
}
|
|
257
|
+
};
|
|
255
258
|
const code = createCode([
|
|
256
259
|
[
|
|
257
260
|
ops.object,
|
|
258
261
|
["a", [ops.literal, 1]],
|
|
259
|
-
["c", [[ops.
|
|
262
|
+
["c", [[ops.inherited, 0], "a"]],
|
|
260
263
|
[
|
|
261
264
|
"_result",
|
|
262
265
|
[
|
|
263
266
|
ops.merge,
|
|
264
|
-
[ops.object, ["a", [ops.getter, [[ops.
|
|
265
|
-
[[ops.scope], "more"],
|
|
266
|
-
[ops.object, ["c", [ops.getter, [[ops.
|
|
267
|
+
[ops.object, ["a", [ops.getter, [[ops.inherited, 1], "a"]]]],
|
|
268
|
+
[[ops.scope, container], "more"],
|
|
269
|
+
[ops.object, ["c", [ops.getter, [[ops.inherited, 1], "c"]]]],
|
|
267
270
|
],
|
|
268
271
|
],
|
|
269
272
|
],
|
|
270
273
|
"_result",
|
|
271
274
|
]);
|
|
272
|
-
const result = await evaluate
|
|
275
|
+
const result = await evaluate(code);
|
|
273
276
|
assert.deepEqual(await Tree.plain(result), { a: 1, b: 2, c: 1 });
|
|
274
277
|
});
|
|
275
278
|
|
|
@@ -311,35 +314,44 @@ describe("ops", () => {
|
|
|
311
314
|
});
|
|
312
315
|
|
|
313
316
|
test("ops.object instantiates an object", async () => {
|
|
314
|
-
const
|
|
317
|
+
const container = {
|
|
315
318
|
upper: (s) => s.toUpperCase(),
|
|
316
|
-
}
|
|
319
|
+
};
|
|
317
320
|
|
|
318
321
|
const code = createCode([
|
|
319
322
|
ops.object,
|
|
320
|
-
["hello", [[[ops.scope], "upper"], "hello"]],
|
|
321
|
-
["world", [[[ops.scope], "upper"], "world"]],
|
|
323
|
+
["hello", [[[ops.scope, container], "upper"], "hello"]],
|
|
324
|
+
["world", [[[ops.scope, container], "upper"], "world"]],
|
|
322
325
|
]);
|
|
323
326
|
|
|
324
|
-
const result = await evaluate
|
|
327
|
+
const result = await evaluate(code);
|
|
325
328
|
assert.strictEqual(result.hello, "HELLO");
|
|
326
329
|
assert.strictEqual(result.world, "WORLD");
|
|
327
330
|
});
|
|
328
331
|
|
|
329
332
|
test("ops.object instantiates an array", async () => {
|
|
330
|
-
const
|
|
333
|
+
const container = {
|
|
331
334
|
upper: (s) => s.toUpperCase(),
|
|
332
|
-
}
|
|
335
|
+
};
|
|
333
336
|
const code = createCode([
|
|
334
337
|
ops.array,
|
|
335
338
|
"Hello",
|
|
336
339
|
1,
|
|
337
|
-
[[[ops.scope], "upper"], "world"],
|
|
340
|
+
[[[ops.scope, container], "upper"], "world"],
|
|
338
341
|
]);
|
|
339
|
-
const result = await evaluate
|
|
342
|
+
const result = await evaluate(code);
|
|
340
343
|
assert.deepEqual(result, ["Hello", 1, "WORLD"]);
|
|
341
344
|
});
|
|
342
345
|
|
|
346
|
+
test("ops.params returns a stack frame", async () => {
|
|
347
|
+
const code = createCode([ops.params, 1]);
|
|
348
|
+
const frame1 = { a: 1 };
|
|
349
|
+
const frame2 = { b: 2 };
|
|
350
|
+
const stack = [frame1, frame2];
|
|
351
|
+
const result = await evaluate(code, { stack });
|
|
352
|
+
assert.strictEqual(result, frame1);
|
|
353
|
+
});
|
|
354
|
+
|
|
343
355
|
test("ops.property returns a property if defined, otherwise traverses", async () => {
|
|
344
356
|
assert.equal(await ops.property({ a: 1 }, "a"), 1);
|
|
345
357
|
assert.equal(await ops.property({ a: 1 }, "b"), undefined);
|
|
@@ -355,30 +367,16 @@ describe("ops", () => {
|
|
|
355
367
|
});
|
|
356
368
|
|
|
357
369
|
describe("ops.scope", () => {
|
|
358
|
-
test("returns the scope of the
|
|
359
|
-
const tree = new DeepObjectTree({
|
|
360
|
-
a: {
|
|
361
|
-
b: {},
|
|
362
|
-
},
|
|
363
|
-
c: 1,
|
|
364
|
-
});
|
|
365
|
-
const a = await tree.get("a");
|
|
366
|
-
const b = await a.get("b");
|
|
367
|
-
const scope = await ops.scope.call(b);
|
|
368
|
-
assert.equal(await scope?.get("c"), 1);
|
|
369
|
-
});
|
|
370
|
-
|
|
371
|
-
test("accepts an optional context", async () => {
|
|
370
|
+
test("returns the scope of the given tree", async () => {
|
|
372
371
|
const tree = new DeepObjectTree({
|
|
373
372
|
a: {
|
|
374
373
|
b: {},
|
|
375
|
-
c: 0, // shouldn't get this
|
|
376
374
|
},
|
|
377
375
|
c: 1,
|
|
378
376
|
});
|
|
379
377
|
const a = await tree.get("a");
|
|
380
378
|
const b = await a.get("b");
|
|
381
|
-
const scope = await ops.scope
|
|
379
|
+
const scope = await ops.scope(b);
|
|
382
380
|
assert.equal(await scope?.get("c"), 1);
|
|
383
381
|
});
|
|
384
382
|
});
|
|
@@ -439,7 +437,7 @@ describe("ops", () => {
|
|
|
439
437
|
test("ops.unpack unpacks a value", async () => {
|
|
440
438
|
const fixture = new String("packed");
|
|
441
439
|
/** @type {any} */ (fixture).unpack = async () => "unpacked";
|
|
442
|
-
const result = await ops.unpack
|
|
440
|
+
const result = await ops.unpack(fixture);
|
|
443
441
|
assert.strictEqual(result, "unpacked");
|
|
444
442
|
});
|
|
445
443
|
|