@weborigami/language 0.6.17 → 0.7.0-beta.2
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 +1 -0
- package/main.js +7 -1
- package/package.json +7 -6
- package/src/compiler/compile.js +10 -3
- package/src/compiler/optimize.js +71 -40
- package/src/compiler/parse.js +1 -1
- package/src/compiler/parserHelpers.js +5 -3
- package/src/handlers/addExtensionKeyFn.js +18 -0
- package/src/handlers/epub_handler.js +54 -0
- package/src/handlers/getSource.js +11 -0
- package/src/handlers/handlers.js +2 -0
- package/src/handlers/htm_handler.js +1 -1
- package/src/handlers/js_handler.js +13 -4
- package/src/handlers/mediaTypeExtensions.json +15 -0
- package/src/handlers/ori_handler.js +8 -7
- package/src/handlers/oridocument_handler.js +19 -28
- package/src/handlers/processOriExport.js +17 -0
- package/src/handlers/tsv_handler.js +1 -1
- package/src/handlers/txt_handler.js +4 -2
- package/src/handlers/xhtml_handler.js +1 -1
- package/src/handlers/yaml_handler.js +6 -3
- package/src/handlers/zip_handler.js +112 -0
- package/src/project/activeProjectRoot.js +9 -0
- package/src/project/getGlobalsForTree.js +5 -0
- package/src/project/{projectGlobals.js → initializeGlobalsForTree.js} +8 -13
- package/src/project/jsGlobals.js +1 -0
- package/src/project/projectConfig.js +2 -2
- package/src/project/projectRootFromPath.js +2 -0
- package/src/protocols/constructHref.js +3 -3
- package/src/protocols/constructSiteTree.js +11 -2
- package/src/protocols/explore.js +1 -1
- package/src/protocols/explorehttp.js +1 -1
- package/src/protocols/fetchAndHandleExtension.js +23 -11
- package/src/protocols/files.js +1 -0
- package/src/protocols/http.js +4 -1
- package/src/protocols/https.js +4 -1
- package/src/protocols/httpstree.js +1 -1
- package/src/protocols/httptree.js +1 -1
- package/src/protocols/package.js +15 -3
- package/src/runtime/AsyncCacheTransform.d.ts +5 -0
- package/src/runtime/AsyncCacheTransform.js +134 -0
- package/src/runtime/HandleExtensionsTransform.d.ts +3 -1
- package/src/runtime/HandleExtensionsTransform.js +18 -2
- package/src/runtime/OrigamiFileMap.d.ts +5 -2
- package/src/runtime/OrigamiFileMap.js +27 -4
- package/src/runtime/ScopeMap.js +72 -0
- package/src/runtime/SyncCacheTransform.d.ts +8 -0
- package/src/runtime/SyncCacheTransform.js +133 -0
- package/src/runtime/SystemCacheMap.js +259 -0
- package/src/runtime/WatchFilesMixin.js +52 -19
- package/src/runtime/enableValueCaching.js +192 -0
- package/src/runtime/execute.js +2 -2
- package/src/runtime/executionContext.js +7 -0
- package/src/runtime/explainReferenceError.js +7 -2
- package/src/runtime/expressionObject.js +54 -46
- package/src/runtime/handleExtension.js +65 -34
- package/src/runtime/interop.js +2 -2
- package/src/runtime/mergeTrees.js +1 -1
- package/src/runtime/ops.js +28 -33
- package/src/runtime/symbols.js +3 -0
- package/src/runtime/systemCache.js +3 -0
- package/src/runtime/volatile.js +14 -0
- package/test/compiler/codeHelpers.js +2 -1
- package/test/compiler/optimize.test.js +62 -54
- package/test/handlers/epub_handler.test.js +27 -0
- package/test/handlers/fixtures/test.zip +0 -0
- package/test/handlers/ori_handler.test.js +22 -3
- package/test/handlers/oridocument_handler.test.js +1 -1
- package/test/handlers/zip_handler.test.js +45 -0
- package/test/protocols/https.test.js +19 -0
- package/test/protocols/package.test.js +7 -2
- package/test/runtime/AsyncCacheTransform.test.js +91 -0
- package/test/runtime/OrigamiFileMap.test.js +26 -23
- package/test/runtime/ScopeMap.test.js +49 -0
- package/test/runtime/SyncCacheTransform.test.js +93 -0
- package/test/runtime/SystemCacheMap.test.js +239 -0
- package/test/runtime/asyncCalcs.js +28 -0
- package/test/runtime/enableValueCaching.test.js +55 -0
- package/test/runtime/errors.test.js +53 -30
- package/test/runtime/evaluate.test.js +9 -4
- package/test/runtime/execute.test.js +6 -1
- package/test/runtime/expressionObject.test.js +55 -15
- package/test/runtime/fetchAndHandleExtension.test.js +24 -0
- package/test/runtime/fixtures/unpack/hello.json +1 -0
- package/test/runtime/handleExtension.test.js +12 -1
- package/test/runtime/ops.test.js +70 -65
- package/test/runtime/syncCalcs.js +27 -0
- package/test/runtime/systemCache.test.js +66 -0
- package/src/runtime/assignPropertyDescriptors.js +0 -23
- package/src/runtime/asyncStorage.js +0 -7
package/src/runtime/ops.js
CHANGED
|
@@ -6,13 +6,15 @@
|
|
|
6
6
|
* @typedef {import("@weborigami/async-tree").SyncOrAsyncMap} SyncOrAsyncMap
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { getParent, isUnpackable, Tree } from "@weborigami/async-tree";
|
|
9
|
+
import { getParent, isUnpackable, SyncMap, Tree } from "@weborigami/async-tree";
|
|
10
10
|
import os from "node:os";
|
|
11
11
|
import execute from "./execute.js";
|
|
12
12
|
import expressionObject from "./expressionObject.js";
|
|
13
13
|
import mergeTrees from "./mergeTrees.js";
|
|
14
14
|
import OrigamiFileMap from "./OrigamiFileMap.js";
|
|
15
|
+
import ScopeMap from "./ScopeMap.js";
|
|
15
16
|
import { codeSymbol } from "./symbols.js";
|
|
17
|
+
import systemCache from "./systemCache.js";
|
|
16
18
|
|
|
17
19
|
function addOpLabel(op, label) {
|
|
18
20
|
Object.defineProperty(op, "toString", {
|
|
@@ -81,31 +83,18 @@ addOpLabel(bitwiseXor, "«ops.bitwiseXor»");
|
|
|
81
83
|
/**
|
|
82
84
|
* Cache the value of the code for an external reference
|
|
83
85
|
*
|
|
84
|
-
* @param {
|
|
85
|
-
* @param {string} path
|
|
86
|
+
* @param {string} cachePath
|
|
86
87
|
* @param {AnnotatedCode} code
|
|
88
|
+
* @param {RuntimeState} state
|
|
87
89
|
*/
|
|
88
|
-
export
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
// Don't await: might get another request for this before promise resolves
|
|
95
|
-
const promise = execute(code);
|
|
96
|
-
|
|
97
|
-
// Save promise so another request will get the same promise
|
|
98
|
-
cache[path] = promise;
|
|
99
|
-
|
|
100
|
-
// Now wait for the value
|
|
101
|
-
const value = await promise;
|
|
102
|
-
|
|
103
|
-
// Update the cache with the actual value
|
|
104
|
-
cache[path] = value;
|
|
105
|
-
|
|
106
|
-
return value;
|
|
90
|
+
export function cache(cachePath, code, state) {
|
|
91
|
+
const result = systemCache.getOrInsertComputedAsync(cachePath, () =>
|
|
92
|
+
execute(code, state),
|
|
93
|
+
);
|
|
94
|
+
return result;
|
|
107
95
|
}
|
|
108
96
|
addOpLabel(cache, "«ops.cache»");
|
|
97
|
+
cache.needsState = true;
|
|
109
98
|
cache.unevaluatedArgs = true;
|
|
110
99
|
|
|
111
100
|
/**
|
|
@@ -114,13 +103,17 @@ cache.unevaluatedArgs = true;
|
|
|
114
103
|
* @param {...AnnotatedCode} args
|
|
115
104
|
*/
|
|
116
105
|
export async function comma(...args) {
|
|
106
|
+
/** @type {RuntimeState} */
|
|
107
|
+
// @ts-ignore
|
|
108
|
+
const state = args.pop(); // The runtime state is passed as the last argument
|
|
117
109
|
let result;
|
|
118
110
|
for (const arg of args) {
|
|
119
|
-
result = await execute(arg);
|
|
111
|
+
result = await execute(arg, state);
|
|
120
112
|
}
|
|
121
113
|
return result;
|
|
122
114
|
}
|
|
123
115
|
addOpLabel(comma, "«ops.comma»");
|
|
116
|
+
comma.needsState = true;
|
|
124
117
|
comma.unevaluatedArgs = true;
|
|
125
118
|
|
|
126
119
|
export async function conditional(condition, truthy, falsy) {
|
|
@@ -143,7 +136,7 @@ export async function construct(constructor, ...args) {
|
|
|
143
136
|
export async function deepText(...args) {
|
|
144
137
|
return Tree.deepText(args);
|
|
145
138
|
}
|
|
146
|
-
addOpLabel(deepText, "«ops.deepText");
|
|
139
|
+
addOpLabel(deepText, "«ops.deepText»");
|
|
147
140
|
|
|
148
141
|
/**
|
|
149
142
|
* Default value for a parameter: if the value is defined, return that;
|
|
@@ -180,8 +173,7 @@ addOpLabel(exponentiation, "«ops.exponentiation»");
|
|
|
180
173
|
export async function flat(...args) {
|
|
181
174
|
// Unpack packed arguments so they can be flattened
|
|
182
175
|
const unpacked = args.map((arg) => (isUnpackable(arg) ? arg.unpack() : arg));
|
|
183
|
-
|
|
184
|
-
return Tree.flat(unpacked, 2);
|
|
176
|
+
return Tree.flat(unpacked);
|
|
185
177
|
}
|
|
186
178
|
addOpLabel(flat, "«ops.flat»");
|
|
187
179
|
|
|
@@ -206,6 +198,7 @@ addOpLabel(greaterThanOrEqual, "«ops.greaterThanOrEqual»");
|
|
|
206
198
|
*/
|
|
207
199
|
export async function homeDirectory(...keys) {
|
|
208
200
|
const tree = new OrigamiFileMap(os.homedir());
|
|
201
|
+
await tree.initializeGlobals();
|
|
209
202
|
return keys.length > 0 ? Tree.traverse(tree, ...keys) : tree;
|
|
210
203
|
}
|
|
211
204
|
addOpLabel(homeDirectory, "«ops.homeDirectory»");
|
|
@@ -488,7 +481,8 @@ export async function property(object, key) {
|
|
|
488
481
|
|
|
489
482
|
if (isUnpackable(object)) {
|
|
490
483
|
object = await object.unpack();
|
|
491
|
-
}
|
|
484
|
+
}
|
|
485
|
+
if (typeof object === "string") {
|
|
492
486
|
object = new String(object);
|
|
493
487
|
} else if (typeof object === "number") {
|
|
494
488
|
object = new Number(object);
|
|
@@ -529,17 +523,18 @@ addOpLabel(rootDirectory, "«ops.rootDirectory»");
|
|
|
529
523
|
/**
|
|
530
524
|
* Return the scope of the current tree
|
|
531
525
|
*
|
|
532
|
-
* @param {
|
|
526
|
+
* @param {RuntimeState} state
|
|
533
527
|
*/
|
|
534
|
-
export async function scope(
|
|
528
|
+
export async function scope(state = {}) {
|
|
529
|
+
const { parent } = state;
|
|
535
530
|
if (!parent) {
|
|
536
|
-
|
|
537
|
-
"Tried to find a value in scope, but no container was provided as the parent.",
|
|
538
|
-
);
|
|
531
|
+
return new SyncMap(); // empty scope if there's no parent
|
|
539
532
|
}
|
|
540
|
-
|
|
533
|
+
const scopeMap = new ScopeMap(parent);
|
|
534
|
+
return scopeMap;
|
|
541
535
|
}
|
|
542
536
|
addOpLabel(scope, "«ops.scope»");
|
|
537
|
+
scope.needsState = true;
|
|
543
538
|
|
|
544
539
|
export function shiftLeft(a, b) {
|
|
545
540
|
return a << b;
|
package/src/runtime/symbols.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
export const cachePathSymbol = Symbol("cachePath");
|
|
1
2
|
export const codeSymbol = Symbol("code");
|
|
2
3
|
export const configSymbol = Symbol("config");
|
|
4
|
+
export const noCacheSymbol = Symbol("noCache");
|
|
3
5
|
export const scopeSymbol = Symbol("scope");
|
|
4
6
|
export const sourceSymbol = Symbol("source");
|
|
7
|
+
export const volatileSymbol = Symbol("volatile");
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { box } from "@weborigami/async-tree";
|
|
2
|
+
import { volatileSymbol } from "./symbols.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Mark the indicated value as volatile so it won't be cached.
|
|
6
|
+
*/
|
|
7
|
+
export default function volatile(value) {
|
|
8
|
+
const boxed = box(value);
|
|
9
|
+
Object.defineProperty(boxed, volatileSymbol, {
|
|
10
|
+
value: true,
|
|
11
|
+
enumerable: false,
|
|
12
|
+
});
|
|
13
|
+
return boxed;
|
|
14
|
+
}
|
|
@@ -23,11 +23,12 @@ export function assertCodeLocations(code) {
|
|
|
23
23
|
*/
|
|
24
24
|
export function createCode(array) {
|
|
25
25
|
const code = array.map((item) =>
|
|
26
|
-
item instanceof Array ? createCode(item) : item
|
|
26
|
+
item instanceof Array ? createCode(item) : item,
|
|
27
27
|
);
|
|
28
28
|
/** @type {any} */ (code).location = {
|
|
29
29
|
end: 0,
|
|
30
30
|
source: {
|
|
31
|
+
relativePath: "test.ori",
|
|
31
32
|
text: "",
|
|
32
33
|
},
|
|
33
34
|
start: 0,
|
|
@@ -46,15 +46,7 @@ describe("optimize", () => {
|
|
|
46
46
|
});
|
|
47
47
|
|
|
48
48
|
test("resolve deeper context references", () => {
|
|
49
|
-
|
|
50
|
-
const code = createCode([
|
|
51
|
-
ops.object,
|
|
52
|
-
["a", [ops.literal, 1]],
|
|
53
|
-
[
|
|
54
|
-
"more",
|
|
55
|
-
[ops.object, ["a", [markers.traverse, [markers.reference, "a"]]]],
|
|
56
|
-
],
|
|
57
|
-
]);
|
|
49
|
+
const expression = `{ a: 1, more: { a } }`;
|
|
58
50
|
const expected = [
|
|
59
51
|
ops.object,
|
|
60
52
|
["a", 1],
|
|
@@ -72,7 +64,7 @@ describe("optimize", () => {
|
|
|
72
64
|
],
|
|
73
65
|
],
|
|
74
66
|
];
|
|
75
|
-
|
|
67
|
+
assertCompile(expression, expected);
|
|
76
68
|
});
|
|
77
69
|
|
|
78
70
|
test("when defining a property, avoid recursive references", () => {
|
|
@@ -103,7 +95,7 @@ describe("optimize", () => {
|
|
|
103
95
|
assertCompile(expression, expected, "shell");
|
|
104
96
|
});
|
|
105
97
|
|
|
106
|
-
describe("resolve reference", () => {
|
|
98
|
+
describe.only("resolve reference", () => {
|
|
107
99
|
test("external reference", () => {
|
|
108
100
|
// Compilation of `folder` where folder isn't a variable
|
|
109
101
|
const code = createCode([
|
|
@@ -113,35 +105,36 @@ describe("optimize", () => {
|
|
|
113
105
|
const parent = {};
|
|
114
106
|
const expected = [
|
|
115
107
|
ops.cache,
|
|
116
|
-
|
|
117
|
-
"folder",
|
|
118
|
-
[
|
|
119
|
-
[ops.scope, parent],
|
|
120
|
-
[ops.literal, "folder"],
|
|
121
|
-
],
|
|
108
|
+
"test.ori/_refs/folder",
|
|
109
|
+
[[ops.scope], [ops.literal, "folder"]],
|
|
122
110
|
];
|
|
123
111
|
const globals = {};
|
|
124
|
-
assertCodeEqual(
|
|
112
|
+
assertCodeEqual(
|
|
113
|
+
optimize(code, { cachePath: "test.ori", globals, parent }),
|
|
114
|
+
expected,
|
|
115
|
+
);
|
|
125
116
|
});
|
|
126
117
|
|
|
127
|
-
test("external reference", () => {
|
|
128
|
-
// Compilation of `
|
|
118
|
+
test("external reference with implied unpack", () => {
|
|
119
|
+
// Compilation of `greet.ori("world")`
|
|
129
120
|
const code = createCode([
|
|
130
|
-
markers.traverse,
|
|
131
|
-
[
|
|
121
|
+
[markers.traverse, [markers.reference, "greet.ori"]],
|
|
122
|
+
[ops.literal, "world"],
|
|
132
123
|
]);
|
|
133
124
|
const parent = {};
|
|
134
125
|
const expected = [
|
|
135
|
-
ops.cache,
|
|
136
|
-
{},
|
|
137
|
-
"index.html",
|
|
138
126
|
[
|
|
139
|
-
|
|
140
|
-
|
|
127
|
+
ops.cache,
|
|
128
|
+
"test.ori/_refs/greet.ori/",
|
|
129
|
+
[ops.unpack, [[ops.scope], [ops.literal, "greet.ori"]]],
|
|
141
130
|
],
|
|
131
|
+
"world",
|
|
142
132
|
];
|
|
143
133
|
const globals = {};
|
|
144
|
-
assertCodeEqual(
|
|
134
|
+
assertCodeEqual(
|
|
135
|
+
optimize(code, { cachePath: "test.ori", globals, parent }),
|
|
136
|
+
expected,
|
|
137
|
+
);
|
|
145
138
|
});
|
|
146
139
|
|
|
147
140
|
test("external reference inside object with matching key", () => {
|
|
@@ -162,18 +155,17 @@ describe("optimize", () => {
|
|
|
162
155
|
ops.getter,
|
|
163
156
|
[
|
|
164
157
|
ops.cache,
|
|
165
|
-
|
|
166
|
-
"posts.txt",
|
|
167
|
-
[
|
|
168
|
-
[ops.scope, parent],
|
|
169
|
-
[ops.literal, "posts.txt"],
|
|
170
|
-
],
|
|
158
|
+
"test.ori/_refs/posts.txt",
|
|
159
|
+
[[ops.scope], [ops.literal, "posts.txt"]],
|
|
171
160
|
],
|
|
172
161
|
],
|
|
173
162
|
],
|
|
174
163
|
];
|
|
175
164
|
const globals = {};
|
|
176
|
-
assertCodeEqual(
|
|
165
|
+
assertCodeEqual(
|
|
166
|
+
optimize(code, { cachePath: "test.ori", globals, parent }),
|
|
167
|
+
expected,
|
|
168
|
+
);
|
|
177
169
|
});
|
|
178
170
|
|
|
179
171
|
test("global reference", () => {
|
|
@@ -233,17 +225,26 @@ describe("optimize", () => {
|
|
|
233
225
|
});
|
|
234
226
|
|
|
235
227
|
test("root directory", () => {
|
|
236
|
-
// Compilation of
|
|
237
|
-
const code = createCode([
|
|
238
|
-
|
|
239
|
-
|
|
228
|
+
// Compilation of `</etc/passwd>`
|
|
229
|
+
const code = createCode([
|
|
230
|
+
markers.traverse,
|
|
231
|
+
[markers.external, "/"],
|
|
232
|
+
[ops.literal, "etc/"],
|
|
233
|
+
[ops.literal, "passwd"],
|
|
234
|
+
]);
|
|
235
|
+
const expected = [
|
|
236
|
+
ops.cache,
|
|
237
|
+
"test.ori/_refs//etc/passwd",
|
|
238
|
+
[[ops.rootDirectory], [ops.literal, "etc/"], [ops.literal, "passwd"]],
|
|
239
|
+
];
|
|
240
|
+
assertCodeEqual(optimize(code, { cachePath: "test.ori" }), expected);
|
|
240
241
|
});
|
|
241
242
|
|
|
242
243
|
test("home directory", () => {
|
|
243
244
|
// Compilation of `<~>`
|
|
244
245
|
const code = createCode([markers.traverse, [ops.homeDirectory]]);
|
|
245
|
-
const expected = [ops.cache,
|
|
246
|
-
assertCodeEqual(optimize(code), expected);
|
|
246
|
+
const expected = [ops.cache, "test.ori/_refs/~", [ops.homeDirectory]];
|
|
247
|
+
assertCodeEqual(optimize(code, { cachePath: "test.ori" }), expected);
|
|
247
248
|
});
|
|
248
249
|
});
|
|
249
250
|
|
|
@@ -259,16 +260,18 @@ describe("optimize", () => {
|
|
|
259
260
|
const parent = {};
|
|
260
261
|
const expected = [
|
|
261
262
|
ops.cache,
|
|
262
|
-
|
|
263
|
-
"path/to/file",
|
|
263
|
+
"test.ori/_refs/path/to/file",
|
|
264
264
|
[
|
|
265
|
-
[ops.scope
|
|
265
|
+
[ops.scope],
|
|
266
266
|
[ops.literal, "path/"],
|
|
267
267
|
[ops.literal, "to/"],
|
|
268
268
|
[ops.literal, "file"],
|
|
269
269
|
],
|
|
270
270
|
];
|
|
271
|
-
assertCodeEqual(
|
|
271
|
+
assertCodeEqual(
|
|
272
|
+
optimize(code, { cachePath: "test.ori", parent }),
|
|
273
|
+
expected,
|
|
274
|
+
);
|
|
272
275
|
});
|
|
273
276
|
|
|
274
277
|
test("implicit external path", () => {
|
|
@@ -282,15 +285,13 @@ describe("optimize", () => {
|
|
|
282
285
|
const parent = {};
|
|
283
286
|
const expected = [
|
|
284
287
|
ops.cache,
|
|
285
|
-
|
|
286
|
-
"package.json/name",
|
|
287
|
-
[
|
|
288
|
-
[ops.scope, parent],
|
|
289
|
-
[ops.literal, "package.json/"],
|
|
290
|
-
[ops.literal, "name"],
|
|
291
|
-
],
|
|
288
|
+
"test.ori/_refs/package.json/name",
|
|
289
|
+
[[ops.scope], [ops.literal, "package.json/"], [ops.literal, "name"]],
|
|
292
290
|
];
|
|
293
|
-
assertCodeEqual(
|
|
291
|
+
assertCodeEqual(
|
|
292
|
+
optimize(code, { cachePath: "test.ori", globals, parent }),
|
|
293
|
+
expected,
|
|
294
|
+
);
|
|
294
295
|
});
|
|
295
296
|
|
|
296
297
|
test("local path", () => {
|
|
@@ -314,9 +315,16 @@ describe("optimize", () => {
|
|
|
314
315
|
});
|
|
315
316
|
|
|
316
317
|
function assertCompile(expression, expected, mode = "shell") {
|
|
318
|
+
const source =
|
|
319
|
+
typeof expression !== "string"
|
|
320
|
+
? expression
|
|
321
|
+
: {
|
|
322
|
+
text: expression,
|
|
323
|
+
relativePath: "test.ori",
|
|
324
|
+
};
|
|
317
325
|
const parent = new SyncMap();
|
|
318
326
|
const globals = {};
|
|
319
|
-
const fn = compile.expression(
|
|
327
|
+
const fn = compile.expression(source, { globals, mode, parent });
|
|
320
328
|
const actual = fn.code;
|
|
321
329
|
assertCodeLocations(actual);
|
|
322
330
|
assertCodeEqual(actual, expected);
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import Zip from "adm-zip";
|
|
2
|
+
import assert from "node:assert";
|
|
3
|
+
import { describe, test } from "node:test";
|
|
4
|
+
import epub_handler from "../../src/handlers/epub_handler.js";
|
|
5
|
+
|
|
6
|
+
describe("EPUB handler", () => {
|
|
7
|
+
test("ensures mimetype file comes first", async () => {
|
|
8
|
+
const tree = {
|
|
9
|
+
EPUB: {
|
|
10
|
+
"index.xhtml": "This is where the book content goes",
|
|
11
|
+
},
|
|
12
|
+
"META-INF": {
|
|
13
|
+
"container.xml": "This is where the metadata goes",
|
|
14
|
+
},
|
|
15
|
+
mimetype: "application/epub+zip",
|
|
16
|
+
};
|
|
17
|
+
const buffer = await epub_handler.pack(tree);
|
|
18
|
+
const unzipped = new Zip(buffer);
|
|
19
|
+
const entries = unzipped.getEntries();
|
|
20
|
+
const entryNames = entries.map((entry) => entry.entryName);
|
|
21
|
+
assert.deepEqual(entryNames, [
|
|
22
|
+
"mimetype",
|
|
23
|
+
"EPUB/index.xhtml",
|
|
24
|
+
"META-INF/container.xml",
|
|
25
|
+
]);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
Binary file
|
|
@@ -3,9 +3,11 @@ import assert from "node:assert";
|
|
|
3
3
|
import { describe, test } from "node:test";
|
|
4
4
|
import ori_handler from "../../src/handlers/ori_handler.js";
|
|
5
5
|
import OrigamiFileMap from "../../src/runtime/OrigamiFileMap.js";
|
|
6
|
+
import { cachePathSymbol } from "../../src/runtime/symbols.js";
|
|
6
7
|
|
|
7
8
|
const fixturesUrl = new URL("fixtures", import.meta.url);
|
|
8
9
|
const fixtures = new OrigamiFileMap(fixturesUrl);
|
|
10
|
+
await fixtures.initializeGlobals();
|
|
9
11
|
|
|
10
12
|
describe(".ori handler", async () => {
|
|
11
13
|
test("loads a string expression", async () => {
|
|
@@ -38,16 +40,18 @@ describe(".ori handler", async () => {
|
|
|
38
40
|
const tree = await ori_handler.unpack(source);
|
|
39
41
|
assert.deepEqual(
|
|
40
42
|
await Tree.traverse(tree, "public", "message"),
|
|
41
|
-
"Hello, world!"
|
|
43
|
+
"Hello, world!",
|
|
42
44
|
);
|
|
43
45
|
});
|
|
44
46
|
|
|
45
47
|
test("loads an object containing an object shorthand", async () => {
|
|
46
|
-
const assets = new ObjectMap({});
|
|
48
|
+
const assets = new ObjectMap({ a: 1, b: 2 });
|
|
47
49
|
const parent = new ObjectMap({ assets });
|
|
48
50
|
const source = `{ assets }`;
|
|
49
51
|
const object = await ori_handler.unpack(source, { parent });
|
|
50
|
-
|
|
52
|
+
const expectedValues = Array.from(assets.values());
|
|
53
|
+
const actualValues = Array.from(object.assets.values());
|
|
54
|
+
assert.deepEqual(actualValues, expectedValues);
|
|
51
55
|
});
|
|
52
56
|
|
|
53
57
|
test("loads a template literal", async () => {
|
|
@@ -84,4 +88,19 @@ describe(".ori handler", async () => {
|
|
|
84
88
|
const indexHtml = await tree["index.html"];
|
|
85
89
|
assert.equal(indexHtml, "Hello, world!");
|
|
86
90
|
});
|
|
91
|
+
|
|
92
|
+
test("enables caching for top-level object", async () => {
|
|
93
|
+
const parent = new ObjectMap({});
|
|
94
|
+
const source = `{
|
|
95
|
+
message = "Hello"
|
|
96
|
+
}`;
|
|
97
|
+
const object = await ori_handler.unpack(source, {
|
|
98
|
+
key: "test.ori",
|
|
99
|
+
parent,
|
|
100
|
+
});
|
|
101
|
+
assert.equal(object[cachePathSymbol], "test.ori/");
|
|
102
|
+
assert.deepEqual(await Tree.plain(object), {
|
|
103
|
+
message: "Hello",
|
|
104
|
+
});
|
|
105
|
+
});
|
|
87
106
|
});
|
|
@@ -22,7 +22,7 @@ describe("Origami document handler", () => {
|
|
|
22
22
|
|
|
23
23
|
test("Argument to template document available as underscore", async () => {
|
|
24
24
|
const text = "<h1>${ _ }</h1>";
|
|
25
|
-
const fn = await oridocument_handler.unpack(text);
|
|
25
|
+
const fn = await oridocument_handler.unpack(text, { key: "test.ori.html" });
|
|
26
26
|
const result = await fn("Home");
|
|
27
27
|
assert.equal(result, "<h1>Home</h1>");
|
|
28
28
|
});
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Tree } from "@weborigami/async-tree";
|
|
2
|
+
import Zip from "adm-zip";
|
|
3
|
+
import assert from "node:assert";
|
|
4
|
+
import fs from "node:fs/promises";
|
|
5
|
+
import { describe, test } from "node:test";
|
|
6
|
+
import zip_handler from "../../src/handlers/zip_handler.js";
|
|
7
|
+
|
|
8
|
+
describe("ZIP handler", () => {
|
|
9
|
+
test("creates a ZIP file as Buffer", async () => {
|
|
10
|
+
const tree = {
|
|
11
|
+
"ReadMe.md": "This is a ReadMe file.",
|
|
12
|
+
sub: {
|
|
13
|
+
"file.txt": "This is a text file in a subfolder.",
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
const buffer = await zip_handler.pack(tree);
|
|
17
|
+
const unzipped = new Zip(buffer);
|
|
18
|
+
const entries = unzipped.getEntries();
|
|
19
|
+
assert.equal(entries.length, 2);
|
|
20
|
+
assert.equal(entries[0].entryName, "ReadMe.md");
|
|
21
|
+
assert.equal(
|
|
22
|
+
entries[0].getData().toString("utf8"),
|
|
23
|
+
"This is a ReadMe file.",
|
|
24
|
+
);
|
|
25
|
+
assert.equal(entries[1].entryName, "sub/file.txt");
|
|
26
|
+
assert.equal(
|
|
27
|
+
entries[1].getData().toString("utf8"),
|
|
28
|
+
"This is a text file in a subfolder.",
|
|
29
|
+
);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test("reads a ZIP file", async () => {
|
|
33
|
+
const fixturePath = new URL("fixtures/test.zip", import.meta.url);
|
|
34
|
+
const buffer = await fs.readFile(fixturePath);
|
|
35
|
+
const tree = await zip_handler.unpack(buffer);
|
|
36
|
+
const plain = await Tree.plain(tree);
|
|
37
|
+
assert.deepEqual(Object.keys(plain), ["ReadMe.md", "sub"]);
|
|
38
|
+
assert.deepEqual(Object.keys(plain.sub), ["file.txt"]);
|
|
39
|
+
assert.equal(String(plain["ReadMe.md"]), "This is a ReadMe file.\n");
|
|
40
|
+
assert.equal(
|
|
41
|
+
String(plain.sub["file.txt"]),
|
|
42
|
+
"This is a text file in a subfolder.\n",
|
|
43
|
+
);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
2
|
+
import { beforeEach, describe, test } from "node:test";
|
|
3
|
+
import https from "../../src/protocols/https.js";
|
|
4
|
+
import systemCache from "../../src/runtime/systemCache.js";
|
|
5
|
+
|
|
6
|
+
describe("https", () => {
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
systemCache.clear();
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
test("caches fetched resources", async () => {
|
|
12
|
+
const result1 = await https("example.com", {});
|
|
13
|
+
assert(systemCache.has("https://example.com"));
|
|
14
|
+
const text = new TextDecoder().decode(result1);
|
|
15
|
+
assert(text.includes("Example Domain"));
|
|
16
|
+
const result2 = await https("example.com", {});
|
|
17
|
+
assert.strictEqual(result1, result2);
|
|
18
|
+
});
|
|
19
|
+
});
|
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import process from "node:process";
|
|
3
3
|
import { describe, test } from "node:test";
|
|
4
|
+
import coreGlobals from "../../src/project/coreGlobals.js";
|
|
4
5
|
import projectRootFromPath from "../../src/project/projectRootFromPath.js";
|
|
5
6
|
import packageProtocol from "../../src/protocols/package.js";
|
|
6
7
|
|
|
7
8
|
describe("package: protocol", () => {
|
|
8
9
|
test("returns a package's main export(s)", async () => {
|
|
9
10
|
// Reproduce the type of evaluation context object the runtime would create
|
|
10
|
-
const
|
|
11
|
-
const
|
|
11
|
+
const parent = await projectRootFromPath(process.cwd());
|
|
12
|
+
const globals = await coreGlobals();
|
|
13
|
+
/** @type {any} */ (parent).globals = globals;
|
|
14
|
+
const state = { globals, parent };
|
|
12
15
|
|
|
13
16
|
const result = await packageProtocol("@weborigami", "async-tree", state);
|
|
17
|
+
|
|
18
|
+
// Try a method from the package
|
|
14
19
|
const { toString } = result;
|
|
15
20
|
assert.equal(toString(123), "123");
|
|
16
21
|
});
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
2
|
+
import { beforeEach, describe, test } from "node:test";
|
|
3
|
+
import systemCache from "../../src/runtime/systemCache.js";
|
|
4
|
+
|
|
5
|
+
// We test AsyncCacheTransform via asyncCalcs since it's a fairly small
|
|
6
|
+
// application of the transform.
|
|
7
|
+
import { Tree } from "@weborigami/async-tree";
|
|
8
|
+
import asyncCalcs from "./asyncCalcs.js";
|
|
9
|
+
|
|
10
|
+
describe("AsyncCacheTransform", () => {
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
systemCache.clear();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test("caches values and records dependencies", async () => {
|
|
16
|
+
// { a = 2 * b, b = c + 1, c = 3 }
|
|
17
|
+
let log = [];
|
|
18
|
+
const { calcs, data } = asyncCalcs([
|
|
19
|
+
[
|
|
20
|
+
"a",
|
|
21
|
+
async () => {
|
|
22
|
+
log.push("a");
|
|
23
|
+
const b = await calcs.get("b");
|
|
24
|
+
return 2 * b;
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
[
|
|
28
|
+
"b",
|
|
29
|
+
async () => {
|
|
30
|
+
log.push("b");
|
|
31
|
+
const c = await calcs.get("c");
|
|
32
|
+
return c + 1;
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
[
|
|
36
|
+
"c",
|
|
37
|
+
async () => {
|
|
38
|
+
log.push("c");
|
|
39
|
+
return 3;
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
]);
|
|
43
|
+
|
|
44
|
+
assert.deepEqual(await Tree.entries(calcs), [
|
|
45
|
+
["a", 8],
|
|
46
|
+
["b", 4],
|
|
47
|
+
["c", 3],
|
|
48
|
+
]);
|
|
49
|
+
assert.deepEqual(log, ["a", "b", "c"]);
|
|
50
|
+
|
|
51
|
+
log = [];
|
|
52
|
+
const a1 = await calcs.get("a");
|
|
53
|
+
assert.strictEqual(a1, 8);
|
|
54
|
+
assert.deepEqual(log, []); // a is cached, no new calcs
|
|
55
|
+
|
|
56
|
+
// Replace formula for a
|
|
57
|
+
// { a = 3 * b, b = c + 1, c = 3 }
|
|
58
|
+
data.set("a", async () => {
|
|
59
|
+
log.push("a");
|
|
60
|
+
const b = await calcs.get("b");
|
|
61
|
+
return 3 * b;
|
|
62
|
+
});
|
|
63
|
+
log = [];
|
|
64
|
+
const a2 = await calcs.get("a");
|
|
65
|
+
assert.strictEqual(a2, 12);
|
|
66
|
+
assert.deepEqual(log, ["a"]); // recalc only a
|
|
67
|
+
|
|
68
|
+
// Replace formula for b
|
|
69
|
+
// { a = 3 * b, b = c + 10, c = 3 }
|
|
70
|
+
data.set("b", async () => {
|
|
71
|
+
log.push("b");
|
|
72
|
+
const c = await calcs.get("c");
|
|
73
|
+
return c + 10;
|
|
74
|
+
});
|
|
75
|
+
log = [];
|
|
76
|
+
const a3 = await calcs.get("a");
|
|
77
|
+
assert.strictEqual(a3, 39);
|
|
78
|
+
assert.deepEqual(log, ["a", "b"]); // recalc a and b
|
|
79
|
+
|
|
80
|
+
// Replace value of c
|
|
81
|
+
// { a = 3 * b, b = c + 10, c = 100 }
|
|
82
|
+
data.set("c", async () => {
|
|
83
|
+
log.push("c");
|
|
84
|
+
return 100;
|
|
85
|
+
});
|
|
86
|
+
log = [];
|
|
87
|
+
const a4 = await calcs.get("a");
|
|
88
|
+
assert.strictEqual(a4, 330);
|
|
89
|
+
assert.deepEqual(log, ["a", "b", "c"]); // recalc all
|
|
90
|
+
});
|
|
91
|
+
});
|