@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.
Files changed (95) hide show
  1. package/index.ts +16 -6
  2. package/main.js +9 -4
  3. package/package.json +4 -3
  4. package/src/compiler/compile.js +10 -4
  5. package/src/compiler/optimize.js +115 -97
  6. package/src/compiler/origami.pegjs +1 -4
  7. package/src/compiler/parse.js +568 -588
  8. package/src/compiler/parserHelpers.js +2 -2
  9. package/src/handlers/css_handler.js +7 -0
  10. package/src/handlers/csv_handler.js +129 -0
  11. package/src/handlers/handlers.js +33 -0
  12. package/src/handlers/htm_handler.js +2 -0
  13. package/src/handlers/html_handler.js +7 -0
  14. package/src/handlers/jpeg_handler.js +62 -0
  15. package/src/handlers/jpg_handler.js +2 -0
  16. package/src/handlers/js_handler.js +51 -0
  17. package/src/handlers/json_handler.js +26 -0
  18. package/src/handlers/md_handler.js +7 -0
  19. package/src/handlers/mjs_handler.js +2 -0
  20. package/src/handlers/ori_handler.js +52 -0
  21. package/src/handlers/oridocument_handler.js +77 -0
  22. package/src/handlers/parseFrontMatter.js +16 -0
  23. package/src/handlers/ts_handler.js +1 -0
  24. package/src/handlers/txt_handler.js +108 -0
  25. package/src/handlers/wasm_handler.js +15 -0
  26. package/src/handlers/xhtml_handler.js +2 -0
  27. package/src/handlers/yaml_handler.js +33 -0
  28. package/src/handlers/yml_handler.js +2 -0
  29. package/src/project/builtins.js +5 -0
  30. package/src/project/coreGlobals.js +17 -0
  31. package/src/{runtime → project}/jsGlobals.js +3 -1
  32. package/src/project/projectConfig.js +36 -0
  33. package/src/project/projectGlobals.js +19 -0
  34. package/src/project/projectRoot.js +59 -0
  35. package/src/protocols/constructHref.js +20 -0
  36. package/src/protocols/constructSiteTree.js +26 -0
  37. package/src/protocols/explore.js +14 -0
  38. package/src/protocols/fetchAndHandleExtension.js +25 -0
  39. package/src/protocols/files.js +26 -0
  40. package/src/protocols/http.js +15 -0
  41. package/src/protocols/https.js +15 -0
  42. package/src/protocols/httpstree.js +14 -0
  43. package/src/protocols/httptree.js +14 -0
  44. package/src/protocols/node.js +13 -0
  45. package/src/protocols/package.js +67 -0
  46. package/src/protocols/protocolGlobals.js +12 -0
  47. package/src/protocols/protocols.js +8 -0
  48. package/src/runtime/EventTargetMixin.js +1 -1
  49. package/src/runtime/HandleExtensionsTransform.js +3 -12
  50. package/src/runtime/ImportModulesMixin.js +4 -10
  51. package/src/runtime/InvokeFunctionsTransform.js +1 -1
  52. package/src/runtime/evaluate.js +15 -8
  53. package/src/runtime/expressionFunction.js +5 -7
  54. package/src/runtime/expressionObject.js +10 -20
  55. package/src/runtime/functionResultsMap.js +1 -3
  56. package/src/runtime/{handlers.js → handleExtension.js} +13 -11
  57. package/src/runtime/mergeTrees.js +1 -8
  58. package/src/runtime/ops.js +83 -90
  59. package/test/compiler/compile.test.js +20 -19
  60. package/test/compiler/optimize.test.js +60 -25
  61. package/test/compiler/parse.test.js +4 -4
  62. package/test/generator/oriEval.js +4 -5
  63. package/test/handlers/csv.handler.test.js +36 -0
  64. package/test/handlers/fixtures/add.wasm +0 -0
  65. package/test/handlers/fixtures/exif.jpeg +0 -0
  66. package/test/handlers/fixtures/frontMatter.md +5 -0
  67. package/test/handlers/fixtures/list.js +4 -0
  68. package/test/handlers/fixtures/multiple.js +4 -0
  69. package/test/handlers/fixtures/obj.js +3 -0
  70. package/test/handlers/fixtures/site.ori +5 -0
  71. package/test/handlers/fixtures/string.js +1 -0
  72. package/test/handlers/fixtures/tag.yaml +5 -0
  73. package/test/handlers/fixtures/test.ori +9 -0
  74. package/test/handlers/jpeg.handler.test.js +18 -0
  75. package/test/handlers/js.handler.test.js +46 -0
  76. package/test/handlers/json.handler.test.js +14 -0
  77. package/test/handlers/ori.handler.test.js +87 -0
  78. package/test/handlers/oridocument.handler.test.js +68 -0
  79. package/test/handlers/txt.handler.test.js +41 -0
  80. package/test/handlers/wasm.handler.test.js +20 -0
  81. package/test/handlers/yaml.handler.test.js +17 -0
  82. package/test/project/fixtures/withConfig/config.ori +4 -0
  83. package/test/project/fixtures/withConfig/subfolder/greet.js +1 -0
  84. package/test/project/fixtures/withPackageJson/package.json +0 -0
  85. package/test/project/jsGlobals.test.js +21 -0
  86. package/test/project/projectConfig.test.js +28 -0
  87. package/test/project/projectRoot.test.js +40 -0
  88. package/test/protocols/package.test.js +11 -0
  89. package/test/runtime/evaluate.test.js +26 -42
  90. package/test/runtime/expressionObject.test.js +16 -20
  91. package/test/runtime/{handlers.test.js → handleExtension.test.js} +4 -20
  92. package/test/runtime/jsGlobals.test.js +4 -6
  93. package/test/runtime/mergeTrees.test.js +2 -4
  94. package/test/runtime/ops.test.js +66 -68
  95. package/src/runtime/getHandlers.js +0 -10
@@ -1,7 +1,11 @@
1
1
  import { ObjectTree } from "@weborigami/async-tree";
2
2
  import { describe, test } from "node:test";
3
3
  import * as compile from "../../src/compiler/compile.js";
4
- import optimize from "../../src/compiler/optimize.js";
4
+ import {
5
+ REFERENCE_INHERITED,
6
+ REFERENCE_PARAM,
7
+ default as optimize,
8
+ } from "../../src/compiler/optimize.js";
5
9
  import { markers } from "../../src/compiler/parserHelpers.js";
6
10
  import { ops } from "../../src/runtime/internal.js";
7
11
  import {
@@ -24,11 +28,17 @@ describe("optimize", () => {
24
28
  [
25
29
  "a",
26
30
  [
27
- [ops.context, 1],
31
+ [ops.params, 0],
28
32
  [ops.literal, "name"],
29
33
  ],
30
34
  ],
31
- ["b", [[ops.context], [ops.literal, "a"]]],
35
+ [
36
+ "b",
37
+ [
38
+ [ops.inherited, 0],
39
+ [ops.literal, "a"],
40
+ ],
41
+ ],
32
42
  ],
33
43
  ];
34
44
  assertCompile(expression, expected);
@@ -54,7 +64,7 @@ describe("optimize", () => {
54
64
  [
55
65
  "a",
56
66
  [
57
- [ops.context, 1],
67
+ [ops.inherited, 1],
58
68
  [ops.literal, "a"],
59
69
  ],
60
70
  ],
@@ -81,7 +91,7 @@ describe("optimize", () => {
81
91
  [
82
92
  "name",
83
93
  [
84
- [ops.context, 1],
94
+ [ops.inherited, 1],
85
95
  [ops.literal, "name"],
86
96
  ],
87
97
  ],
@@ -99,14 +109,18 @@ describe("optimize", () => {
99
109
  markers.traverse,
100
110
  [markers.reference, "folder"],
101
111
  ]);
112
+ const parent = {};
102
113
  const expected = [
103
114
  ops.cache,
104
115
  {},
105
116
  "folder",
106
- [[ops.scope], [ops.literal, "folder"]],
117
+ [
118
+ [ops.scope, parent],
119
+ [ops.literal, "folder"],
120
+ ],
107
121
  ];
108
122
  const globals = {};
109
- assertCodeEqual(optimize(code, { globals }), expected);
123
+ assertCodeEqual(optimize(code, { globals, parent }), expected);
110
124
  });
111
125
 
112
126
  test("external reference", () => {
@@ -115,14 +129,18 @@ describe("optimize", () => {
115
129
  markers.traverse,
116
130
  [markers.reference, "index.html"],
117
131
  ]);
132
+ const parent = {};
118
133
  const expected = [
119
134
  ops.cache,
120
135
  {},
121
136
  "index.html",
122
- [[ops.scope], [ops.literal, "index.html"]],
137
+ [
138
+ [ops.scope, parent],
139
+ [ops.literal, "index.html"],
140
+ ],
123
141
  ];
124
142
  const globals = {};
125
- assertCodeEqual(optimize(code, { globals }), expected);
143
+ assertCodeEqual(optimize(code, { globals, parent }), expected);
126
144
  });
127
145
 
128
146
  test("external reference inside object with matching key", () => {
@@ -134,6 +152,7 @@ describe("optimize", () => {
134
152
  [ops.getter, [markers.traverse, [markers.reference, "posts.txt"]]],
135
153
  ],
136
154
  ]);
155
+ const parent = {};
137
156
  const expected = [
138
157
  ops.object,
139
158
  [
@@ -145,7 +164,7 @@ describe("optimize", () => {
145
164
  {},
146
165
  "posts.txt",
147
166
  [
148
- [ops.scope, [ops.context, 1]],
167
+ [ops.scope, parent],
149
168
  [ops.literal, "posts.txt"],
150
169
  ],
151
170
  ],
@@ -153,7 +172,7 @@ describe("optimize", () => {
153
172
  ],
154
173
  ];
155
174
  const globals = {};
156
- assertCodeEqual(optimize(code, { globals }), expected);
175
+ assertCodeEqual(optimize(code, { globals, parent }), expected);
157
176
  });
158
177
 
159
178
  test("global reference", () => {
@@ -176,27 +195,37 @@ describe("optimize", () => {
176
195
  });
177
196
 
178
197
  test("local reference", () => {
179
- // Compilation of `post` where post is a local variable
198
+ // Compilation of `post` where post is a local parameter
180
199
  const code = createCode([markers.traverse, [markers.reference, "post"]]);
181
200
  const globals = { post: {} }; // local should take precedence
182
- const locals = [["post"]];
201
+ const locals = [{ type: REFERENCE_PARAM, names: ["post"] }];
183
202
  const actual = optimize(code, { globals, locals });
184
- const expected = [[ops.context], [ops.literal, "post"]];
203
+ const expected = [
204
+ [ops.params, 0],
205
+ [ops.literal, "post"],
206
+ ];
185
207
  assertCodeEqual(actual, expected);
186
208
  });
187
209
 
188
210
  test("local reference and property", () => {
189
- // Compilation of `post.author.name` where `post` is a local variable
211
+ // Compilation of `post.author.name` where `post` is a local inherited property
190
212
  const code = createCode([
191
213
  markers.traverse,
192
214
  [markers.reference, "post.author.name"],
193
215
  ]);
194
216
  const globals = { post: {} }; // local should take precedence
195
- const locals = [["post"]];
217
+ const locals = [{ type: REFERENCE_INHERITED, names: ["post"] }];
196
218
  const actual = optimize(code, { globals, locals });
197
219
  const expected = [
198
220
  ops.property,
199
- [ops.property, [[ops.context], [ops.literal, "post"]], "author"],
221
+ [
222
+ ops.property,
223
+ [
224
+ [ops.inherited, 0],
225
+ [ops.literal, "post"],
226
+ ],
227
+ "author",
228
+ ],
200
229
  "name",
201
230
  ];
202
231
  assertCodeEqual(actual, expected);
@@ -226,18 +255,19 @@ describe("optimize", () => {
226
255
  [ops.literal, "to/"],
227
256
  [ops.literal, "file"],
228
257
  ]);
258
+ const parent = {};
229
259
  const expected = [
230
260
  ops.cache,
231
261
  {},
232
262
  "path/to/file",
233
263
  [
234
- [ops.scope],
264
+ [ops.scope, parent],
235
265
  [ops.literal, "path/"],
236
266
  [ops.literal, "to/"],
237
267
  [ops.literal, "file"],
238
268
  ],
239
269
  ];
240
- assertCodeEqual(optimize(code), expected);
270
+ assertCodeEqual(optimize(code, { parent }), expected);
241
271
  });
242
272
 
243
273
  test("implicit external path", () => {
@@ -248,27 +278,32 @@ describe("optimize", () => {
248
278
  [ops.literal, "name"],
249
279
  ]);
250
280
  const globals = {};
281
+ const parent = {};
251
282
  const expected = [
252
283
  ops.cache,
253
284
  {},
254
285
  "package.json/name",
255
- [[ops.scope], [ops.literal, "package.json/"], [ops.literal, "name"]],
286
+ [
287
+ [ops.scope, parent],
288
+ [ops.literal, "package.json/"],
289
+ [ops.literal, "name"],
290
+ ],
256
291
  ];
257
- assertCodeEqual(optimize(code, { globals }), expected);
292
+ assertCodeEqual(optimize(code, { globals, parent }), expected);
258
293
  });
259
294
 
260
295
  test("local path", () => {
261
- // Compilation of `page/title` where page is a local variable
296
+ // Compilation of `page/title` where page is a local parameter
262
297
  const code = createCode([
263
298
  markers.traverse,
264
299
  [markers.reference, "page/"],
265
300
  [ops.literal, "title"],
266
301
  ]);
267
302
  const globals = {};
268
- const locals = [["page"]];
303
+ const locals = [{ type: REFERENCE_PARAM, names: ["page"] }];
269
304
  const actual = optimize(code, { globals, locals });
270
305
  const expected = [
271
- [ops.context],
306
+ [ops.params, 0],
272
307
  [ops.literal, "page/"],
273
308
  [ops.literal, "title"],
274
309
  ];
@@ -279,7 +314,7 @@ describe("optimize", () => {
279
314
 
280
315
  function assertCompile(expression, expected, mode = "shell") {
281
316
  const parent = new ObjectTree({});
282
- const globals = new ObjectTree({});
317
+ const globals = {};
283
318
  const fn = compile.expression(expression, { globals, mode, parent });
284
319
  const actual = fn.code;
285
320
  assertCodeLocations(actual);
@@ -347,10 +347,10 @@ describe("Origami parser", () => {
347
347
  [ops.lambda, [], [ops.lambda, [], [ops.literal, 1]]],
348
348
  [ops.literal, 0],
349
349
  ]);
350
- assertParse("conditionalExpression", "false ? =1 : 0", [
350
+ assertParse("conditionalExpression", "false ? () => 1 : 0", [
351
351
  ops.conditional,
352
352
  [markers.traverse, [markers.reference, "false"]],
353
- [ops.lambda, [], [ops.lambda, [[ops.literal, "_"]], [ops.literal, 1]]],
353
+ [ops.lambda, [], [ops.lambda, [], [ops.literal, 1]]],
354
354
  [ops.literal, 0],
355
355
  ]);
356
356
  });
@@ -1008,9 +1008,9 @@ Body`,
1008
1008
  "_result",
1009
1009
  [
1010
1010
  ops.merge,
1011
- [ops.object, ["a", [ops.getter, [[ops.context, 1], "a"]]]],
1011
+ [ops.object, ["a", [ops.getter, [[ops.inherited, 1], "a"]]]],
1012
1012
  [markers.traverse, [markers.reference, "more"]],
1013
- [ops.object, ["c", [ops.getter, [[ops.context, 1], "c"]]]],
1013
+ [ops.object, ["c", [ops.getter, [[ops.inherited, 1], "c"]]]],
1014
1014
  ],
1015
1015
  ],
1016
1016
  ],
@@ -1,15 +1,14 @@
1
- import { ObjectTree } from "@weborigami/async-tree";
2
1
  import * as compile from "../../src/compiler/compile.js";
3
2
 
4
3
  export default async function oriEval(source) {
5
- const builtins = new ObjectTree({
4
+ const globals = {
6
5
  false: false,
7
6
  NaN: NaN,
8
7
  null: null,
9
8
  true: true,
10
9
  undefined: undefined,
11
- });
12
- const compiled = compile.program(source);
13
- const result = await compiled.call(builtins);
10
+ };
11
+ const compiled = compile.program(source, { globals });
12
+ const result = await compiled();
14
13
  return result;
15
14
  }
@@ -0,0 +1,36 @@
1
+ import assert from "node:assert";
2
+ import { describe, test } from "node:test";
3
+ import csv_handler from "../../src/handlers/csv_handler.js";
4
+
5
+ describe(".csv handler", () => {
6
+ test("parses CSV text into array of objects", () => {
7
+ const csvText = `name,age,city
8
+ Alice,30,"New York, NY"
9
+ Bob,25,Los Angeles
10
+ "Carol ""CJ""",22,Chicago`;
11
+ const result = csv_handler.unpack(csvText);
12
+ assert.deepStrictEqual(result, [
13
+ { name: "Alice", age: "30", city: "New York, NY" },
14
+ { name: "Bob", age: "25", city: "Los Angeles" },
15
+ { name: 'Carol "CJ"', age: "22", city: "Chicago" },
16
+ ]);
17
+ });
18
+
19
+ test("handles CRLF line endings", () => {
20
+ const textCRLF = `name,age,city\r\nAlice,30,"New York, NY"\r\nBob,25,Los Angeles\r\n`;
21
+ const expected = [
22
+ { name: "Alice", age: "30", city: "New York, NY" },
23
+ { name: "Bob", age: "25", city: "Los Angeles" },
24
+ ];
25
+ const result = csv_handler.unpack(textCRLF);
26
+ assert.deepStrictEqual(result, expected);
27
+ });
28
+
29
+ test("throws an error for unmatched quotes", () => {
30
+ // Provide a CSV string with an unmatched quote.
31
+ const badCSV = `name,age\r\nAlice,30\r\nBob,"25\r\n`;
32
+ assert.throws(() => {
33
+ csv_handler.unpack(badCSV);
34
+ }, /unmatched quote/);
35
+ });
36
+ });
Binary file
Binary file
@@ -0,0 +1,5 @@
1
+ ---
2
+ a: 1
3
+ ---
4
+
5
+ Body **text**
@@ -0,0 +1,4 @@
1
+ // A function with a spread parameter
2
+ export default function list(...args) {
3
+ return args.join(",");
4
+ }
@@ -0,0 +1,4 @@
1
+ // Exports multiple things
2
+
3
+ export const n = 1;
4
+ export const s = "string";
@@ -0,0 +1,3 @@
1
+ export default {
2
+ a: 1,
3
+ };
@@ -0,0 +1,5 @@
1
+ {
2
+ index.html = index.ori()
3
+ index.ori = () => `Hello, ${ name }!`
4
+ name = "world"
5
+ }
@@ -0,0 +1 @@
1
+ export default "This is a string.";
@@ -0,0 +1,5 @@
1
+ names:
2
+ - Alice
3
+ - Bob
4
+ - Carol
5
+ greetings: !ori "@map(names, =`Hello, {{ _ }}!`)"
@@ -0,0 +1,9 @@
1
+ {
2
+ names = [
3
+ 'Alice',
4
+ 'Bob',
5
+ 'Carol'
6
+ ]
7
+
8
+ greetings = map(=`Hello, ${ _ }!`)(names)
9
+ }
@@ -0,0 +1,18 @@
1
+ import assert from "node:assert";
2
+ import fs from "node:fs/promises";
3
+ import { describe, test } from "node:test";
4
+ import jpeg_handler from "../../src/handlers/jpeg_handler.js";
5
+
6
+ describe(".jpeg handler", () => {
7
+ test("loads Exif metadata", async () => {
8
+ const fixturePath = new URL("fixtures/exif.jpeg", import.meta.url);
9
+ const image = await fs.readFile(fixturePath);
10
+ const data = await jpeg_handler.unpack(image);
11
+ assert.equal(data.exif.LensMake, "Apple");
12
+ assert.equal(
13
+ data.exif.ModifyDate.toISOString(),
14
+ "2023-11-13T18:44:11.000Z"
15
+ );
16
+ assert.equal(data.exif.Orientation, 1);
17
+ });
18
+ });
@@ -0,0 +1,46 @@
1
+ import { FileTree } from "@weborigami/async-tree";
2
+ import assert from "node:assert";
3
+ import { describe, test } from "node:test";
4
+ import js_handler from "../../src/handlers/js_handler.js";
5
+ import ImportModulesMixin from "../../src/runtime/ImportModulesMixin.js";
6
+
7
+ const fixturesUrl = new URL("fixtures", import.meta.url);
8
+ const fixturesTree = new (ImportModulesMixin(FileTree))(fixturesUrl);
9
+
10
+ describe(".js handler", () => {
11
+ test("loads .js file that exports a string", async () => {
12
+ const buffer = await fixturesTree.get("string.js");
13
+ const text = await js_handler.unpack(buffer, {
14
+ key: "string.js",
15
+ parent: fixturesTree,
16
+ });
17
+ assert.equal(text, "This is a string.");
18
+ });
19
+
20
+ test("loads .js file that exports a function", async () => {
21
+ const buffer = await fixturesTree.get("list.js");
22
+ const list = await js_handler.unpack(buffer, {
23
+ key: "list.js",
24
+ parent: fixturesTree,
25
+ });
26
+ assert.equal(await list("a", "b", "c"), "a,b,c");
27
+ });
28
+
29
+ test("loads .js file that exports an object", async () => {
30
+ const buffer = await fixturesTree.get("obj.js");
31
+ const obj = await js_handler.unpack(buffer, {
32
+ key: "obj.js",
33
+ parent: fixturesTree,
34
+ });
35
+ assert.deepEqual(obj, { a: 1 });
36
+ });
37
+
38
+ test("returns multiple exports as an object", async () => {
39
+ const buffer = await fixturesTree.get("multiple.js");
40
+ const obj = await js_handler.unpack(buffer, {
41
+ key: "multiple.js",
42
+ parent: fixturesTree,
43
+ });
44
+ assert.deepEqual(obj, { n: 1, s: "string" });
45
+ });
46
+ });
@@ -0,0 +1,14 @@
1
+ import assert from "node:assert";
2
+ import { describe, test } from "node:test";
3
+ import json_handler from "../../src/handlers/json_handler.js";
4
+
5
+ describe(".json handler", () => {
6
+ test("loads input as a JSON file", async () => {
7
+ const text = `{ "a": 1, "b": 2 }`;
8
+ const obj = await json_handler.unpack(text);
9
+ assert.deepEqual(obj, {
10
+ a: 1,
11
+ b: 2,
12
+ });
13
+ });
14
+ });
@@ -0,0 +1,87 @@
1
+ import { ObjectTree, Tree } from "@weborigami/async-tree";
2
+ import assert from "node:assert";
3
+ import { describe, test } from "node:test";
4
+ import ori_handler from "../../src/handlers/ori_handler.js";
5
+ import OrigamiFiles from "../../src/runtime/OrigamiFiles.js";
6
+
7
+ const fixturesUrl = new URL("fixtures", import.meta.url);
8
+ const fixtures = new OrigamiFiles(fixturesUrl);
9
+
10
+ describe(".ori handler", async () => {
11
+ test("loads a string expression", async () => {
12
+ const source = `"Hello"`;
13
+ const text = await ori_handler.unpack(source);
14
+ assert.equal(text, "Hello");
15
+ });
16
+
17
+ test("loads a tree expression", async () => {
18
+ const parent = new ObjectTree({
19
+ name: "world",
20
+ });
21
+ const source = `{
22
+ message = \`Hello, \${name}!\`
23
+ }`;
24
+ const tree = await ori_handler.unpack(source, { parent });
25
+ assert.deepEqual(await Tree.plain(tree), {
26
+ message: "Hello, world!",
27
+ });
28
+ assert.equal(await tree.message, "Hello, world!");
29
+ });
30
+
31
+ test("loads a tree with a nested tree", async () => {
32
+ const source = `{
33
+ name = "world",
34
+ public = {
35
+ message = \`Hello, \${name}!\`
36
+ }
37
+ }`;
38
+ const tree = await ori_handler.unpack(source);
39
+ assert.deepEqual(
40
+ await Tree.traverse(tree, "public", "message"),
41
+ "Hello, world!"
42
+ );
43
+ });
44
+
45
+ test("loads an object containing an object shorthand", async () => {
46
+ const assets = new ObjectTree({});
47
+ const parent = new ObjectTree({ assets });
48
+ const source = `{ assets }`;
49
+ const object = await ori_handler.unpack(source, { parent });
50
+ assert.equal(object.assets, assets);
51
+ });
52
+
53
+ test("loads a template literal", async () => {
54
+ const scope = new ObjectTree({
55
+ name: "Alice",
56
+ });
57
+ const source = `\`Hello, \${name}!\``;
58
+ const unpackedText = await ori_handler.unpack(source, {
59
+ parent: scope,
60
+ });
61
+ assert.deepEqual(unpackedText, "Hello, Alice!");
62
+ });
63
+
64
+ test("loads a template lambda that reads from parent scope", async () => {
65
+ const parent = new ObjectTree({
66
+ name: "Alice",
67
+ });
68
+ const source = `() => \`Hello, \${name}!\``;
69
+ const templateFn = await ori_handler.unpack(source, { parent });
70
+ const value = await templateFn();
71
+ assert.equal(value, "Hello, Alice!");
72
+ });
73
+
74
+ test("loads a template lambda that accepts input", async () => {
75
+ const source = `(_) => \`Hello, \${ _.name }!\``;
76
+ const templateFn = await ori_handler.unpack(source);
77
+ const value = await templateFn({ name: "Alice" });
78
+ assert.deepEqual(value, "Hello, Alice!");
79
+ });
80
+
81
+ test("loads a tree that includes a template", async () => {
82
+ const source = await fixtures.get("site.ori");
83
+ const tree = await ori_handler.unpack(source);
84
+ const indexHtml = await tree["index.html"];
85
+ assert.equal(indexHtml, "Hello, world!");
86
+ });
87
+ });
@@ -0,0 +1,68 @@
1
+ import { ObjectTree } from "@weborigami/async-tree";
2
+ import assert from "node:assert";
3
+ import { describe, test } from "node:test";
4
+ import oridocument_handler from "../../src/handlers/oridocument_handler.js";
5
+
6
+ describe("Origami document handler", () => {
7
+ test("unpacks text with Origami expressions", async () => {
8
+ const parent = new ObjectTree({
9
+ name: "world",
10
+ });
11
+ const text = "Hello, ${ name }!";
12
+ const fn = await oridocument_handler.unpack(text, {
13
+ key: "test.ori.txt",
14
+ parent,
15
+ });
16
+ const result = await fn();
17
+ assert.equal(result, "Hello, world!");
18
+
19
+ // Test sidecar keyFn
20
+ assert.equal(fn.key(null, "data.json"), "data.txt");
21
+ });
22
+
23
+ test("Argument to template document available as underscore", async () => {
24
+ const text = "<h1>${ _ }</h1>";
25
+ const fn = await oridocument_handler.unpack(text);
26
+ const result = await fn("Home");
27
+ assert.equal(result, "<h1>Home</h1>");
28
+ });
29
+
30
+ test("YAML front matter is returned with _body", async () => {
31
+ const parent = new ObjectTree({
32
+ message: "Hello",
33
+ });
34
+ const text = `---
35
+ name: world
36
+ ---
37
+ \${ message }, \${ name }!`;
38
+ const result = await oridocument_handler.unpack(text, { parent });
39
+ assert.deepEqual(result.name, "world");
40
+ assert.equal(result._body, "Hello, world!");
41
+ });
42
+
43
+ test("unpacks a document with Origami front matter", async () => {
44
+ const text = `---
45
+ {
46
+ sum: 1 + 1
47
+ _body: _template()
48
+ }
49
+ ---
50
+ Body text`;
51
+ const result = await oridocument_handler.unpack(text);
52
+ assert.deepEqual(result, {
53
+ sum: 2,
54
+ _body: "Body text",
55
+ });
56
+ });
57
+
58
+ test("Origami front matter can refer to _template as a macro", async () => {
59
+ const text = `---
60
+ (name) => _template()
61
+ ---
62
+ Hello, \${ name }!
63
+ `;
64
+ const fn = await oridocument_handler.unpack(text);
65
+ const result = await fn("world");
66
+ assert.equal(result, "Hello, world!\n");
67
+ });
68
+ });
@@ -0,0 +1,41 @@
1
+ import assert from "node:assert";
2
+ import { describe, test } from "node:test";
3
+ import txt_handler from "../../src/handlers/txt_handler.js";
4
+
5
+ describe("text handler", () => {
6
+ test("packs an object as YAML with front matter", async () => {
7
+ const object = {
8
+ _body: "Body text",
9
+ a: 1,
10
+ };
11
+ const packed = await txt_handler.pack(object);
12
+ assert.equal(packed, "---\na: 1\n---\nBody text");
13
+ });
14
+
15
+ test("unpacks text without data", async () => {
16
+ const text = "Body text";
17
+ const result = await txt_handler.unpack(text);
18
+ assert.equal(result, text);
19
+ });
20
+
21
+ test("unpacks a document with YAML/JSON front matter", async () => {
22
+ const text = "---\na: 1\n---\nBody text";
23
+ const document = await txt_handler.unpack(text);
24
+ assert.deepEqual(document, {
25
+ a: 1,
26
+ _body: "Body text",
27
+ });
28
+ });
29
+
30
+ test("unpacks a document with Origami front matter", async () => {
31
+ const text = `---
32
+ { sum: 1 + 1 }
33
+ ---
34
+ Body text`;
35
+ const document = await txt_handler.unpack(text);
36
+ assert.deepEqual(document, {
37
+ sum: 2,
38
+ _body: "Body text",
39
+ });
40
+ });
41
+ });
@@ -0,0 +1,20 @@
1
+ import { FileTree } from "@weborigami/async-tree";
2
+ import assert from "node:assert";
3
+ import { describe, test } from "node:test";
4
+ import wasm_handler from "../../src/handlers/wasm_handler.js";
5
+ import ImportModulesMixin from "../../src/runtime/ImportModulesMixin.js";
6
+
7
+ const fixturesUrl = new URL("fixtures", import.meta.url);
8
+ const fixturesTree = new (ImportModulesMixin(FileTree))(fixturesUrl);
9
+
10
+ describe(".wasm handler", () => {
11
+ test("loads .wasm file that exports a function", async () => {
12
+ const buffer = await fixturesTree.get("add.wasm");
13
+ const { add } = await wasm_handler.unpack(buffer, {
14
+ key: "add.wasm",
15
+ parent: fixturesTree,
16
+ });
17
+ const sum = add(1, 2);
18
+ assert.strictEqual(sum, 3);
19
+ });
20
+ });