@weborigami/language 0.6.16 → 0.7.0-beta.1
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 +6 -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/getSource.js +11 -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 +20 -11
- 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/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/ori_handler.test.js +22 -3
- package/test/handlers/oridocument_handler.test.js +1 -1
- 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
|
@@ -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);
|
|
@@ -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,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
|
+
});
|
|
@@ -1,40 +1,43 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import * as fs from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import { describe, test } from "node:test";
|
|
4
|
+
import { afterEach, beforeEach, describe, test } from "node:test";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import OrigamiFileMap from "../../src/runtime/OrigamiFileMap.js";
|
|
7
7
|
|
|
8
8
|
const dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
-
const tempDirectory = path.join(dirname, "fixtures/temp
|
|
9
|
+
const tempDirectory = path.join(dirname, "fixtures/temp");
|
|
10
10
|
|
|
11
11
|
describe("OrigamiFileMap", () => {
|
|
12
|
-
|
|
13
|
-
createTempDirectory();
|
|
12
|
+
let tempFiles;
|
|
14
13
|
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
// Remove any existing files or directories inside the temp directory so
|
|
16
|
+
// tests start from a clean slate. Use force so this is safe if the
|
|
17
|
+
// directory doesn't exist.
|
|
18
|
+
fs.rmSync(tempDirectory, { force: true, recursive: true });
|
|
19
|
+
fs.mkdirSync(tempDirectory, { recursive: true });
|
|
20
|
+
tempFiles = new OrigamiFileMap(tempDirectory);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
afterEach(() => {
|
|
24
|
+
// HACK: should removeEventListener, which should stop watching
|
|
25
|
+
tempFiles.unwatch();
|
|
26
|
+
fs.rmSync(tempDirectory, { force: true, recursive: true });
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test("can watch its folder for changes", { timeout: 2000 }, async () => {
|
|
30
|
+
const changedFilePath = await new Promise(async (resolve) => {
|
|
17
31
|
tempFiles.addEventListener("change", (event) => {
|
|
18
|
-
resolve(/** @type {any} */ (event).options.
|
|
32
|
+
resolve(/** @type {any} */ (event).options.filePath);
|
|
19
33
|
});
|
|
34
|
+
await tempFiles.watch();
|
|
20
35
|
tempFiles.set(
|
|
21
|
-
"
|
|
22
|
-
"This file is left over from testing and can be removed."
|
|
36
|
+
"temp.txt",
|
|
37
|
+
"This file is left over from testing and can be removed.",
|
|
23
38
|
);
|
|
24
39
|
});
|
|
25
|
-
|
|
26
|
-
assert.equal(
|
|
40
|
+
|
|
41
|
+
assert.equal(path.basename(changedFilePath), "temp.txt");
|
|
27
42
|
});
|
|
28
43
|
});
|
|
29
|
-
|
|
30
|
-
function createTempDirectory() {
|
|
31
|
-
// Remove any existing files or directories inside the temp directory so
|
|
32
|
-
// tests start from a clean slate. Use force so this is safe if the
|
|
33
|
-
// directory doesn't exist.
|
|
34
|
-
fs.rmSync(tempDirectory, { force: true, recursive: true });
|
|
35
|
-
fs.mkdirSync(tempDirectory, { recursive: true });
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function removeTempDirectory() {
|
|
39
|
-
fs.rmSync(tempDirectory, { force: true, recursive: true });
|
|
40
|
-
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { ObjectMap } from "@weborigami/async-tree";
|
|
2
|
+
import assert from "node:assert";
|
|
3
|
+
import { describe, test } from "node:test";
|
|
4
|
+
import ScopeMap from "../../src/runtime/ScopeMap.js";
|
|
5
|
+
import { cachePathSymbol } from "../../src/runtime/symbols.js";
|
|
6
|
+
import SyncCacheTransform from "../../src/runtime/SyncCacheTransform.js";
|
|
7
|
+
import systemCache from "../../src/runtime/systemCache.js";
|
|
8
|
+
|
|
9
|
+
describe("scope", () => {
|
|
10
|
+
test("gets the first defined value from the scope trees", async () => {
|
|
11
|
+
class SyncCacheObjectMap extends SyncCacheTransform(ObjectMap) {}
|
|
12
|
+
|
|
13
|
+
/** @type {any} */
|
|
14
|
+
const outer = new SyncCacheObjectMap({
|
|
15
|
+
a: 1,
|
|
16
|
+
b: 2,
|
|
17
|
+
});
|
|
18
|
+
outer[cachePathSymbol] = "outer";
|
|
19
|
+
|
|
20
|
+
/** @type {any} */
|
|
21
|
+
const inner = new SyncCacheObjectMap({
|
|
22
|
+
a: 3,
|
|
23
|
+
});
|
|
24
|
+
inner[cachePathSymbol] = "outer/inner";
|
|
25
|
+
|
|
26
|
+
inner.parent = outer;
|
|
27
|
+
const innerScope = new ScopeMap(inner);
|
|
28
|
+
|
|
29
|
+
// Inner tree has precedence
|
|
30
|
+
const a = systemCache.getOrInsertComputed("test/a", () =>
|
|
31
|
+
innerScope.get("a"),
|
|
32
|
+
);
|
|
33
|
+
assert.equal(a, 3);
|
|
34
|
+
|
|
35
|
+
// If tree doesn't have value, finds value from parent
|
|
36
|
+
const b = systemCache.getOrInsertComputed("test/b", () =>
|
|
37
|
+
innerScope.get("b"),
|
|
38
|
+
);
|
|
39
|
+
assert.equal(b, 2);
|
|
40
|
+
|
|
41
|
+
const c = systemCache.getOrInsertComputed("test/c", () =>
|
|
42
|
+
innerScope.get("c"),
|
|
43
|
+
);
|
|
44
|
+
assert.equal(c, undefined);
|
|
45
|
+
|
|
46
|
+
assert.deepEqual([...innerScope.keys()], ["a", "b"]);
|
|
47
|
+
assert.deepEqual(/** @type {any} */ (innerScope).trees, [inner, outer]);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
@@ -0,0 +1,93 @@
|
|
|
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 SyncCacheTransform via syncCalcs since it's a fairly small
|
|
6
|
+
// application of the transform.
|
|
7
|
+
import syncCalcs from "./syncCalcs.js";
|
|
8
|
+
|
|
9
|
+
describe("SyncCacheTransform", () => {
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
systemCache.clear();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
test("caches values and records dependencies", () => {
|
|
15
|
+
// { a = 2 * b, b = c + 1, c = 3 }
|
|
16
|
+
let log = [];
|
|
17
|
+
const { calcs, data } = syncCalcs([
|
|
18
|
+
[
|
|
19
|
+
"a",
|
|
20
|
+
() => {
|
|
21
|
+
log.push("a");
|
|
22
|
+
const b = calcs.get("b");
|
|
23
|
+
return 2 * b;
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
[
|
|
27
|
+
"b",
|
|
28
|
+
() => {
|
|
29
|
+
log.push("b");
|
|
30
|
+
const c = calcs.get("c");
|
|
31
|
+
return c + 1;
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
[
|
|
35
|
+
"c",
|
|
36
|
+
() => {
|
|
37
|
+
log.push("c");
|
|
38
|
+
return 3;
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
]);
|
|
42
|
+
|
|
43
|
+
assert.deepEqual(
|
|
44
|
+
[...calcs.entries()],
|
|
45
|
+
[
|
|
46
|
+
["a", 8],
|
|
47
|
+
["b", 4],
|
|
48
|
+
["c", 3],
|
|
49
|
+
],
|
|
50
|
+
);
|
|
51
|
+
assert.deepEqual(log, ["a", "b", "c"]);
|
|
52
|
+
|
|
53
|
+
log = [];
|
|
54
|
+
const a1 = calcs.get("a");
|
|
55
|
+
assert.strictEqual(a1, 8);
|
|
56
|
+
assert.deepEqual(log, []); // a is cached, no new calcs
|
|
57
|
+
|
|
58
|
+
// Replace formula for a
|
|
59
|
+
// { a = 3 * b, b = c + 1, c = 3 }
|
|
60
|
+
data.set("a", () => {
|
|
61
|
+
log.push("a");
|
|
62
|
+
const b = calcs.get("b");
|
|
63
|
+
return 3 * b;
|
|
64
|
+
});
|
|
65
|
+
log = [];
|
|
66
|
+
const a2 = calcs.get("a");
|
|
67
|
+
assert.strictEqual(a2, 12);
|
|
68
|
+
assert.deepEqual(log, ["a"]); // recalc only a
|
|
69
|
+
|
|
70
|
+
// Replace formula for b
|
|
71
|
+
// { a = 3 * b, b = c + 10, c = 3 }
|
|
72
|
+
data.set("b", () => {
|
|
73
|
+
log.push("b");
|
|
74
|
+
const c = calcs.get("c");
|
|
75
|
+
return c + 10;
|
|
76
|
+
});
|
|
77
|
+
log = [];
|
|
78
|
+
const a3 = calcs.get("a");
|
|
79
|
+
assert.strictEqual(a3, 39);
|
|
80
|
+
assert.deepEqual(log, ["a", "b"]); // recalc a and b
|
|
81
|
+
|
|
82
|
+
// Replace value of c
|
|
83
|
+
// { a = 3 * b, b = c + 10, c = 100 }
|
|
84
|
+
data.set("c", () => {
|
|
85
|
+
log.push("c");
|
|
86
|
+
return 100;
|
|
87
|
+
});
|
|
88
|
+
log = [];
|
|
89
|
+
const a4 = calcs.get("a");
|
|
90
|
+
assert.strictEqual(a4, 330);
|
|
91
|
+
assert.deepEqual(log, ["a", "b", "c"]); // recalc all
|
|
92
|
+
});
|
|
93
|
+
});
|