@weborigami/language 0.6.13 → 0.6.14

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.6.13",
3
+ "version": "0.6.14",
4
4
  "description": "Web Origami expression language compiler and runtime",
5
5
  "type": "module",
6
6
  "main": "./main.js",
@@ -11,7 +11,7 @@
11
11
  "typescript": "5.9.3"
12
12
  },
13
13
  "dependencies": {
14
- "@weborigami/async-tree": "0.6.13",
14
+ "@weborigami/async-tree": "0.6.14",
15
15
  "exif-parser": "0.1.12",
16
16
  "watcher": "2.3.1",
17
17
  "yaml": "2.8.2"
@@ -57,7 +57,7 @@ export default async function handleExtension(value, key, parent = null) {
57
57
  }
58
58
  }
59
59
  }
60
- value;
60
+
61
61
  return value;
62
62
  }
63
63
 
@@ -1,4 +1,10 @@
1
- import { isPlainObject, isUnpackable, Tree } from "@weborigami/async-tree";
1
+ import {
2
+ isPlainObject,
3
+ isUnpackable,
4
+ symbols,
5
+ trailingSlash,
6
+ Tree,
7
+ } from "@weborigami/async-tree";
2
8
  import assignPropertyDescriptors from "./assignPropertyDescriptors.js";
3
9
 
4
10
  /**
@@ -28,11 +34,48 @@ export default async function mergeTrees(...trees) {
28
34
 
29
35
  // If all trees are plain objects, return a plain object.
30
36
  if (unpacked.every((tree) => isPlainObject(tree) && !Tree.isMap(tree))) {
31
- // Use our Object.assign variation that avoids invoking property getters.
32
- return assignPropertyDescriptors({}, ...unpacked);
37
+ return mergePlainObjects(...unpacked);
33
38
  }
34
39
 
35
40
  // Merge the trees.
36
41
  const result = Tree.merge(...unpacked);
37
42
  return result;
38
43
  }
44
+
45
+ // Return the merged keys for the given objects, respecting any keys method on
46
+ // objects that were created using Origami object literals. This preserves
47
+ // trailing slashes.
48
+ function mergeObjectKeys(objects) {
49
+ const mergedKeys = new Set();
50
+ for (const obj of objects) {
51
+ const objectKeys =
52
+ typeof obj[symbols.keys] === "function"
53
+ ? obj[symbols.keys]()
54
+ : Object.keys(obj);
55
+ for (const key of objectKeys) {
56
+ const alternateKey = trailingSlash.toggle(key);
57
+ if (mergedKeys.has(alternateKey)) {
58
+ // The new key differs from a key from an earlier object key only by the
59
+ // presence or absence of a trailing slash. Replace that old key.
60
+ mergedKeys.delete(alternateKey);
61
+ }
62
+ mergedKeys.add(key);
63
+ }
64
+ }
65
+ return Array.from(mergedKeys);
66
+ }
67
+
68
+ function mergePlainObjects(...objects) {
69
+ // Use our Object.assign variation that avoids invoking property getters.
70
+ const result = assignPropertyDescriptors({}, ...objects);
71
+
72
+ // Attach a keys method
73
+ Object.defineProperty(result, symbols.keys, {
74
+ configurable: true,
75
+ enumerable: false,
76
+ value: () => mergeObjectKeys(objects),
77
+ writable: true,
78
+ });
79
+
80
+ return result;
81
+ }
@@ -173,12 +173,12 @@ export function exponentiation(a, b) {
173
173
  addOpLabel(exponentiation, "«ops.exponentiation»");
174
174
 
175
175
  /**
176
- * Flatten the values of the given trees to a depth of 1
176
+ * Flatten the values of the given trees by one level.
177
177
  *
178
178
  * @param {...any} args
179
179
  */
180
180
  export async function flat(...args) {
181
- return Tree.flat(args, 2); // add 1 to account for the array of arguments
181
+ return Tree.flat(args);
182
182
  }
183
183
  addOpLabel(flat, "«ops.flat»");
184
184
 
@@ -1,4 +1,4 @@
1
- import { ObjectMap, Tree } from "@weborigami/async-tree";
1
+ import { ObjectMap, symbols, Tree } from "@weborigami/async-tree";
2
2
  import assert from "node:assert";
3
3
  import { describe, test } from "node:test";
4
4
  import mergeTrees from "../../src/runtime/mergeTrees.js";
@@ -23,7 +23,7 @@ describe("mergeTrees", () => {
23
23
  calledBar = true;
24
24
  return true;
25
25
  },
26
- }
26
+ },
27
27
  );
28
28
 
29
29
  // Shouldn't call functions when just getting keys
@@ -40,6 +40,22 @@ describe("mergeTrees", () => {
40
40
  });
41
41
  });
42
42
 
43
+ test("respects trailing slashes in keys", async () => {
44
+ const result = await mergeTrees(
45
+ {
46
+ "a/": 1, // key will be overwritten
47
+ b: 2, // key will be overwritten
48
+ c: 3,
49
+ },
50
+ {
51
+ a: 4,
52
+ "b/": 5,
53
+ },
54
+ );
55
+ const keys = result[symbols.keys]();
56
+ assert.deepEqual(keys, ["c", "a", "b/"]);
57
+ });
58
+
43
59
  test("merges heterogenous arguments as trees", async () => {
44
60
  const tree = await mergeTrees(
45
61
  new ObjectMap({
@@ -49,7 +65,7 @@ describe("mergeTrees", () => {
49
65
  {
50
66
  b: 3,
51
67
  c: 4,
52
- }
68
+ },
53
69
  );
54
70
  assert.deepEqual(await Tree.plain(tree), {
55
71
  a: 1,
@@ -159,7 +159,14 @@ describe("ops", () => {
159
159
  });
160
160
  const array = [5, 6];
161
161
  const result = await ops.flat(object, tree, array);
162
- assert.deepEqual(result, [1, 2, 3, 4, 5, 6]);
162
+ assert.deepEqual(await Tree.plain(result), {
163
+ a: 1,
164
+ b: 2,
165
+ c: 3,
166
+ d: 4,
167
+ 0: 5,
168
+ 1: 6,
169
+ });
163
170
  });
164
171
  });
165
172