@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/index.ts
CHANGED
package/main.js
CHANGED
|
@@ -5,12 +5,15 @@ export { default as isOrigamiFrontMatter } from "./src/compiler/isOrigamiFrontMa
|
|
|
5
5
|
export { parse } from "./src/compiler/parse.js";
|
|
6
6
|
export * from "./src/compiler/parserHelpers.js";
|
|
7
7
|
export * as Handlers from "./src/handlers/handlers.js";
|
|
8
|
+
export * as activeProjectRoot from "./src/project/activeProjectRoot.js";
|
|
8
9
|
export { default as coreGlobals } from "./src/project/coreGlobals.js";
|
|
10
|
+
export { default as getGlobalsForTree } from "./src/project/getGlobalsForTree.js";
|
|
11
|
+
export { default as initializeGlobalsForTree } from "./src/project/initializeGlobalsForTree.js";
|
|
9
12
|
export { default as jsGlobals } from "./src/project/jsGlobals.js";
|
|
10
13
|
export { default as projectConfig } from "./src/project/projectConfig.js";
|
|
11
|
-
export { default as projectGlobals } from "./src/project/projectGlobals.js";
|
|
12
14
|
export { default as projectRoot } from "./src/project/projectRoot.js";
|
|
13
15
|
export { default as projectRootFromPath } from "./src/project/projectRootFromPath.js";
|
|
16
|
+
export { default as fetchAndHandleExtension } from "./src/protocols/fetchAndHandleExtension.js";
|
|
14
17
|
export * as Protocols from "./src/protocols/protocols.js";
|
|
15
18
|
export { formatError, highlightError, lineInfo } from "./src/runtime/errors.js";
|
|
16
19
|
export { default as evaluate } from "./src/runtime/evaluate.js";
|
|
@@ -25,5 +28,8 @@ export { default as ImportModulesMixin } from "./src/runtime/ImportModulesMixin.
|
|
|
25
28
|
export * as moduleCache from "./src/runtime/moduleCache.js";
|
|
26
29
|
export { default as OrigamiFileMap } from "./src/runtime/OrigamiFileMap.js";
|
|
27
30
|
export * as symbols from "./src/runtime/symbols.js";
|
|
31
|
+
export { default as systemCache } from "./src/runtime/systemCache.js";
|
|
32
|
+
export { default as SystemCacheMap } from "./src/runtime/SystemCacheMap.js";
|
|
28
33
|
export { default as TreeEvent } from "./src/runtime/TreeEvent.js";
|
|
34
|
+
export { default as volatile } from "./src/runtime/volatile.js";
|
|
29
35
|
export { default as WatchFilesMixin } from "./src/runtime/WatchFilesMixin.js";
|
package/package.json
CHANGED
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@weborigami/language",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0-beta.2",
|
|
4
4
|
"description": "Web Origami expression language compiler and runtime",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./main.js",
|
|
7
7
|
"types": "./index.ts",
|
|
8
8
|
"devDependencies": {
|
|
9
|
-
"@types/node": "25.
|
|
10
|
-
"peggy": "5.0
|
|
11
|
-
"typescript": "
|
|
9
|
+
"@types/node": "25.9.1",
|
|
10
|
+
"peggy": "5.1.0",
|
|
11
|
+
"typescript": "6.0.3"
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@weborigami/async-tree": "0.
|
|
14
|
+
"@weborigami/async-tree": "0.7.0-beta.2",
|
|
15
|
+
"adm-zip": "0.5.17",
|
|
15
16
|
"exif-parser": "0.1.12",
|
|
16
17
|
"watcher": "2.3.1",
|
|
17
|
-
"yaml": "2.
|
|
18
|
+
"yaml": "2.9.0"
|
|
18
19
|
},
|
|
19
20
|
"scripts": {
|
|
20
21
|
"build": "peggy --allowed-start-rules=\"*\" --format es src/compiler/origami.pegjs --output src/compiler/parse.js",
|
package/src/compiler/compile.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
|
+
import { trailingSlash } from "@weborigami/async-tree";
|
|
1
2
|
import { createExpressionFunction } from "../runtime/expressionFunction.js";
|
|
2
3
|
import optimize from "./optimize.js";
|
|
3
4
|
import { parse } from "./parse.js";
|
|
4
5
|
|
|
6
|
+
// For caching while evaluating expressions with no identified source file
|
|
7
|
+
let count = 0;
|
|
8
|
+
|
|
5
9
|
/**
|
|
6
10
|
* Compile the given Origami source code into a JavaScript function.
|
|
7
11
|
*
|
|
@@ -27,12 +31,15 @@ function compile(source, options) {
|
|
|
27
31
|
startRule,
|
|
28
32
|
});
|
|
29
33
|
|
|
34
|
+
// Select a path the code will use for caching
|
|
35
|
+
const cachePath = source.cachePath ?? `_compile${count++}`;
|
|
36
|
+
const objectCachePath = cachePath ? trailingSlash.add(cachePath) : null;
|
|
37
|
+
|
|
30
38
|
// Optimize the code
|
|
31
|
-
const cache = mode === "program" ? {} : null;
|
|
32
39
|
const optimized = optimize(code, {
|
|
33
|
-
|
|
40
|
+
cachePath,
|
|
34
41
|
globals,
|
|
35
|
-
|
|
42
|
+
objectCachePath,
|
|
36
43
|
});
|
|
37
44
|
|
|
38
45
|
// Create a function that executes the optimized code.
|
package/src/compiler/optimize.js
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
propertyInfo,
|
|
7
7
|
} from "../runtime/expressionObject.js";
|
|
8
8
|
import { ops } from "../runtime/internal.js";
|
|
9
|
+
import SystemCacheMap from "../runtime/SystemCacheMap.js";
|
|
9
10
|
import { annotate, markers, spanLocations } from "./parserHelpers.js";
|
|
10
11
|
|
|
11
12
|
export const REFERENCE_PARAM = 1;
|
|
@@ -30,9 +31,9 @@ export const REFERENCE_EXTERNAL = 4;
|
|
|
30
31
|
* @returns {AnnotatedCode}
|
|
31
32
|
*/
|
|
32
33
|
export default function optimize(code, options = {}) {
|
|
34
|
+
// Cache path for this source file
|
|
35
|
+
const cachePath = options.cachePath ?? null;
|
|
33
36
|
const globals = options.globals ?? jsGlobals;
|
|
34
|
-
const cache = options.cache === undefined ? {} : options.cache;
|
|
35
|
-
const parent = options.parent ?? null;
|
|
36
37
|
|
|
37
38
|
// The locals is an array, one item for each function or object context that
|
|
38
39
|
// has been entered. The array grows to the right. Array items are objects
|
|
@@ -48,7 +49,7 @@ export default function optimize(code, options = {}) {
|
|
|
48
49
|
return globals[args[0]];
|
|
49
50
|
|
|
50
51
|
case markers.traverse:
|
|
51
|
-
return resolvePath(code, globals,
|
|
52
|
+
return resolvePath(code, globals, locals, cachePath);
|
|
52
53
|
|
|
53
54
|
case ops.lambda:
|
|
54
55
|
const parameters = args[1];
|
|
@@ -65,8 +66,7 @@ export default function optimize(code, options = {}) {
|
|
|
65
66
|
return inlineLiteral(code);
|
|
66
67
|
|
|
67
68
|
case ops.object:
|
|
68
|
-
const
|
|
69
|
-
const propertyNames = getPropertyNames(entries);
|
|
69
|
+
const propertyNames = getPropertyNames(args);
|
|
70
70
|
locals.push({
|
|
71
71
|
type: REFERENCE_INHERITED,
|
|
72
72
|
names: propertyNames,
|
|
@@ -75,37 +75,48 @@ export default function optimize(code, options = {}) {
|
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
// Optimize children
|
|
78
|
-
|
|
78
|
+
let optimized = annotate(
|
|
79
79
|
code.map((child, index) => {
|
|
80
|
-
if (op === ops.object
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
80
|
+
if (op === ops.object) {
|
|
81
|
+
if (index === 0) {
|
|
82
|
+
return child; // return op as is
|
|
83
|
+
} else {
|
|
84
|
+
// Object entry
|
|
85
|
+
const [key, value] = child;
|
|
86
|
+
const adjustedLocals = avoidLocalRecursion(locals, key);
|
|
87
|
+
const isStringKey = typeof key === "string";
|
|
88
|
+
const childOptions = {
|
|
89
|
+
...options,
|
|
90
|
+
locals: adjustedLocals,
|
|
91
|
+
};
|
|
92
|
+
const optimizedKey = isStringKey
|
|
85
93
|
? key
|
|
86
|
-
: optimize(/** @type {AnnotatedCode} */ (key),
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
});
|
|
94
|
-
return annotate([optimizedKey, optimizedValue], child.location);
|
|
94
|
+
: optimize(/** @type {AnnotatedCode} */ (key), childOptions);
|
|
95
|
+
const optimizedValue = optimize(
|
|
96
|
+
/** @type {AnnotatedCode} */ (value),
|
|
97
|
+
childOptions,
|
|
98
|
+
);
|
|
99
|
+
return annotate([optimizedKey, optimizedValue], child.location);
|
|
100
|
+
}
|
|
95
101
|
} else if (Array.isArray(child) && "location" in child) {
|
|
96
102
|
// Review: Aside from ops.object (above), what non-instruction arrays
|
|
97
103
|
// does this descend into?
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
});
|
|
104
|
+
const childOptions = { ...options, locals };
|
|
105
|
+
// Objects below here are detached from top-level object; not cached
|
|
106
|
+
delete childOptions.objectCachePath;
|
|
107
|
+
return optimize(/** @type {AnnotatedCode} */ (child), childOptions);
|
|
102
108
|
} else {
|
|
103
109
|
return child;
|
|
104
110
|
}
|
|
105
111
|
}),
|
|
106
|
-
code.location
|
|
112
|
+
code.location,
|
|
107
113
|
);
|
|
108
114
|
|
|
115
|
+
if (optimized[0] instanceof Array && optimized[0][0] === ops.cache) {
|
|
116
|
+
// An external reference in function position: add implied unpack
|
|
117
|
+
optimized = impliedUnpack(optimized);
|
|
118
|
+
}
|
|
119
|
+
|
|
109
120
|
return annotate(optimized, code.location);
|
|
110
121
|
}
|
|
111
122
|
|
|
@@ -129,7 +140,7 @@ function avoidLocalRecursion(locals, key) {
|
|
|
129
140
|
const matchingKeyIndex = locals[currentFrameIndex].names.findIndex(
|
|
130
141
|
(name) =>
|
|
131
142
|
// Ignore trailing slashes when comparing keys
|
|
132
|
-
trailingSlash.remove(name) === trailingSlash.remove(key)
|
|
143
|
+
trailingSlash.remove(name) === trailingSlash.remove(key),
|
|
133
144
|
);
|
|
134
145
|
|
|
135
146
|
if (matchingKeyIndex >= 0) {
|
|
@@ -147,10 +158,11 @@ function avoidLocalRecursion(locals, key) {
|
|
|
147
158
|
}
|
|
148
159
|
}
|
|
149
160
|
|
|
150
|
-
function
|
|
161
|
+
function cacheExternalPath(code, cachePath) {
|
|
151
162
|
const keys = code.map(keyFromCode).filter((key) => key !== null);
|
|
152
|
-
const
|
|
153
|
-
|
|
163
|
+
const keysPath = pathFromKeys(keys);
|
|
164
|
+
const refCachePath = SystemCacheMap.joinPath(cachePath, "_refs", keysPath);
|
|
165
|
+
return annotate([ops.cache, refCachePath, code], code.location);
|
|
154
166
|
}
|
|
155
167
|
|
|
156
168
|
// A reference with periods like x.y.z
|
|
@@ -180,8 +192,8 @@ function compoundReference(key, globals, locals, location) {
|
|
|
180
192
|
return { type: headReference.type, result };
|
|
181
193
|
}
|
|
182
194
|
|
|
183
|
-
function externalReference(key,
|
|
184
|
-
const scope = annotate([ops.scope
|
|
195
|
+
function externalReference(key, location) {
|
|
196
|
+
const scope = annotate([ops.scope], location);
|
|
185
197
|
const literal = annotate([ops.literal, key], location);
|
|
186
198
|
return annotate([scope, literal], location);
|
|
187
199
|
}
|
|
@@ -193,7 +205,7 @@ function findLocalDetails(key, locals) {
|
|
|
193
205
|
for (let i = locals.length - 1; i >= 0; i--) {
|
|
194
206
|
const { type, names } = locals[i];
|
|
195
207
|
const local = names.find(
|
|
196
|
-
(name) => trailingSlash.remove(name) === normalized
|
|
208
|
+
(name) => trailingSlash.remove(name) === normalized,
|
|
197
209
|
);
|
|
198
210
|
if (local) {
|
|
199
211
|
const depth = type === REFERENCE_PARAM ? paramDepth : inheritedDepth;
|
|
@@ -221,6 +233,25 @@ function globalReference(key, globals) {
|
|
|
221
233
|
return globals[normalized];
|
|
222
234
|
}
|
|
223
235
|
|
|
236
|
+
// The code starts with a cached external reference in function position. If the
|
|
237
|
+
// cache path doesn't end in a trailing slash, wrap the external reference in an
|
|
238
|
+
// unpack operation.
|
|
239
|
+
function impliedUnpack(code) {
|
|
240
|
+
const [opCache, cachePath, refCode] = code[0];
|
|
241
|
+
if (cachePath.endsWith("/")) {
|
|
242
|
+
// Will already be unpacked
|
|
243
|
+
return code;
|
|
244
|
+
}
|
|
245
|
+
const modifiedPath = cachePath + "/";
|
|
246
|
+
const modifiedRefCode = annotate([ops.unpack, refCode], code[0].location);
|
|
247
|
+
const unpacked = annotate(
|
|
248
|
+
[opCache, modifiedPath, modifiedRefCode],
|
|
249
|
+
code[0].location,
|
|
250
|
+
);
|
|
251
|
+
const modified = annotate([unpacked, ...code.slice(1)], code.location);
|
|
252
|
+
return modified;
|
|
253
|
+
}
|
|
254
|
+
|
|
224
255
|
function inheritedReference(key, depth, location) {
|
|
225
256
|
const literal = annotate([ops.literal, key], location);
|
|
226
257
|
const inherited = annotate([ops.inherited, depth], location);
|
|
@@ -283,7 +314,7 @@ function paramReference(key, depth, location) {
|
|
|
283
314
|
return annotate([params, literal], location);
|
|
284
315
|
}
|
|
285
316
|
|
|
286
|
-
function reference(code, globals,
|
|
317
|
+
function reference(code, globals, locals) {
|
|
287
318
|
const key = keyFromCode(code);
|
|
288
319
|
const normalized = trailingSlash.remove(key);
|
|
289
320
|
const location = code.location;
|
|
@@ -306,7 +337,7 @@ function reference(code, globals, parent, locals) {
|
|
|
306
337
|
// Explicit external reference
|
|
307
338
|
return {
|
|
308
339
|
type: REFERENCE_EXTERNAL,
|
|
309
|
-
result: externalReference(key,
|
|
340
|
+
result: externalReference(key, location),
|
|
310
341
|
};
|
|
311
342
|
}
|
|
312
343
|
|
|
@@ -325,15 +356,15 @@ function reference(code, globals, parent, locals) {
|
|
|
325
356
|
// Must be external
|
|
326
357
|
return {
|
|
327
358
|
type: REFERENCE_EXTERNAL,
|
|
328
|
-
result: externalReference(key,
|
|
359
|
+
result: externalReference(key, location),
|
|
329
360
|
};
|
|
330
361
|
}
|
|
331
362
|
|
|
332
|
-
function resolvePath(code, globals,
|
|
363
|
+
function resolvePath(code, globals, locals, cachePath) {
|
|
333
364
|
const args = code.slice(1);
|
|
334
365
|
const [head, ...tail] = args;
|
|
335
366
|
|
|
336
|
-
let { type, result } = reference(head, globals,
|
|
367
|
+
let { type, result } = reference(head, globals, locals);
|
|
337
368
|
|
|
338
369
|
if (tail.length > 0) {
|
|
339
370
|
// If the result is a traversal, we can safely extend it
|
|
@@ -352,9 +383,9 @@ function resolvePath(code, globals, parent, locals, cache) {
|
|
|
352
383
|
}
|
|
353
384
|
}
|
|
354
385
|
|
|
355
|
-
if (type === REFERENCE_EXTERNAL &&
|
|
356
|
-
//
|
|
357
|
-
return
|
|
386
|
+
if (type === REFERENCE_EXTERNAL && cachePath) {
|
|
387
|
+
// External references should be cached
|
|
388
|
+
return cacheExternalPath(result, cachePath);
|
|
358
389
|
}
|
|
359
390
|
|
|
360
391
|
return result;
|
package/src/compiler/parse.js
CHANGED
|
@@ -341,9 +341,10 @@ function makeMerge(spreads, location) {
|
|
|
341
341
|
|
|
342
342
|
for (const spread of spreads) {
|
|
343
343
|
if (spread[0] === ops.object) {
|
|
344
|
-
|
|
344
|
+
const spreadEntries = spread.slice(1);
|
|
345
|
+
topEntries.push(...spreadEntries);
|
|
345
346
|
// Also add an object to the result with indirect references
|
|
346
|
-
const indirectEntries =
|
|
347
|
+
const indirectEntries = spreadEntries.map((entry) => {
|
|
347
348
|
const [key] = entry;
|
|
348
349
|
const parent = annotate([ops.inherited, 1], entry.location);
|
|
349
350
|
const reference = annotate([parent, key], entry.location);
|
|
@@ -389,7 +390,8 @@ export function makeObject(entries, location) {
|
|
|
389
390
|
if (key === markers.spread) {
|
|
390
391
|
if (value[0] === ops.object) {
|
|
391
392
|
// Spread of an object; fold into current object
|
|
392
|
-
|
|
393
|
+
const spreadEntries = value.slice(1);
|
|
394
|
+
currentEntries.push(...spreadEntries);
|
|
393
395
|
} else {
|
|
394
396
|
// Spread of a tree; accumulate
|
|
395
397
|
if (currentEntries.length > 0) {
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { extension, trailingSlash } from "@weborigami/async-tree";
|
|
2
|
+
|
|
3
|
+
// Return a function that adds the given extension
|
|
4
|
+
export default function addExtensionKeyFn(resultExtension) {
|
|
5
|
+
const keyFn = (sourceValue, sourceKey) => {
|
|
6
|
+
if (sourceKey === undefined) {
|
|
7
|
+
return undefined;
|
|
8
|
+
}
|
|
9
|
+
const normalizedKey = trailingSlash.remove(sourceKey);
|
|
10
|
+
const sourceExtension = extension.extname(normalizedKey);
|
|
11
|
+
const resultKey = sourceExtension
|
|
12
|
+
? extension.replace(normalizedKey, sourceExtension, resultExtension)
|
|
13
|
+
: normalizedKey + resultExtension;
|
|
14
|
+
return resultKey;
|
|
15
|
+
};
|
|
16
|
+
keyFn.needsSourceValue = false;
|
|
17
|
+
return keyFn;
|
|
18
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { AsyncMap, isUnpackable, Tree } from "@weborigami/async-tree";
|
|
2
|
+
import addExtensionKeyFn from "./addExtensionKeyFn.js";
|
|
3
|
+
import zip_handler from "./zip_handler.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Handler for EPUB files
|
|
7
|
+
*/
|
|
8
|
+
const epub_handler = {
|
|
9
|
+
mediaType: "application/epub+zip",
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Package a tree of files as an EPUB file in Buffer form.
|
|
13
|
+
*
|
|
14
|
+
* This calls the pack() method for ZIP files, but ensures the `mimetype` file
|
|
15
|
+
* is the first file in the package -- a requirement for EPUB files.
|
|
16
|
+
*
|
|
17
|
+
* @param {import("@weborigami/async-tree").Maplike} maplike
|
|
18
|
+
*/
|
|
19
|
+
async pack(maplike) {
|
|
20
|
+
if (isUnpackable(maplike)) {
|
|
21
|
+
maplike = await maplike.unpack();
|
|
22
|
+
}
|
|
23
|
+
const tree = Tree.from(maplike, { deep: true });
|
|
24
|
+
return zip_handler.pack(mimetypeFirst(tree));
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
async unpack(buffer, options) {
|
|
28
|
+
return zip_handler.unpack(buffer, options);
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/** @type {any} */ (epub_handler.pack).key = addExtensionKeyFn(".epub");
|
|
33
|
+
|
|
34
|
+
export default epub_handler;
|
|
35
|
+
|
|
36
|
+
// A tree with its `mimetype` file first
|
|
37
|
+
function mimetypeFirst(tree) {
|
|
38
|
+
return Object.assign(new AsyncMap(), {
|
|
39
|
+
async get(key) {
|
|
40
|
+
return tree.get(key);
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
async *keys() {
|
|
44
|
+
const keys = await Tree.keys(tree);
|
|
45
|
+
// Move `mimetype` (if present) to the front of the list.
|
|
46
|
+
const index = keys.indexOf("mimetype");
|
|
47
|
+
if (index >= 0) {
|
|
48
|
+
keys.splice(index, 1);
|
|
49
|
+
keys.unshift("mimetype");
|
|
50
|
+
}
|
|
51
|
+
yield* keys;
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getParent, toString } from "@weborigami/async-tree";
|
|
2
|
+
import SystemCacheMap from "../runtime/SystemCacheMap.js";
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Given packed source text and a handler's options, return a source
|
|
@@ -9,6 +10,7 @@ export default function getSource(packed, options = {}) {
|
|
|
9
10
|
|
|
10
11
|
// Try to determine a URL for error messages
|
|
11
12
|
const sourceName = options.key;
|
|
13
|
+
let cachePath;
|
|
12
14
|
let url;
|
|
13
15
|
if (sourceName) {
|
|
14
16
|
if (/** @type {any} */ (parent)?.url) {
|
|
@@ -24,6 +26,11 @@ export default function getSource(packed, options = {}) {
|
|
|
24
26
|
parentHref += "/";
|
|
25
27
|
}
|
|
26
28
|
url = new URL(sourceName, parentHref);
|
|
29
|
+
|
|
30
|
+
const parentPath = /** @type {any} */ (parent).path;
|
|
31
|
+
cachePath = SystemCacheMap.joinPath(parentPath, sourceName);
|
|
32
|
+
} else {
|
|
33
|
+
cachePath = sourceName;
|
|
27
34
|
}
|
|
28
35
|
}
|
|
29
36
|
|
|
@@ -32,5 +39,9 @@ export default function getSource(packed, options = {}) {
|
|
|
32
39
|
name: options.key,
|
|
33
40
|
url,
|
|
34
41
|
};
|
|
42
|
+
if (cachePath) {
|
|
43
|
+
source.cachePath = cachePath;
|
|
44
|
+
}
|
|
45
|
+
|
|
35
46
|
return source;
|
|
36
47
|
}
|
package/src/handlers/handlers.js
CHANGED
|
@@ -20,6 +20,7 @@ export { default as txt_handler } from "./txt_handler.js";
|
|
|
20
20
|
|
|
21
21
|
export { default as css_handler } from "./css_handler.js";
|
|
22
22
|
export { default as csv_handler } from "./csv_handler.js";
|
|
23
|
+
export { default as epub_handler } from "./epub_handler.js";
|
|
23
24
|
export { default as htm_handler } from "./htm_handler.js";
|
|
24
25
|
export { default as html_handler } from "./html_handler.js";
|
|
25
26
|
export { default as jpeg_handler } from "./jpeg_handler.js";
|
|
@@ -33,3 +34,4 @@ export { default as wasm_handler } from "./wasm_handler.js";
|
|
|
33
34
|
export { default as xhtml_handler } from "./xhtml_handler.js";
|
|
34
35
|
export { default as yaml_handler } from "./yaml_handler.js";
|
|
35
36
|
export { default as yml_handler } from "./yml_handler.js";
|
|
37
|
+
export { default as zip_handler } from "./zip_handler.js";
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// .htm is a synonynm for .html
|
|
2
|
-
export {
|
|
2
|
+
export { default } from "./html_handler.js";
|
|
@@ -10,23 +10,32 @@ export default {
|
|
|
10
10
|
/** @type {import("@weborigami/async-tree").UnpackFunction} */
|
|
11
11
|
async unpack(packed, options = {}) {
|
|
12
12
|
const { key, parent } = options;
|
|
13
|
-
|
|
13
|
+
let importTarget = parent;
|
|
14
|
+
while (importTarget.source && !importTarget.import) {
|
|
15
|
+
importTarget = importTarget.source;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (!importTarget) {
|
|
14
19
|
throw new TypeError(
|
|
15
20
|
"The parent tree must support importing modules to unpack JavaScript files.",
|
|
16
21
|
);
|
|
17
22
|
}
|
|
18
23
|
|
|
19
|
-
const object = await
|
|
24
|
+
const object = await importTarget.import?.(key);
|
|
20
25
|
|
|
21
26
|
let bound;
|
|
27
|
+
let bindTarget = parent;
|
|
28
|
+
while (bindTarget.result) {
|
|
29
|
+
bindTarget = bindTarget.result;
|
|
30
|
+
}
|
|
22
31
|
if ("default" in object) {
|
|
23
32
|
// Module with a default export; return that.
|
|
24
|
-
bound = bindToParent(object.default,
|
|
33
|
+
bound = bindToParent(object.default, bindTarget);
|
|
25
34
|
} else {
|
|
26
35
|
// Module with multiple named exports.
|
|
27
36
|
bound = {};
|
|
28
37
|
for (const [name, value] of Object.entries(object)) {
|
|
29
|
-
bound[name] = bindToParent(value,
|
|
38
|
+
bound[name] = bindToParent(value, bindTarget);
|
|
30
39
|
}
|
|
31
40
|
}
|
|
32
41
|
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"application/json": ".json",
|
|
3
|
+
"application/wasm": ".wasm",
|
|
4
|
+
"application/xhtml+xml": ".xhtml",
|
|
5
|
+
"application/xml": ".xml",
|
|
6
|
+
"application/yaml": ".yaml",
|
|
7
|
+
"image/jpeg": ".jpg",
|
|
8
|
+
"image/jpg": ".jpg",
|
|
9
|
+
"text/css": ".css",
|
|
10
|
+
"text/csv": ".csv",
|
|
11
|
+
"text/html": ".html",
|
|
12
|
+
"text/plain": ".txt",
|
|
13
|
+
"text/tsv": ".tsv",
|
|
14
|
+
"text/xml": ".xml"
|
|
15
|
+
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { getParent
|
|
1
|
+
import { getParent } from "@weborigami/async-tree";
|
|
2
2
|
import * as compile from "../compiler/compile.js";
|
|
3
|
-
import
|
|
3
|
+
import coreGlobals from "../project/coreGlobals.js";
|
|
4
|
+
import getGlobalsForTree from "../project/getGlobalsForTree.js";
|
|
4
5
|
import getSource from "./getSource.js";
|
|
6
|
+
import processOriExport from "./processOriExport.js";
|
|
5
7
|
|
|
6
8
|
/**
|
|
7
9
|
* An Origami expression file
|
|
@@ -18,7 +20,8 @@ export default {
|
|
|
18
20
|
|
|
19
21
|
// Compile the source code as an Origami program
|
|
20
22
|
const compiler = options.compiler ?? compile.program;
|
|
21
|
-
const globals =
|
|
23
|
+
const globals =
|
|
24
|
+
options.globals ?? getGlobalsForTree(parent) ?? (await coreGlobals());
|
|
22
25
|
const fn = compiler(source, {
|
|
23
26
|
globals,
|
|
24
27
|
mode: "program",
|
|
@@ -26,11 +29,9 @@ export default {
|
|
|
26
29
|
});
|
|
27
30
|
|
|
28
31
|
// Evaluate the program
|
|
29
|
-
|
|
32
|
+
let result = await fn();
|
|
30
33
|
|
|
31
|
-
|
|
32
|
-
setParent(result, parent);
|
|
33
|
-
}
|
|
34
|
+
result = processOriExport(result, source, parent);
|
|
34
35
|
|
|
35
36
|
return result;
|
|
36
37
|
},
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
import { extension, getParent
|
|
1
|
+
import { extension, getParent } from "@weborigami/async-tree";
|
|
2
2
|
import * as compile from "../compiler/compile.js";
|
|
3
|
-
import
|
|
3
|
+
import coreGlobals from "../project/coreGlobals.js";
|
|
4
|
+
import getGlobalsForTree from "../project/getGlobalsForTree.js";
|
|
5
|
+
import addExtensionKeyFn from "./addExtensionKeyFn.js";
|
|
4
6
|
import getSource from "./getSource.js";
|
|
7
|
+
import processOriExport from "./processOriExport.js";
|
|
5
8
|
|
|
6
9
|
/**
|
|
7
10
|
* An Origami template document: a plain text file that contains Origami
|
|
@@ -16,41 +19,29 @@ export default {
|
|
|
16
19
|
const source = getSource(packed, options);
|
|
17
20
|
|
|
18
21
|
// Compile the source code as an Origami template document
|
|
19
|
-
const globals =
|
|
20
|
-
|
|
22
|
+
const globals =
|
|
23
|
+
options.globals ?? getGlobalsForTree(parent) ?? (await coreGlobals());
|
|
24
|
+
const fn = compile.templateDocument(source, {
|
|
21
25
|
front: options.front,
|
|
22
26
|
globals,
|
|
23
27
|
mode: "program",
|
|
24
28
|
parent,
|
|
25
29
|
});
|
|
26
30
|
|
|
27
|
-
// Invoke the definition to get back the template function
|
|
28
|
-
|
|
31
|
+
// Invoke the definition to get back the template function or object
|
|
32
|
+
let result = await fn();
|
|
29
33
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
if (
|
|
33
|
-
|
|
34
|
-
|
|
34
|
+
result = processOriExport(result, source, parent);
|
|
35
|
+
|
|
36
|
+
if (result instanceof Function) {
|
|
37
|
+
const key = options.key;
|
|
38
|
+
const resultExtension = key ? extension.extname(key) : null;
|
|
39
|
+
if (resultExtension && Object.isExtensible(result)) {
|
|
40
|
+
// Add sidecar function so this template can be used in a map.
|
|
41
|
+
result.key = addExtensionKeyFn(resultExtension);
|
|
42
|
+
}
|
|
35
43
|
}
|
|
36
44
|
|
|
37
45
|
return result;
|
|
38
46
|
},
|
|
39
47
|
};
|
|
40
|
-
|
|
41
|
-
// Return a function that adds the given extension
|
|
42
|
-
function addExtension(resultExtension) {
|
|
43
|
-
const keyFn = (sourceValue, sourceKey) => {
|
|
44
|
-
if (sourceKey === undefined) {
|
|
45
|
-
return undefined;
|
|
46
|
-
}
|
|
47
|
-
const normalizedKey = trailingSlash.remove(sourceKey);
|
|
48
|
-
const sourceExtension = extension.extname(normalizedKey);
|
|
49
|
-
const resultKey = sourceExtension
|
|
50
|
-
? extension.replace(normalizedKey, sourceExtension, resultExtension)
|
|
51
|
-
: normalizedKey + resultExtension;
|
|
52
|
-
return resultKey;
|
|
53
|
-
};
|
|
54
|
-
keyFn.needsSourceValue = false;
|
|
55
|
-
return keyFn;
|
|
56
|
-
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { setParent, trailingSlash } from "@weborigami/async-tree";
|
|
2
|
+
import enableValueCaching from "../runtime/enableValueCaching.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Given an object that's the top-level result of an Origami file, perform any
|
|
6
|
+
* necessary processing.
|
|
7
|
+
*/
|
|
8
|
+
export default function processOriExport(value, source, parent) {
|
|
9
|
+
setParent(value, parent);
|
|
10
|
+
|
|
11
|
+
if (source.cachePath) {
|
|
12
|
+
const cachePath = trailingSlash.add(source.cachePath);
|
|
13
|
+
value = enableValueCaching(value, cachePath);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return value;
|
|
17
|
+
}
|