@weborigami/language 0.6.8 → 0.6.10
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 +19 -4
- package/main.js +5 -2
- package/package.json +2 -2
- package/src/compiler/compile.js +11 -3
- package/src/compiler/origami.pegjs +4 -2
- package/src/compiler/parse.js +77 -68
- package/src/compiler/parserHelpers.js +13 -13
- package/src/handlers/getPackedPath.js +17 -0
- package/src/handlers/jpeg_handler.js +5 -0
- package/src/handlers/js_handler.js +3 -3
- package/src/handlers/json_handler.js +3 -1
- package/src/handlers/tsv_handler.js +1 -1
- package/src/handlers/yaml_handler.js +1 -1
- package/src/project/jsGlobals.js +3 -3
- package/src/protocols/package.js +3 -3
- package/src/runtime/asyncStorage.js +7 -0
- package/src/runtime/codeFragment.js +4 -3
- package/src/runtime/errors.js +82 -129
- package/src/runtime/evaluate.js +8 -77
- package/src/runtime/execute.js +82 -0
- package/src/runtime/explainReferenceError.js +248 -0
- package/src/runtime/explainTraverseError.js +77 -0
- package/src/runtime/expressionFunction.js +8 -7
- package/src/runtime/expressionObject.js +9 -6
- package/src/runtime/handleExtension.js +22 -8
- package/src/runtime/internal.js +1 -1
- package/src/runtime/interop.js +15 -0
- package/src/runtime/ops.js +24 -19
- package/src/runtime/symbols.js +0 -1
- package/src/runtime/typos.js +22 -3
- package/test/compiler/compile.test.js +7 -103
- package/test/compiler/parse.test.js +38 -31
- package/test/project/fixtures/withPackageJson/subfolder/README.md +1 -0
- package/test/runtime/errors.test.js +296 -0
- package/test/runtime/evaluate.test.js +110 -34
- package/test/runtime/execute.test.js +41 -0
- package/test/runtime/expressionObject.test.js +4 -4
- package/test/runtime/ops.test.js +36 -35
- package/test/runtime/typos.test.js +2 -0
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { Tree } from "@weborigami/async-tree";
|
|
2
1
|
import assert from "node:assert";
|
|
3
2
|
import { describe, test } from "node:test";
|
|
4
3
|
import * as compile from "../../src/compiler/compile.js";
|
|
@@ -10,89 +9,13 @@ const globals = {
|
|
|
10
9
|
name: "Alice",
|
|
11
10
|
};
|
|
12
11
|
|
|
12
|
+
/**
|
|
13
|
+
* More complex tests for the compile() function
|
|
14
|
+
*
|
|
15
|
+
* The evaluate2.test.js file contains more basic tests, covering more language
|
|
16
|
+
* features, which compare the output of evaluate2() against an expected result.
|
|
17
|
+
*/
|
|
13
18
|
describe("compile", () => {
|
|
14
|
-
test("array", async () => {
|
|
15
|
-
await assertCompile("[]", []);
|
|
16
|
-
await assertCompile("[ 1, 2, 3, ]", [1, 2, 3]);
|
|
17
|
-
await assertCompile("[\n'a'\n'b'\n'c'\n]", ["a", "b", "c"]);
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
test("functionComposition", async () => {
|
|
21
|
-
await assertCompile("greet()", "Hello, undefined!");
|
|
22
|
-
await assertCompile("greet(name)", "Hello, Alice!");
|
|
23
|
-
await assertCompile("greet 'world'", "Hello, world!", { mode: "shell" });
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
test("function call with spread", async () => {
|
|
27
|
-
await assertCompile(
|
|
28
|
-
`concat("Hello", ...[", ", name], "!")`,
|
|
29
|
-
"Hello, Alice!"
|
|
30
|
-
);
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
test("angle bracket path", async () => {
|
|
34
|
-
await assertCompile("<data>", "Bob", {
|
|
35
|
-
target: {
|
|
36
|
-
data: "Bob",
|
|
37
|
-
},
|
|
38
|
-
});
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
test("object literal", async () => {
|
|
42
|
-
await assertCompile("{ message = greet(name) }", {
|
|
43
|
-
message: "Hello, Alice!",
|
|
44
|
-
});
|
|
45
|
-
await assertCompile(
|
|
46
|
-
"{ message = greet(name) }",
|
|
47
|
-
{
|
|
48
|
-
message: "Hello, Alice!",
|
|
49
|
-
},
|
|
50
|
-
{ mode: "shell" }
|
|
51
|
-
);
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
test("object with computed property key", async () => {
|
|
55
|
-
await assertCompile("{ [name] = greet(name) }", {
|
|
56
|
-
Alice: "Hello, Alice!",
|
|
57
|
-
});
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
test("merge", async () => {
|
|
61
|
-
{
|
|
62
|
-
const globals = {
|
|
63
|
-
more: {
|
|
64
|
-
b: 2,
|
|
65
|
-
},
|
|
66
|
-
};
|
|
67
|
-
const fn = compile.expression(
|
|
68
|
-
`
|
|
69
|
-
{
|
|
70
|
-
a: 1
|
|
71
|
-
...more
|
|
72
|
-
c: a
|
|
73
|
-
}
|
|
74
|
-
`,
|
|
75
|
-
{ globals }
|
|
76
|
-
);
|
|
77
|
-
const result = await fn();
|
|
78
|
-
assert.deepEqual(await Tree.plain(result), {
|
|
79
|
-
a: 1,
|
|
80
|
-
b: 2,
|
|
81
|
-
c: 1,
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
test("number", async () => {
|
|
87
|
-
await assertCompile("1", 1);
|
|
88
|
-
await assertCompile("3.14159", 3.14159);
|
|
89
|
-
await assertCompile("-1", -1);
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
test("sync object", async () => {
|
|
93
|
-
await assertCompile("{a:1, b:2}", { a: 1, b: 2 });
|
|
94
|
-
await assertCompile("{ a: { b: { c: 0 } } }", { a: { b: { c: 0 } } });
|
|
95
|
-
});
|
|
96
19
|
|
|
97
20
|
test("async object", async () => {
|
|
98
21
|
const fn = compile.expression("{ a: { b = name }}", { globals });
|
|
@@ -141,21 +64,13 @@ describe("compile", () => {
|
|
|
141
64
|
|
|
142
65
|
test("templateDocument", async () => {
|
|
143
66
|
const defineTemplateFn = compile.templateDocument(
|
|
144
|
-
"Documents can contain ` backticks"
|
|
67
|
+
"Documents can contain ` backticks",
|
|
145
68
|
);
|
|
146
69
|
const templateFn = await defineTemplateFn();
|
|
147
70
|
const value = await templateFn();
|
|
148
71
|
assert.deepEqual(value, "Documents can contain ` backticks");
|
|
149
72
|
});
|
|
150
73
|
|
|
151
|
-
test("templateLiteral", async () => {
|
|
152
|
-
await assertCompile("`Hello, ${name}!`", "Hello, Alice!");
|
|
153
|
-
await assertCompile(
|
|
154
|
-
"`escape characters with \\`backslash\\``",
|
|
155
|
-
"escape characters with `backslash`"
|
|
156
|
-
);
|
|
157
|
-
});
|
|
158
|
-
|
|
159
74
|
test("tagged template string array is identical across calls", async () => {
|
|
160
75
|
let saved;
|
|
161
76
|
const globals = {
|
|
@@ -177,14 +92,3 @@ describe("compile", () => {
|
|
|
177
92
|
assert.equal(bob, "Hello, Bob!");
|
|
178
93
|
});
|
|
179
94
|
});
|
|
180
|
-
|
|
181
|
-
async function assertCompile(text, expected, options = {}) {
|
|
182
|
-
const mode = options.mode ?? "program";
|
|
183
|
-
const parent = options.target ?? null;
|
|
184
|
-
const fn = compile.expression(text, { globals, mode, parent });
|
|
185
|
-
let result = await fn();
|
|
186
|
-
if (Tree.isMaplike(result)) {
|
|
187
|
-
result = await Tree.plain(result);
|
|
188
|
-
}
|
|
189
|
-
assert.deepEqual(result, expected);
|
|
190
|
-
}
|
|
@@ -31,7 +31,7 @@ describe("Origami parser", () => {
|
|
|
31
31
|
[
|
|
32
32
|
markers.traverse,
|
|
33
33
|
[markers.external, "Path with spaces (and parens).html"],
|
|
34
|
-
]
|
|
34
|
+
],
|
|
35
35
|
);
|
|
36
36
|
assertParse("angleBracketLiteral", "<foo/bar/baz>", [
|
|
37
37
|
markers.traverse,
|
|
@@ -113,7 +113,7 @@ describe("Origami parser", () => {
|
|
|
113
113
|
2
|
|
114
114
|
3
|
|
115
115
|
]`,
|
|
116
|
-
[ops.array, [ops.literal, 1], [ops.literal, 2], [ops.literal, 3]]
|
|
116
|
+
[ops.array, [ops.literal, 1], [ops.literal, 2], [ops.literal, 3]],
|
|
117
117
|
);
|
|
118
118
|
});
|
|
119
119
|
|
|
@@ -270,7 +270,7 @@ describe("Origami parser", () => {
|
|
|
270
270
|
assertThrows(
|
|
271
271
|
"arrowFunction",
|
|
272
272
|
"([a, a]) => a",
|
|
273
|
-
`Duplicate parameter name "a"
|
|
273
|
+
`Duplicate parameter name "a"`,
|
|
274
274
|
);
|
|
275
275
|
});
|
|
276
276
|
|
|
@@ -321,7 +321,7 @@ describe("Origami parser", () => {
|
|
|
321
321
|
assertThrows(
|
|
322
322
|
"arrowFunction",
|
|
323
323
|
"({ a: c, b: c }) => c",
|
|
324
|
-
`Duplicate parameter name "c"
|
|
324
|
+
`Duplicate parameter name "c"`,
|
|
325
325
|
);
|
|
326
326
|
});
|
|
327
327
|
|
|
@@ -739,7 +739,7 @@ describe("Origami parser", () => {
|
|
|
739
739
|
---
|
|
740
740
|
Body`,
|
|
741
741
|
'Expected "---"',
|
|
742
|
-
{ line: 2, column: 14 }
|
|
742
|
+
{ line: 2, column: 14 },
|
|
743
743
|
);
|
|
744
744
|
});
|
|
745
745
|
|
|
@@ -752,10 +752,15 @@ a : 1
|
|
|
752
752
|
---
|
|
753
753
|
Body`,
|
|
754
754
|
"Unexpected flow-map-end token",
|
|
755
|
-
{ line: 3, column: 1 }
|
|
755
|
+
{ line: 3, column: 1 },
|
|
756
756
|
);
|
|
757
757
|
});
|
|
758
758
|
|
|
759
|
+
test("escapedChar", () => {
|
|
760
|
+
assertParse("escapedChar", String.raw`\n`, "\n");
|
|
761
|
+
assertParse("escapedChar", String.raw`\\`, `\\`);
|
|
762
|
+
});
|
|
763
|
+
|
|
759
764
|
test("exponentiationExpression", () => {
|
|
760
765
|
assertParse("exponentiationExpression", "2 ** 2 ** 3", [
|
|
761
766
|
ops.exponentiation,
|
|
@@ -848,7 +853,7 @@ Body`,
|
|
|
848
853
|
"x //comment",
|
|
849
854
|
[markers.traverse, [markers.reference, "x"]],
|
|
850
855
|
"program",
|
|
851
|
-
false
|
|
856
|
+
false,
|
|
852
857
|
);
|
|
853
858
|
});
|
|
854
859
|
|
|
@@ -940,7 +945,7 @@ Body`,
|
|
|
940
945
|
assertParse("expression", "tag`Hello, ${name}!`", [
|
|
941
946
|
[markers.traverse, [markers.reference, "tag"]],
|
|
942
947
|
[ops.literal, ["Hello, ", "!"]],
|
|
943
|
-
[ops.
|
|
948
|
+
[ops.deepText, [markers.traverse, [markers.reference, "name"]]],
|
|
944
949
|
]);
|
|
945
950
|
assertParse("expression", "=tag`Hello, ${_}!`", [
|
|
946
951
|
ops.lambda,
|
|
@@ -949,7 +954,7 @@ Body`,
|
|
|
949
954
|
[
|
|
950
955
|
[markers.traverse, [markers.reference, "tag"]],
|
|
951
956
|
[ops.literal, ["Hello, ", "!"]],
|
|
952
|
-
[ops.
|
|
957
|
+
[ops.deepText, [markers.traverse, [markers.reference, "_"]]],
|
|
953
958
|
],
|
|
954
959
|
]);
|
|
955
960
|
assertParse("expression", "(post, slug) => fn.js(post, slug)", [
|
|
@@ -1013,7 +1018,7 @@ Body`,
|
|
|
1013
1018
|
],
|
|
1014
1019
|
],
|
|
1015
1020
|
],
|
|
1016
|
-
]
|
|
1021
|
+
],
|
|
1017
1022
|
);
|
|
1018
1023
|
});
|
|
1019
1024
|
});
|
|
@@ -1032,7 +1037,7 @@ Body`,
|
|
|
1032
1037
|
[[markers.traverse, [markers.reference, "_template"]]],
|
|
1033
1038
|
],
|
|
1034
1039
|
"program",
|
|
1035
|
-
false
|
|
1040
|
+
false,
|
|
1036
1041
|
);
|
|
1037
1042
|
});
|
|
1038
1043
|
|
|
@@ -1065,17 +1070,17 @@ Body`,
|
|
|
1065
1070
|
assertThrows(
|
|
1066
1071
|
"identifier",
|
|
1067
1072
|
"1stCharacterIsNumber",
|
|
1068
|
-
"Expected JavaScript identifier start"
|
|
1073
|
+
"Expected JavaScript identifier start",
|
|
1069
1074
|
);
|
|
1070
1075
|
assertThrows(
|
|
1071
1076
|
"identifier",
|
|
1072
1077
|
"has space",
|
|
1073
|
-
"Expected JavaScript identifier continuation"
|
|
1078
|
+
"Expected JavaScript identifier continuation",
|
|
1074
1079
|
);
|
|
1075
1080
|
assertThrows(
|
|
1076
1081
|
"identifier",
|
|
1077
1082
|
"foo.bar",
|
|
1078
|
-
"Expected JavaScript identifier continuation"
|
|
1083
|
+
"Expected JavaScript identifier continuation",
|
|
1079
1084
|
);
|
|
1080
1085
|
});
|
|
1081
1086
|
|
|
@@ -1108,7 +1113,7 @@ Body`,
|
|
|
1108
1113
|
[markers.traverse, [markers.reference, "tree/"], [ops.literal, "key"]],
|
|
1109
1114
|
[markers.traverse, [markers.reference, "arg"]],
|
|
1110
1115
|
],
|
|
1111
|
-
"shell"
|
|
1116
|
+
"shell",
|
|
1112
1117
|
);
|
|
1113
1118
|
assertParse("implicitParenthesesCallExpression", "foo.js bar.ori 'arg'", [
|
|
1114
1119
|
[markers.traverse, [markers.reference, "foo.js"]],
|
|
@@ -1133,7 +1138,7 @@ Body`,
|
|
|
1133
1138
|
[markers.traverse, [markers.reference, "b.json"]],
|
|
1134
1139
|
[ops.array, [markers.traverse, [markers.reference, "c.json"]]],
|
|
1135
1140
|
],
|
|
1136
|
-
]
|
|
1141
|
+
],
|
|
1137
1142
|
);
|
|
1138
1143
|
});
|
|
1139
1144
|
|
|
@@ -1183,7 +1188,7 @@ Body`,
|
|
|
1183
1188
|
"/*\nHello, world!\n*/",
|
|
1184
1189
|
null,
|
|
1185
1190
|
"program",
|
|
1186
|
-
false
|
|
1191
|
+
false,
|
|
1187
1192
|
);
|
|
1188
1193
|
});
|
|
1189
1194
|
|
|
@@ -1286,7 +1291,7 @@ Body`,
|
|
|
1286
1291
|
ops.object,
|
|
1287
1292
|
["a", [ops.getter, [markers.traverse, [markers.reference, "b"]]]],
|
|
1288
1293
|
["b", [ops.literal, 2]],
|
|
1289
|
-
]
|
|
1294
|
+
],
|
|
1290
1295
|
);
|
|
1291
1296
|
assertParse("objectLiteral", "{ a: { b: 1 } }", [
|
|
1292
1297
|
ops.object,
|
|
@@ -1544,7 +1549,7 @@ Body`,
|
|
|
1544
1549
|
"tree/",
|
|
1545
1550
|
[[ops.literal, "tree/"]],
|
|
1546
1551
|
undefined,
|
|
1547
|
-
false
|
|
1552
|
+
false,
|
|
1548
1553
|
);
|
|
1549
1554
|
assertParse(
|
|
1550
1555
|
"pathKeys",
|
|
@@ -1554,7 +1559,7 @@ Body`,
|
|
|
1554
1559
|
[ops.literal, "12"],
|
|
1555
1560
|
],
|
|
1556
1561
|
undefined,
|
|
1557
|
-
false
|
|
1562
|
+
false,
|
|
1558
1563
|
);
|
|
1559
1564
|
assertParse(
|
|
1560
1565
|
"pathKeys",
|
|
@@ -1565,7 +1570,7 @@ Body`,
|
|
|
1565
1570
|
[ops.literal, "bar"],
|
|
1566
1571
|
],
|
|
1567
1572
|
undefined,
|
|
1568
|
-
false
|
|
1573
|
+
false,
|
|
1569
1574
|
);
|
|
1570
1575
|
assertParse(
|
|
1571
1576
|
"pathKeys",
|
|
@@ -1577,7 +1582,7 @@ Body`,
|
|
|
1577
1582
|
[ops.literal, "b"],
|
|
1578
1583
|
],
|
|
1579
1584
|
undefined,
|
|
1580
|
-
false
|
|
1585
|
+
false,
|
|
1581
1586
|
);
|
|
1582
1587
|
});
|
|
1583
1588
|
|
|
@@ -1674,12 +1679,14 @@ Body`,
|
|
|
1674
1679
|
`,
|
|
1675
1680
|
[ops.literal, "Hello"],
|
|
1676
1681
|
"program",
|
|
1677
|
-
false
|
|
1682
|
+
false,
|
|
1678
1683
|
);
|
|
1679
1684
|
});
|
|
1680
1685
|
|
|
1681
1686
|
test("regexLiteral", () => {
|
|
1682
1687
|
assertParse("regexLiteral", "/abc+/g", [ops.literal, /abc+/g]);
|
|
1688
|
+
assertParse("regexLiteral", String.raw`/\//`, [ops.literal, /\//]);
|
|
1689
|
+
assertParse("regexLiteral", String.raw`/\d+/`, [ops.literal, /\d+/]);
|
|
1683
1690
|
});
|
|
1684
1691
|
|
|
1685
1692
|
test("relationalExpression", () => {
|
|
@@ -1757,7 +1764,7 @@ Body`,
|
|
|
1757
1764
|
"// Hello, world!",
|
|
1758
1765
|
null,
|
|
1759
1766
|
"program",
|
|
1760
|
-
false
|
|
1767
|
+
false,
|
|
1761
1768
|
);
|
|
1762
1769
|
});
|
|
1763
1770
|
|
|
@@ -1815,7 +1822,7 @@ Body text`,
|
|
|
1815
1822
|
ops.object,
|
|
1816
1823
|
["title", [ops.literal, "Title goes here"]],
|
|
1817
1824
|
["_body", [ops.templateIndent, [ops.literal, ["Body text"]]]],
|
|
1818
|
-
]
|
|
1825
|
+
],
|
|
1819
1826
|
);
|
|
1820
1827
|
});
|
|
1821
1828
|
|
|
@@ -1842,7 +1849,7 @@ Body text`,
|
|
|
1842
1849
|
],
|
|
1843
1850
|
],
|
|
1844
1851
|
],
|
|
1845
|
-
"shell"
|
|
1852
|
+
"shell",
|
|
1846
1853
|
);
|
|
1847
1854
|
});
|
|
1848
1855
|
|
|
@@ -1891,7 +1898,7 @@ Body text`,
|
|
|
1891
1898
|
"${foo}",
|
|
1892
1899
|
[markers.traverse, [markers.reference, "foo"]],
|
|
1893
1900
|
"shell",
|
|
1894
|
-
false
|
|
1901
|
+
false,
|
|
1895
1902
|
);
|
|
1896
1903
|
});
|
|
1897
1904
|
|
|
@@ -1965,7 +1972,7 @@ Body text`,
|
|
|
1965
1972
|
`,
|
|
1966
1973
|
null,
|
|
1967
1974
|
"program",
|
|
1968
|
-
false
|
|
1975
|
+
false,
|
|
1969
1976
|
);
|
|
1970
1977
|
});
|
|
1971
1978
|
});
|
|
@@ -1975,7 +1982,7 @@ function assertParse(
|
|
|
1975
1982
|
source,
|
|
1976
1983
|
expected,
|
|
1977
1984
|
mode = "shell",
|
|
1978
|
-
checkLocation = true
|
|
1985
|
+
checkLocation = true,
|
|
1979
1986
|
) {
|
|
1980
1987
|
const code = parse(source, {
|
|
1981
1988
|
grammarSource: { text: source },
|
|
@@ -1990,7 +1997,7 @@ function assertParse(
|
|
|
1990
1997
|
assertCodeLocations(code);
|
|
1991
1998
|
const resultSource = code.location.source.text.slice(
|
|
1992
1999
|
code.location.start.offset,
|
|
1993
|
-
code.location.end.offset
|
|
2000
|
+
code.location.end.offset,
|
|
1994
2001
|
);
|
|
1995
2002
|
assert.equal(resultSource, source);
|
|
1996
2003
|
}
|
|
@@ -2010,7 +2017,7 @@ function assertThrows(startRule, source, message, position, mode = "program") {
|
|
|
2010
2017
|
} catch (/** @type {any} */ error) {
|
|
2011
2018
|
assert(
|
|
2012
2019
|
error.message.includes(message),
|
|
2013
|
-
`Error message incorrect:\n expected: "${message}"\n actual: "${error.message}"
|
|
2020
|
+
`Error message incorrect:\n expected: "${message}"\n actual: "${error.message}"`,
|
|
2014
2021
|
);
|
|
2015
2022
|
if (position) {
|
|
2016
2023
|
assert.equal(error.location.start.line, position.line);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
This file exists to force creation of the containing folder.
|