@weborigami/language 0.6.17 → 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.
Files changed (83) hide show
  1. package/index.ts +1 -0
  2. package/main.js +7 -1
  3. package/package.json +6 -6
  4. package/src/compiler/compile.js +10 -3
  5. package/src/compiler/optimize.js +71 -40
  6. package/src/compiler/parse.js +1 -1
  7. package/src/compiler/parserHelpers.js +5 -3
  8. package/src/handlers/getSource.js +11 -0
  9. package/src/handlers/htm_handler.js +1 -1
  10. package/src/handlers/js_handler.js +13 -4
  11. package/src/handlers/mediaTypeExtensions.json +15 -0
  12. package/src/handlers/ori_handler.js +8 -7
  13. package/src/handlers/oridocument_handler.js +17 -10
  14. package/src/handlers/processOriExport.js +17 -0
  15. package/src/handlers/tsv_handler.js +1 -1
  16. package/src/handlers/txt_handler.js +4 -2
  17. package/src/handlers/xhtml_handler.js +1 -1
  18. package/src/handlers/yaml_handler.js +6 -3
  19. package/src/project/activeProjectRoot.js +9 -0
  20. package/src/project/getGlobalsForTree.js +5 -0
  21. package/src/project/{projectGlobals.js → initializeGlobalsForTree.js} +8 -13
  22. package/src/project/jsGlobals.js +1 -0
  23. package/src/project/projectConfig.js +2 -2
  24. package/src/project/projectRootFromPath.js +2 -0
  25. package/src/protocols/constructHref.js +3 -3
  26. package/src/protocols/constructSiteTree.js +11 -2
  27. package/src/protocols/explore.js +1 -1
  28. package/src/protocols/explorehttp.js +1 -1
  29. package/src/protocols/fetchAndHandleExtension.js +23 -11
  30. package/src/protocols/files.js +1 -0
  31. package/src/protocols/http.js +4 -1
  32. package/src/protocols/https.js +4 -1
  33. package/src/protocols/httpstree.js +1 -1
  34. package/src/protocols/httptree.js +1 -1
  35. package/src/protocols/package.js +15 -3
  36. package/src/runtime/AsyncCacheTransform.d.ts +5 -0
  37. package/src/runtime/AsyncCacheTransform.js +134 -0
  38. package/src/runtime/HandleExtensionsTransform.d.ts +3 -1
  39. package/src/runtime/HandleExtensionsTransform.js +18 -2
  40. package/src/runtime/OrigamiFileMap.d.ts +5 -2
  41. package/src/runtime/OrigamiFileMap.js +27 -4
  42. package/src/runtime/ScopeMap.js +72 -0
  43. package/src/runtime/SyncCacheTransform.d.ts +8 -0
  44. package/src/runtime/SyncCacheTransform.js +133 -0
  45. package/src/runtime/SystemCacheMap.js +259 -0
  46. package/src/runtime/WatchFilesMixin.js +52 -19
  47. package/src/runtime/enableValueCaching.js +192 -0
  48. package/src/runtime/execute.js +2 -2
  49. package/src/runtime/executionContext.js +7 -0
  50. package/src/runtime/explainReferenceError.js +7 -2
  51. package/src/runtime/expressionObject.js +54 -46
  52. package/src/runtime/handleExtension.js +65 -34
  53. package/src/runtime/interop.js +2 -2
  54. package/src/runtime/mergeTrees.js +1 -1
  55. package/src/runtime/ops.js +28 -33
  56. package/src/runtime/symbols.js +3 -0
  57. package/src/runtime/systemCache.js +3 -0
  58. package/src/runtime/volatile.js +14 -0
  59. package/test/compiler/codeHelpers.js +2 -1
  60. package/test/compiler/optimize.test.js +62 -54
  61. package/test/handlers/ori_handler.test.js +22 -3
  62. package/test/handlers/oridocument_handler.test.js +1 -1
  63. package/test/protocols/https.test.js +19 -0
  64. package/test/protocols/package.test.js +7 -2
  65. package/test/runtime/AsyncCacheTransform.test.js +91 -0
  66. package/test/runtime/OrigamiFileMap.test.js +26 -23
  67. package/test/runtime/ScopeMap.test.js +49 -0
  68. package/test/runtime/SyncCacheTransform.test.js +93 -0
  69. package/test/runtime/SystemCacheMap.test.js +239 -0
  70. package/test/runtime/asyncCalcs.js +28 -0
  71. package/test/runtime/enableValueCaching.test.js +55 -0
  72. package/test/runtime/errors.test.js +53 -30
  73. package/test/runtime/evaluate.test.js +9 -4
  74. package/test/runtime/execute.test.js +6 -1
  75. package/test/runtime/expressionObject.test.js +55 -15
  76. package/test/runtime/fetchAndHandleExtension.test.js +24 -0
  77. package/test/runtime/fixtures/unpack/hello.json +1 -0
  78. package/test/runtime/handleExtension.test.js +12 -1
  79. package/test/runtime/ops.test.js +70 -65
  80. package/test/runtime/syncCalcs.js +27 -0
  81. package/test/runtime/systemCache.test.js +66 -0
  82. package/src/runtime/assignPropertyDescriptors.js +0 -23
  83. package/src/runtime/asyncStorage.js +0 -7
package/index.ts CHANGED
@@ -90,6 +90,7 @@ export type RuntimeState = {
90
90
  * Source code representation used by the parser.
91
91
  */
92
92
  export type Source = {
93
+ cachePath?: string;
93
94
  name?: string;
94
95
  text: string;
95
96
  url?: URL;
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,20 @@
1
1
  {
2
2
  "name": "@weborigami/language",
3
- "version": "0.6.17",
3
+ "version": "0.7.0-beta.1",
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.3.2",
10
- "peggy": "5.0.6",
11
- "typescript": "5.9.3"
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.6.17",
14
+ "@weborigami/async-tree": "0.7.0-beta.1",
15
15
  "exif-parser": "0.1.12",
16
16
  "watcher": "2.3.1",
17
- "yaml": "2.8.2"
17
+ "yaml": "2.9.0"
18
18
  },
19
19
  "scripts": {
20
20
  "build": "peggy --allowed-start-rules=\"*\" --format es src/compiler/origami.pegjs --output src/compiler/parse.js",
@@ -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
- cache,
40
+ cachePath,
34
41
  globals,
35
- parent,
42
+ objectCachePath,
36
43
  });
37
44
 
38
45
  // Create a function that executes the optimized code.
@@ -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, parent, locals, cache);
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 entries = args;
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
- const optimized = annotate(
78
+ let optimized = annotate(
79
79
  code.map((child, index) => {
80
- if (op === ops.object && index > 0) {
81
- const [key, value] = child;
82
- const adjustedLocals = avoidLocalRecursion(locals, key);
83
- const optimizedKey =
84
- typeof key === "string"
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
- ...options,
88
- locals: adjustedLocals,
89
- });
90
- const optimizedValue = optimize(/** @type {AnnotatedCode} */ (value), {
91
- ...options,
92
- locals: adjustedLocals,
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
- return optimize(/** @type {AnnotatedCode} */ (child), {
99
- ...options,
100
- locals,
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 cachePath(code, cache) {
161
+ function cacheExternalPath(code, cachePath) {
151
162
  const keys = code.map(keyFromCode).filter((key) => key !== null);
152
- const path = pathFromKeys(keys);
153
- return annotate([ops.cache, cache, path, code], code.location);
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, parent, location) {
184
- const scope = annotate([ops.scope, parent], location);
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, parent, locals) {
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, parent, location),
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, parent, location),
359
+ result: externalReference(key, location),
329
360
  };
330
361
  }
331
362
 
332
- function resolvePath(code, globals, parent, locals, cache) {
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, parent, locals);
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 && cache !== null) {
356
- // Cache external path
357
- return cachePath(result, cache);
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;
@@ -1,4 +1,4 @@
1
- // @generated by Peggy 5.0.6.
1
+ // @generated by Peggy 5.1.0.
2
2
  //
3
3
  // https://peggyjs.org/
4
4
 
@@ -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
- topEntries.push(...spread.slice(1));
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 = spread.slice(1).map((entry) => {
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
- currentEntries.push(...value.slice(1));
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) {
@@ -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
  }
@@ -1,2 +1,2 @@
1
1
  // .htm is a synonynm for .html
2
- export { html_handler as default } from "./handlers.js";
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
- if (!(parent && "import" in parent)) {
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 /** @type {any} */ (parent).import?.(key);
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, parent);
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, parent);
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, setParent } from "@weborigami/async-tree";
1
+ import { getParent } from "@weborigami/async-tree";
2
2
  import * as compile from "../compiler/compile.js";
3
- import projectGlobals from "../project/projectGlobals.js";
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 = options.globals ?? (await projectGlobals(parent));
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
- const result = await fn();
32
+ let result = await fn();
30
33
 
31
- if (parent) {
32
- setParent(result, parent);
33
- }
34
+ result = processOriExport(result, source, parent);
34
35
 
35
36
  return result;
36
37
  },
@@ -1,7 +1,9 @@
1
1
  import { extension, getParent, trailingSlash } from "@weborigami/async-tree";
2
2
  import * as compile from "../compiler/compile.js";
3
- import projectGlobals from "../project/projectGlobals.js";
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 template document: a plain text file that contains Origami
@@ -16,22 +18,27 @@ export default {
16
18
  const source = getSource(packed, options);
17
19
 
18
20
  // Compile the source code as an Origami template document
19
- const globals = options.globals ?? (await projectGlobals(parent));
20
- const defineFn = compile.templateDocument(source, {
21
+ const globals =
22
+ options.globals ?? getGlobalsForTree(parent) ?? (await coreGlobals());
23
+ const fn = compile.templateDocument(source, {
21
24
  front: options.front,
22
25
  globals,
23
26
  mode: "program",
24
27
  parent,
25
28
  });
26
29
 
27
- // Invoke the definition to get back the template function
28
- const result = await defineFn();
30
+ // Invoke the definition to get back the template function or object
31
+ let result = await fn();
29
32
 
30
- const key = options.key;
31
- const resultExtension = key ? extension.extname(key) : null;
32
- if (resultExtension && Object.isExtensible(result)) {
33
- // Add sidecar function so this template can be used in a map.
34
- result.key = addExtension(resultExtension);
33
+ result = processOriExport(result, source, parent);
34
+
35
+ if (result instanceof Function) {
36
+ const key = options.key;
37
+ const resultExtension = key ? extension.extname(key) : null;
38
+ if (resultExtension && Object.isExtensible(result)) {
39
+ // Add sidecar function so this template can be used in a map.
40
+ result.key = addExtension(resultExtension);
41
+ }
35
42
  }
36
43
 
37
44
  return result;
@@ -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
+ }
@@ -1,7 +1,7 @@
1
1
  import { symbols, toString } from "@weborigami/async-tree";
2
2
 
3
3
  export default {
4
- mediaType: "text/csv",
4
+ mediaType: "text/tsv",
5
5
 
6
6
  unpack(packed, options = {}) {
7
7
  const parent = options.parent ?? null;
@@ -6,7 +6,8 @@ import {
6
6
  } from "@weborigami/async-tree";
7
7
  import * as YAMLModule from "yaml";
8
8
  import * as compile from "../compiler/compile.js";
9
- import projectGlobals from "../project/projectGlobals.js";
9
+ import coreGlobals from "../project/coreGlobals.js";
10
+ import getGlobalsForTree from "../project/getGlobalsForTree.js";
10
11
  import parseFrontMatter from "./parseFrontMatter.js";
11
12
 
12
13
  // The "yaml" package doesn't seem to provide a default export that the browser can
@@ -80,7 +81,8 @@ export default {
80
81
  const { body, frontText, isOrigami } = parsed;
81
82
  let frontData;
82
83
  if (isOrigami) {
83
- const globals = await projectGlobals(parent);
84
+ const globals =
85
+ options.globals ?? getGlobalsForTree(parent) ?? (await coreGlobals());
84
86
  const compiled = compile.expression(frontText.trim(), {
85
87
  globals,
86
88
  parent,
@@ -1,2 +1,2 @@
1
1
  // .xhtml is a synonynm for .html
2
- export { html_handler as default } from "./handlers.js";
2
+ export { default } from "./html_handler.js";
@@ -8,7 +8,8 @@ import {
8
8
  } from "@weborigami/async-tree";
9
9
  import * as YAMLModule from "yaml";
10
10
  import * as compile from "../compiler/compile.js";
11
- import projectGlobals from "../project/projectGlobals.js";
11
+ import coreGlobals from "../project/coreGlobals.js";
12
+ import getGlobalsForTree from "../project/getGlobalsForTree.js";
12
13
  import getSource from "./getSource.js";
13
14
 
14
15
  // The "yaml" package doesn't seem to provide a default export that the browser can
@@ -104,7 +105,8 @@ export default {
104
105
  };
105
106
 
106
107
  async function oriCallTagForParent(parent, options, yaml) {
107
- const globals = await projectGlobals(parent);
108
+ const globals =
109
+ options.globals ?? getGlobalsForTree(parent) ?? (await coreGlobals());
108
110
  return {
109
111
  collection: "seq",
110
112
 
@@ -168,7 +170,8 @@ async function oriCallTagForParent(parent, options, yaml) {
168
170
  // Define the !ori tag for YAML parsing. This will run in the context of the
169
171
  // supplied parent.
170
172
  async function oriTagForParent(parent, options, yaml) {
171
- const globals = await projectGlobals(parent);
173
+ const globals =
174
+ options.globals ?? getGlobalsForTree(parent) ?? (await coreGlobals());
172
175
  return {
173
176
  resolve(text) {
174
177
  hasOriTags = true;
@@ -0,0 +1,9 @@
1
+ let activeProjectRoot = null;
2
+
3
+ export function get() {
4
+ return activeProjectRoot;
5
+ }
6
+
7
+ export function set(projectRoot) {
8
+ activeProjectRoot = projectRoot;
9
+ }
@@ -0,0 +1,5 @@
1
+ import { Tree } from "@weborigami/async-tree";
2
+
3
+ export default function getGlobalsForTree(map) {
4
+ return map ? Tree.root(map).globals : null;
5
+ }