@weborigami/language 0.0.71-beta.1 → 0.0.72

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@weborigami/language",
3
- "version": "0.0.71-beta.1",
3
+ "version": "0.0.72",
4
4
  "description": "Web Origami expression language compiler and runtime",
5
5
  "type": "module",
6
6
  "main": "./main.js",
@@ -11,8 +11,8 @@
11
11
  "typescript": "5.6.2"
12
12
  },
13
13
  "dependencies": {
14
- "@weborigami/async-tree": "0.0.71-beta.1",
15
- "@weborigami/types": "0.0.71-beta.1",
14
+ "@weborigami/async-tree": "0.0.72",
15
+ "@weborigami/types": "0.0.72",
16
16
  "watcher": "2.3.1"
17
17
  },
18
18
  "scripts": {
@@ -1,3 +1,4 @@
1
+ import { trailingSlash } from "@weborigami/async-tree";
1
2
  import * as ops from "../runtime/ops.js";
2
3
 
3
4
  // Parser helpers
@@ -21,9 +22,15 @@ function avoidRecursivePropertyCalls(code, key) {
21
22
  return code;
22
23
  }
23
24
  let modified;
24
- if (code[0] === ops.scope && code[1] === key) {
25
+ if (
26
+ code[0] === ops.scope &&
27
+ trailingSlash.remove(code[1]) === trailingSlash.remove(key)
28
+ ) {
25
29
  // Rewrite to avoid recursion
26
- modified = [ops.inherited, key];
30
+ modified = [ops.inherited, code[1]];
31
+ } else if (code[0] === ops.lambda && code[1].includes(key)) {
32
+ // Lambda that defines the key; don't rewrite
33
+ return code;
27
34
  } else {
28
35
  // Process any nested code
29
36
  modified = code.map((value) => avoidRecursivePropertyCalls(value, key));
@@ -53,7 +53,7 @@ export async function getExtensionHandler(parent, extension) {
53
53
  handlersForContainer.set(parent, handlers);
54
54
  }
55
55
 
56
- const handlerName = `${extension.slice(1)}_handler`;
56
+ const handlerName = `${extension.slice(1)}.handler`;
57
57
  const parentScope = scope(parent);
58
58
 
59
59
  /** @type {Promise<ExtensionHandler>} */
@@ -95,7 +95,7 @@ export async function handleExtension(parent, value, key) {
95
95
  }
96
96
 
97
97
  // Special case: `.ori.<ext>` extensions are Origami documents.
98
- const extension = key.match(/\.ori\.\S+$/) ? ".ori_document" : extname(key);
98
+ const extension = key.match(/\.ori\.\S+$/) ? ".oridocument" : extname(key);
99
99
  if (extension) {
100
100
  const handler = await getExtensionHandler(parent, extension);
101
101
  if (handler) {
@@ -62,10 +62,6 @@ export async function cache(key, cache) {
62
62
  return value;
63
63
  }
64
64
 
65
- // The assign op is a placeholder for an assignment declaration.
66
- // It is only used during parsing -- it shouldn't be executed.
67
- export const assign = "«ops.assign»";
68
-
69
65
  /**
70
66
  * Concatenate the given arguments.
71
67
  *
@@ -142,6 +138,18 @@ async function constructSiteTree(protocol, treeClass, parent, host, ...keys) {
142
138
  return lastKey ? result.get(lastKey) : result;
143
139
  }
144
140
 
141
+ /**
142
+ * A site tree with JSON Keys via HTTPS.
143
+ *
144
+ * @this {AsyncTree|null}
145
+ * @param {string} host
146
+ * @param {...string} keys
147
+ */
148
+ export function explorableSite(host, ...keys) {
149
+ return constructSiteTree("https:", ExplorableSiteTree, this, host, ...keys);
150
+ }
151
+ addOpLabel(explorableSite, "«ops.explorableSite»");
152
+
145
153
  /**
146
154
  * Fetch the resource at the given href.
147
155
  *
@@ -171,18 +179,6 @@ async function fetchResponse(href) {
171
179
  */
172
180
  export const getter = new String("«ops.getter»");
173
181
 
174
- /**
175
- * A site tree with JSON Keys via HTTPS.
176
- *
177
- * @this {AsyncTree|null}
178
- * @param {string} host
179
- * @param {...string} keys
180
- */
181
- export function explorableSite(host, ...keys) {
182
- return constructSiteTree("https:", ExplorableSiteTree, this, host, ...keys);
183
- }
184
- addOpLabel(explorableSite, "«ops.explorableSite»");
185
-
186
182
  /**
187
183
  * Construct a files tree for the filesystem root.
188
184
  *
@@ -257,12 +253,11 @@ export function lambda(parameters, code) {
257
253
 
258
254
  /** @this {AsyncTree|null} */
259
255
  async function invoke(...args) {
260
- // Add arguments and @recurse to scope.
256
+ // Add arguments to scope.
261
257
  const ambients = {};
262
258
  for (const parameter of parameters) {
263
259
  ambients[parameter] = args.shift();
264
260
  }
265
- ambients["@recurse"] = invoke;
266
261
  const ambientTree = new ObjectTree(ambients);
267
262
  ambientTree.parent = this;
268
263
 
@@ -406,6 +406,18 @@ describe("Origami parser", () => {
406
406
  assertParse("objectEntry", "foo", ["foo", [ops.inherited, "foo"]]);
407
407
  assertParse("objectEntry", "x: y", ["x", [ops.scope, "y"]]);
408
408
  assertParse("objectEntry", "a: a", ["a", [ops.inherited, "a"]]);
409
+ assertParse("objectEntry", "a: (a) => a", [
410
+ "a",
411
+ [ops.lambda, ["a"], [ops.scope, "a"]],
412
+ ]);
413
+ assertParse("objectEntry", "posts/: @map(posts, post.ori)", [
414
+ "posts/",
415
+ [
416
+ [ops.scope, "@map"],
417
+ [ops.inherited, "posts"],
418
+ [ops.scope, "post.ori"],
419
+ ],
420
+ ]);
409
421
  });
410
422
 
411
423
  test("objectGetter", () => {
@@ -54,7 +54,7 @@ describe("expressionObject", () => {
54
54
  test("returned object values can be unpacked", async () => {
55
55
  const entries = [["data.json", `{ "a": 1 }`]];
56
56
  const parent = new ObjectTree({
57
- json_handler: {
57
+ "json.handler": {
58
58
  unpack: JSON.parse,
59
59
  },
60
60
  });
@@ -26,7 +26,7 @@ describe("extensions", () => {
26
26
 
27
27
  function createFixture() {
28
28
  const parent = new ObjectTree({
29
- json_handler: {
29
+ "json.handler": {
30
30
  unpack: (buffer) => JSON.parse(String(buffer)),
31
31
  },
32
32
  });
@@ -79,15 +79,6 @@ describe("ops", () => {
79
79
  assert.equal(result, "yx");
80
80
  });
81
81
 
82
- test("ops.lambda function can reference itself with @recurse", async () => {
83
- const code = createCode([ops.lambda, ["_"], [ops.scope, "@recurse"]]);
84
- const fn = await evaluate.call(null, code);
85
- const result = await fn();
86
- // We're expecting the function to return itself, but testing recursion is
87
- // messy. We just confirm that the result has the same code as the original.
88
- assert.equal(result.code, fn.code);
89
- });
90
-
91
82
  test("ops.object instantiates an object", async () => {
92
83
  const scope = new ObjectTree({
93
84
  upper: (s) => s.toUpperCase(),