@weborigami/language 0.0.35
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 +30 -0
- package/main.js +21 -0
- package/package.json +24 -0
- package/src/compiler/code.d.ts +3 -0
- package/src/compiler/compile.js +55 -0
- package/src/compiler/origami.pegjs +277 -0
- package/src/compiler/parse.js +2292 -0
- package/src/compiler/parserHelpers.js +26 -0
- package/src/runtime/EventTargetMixin.d.ts +9 -0
- package/src/runtime/EventTargetMixin.js +117 -0
- package/src/runtime/ExpressionTree.js +20 -0
- package/src/runtime/FileLoadersTransform.d.ts +5 -0
- package/src/runtime/FileLoadersTransform.js +43 -0
- package/src/runtime/ImportModulesMixin.d.ts +5 -0
- package/src/runtime/ImportModulesMixin.js +48 -0
- package/src/runtime/InheritScopeMixin.js +34 -0
- package/src/runtime/InheritScopeMixin.ts +9 -0
- package/src/runtime/InvokeFunctionsTransform.d.ts +5 -0
- package/src/runtime/InvokeFunctionsTransform.js +27 -0
- package/src/runtime/OrigamiFiles.d.ts +11 -0
- package/src/runtime/OrigamiFiles.js +9 -0
- package/src/runtime/OrigamiTransform.d.ts +11 -0
- package/src/runtime/OrigamiTransform.js +11 -0
- package/src/runtime/OrigamiTree.js +4 -0
- package/src/runtime/ReadMe.md +1 -0
- package/src/runtime/Scope.js +89 -0
- package/src/runtime/TreeEvent.js +6 -0
- package/src/runtime/WatchFilesMixin.d.ts +5 -0
- package/src/runtime/WatchFilesMixin.js +58 -0
- package/src/runtime/concatTreeValues.js +46 -0
- package/src/runtime/evaluate.js +90 -0
- package/src/runtime/expressionFunction.js +33 -0
- package/src/runtime/extname.js +20 -0
- package/src/runtime/format.js +126 -0
- package/src/runtime/functionResultsMap.js +28 -0
- package/src/runtime/internal.js +20 -0
- package/src/runtime/ops.js +222 -0
- package/test/compiler/compile.test.js +64 -0
- package/test/compiler/parse.test.js +389 -0
- package/test/runtime/EventTargetMixin.test.js +68 -0
- package/test/runtime/ExpressionTree.test.js +27 -0
- package/test/runtime/FileLoadersTransform.test.js +41 -0
- package/test/runtime/InheritScopeMixin.test.js +29 -0
- package/test/runtime/OrigamiFiles.test.js +37 -0
- package/test/runtime/Scope.test.js +37 -0
- package/test/runtime/concatTreeValues.js +20 -0
- package/test/runtime/evaluate.test.js +55 -0
- package/test/runtime/fixtures/foo.js +1 -0
- package/test/runtime/fixtures/makeTest/a +1 -0
- package/test/runtime/fixtures/makeTest/b = a +0 -0
- package/test/runtime/fixtures/metagraphs/foo.txt +1 -0
- package/test/runtime/fixtures/metagraphs/greeting = this('world').js +3 -0
- package/test/runtime/fixtures/metagraphs/obj = this.json +5 -0
- package/test/runtime/fixtures/metagraphs/sample.txt = this().js +3 -0
- package/test/runtime/fixtures/metagraphs/string = this.json +1 -0
- package/test/runtime/fixtures/metagraphs/value = fn() +0 -0
- package/test/runtime/fixtures/programs/context.yaml +4 -0
- package/test/runtime/fixtures/programs/files.yaml +2 -0
- package/test/runtime/fixtures/programs/obj.yaml +3 -0
- package/test/runtime/fixtures/programs/simple.yaml +2 -0
- package/test/runtime/fixtures/subgraph = this.js +5 -0
- package/test/runtime/fixtures/templates/greet.orit +4 -0
- package/test/runtime/fixtures/templates/index.orit +15 -0
- package/test/runtime/fixtures/templates/names.yaml +3 -0
- package/test/runtime/fixtures/templates/plain.txt +1 -0
- package/test/runtime/fixtures/virtualKeys/.keys.json +1 -0
- package/test/runtime/format.test.js +66 -0
- package/test/runtime/functionResultsMap.test.js +27 -0
- package/test/runtime/ops.test.js +111 -0
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
2
|
+
import { describe, test } from "node:test";
|
|
3
|
+
import { parse } from "../../src/compiler/parse.js";
|
|
4
|
+
import * as ops from "../../src/runtime/ops.js";
|
|
5
|
+
|
|
6
|
+
describe("Origami parser", () => {
|
|
7
|
+
test("absoluteFilePath", () => {
|
|
8
|
+
assertParse("absoluteFilePath", "/foo/bar", [
|
|
9
|
+
[ops.filesRoot],
|
|
10
|
+
"foo",
|
|
11
|
+
"bar",
|
|
12
|
+
]);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test("argsChain", () => {
|
|
16
|
+
assertParse("argsChain", "(a)(b)(c)", [
|
|
17
|
+
[[ops.scope, "a"]],
|
|
18
|
+
[[ops.scope, "b"]],
|
|
19
|
+
[[ops.scope, "c"]],
|
|
20
|
+
]);
|
|
21
|
+
assertParse("argsChain", "(a)/b(c)", [
|
|
22
|
+
[[ops.scope, "a"]],
|
|
23
|
+
["b"],
|
|
24
|
+
[[ops.scope, "c"]],
|
|
25
|
+
]);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test("array", () => {
|
|
29
|
+
assertParse("array", "[]", [ops.array]);
|
|
30
|
+
assertParse("array", "[1, 2, 3]", [ops.array, 1, 2, 3]);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("assignment", () => {
|
|
34
|
+
assertParse("assignment", "data = obj.json", [
|
|
35
|
+
"data",
|
|
36
|
+
[ops.scope, "obj.json"],
|
|
37
|
+
]);
|
|
38
|
+
assertParse("assignment", "foo = fn 'bar'", [
|
|
39
|
+
"foo",
|
|
40
|
+
[[ops.scope, "fn"], "bar"],
|
|
41
|
+
]);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test("assignmentOrShorthand", () => {
|
|
45
|
+
assertParse("assignmentOrShorthand", "foo", [
|
|
46
|
+
"foo",
|
|
47
|
+
[ops.inherited, "foo"],
|
|
48
|
+
]);
|
|
49
|
+
assertParse("assignmentOrShorthand", "foo = 1", ["foo", 1]);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test("expr", () => {
|
|
53
|
+
assertParse("expr", "obj.json", [ops.scope, "obj.json"]);
|
|
54
|
+
assertParse("expr", "(fn a, b, c)", [
|
|
55
|
+
[ops.scope, "fn"],
|
|
56
|
+
[ops.scope, "a"],
|
|
57
|
+
[ops.scope, "b"],
|
|
58
|
+
[ops.scope, "c"],
|
|
59
|
+
]);
|
|
60
|
+
assertParse("expr", "foo.bar('hello', 'world')", [
|
|
61
|
+
[ops.scope, "foo.bar"],
|
|
62
|
+
"hello",
|
|
63
|
+
"world",
|
|
64
|
+
]);
|
|
65
|
+
assertParse("expr", "(fn)('a')", [[ops.scope, "fn"], "a"]);
|
|
66
|
+
assertParse("expr", "1", 1);
|
|
67
|
+
assertParse("expr", "{ a: 1, b: 2 }", [ops.object, ["a", 1], ["b", 2]]);
|
|
68
|
+
assertParse("expr", "serve { index.html: 'hello' }", [
|
|
69
|
+
[ops.scope, "serve"],
|
|
70
|
+
[ops.object, ["index.html", "hello"]],
|
|
71
|
+
]);
|
|
72
|
+
assertParse("expr", "fn =`x`", [
|
|
73
|
+
[ops.scope, "fn"],
|
|
74
|
+
[ops.lambda, "x"],
|
|
75
|
+
]);
|
|
76
|
+
assertParse("expr", "copy app(formulas), files 'snapshot'", [
|
|
77
|
+
[ops.scope, "copy"],
|
|
78
|
+
[
|
|
79
|
+
[ops.scope, "app"],
|
|
80
|
+
[ops.scope, "formulas"],
|
|
81
|
+
],
|
|
82
|
+
[[ops.scope, "files"], "snapshot"],
|
|
83
|
+
]);
|
|
84
|
+
assertParse("expr", "@map =`<li>{{_}}</li>`", [
|
|
85
|
+
[ops.scope, "@map"],
|
|
86
|
+
[ops.lambda, [ops.concat, "<li>", [ops.scope, "_"], "</li>"]],
|
|
87
|
+
]);
|
|
88
|
+
assertParse("expr", `"https://example.com"`, "https://example.com");
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test("expression", () => {
|
|
92
|
+
assertParse(
|
|
93
|
+
"expression",
|
|
94
|
+
`
|
|
95
|
+
{
|
|
96
|
+
index.html = index.orit(teamData.yaml)
|
|
97
|
+
thumbnails = @map(images, { valueMap: thumbnail.js })
|
|
98
|
+
}
|
|
99
|
+
`,
|
|
100
|
+
[
|
|
101
|
+
ops.tree,
|
|
102
|
+
[
|
|
103
|
+
"index.html",
|
|
104
|
+
[
|
|
105
|
+
[ops.scope, "index.orit"],
|
|
106
|
+
[ops.scope, "teamData.yaml"],
|
|
107
|
+
],
|
|
108
|
+
],
|
|
109
|
+
[
|
|
110
|
+
"thumbnails",
|
|
111
|
+
[
|
|
112
|
+
[ops.scope, "@map"],
|
|
113
|
+
[ops.scope, "images"],
|
|
114
|
+
[ops.object, ["valueMap", [ops.scope, "thumbnail.js"]]],
|
|
115
|
+
],
|
|
116
|
+
],
|
|
117
|
+
]
|
|
118
|
+
);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test("functionComposition", () => {
|
|
122
|
+
assertParse("functionComposition", "fn()", [[ops.scope, "fn"], undefined]);
|
|
123
|
+
assertParse("functionComposition", "fn(arg)", [
|
|
124
|
+
[ops.scope, "fn"],
|
|
125
|
+
[ops.scope, "arg"],
|
|
126
|
+
]);
|
|
127
|
+
assertParse("functionComposition", "fn(a, b)", [
|
|
128
|
+
[ops.scope, "fn"],
|
|
129
|
+
[ops.scope, "a"],
|
|
130
|
+
[ops.scope, "b"],
|
|
131
|
+
]);
|
|
132
|
+
assertParse("functionComposition", "fn()(arg)", [
|
|
133
|
+
[[ops.scope, "fn"], undefined],
|
|
134
|
+
[ops.scope, "arg"],
|
|
135
|
+
]);
|
|
136
|
+
assertParse("functionComposition", "fn()/key", [
|
|
137
|
+
[[ops.scope, "fn"], undefined],
|
|
138
|
+
"key",
|
|
139
|
+
]);
|
|
140
|
+
assertParse("functionComposition", "tree/", [[ops.scope, "tree"], ""]);
|
|
141
|
+
assertParse("functionComposition", "tree/key", [
|
|
142
|
+
[ops.scope, "tree"],
|
|
143
|
+
"key",
|
|
144
|
+
]);
|
|
145
|
+
assertParse("functionComposition", "tree/foo/bar", [
|
|
146
|
+
[ops.scope, "tree"],
|
|
147
|
+
"foo",
|
|
148
|
+
"bar",
|
|
149
|
+
]);
|
|
150
|
+
assertParse("functionComposition", "tree/key()", [
|
|
151
|
+
[[ops.scope, "tree"], "key"],
|
|
152
|
+
undefined,
|
|
153
|
+
]);
|
|
154
|
+
assertParse("functionComposition", "fn()/key()", [
|
|
155
|
+
[[[ops.scope, "fn"], undefined], "key"],
|
|
156
|
+
undefined,
|
|
157
|
+
]);
|
|
158
|
+
assertParse("functionComposition", "(fn())('arg')", [
|
|
159
|
+
[[ops.scope, "fn"], undefined],
|
|
160
|
+
"arg",
|
|
161
|
+
]);
|
|
162
|
+
assertParse("functionComposition", "fn('a')('b')", [
|
|
163
|
+
[[ops.scope, "fn"], "a"],
|
|
164
|
+
"b",
|
|
165
|
+
]);
|
|
166
|
+
assertParse("functionComposition", "(fn())(a, b)", [
|
|
167
|
+
[[ops.scope, "fn"], undefined],
|
|
168
|
+
[ops.scope, "a"],
|
|
169
|
+
[ops.scope, "b"],
|
|
170
|
+
]);
|
|
171
|
+
assertParse("functionComposition", "{ a: 1, b: 2}/b", [
|
|
172
|
+
[ops.object, ["a", 1], ["b", 2]],
|
|
173
|
+
"b",
|
|
174
|
+
]);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
test("group", () => {
|
|
178
|
+
assertParse("group", "(hello)", [ops.scope, "hello"]);
|
|
179
|
+
assertParse("group", "(((nested)))", [ops.scope, "nested"]);
|
|
180
|
+
assertParse("group", "(fn())", [[ops.scope, "fn"], undefined]);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
test("host", () => {
|
|
184
|
+
assertParse("host", "abc", "abc");
|
|
185
|
+
assertParse("host", "abc:123", "abc:123");
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
test("identifier", () => {
|
|
189
|
+
assertParse("identifier", "abc", "abc");
|
|
190
|
+
assertParse("identifier", "index.html", "index.html");
|
|
191
|
+
assertParse("identifier", "foo\\ bar", "foo bar");
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
test("implicitParensCall", () => {
|
|
195
|
+
assertParse("implicitParensCall", "fn arg", [
|
|
196
|
+
[ops.scope, "fn"],
|
|
197
|
+
[ops.scope, "arg"],
|
|
198
|
+
]);
|
|
199
|
+
assertParse("implicitParensCall", "fn 'a', 'b'", [
|
|
200
|
+
[ops.scope, "fn"],
|
|
201
|
+
"a",
|
|
202
|
+
"b",
|
|
203
|
+
]);
|
|
204
|
+
assertParse("implicitParensCall", "fn a(b), c", [
|
|
205
|
+
[ops.scope, "fn"],
|
|
206
|
+
[
|
|
207
|
+
[ops.scope, "a"],
|
|
208
|
+
[ops.scope, "b"],
|
|
209
|
+
],
|
|
210
|
+
[ops.scope, "c"],
|
|
211
|
+
]);
|
|
212
|
+
assertParse("implicitParensCall", "fn1 fn2 'arg'", [
|
|
213
|
+
[ops.scope, "fn1"],
|
|
214
|
+
[[ops.scope, "fn2"], "arg"],
|
|
215
|
+
]);
|
|
216
|
+
assertParse("implicitParensCall", "(fn()) 'arg'", [
|
|
217
|
+
[[ops.scope, "fn"], undefined],
|
|
218
|
+
"arg",
|
|
219
|
+
]);
|
|
220
|
+
assertParse("implicitParensCall", "https://example.com/tree.yaml 'key'", [
|
|
221
|
+
[ops.https, "example.com", "tree.yaml"],
|
|
222
|
+
"key",
|
|
223
|
+
]);
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
test("lambda", () => {
|
|
227
|
+
assertParse("lambda", "=message", [ops.lambda, [ops.scope, "message"]]);
|
|
228
|
+
assertParse("lambda", "=`Hello, {{name}}.`", [
|
|
229
|
+
ops.lambda,
|
|
230
|
+
[ops.concat, "Hello, ", [ops.scope, "name"], "."],
|
|
231
|
+
]);
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
test("leadingSlashPath", () => {
|
|
235
|
+
assertParse("leadingSlashPath", "/tree/", ["tree", ""]);
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
describe("list", () => {
|
|
239
|
+
assertParse("list", "1", [1]);
|
|
240
|
+
assertParse("list", "1,2,3", [1, 2, 3]);
|
|
241
|
+
assertParse("list", "1, 2, 3,", [1, 2, 3]);
|
|
242
|
+
assertParse("list", "1 , 2 , 3", [1, 2, 3]);
|
|
243
|
+
assertParse("list", "1\n2\n3", [1, 2, 3]);
|
|
244
|
+
assertParse("list", "'a' , 'b' , 'c'", ["a", "b", "c"]);
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
test("number", () => {
|
|
248
|
+
assertParse("number", "123", 123);
|
|
249
|
+
assertParse("number", "-456", -456);
|
|
250
|
+
assertParse("number", ".5", 0.5);
|
|
251
|
+
assertParse("number", "123.45", 123.45);
|
|
252
|
+
assertParse("number", "-678.90", -678.9);
|
|
253
|
+
assertParse("number", "+123", 123);
|
|
254
|
+
assertParse("number", "+456.78", 456.78);
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
test("object", () => {
|
|
258
|
+
assertParse("object", "{}", [ops.object]);
|
|
259
|
+
assertParse("object", "{ a: 1, b }", [
|
|
260
|
+
ops.object,
|
|
261
|
+
["a", 1],
|
|
262
|
+
["b", [ops.inherited, "b"]],
|
|
263
|
+
]);
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
test("objectProperty", () => {
|
|
267
|
+
assertParse("objectProperty", "a: 1", ["a", 1]);
|
|
268
|
+
assertParse("objectProperty", "name: 'Alice'", ["name", "Alice"]);
|
|
269
|
+
assertParse("objectProperty", "x: fn('a')", [
|
|
270
|
+
"x",
|
|
271
|
+
[[ops.scope, "fn"], "a"],
|
|
272
|
+
]);
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
test("objectPropertyOrShorthand", () => {
|
|
276
|
+
assertParse("objectPropertyOrShorthand", "foo", [
|
|
277
|
+
"foo",
|
|
278
|
+
[ops.inherited, "foo"],
|
|
279
|
+
]);
|
|
280
|
+
assertParse("objectPropertyOrShorthand", "x: y", ["x", [ops.scope, "y"]]);
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
test("parensArgs", () => {
|
|
284
|
+
assertParse("parensArgs", "()", [undefined]);
|
|
285
|
+
assertParse("parensArgs", "(a, b, c)", [
|
|
286
|
+
[ops.scope, "a"],
|
|
287
|
+
[ops.scope, "b"],
|
|
288
|
+
[ops.scope, "c"],
|
|
289
|
+
]);
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
test("path", () => {
|
|
293
|
+
assertParse("path", "tree/", ["tree", ""]);
|
|
294
|
+
assertParse("path", "month/12", ["month", "12"]);
|
|
295
|
+
assertParse("path", "tree/foo/bar", ["tree", "foo", "bar"]);
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
test("protocolCall", () => {
|
|
299
|
+
assertParse("protocolCall", "foo://bar", [[ops.scope, "foo"], "bar"]);
|
|
300
|
+
assertParse("protocolCall", "https://example.com/foo/", [
|
|
301
|
+
ops.https,
|
|
302
|
+
"example.com",
|
|
303
|
+
"foo",
|
|
304
|
+
"",
|
|
305
|
+
]);
|
|
306
|
+
assertParse("protocolCall", "http:example.com", [ops.http, "example.com"]);
|
|
307
|
+
assertParse("protocolCall", "http://localhost:5000/foo", [
|
|
308
|
+
ops.http,
|
|
309
|
+
"localhost:5000",
|
|
310
|
+
"foo",
|
|
311
|
+
]);
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
test("scopeReference", () => {
|
|
315
|
+
assertParse("scopeReference", "x", [ops.scope, "x"]);
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
test("string", () => {
|
|
319
|
+
assertParse("string", '"foo"', "foo");
|
|
320
|
+
assertParse("string", "'bar'", "bar");
|
|
321
|
+
assertParse("string", '"foo bar"', "foo bar");
|
|
322
|
+
assertParse("string", "'bar baz'", "bar baz");
|
|
323
|
+
assertParse("string", `"foo\\"s bar"`, `foo"s bar`);
|
|
324
|
+
assertParse("string", `'bar\\'s baz'`, `bar's baz`);
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
test("templateDocument", () => {
|
|
328
|
+
assertParse("templateDocument", "hello{{foo}}world", [
|
|
329
|
+
ops.lambda,
|
|
330
|
+
[ops.concat, "hello", [ops.scope, "foo"], "world"],
|
|
331
|
+
]);
|
|
332
|
+
assertParse("templateDocument", "Documents can contain ` backticks", [
|
|
333
|
+
ops.lambda,
|
|
334
|
+
"Documents can contain ` backticks",
|
|
335
|
+
]);
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
test("templateLiteral", () => {
|
|
339
|
+
assertParse("templateLiteral", "`Hello, world.`", "Hello, world.");
|
|
340
|
+
assertParse("templateLiteral", "`foo {{x}} bar`", [
|
|
341
|
+
ops.concat,
|
|
342
|
+
"foo ",
|
|
343
|
+
[ops.scope, "x"],
|
|
344
|
+
" bar",
|
|
345
|
+
]);
|
|
346
|
+
assertParse("templateLiteral", "`{{`nested`}}`", "nested");
|
|
347
|
+
assertParse("templateLiteral", "`{{map(people, =`{{name}}`)}}`", [
|
|
348
|
+
ops.concat,
|
|
349
|
+
[
|
|
350
|
+
[ops.scope, "map"],
|
|
351
|
+
[ops.scope, "people"],
|
|
352
|
+
[ops.lambda, [ops.concat, [ops.scope, "name"]]],
|
|
353
|
+
],
|
|
354
|
+
]);
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
test("templateSubstitution", () => {
|
|
358
|
+
assertParse("templateSubstitution", "{{foo}}", [ops.scope, "foo"]);
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
test("tree", () => {
|
|
362
|
+
assertParse("tree", "{}", [ops.tree]);
|
|
363
|
+
assertParse("tree", "{ a = 1, b }", [
|
|
364
|
+
ops.tree,
|
|
365
|
+
["a", 1],
|
|
366
|
+
["b", [ops.inherited, "b"]],
|
|
367
|
+
]);
|
|
368
|
+
assertParse("tree", "{ x = fn('a') }", [
|
|
369
|
+
ops.tree,
|
|
370
|
+
["x", [[ops.scope, "fn"], "a"]],
|
|
371
|
+
]);
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
test("whitespace block", () => {
|
|
375
|
+
assertParse(
|
|
376
|
+
"__",
|
|
377
|
+
`
|
|
378
|
+
# First line of comment
|
|
379
|
+
# Second line of comment
|
|
380
|
+
`,
|
|
381
|
+
""
|
|
382
|
+
);
|
|
383
|
+
});
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
function assertParse(startRule, source, expected) {
|
|
387
|
+
const actual = parse(source, { startRule });
|
|
388
|
+
assert.deepEqual(actual, expected);
|
|
389
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
2
|
+
import { describe, test } from "node:test";
|
|
3
|
+
import EventTargetMixin from "../../src/runtime/EventTargetMixin.js";
|
|
4
|
+
|
|
5
|
+
class EventTargetTest extends EventTargetMixin(Object) {}
|
|
6
|
+
|
|
7
|
+
describe("EventTargetMixin", () => {
|
|
8
|
+
test("add and dispatch event", () => {
|
|
9
|
+
const fixture = new EventTargetTest();
|
|
10
|
+
const event = new Event("test");
|
|
11
|
+
let callCount = 0;
|
|
12
|
+
const callback = () => {
|
|
13
|
+
callCount++;
|
|
14
|
+
};
|
|
15
|
+
fixture.addEventListener("test", callback);
|
|
16
|
+
// Add twice, ensure that the callback is only called once.
|
|
17
|
+
fixture.addEventListener("test", callback);
|
|
18
|
+
const dispatched = fixture.dispatchEvent(event);
|
|
19
|
+
assert(dispatched);
|
|
20
|
+
assert.equal(callCount, 1);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test("dispatch event with no listeners", () => {
|
|
24
|
+
const fixture = new EventTargetTest();
|
|
25
|
+
const event = new Event("test");
|
|
26
|
+
const takeDefaultAction = fixture.dispatchEvent(event);
|
|
27
|
+
assert(takeDefaultAction);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test("remove event listener", () => {
|
|
31
|
+
const fixture = new EventTargetTest();
|
|
32
|
+
const event = new Event("test");
|
|
33
|
+
let callCount = 0;
|
|
34
|
+
const callback = () => {
|
|
35
|
+
callCount++;
|
|
36
|
+
};
|
|
37
|
+
fixture.addEventListener("test", callback);
|
|
38
|
+
fixture.removeEventListener("test", callback);
|
|
39
|
+
fixture.dispatchEvent(event);
|
|
40
|
+
assert.equal(callCount, 0);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("stop immediate propagation", () => {
|
|
44
|
+
const fixture = new EventTargetTest();
|
|
45
|
+
const event = new Event("test");
|
|
46
|
+
let callCount = 0;
|
|
47
|
+
fixture.addEventListener("test", (event) => {
|
|
48
|
+
callCount++;
|
|
49
|
+
event.stopImmediatePropagation();
|
|
50
|
+
});
|
|
51
|
+
fixture.addEventListener("test", () => {
|
|
52
|
+
callCount++;
|
|
53
|
+
});
|
|
54
|
+
fixture.dispatchEvent(event);
|
|
55
|
+
assert.equal(callCount, 1);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test("prevent default", () => {
|
|
59
|
+
const fixture = new EventTargetTest();
|
|
60
|
+
const event = new Event("test");
|
|
61
|
+
fixture.addEventListener("test", (event) => {
|
|
62
|
+
event.preventDefault();
|
|
63
|
+
});
|
|
64
|
+
const takeDefaultAction = fixture.dispatchEvent(event);
|
|
65
|
+
assert(!takeDefaultAction);
|
|
66
|
+
assert(event.defaultPrevented);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
2
|
+
import { describe, test } from "node:test";
|
|
3
|
+
|
|
4
|
+
import { Tree } from "@weborigami/async-tree";
|
|
5
|
+
import {
|
|
6
|
+
ExpressionTree,
|
|
7
|
+
expressionFunction,
|
|
8
|
+
ops,
|
|
9
|
+
} from "../../src/runtime/internal.js";
|
|
10
|
+
|
|
11
|
+
describe("ExpressionTree", () => {
|
|
12
|
+
test("evaluates expressions, returns other values as is", async () => {
|
|
13
|
+
const tree = new ExpressionTree({
|
|
14
|
+
name: "Alice",
|
|
15
|
+
message: expressionFunction.createExpressionFunction([
|
|
16
|
+
ops.concat,
|
|
17
|
+
"Hello, ",
|
|
18
|
+
[ops.scope, "name"],
|
|
19
|
+
"!",
|
|
20
|
+
]),
|
|
21
|
+
});
|
|
22
|
+
assert.deepEqual(await Tree.plain(tree), {
|
|
23
|
+
name: "Alice",
|
|
24
|
+
message: "Hello, Alice!",
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { ObjectTree, Tree } from "@weborigami/async-tree";
|
|
2
|
+
import assert from "node:assert";
|
|
3
|
+
import { describe, test } from "node:test";
|
|
4
|
+
import FileLoadersTransform from "../../src/runtime/FileLoadersTransform.js";
|
|
5
|
+
import Scope from "../../src/runtime/Scope.js";
|
|
6
|
+
|
|
7
|
+
describe("FileLoadersTransform", () => {
|
|
8
|
+
test("invokes an appropriate loader for a .json file extension", async () => {
|
|
9
|
+
const fixture = createFixture();
|
|
10
|
+
const numberValue = await fixture.get("foo");
|
|
11
|
+
assert(typeof numberValue === "number");
|
|
12
|
+
assert.equal(numberValue, 1);
|
|
13
|
+
const jsonFile = await fixture.get("bar.json");
|
|
14
|
+
assert.equal(String(jsonFile), `{ "bar": 2 }`);
|
|
15
|
+
const data = await jsonFile.unpack();
|
|
16
|
+
assert.deepEqual(data, { bar: 2 });
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test("input that isn't string-like is returned as is", async () => {
|
|
20
|
+
const obj = { bar: 2 };
|
|
21
|
+
const fixture = createFixture();
|
|
22
|
+
const jsonFile = await fixture.get("bar.json");
|
|
23
|
+
assert.deepEqual(await Tree.plain(jsonFile), obj);
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
function createFixture() {
|
|
28
|
+
/** @type {import("@weborigami/types").AsyncTree} */
|
|
29
|
+
let tree = new (FileLoadersTransform(ObjectTree))({
|
|
30
|
+
foo: 1, // No extension, should be left alone
|
|
31
|
+
"bar.json": `{ "bar": 2 }`,
|
|
32
|
+
});
|
|
33
|
+
/** @type {any} */
|
|
34
|
+
const scope = {
|
|
35
|
+
"@loaders": {
|
|
36
|
+
json: (input) => JSON.parse(input),
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
tree = Scope.treeWithScope(tree, scope);
|
|
40
|
+
return tree;
|
|
41
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { ObjectTree } from "@weborigami/async-tree";
|
|
2
|
+
import assert from "node:assert";
|
|
3
|
+
import { describe, test } from "node:test";
|
|
4
|
+
import InheritScopeMixin from "../../src/runtime/InheritScopeMixin.js";
|
|
5
|
+
|
|
6
|
+
describe("InheritScopeMixin", () => {
|
|
7
|
+
test("creates a scope that includes a tree and its parent", async () => {
|
|
8
|
+
const fixture = new (InheritScopeMixin(ObjectTree))({
|
|
9
|
+
b: 2,
|
|
10
|
+
});
|
|
11
|
+
fixture.parent = new ObjectTree({
|
|
12
|
+
a: 1,
|
|
13
|
+
});
|
|
14
|
+
assert.deepEqual(await fixture.scope?.get("b"), 2);
|
|
15
|
+
assert.deepEqual(await fixture.scope?.get("a"), 1);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test("adds a subtree's parent to the subtrees's scope", async () => {
|
|
19
|
+
const fixture = new (InheritScopeMixin(ObjectTree))({
|
|
20
|
+
a: 1,
|
|
21
|
+
subtree: {
|
|
22
|
+
b: 2,
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
const subtree = await fixture.get("subtree");
|
|
26
|
+
assert.deepEqual(await subtree.scope.get("b"), 2);
|
|
27
|
+
assert.deepEqual(await subtree.scope.get("a"), 1);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
2
|
+
import * as fs from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { describe, test } from "node:test";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import OrigamiFiles from "../../src/runtime/OrigamiFiles.js";
|
|
7
|
+
|
|
8
|
+
const dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
const tempDirectory = path.join(dirname, "fixtures/temp");
|
|
10
|
+
|
|
11
|
+
describe("OrigamiFiles", () => {
|
|
12
|
+
test("can watch its folder for changes", { timeout: 2000 }, async () => {
|
|
13
|
+
await createTempDirectory();
|
|
14
|
+
const tempFiles = new OrigamiFiles(tempDirectory);
|
|
15
|
+
const changedFileName = await new Promise(async (resolve) => {
|
|
16
|
+
// @ts-ignore
|
|
17
|
+
tempFiles.addEventListener("change", (event) => {
|
|
18
|
+
resolve(/** @type {any} */ (event).options.key);
|
|
19
|
+
});
|
|
20
|
+
// @ts-ignore
|
|
21
|
+
await tempFiles.set(
|
|
22
|
+
"foo.txt",
|
|
23
|
+
"This file is left over from testing and can be removed."
|
|
24
|
+
);
|
|
25
|
+
});
|
|
26
|
+
await removeTempDirectory();
|
|
27
|
+
assert.equal(changedFileName, "foo.txt");
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
async function createTempDirectory() {
|
|
32
|
+
await fs.mkdir(tempDirectory, { recursive: true });
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function removeTempDirectory() {
|
|
36
|
+
await fs.rm(tempDirectory, { recursive: true });
|
|
37
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
2
|
+
import { describe, test } from "node:test";
|
|
3
|
+
import Scope from "../../src/runtime/Scope.js";
|
|
4
|
+
|
|
5
|
+
describe("Scope", () => {
|
|
6
|
+
test("composes and flattens scopes and trees passed to it", async () => {
|
|
7
|
+
const treeA = {
|
|
8
|
+
a: 1,
|
|
9
|
+
};
|
|
10
|
+
const treeB = {
|
|
11
|
+
b: 2,
|
|
12
|
+
};
|
|
13
|
+
const treeC = {
|
|
14
|
+
c: 3,
|
|
15
|
+
};
|
|
16
|
+
const scope1 = new Scope(treeA, treeB);
|
|
17
|
+
const scope2 = new Scope(scope1, treeC);
|
|
18
|
+
const objects = scope2.trees.map(
|
|
19
|
+
(tree) => /** @type {any} */ (tree).object
|
|
20
|
+
);
|
|
21
|
+
assert.deepEqual(objects, [treeA, treeB, treeC]);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test("gets the first defined value from the scope trees", async () => {
|
|
25
|
+
const scope = new Scope(
|
|
26
|
+
{
|
|
27
|
+
a: 1,
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
a: 2,
|
|
31
|
+
b: 3,
|
|
32
|
+
}
|
|
33
|
+
);
|
|
34
|
+
assert.equal(await scope.get("a"), 1);
|
|
35
|
+
assert.equal(await scope.get("b"), 3);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Tree } from "@weborigami/async-tree";
|
|
2
|
+
import assert from "node:assert";
|
|
3
|
+
import { describe, test } from "node:test";
|
|
4
|
+
import concatTreeValues from "../../src/runtime/concatTreeValues.js";
|
|
5
|
+
|
|
6
|
+
describe("concatTreeValues", () => {
|
|
7
|
+
test("concatenates deep tree values", async () => {
|
|
8
|
+
const tree = Tree.from({
|
|
9
|
+
a: "A",
|
|
10
|
+
b: "B",
|
|
11
|
+
c: "C",
|
|
12
|
+
more: {
|
|
13
|
+
d: "D",
|
|
14
|
+
e: "E",
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
const result = await concatTreeValues.call(null, tree);
|
|
18
|
+
assert.equal(result, "ABCDE");
|
|
19
|
+
});
|
|
20
|
+
});
|