@weborigami/language 0.6.10 → 0.6.11

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.10",
3
+ "version": "0.6.11",
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.10",
14
+ "@weborigami/async-tree": "0.6.11",
15
15
  "exif-parser": "0.1.12",
16
16
  "watcher": "2.3.1",
17
17
  "yaml": "2.8.1"
@@ -634,8 +634,7 @@ function makePossibleSpreadCall(target, args, location) {
634
634
  return [target, ...args];
635
635
  }
636
636
 
637
- // Get function's apply method
638
- const applyMethod = annotate([ops.property, target, "apply"], location);
637
+ // We'll need to use ops.apply and ops.flat to handle the spreads.
639
638
  const wrappedArgs = args.map((arg) => {
640
639
  if (arg[0] === markers.spread) {
641
640
  return arg[1];
@@ -643,8 +642,8 @@ function makePossibleSpreadCall(target, args, location) {
643
642
  return annotate([ops.array, arg], arg.location);
644
643
  }
645
644
  });
646
- const flatCall = annotate([ops.flat, ...wrappedArgs], location);
647
- return [applyMethod, null, flatCall];
645
+ const flattened = annotate([ops.flat, ...wrappedArgs], location);
646
+ return annotate([ops.apply, target, flattened], location);
648
647
  }
649
648
 
650
649
  /**
@@ -1,4 +1,5 @@
1
1
  import {
2
+ castArraylike,
2
3
  getParent,
3
4
  isUnpackable,
4
5
  symbols,
@@ -82,8 +83,13 @@ export default {
82
83
  }
83
84
 
84
85
  if (hasOriTags) {
85
- // Resolve any promises in the data.
86
- data = await Tree.plain(data);
86
+ // Invoke any functions and resolve any promises in the deep data.
87
+ const tree = Tree.from(data, { deep: true });
88
+ data = await Tree.mapReduce(
89
+ tree,
90
+ async (value) => (value instanceof Function ? await value() : value),
91
+ (mapped) => castArraylike(mapped),
92
+ );
87
93
  }
88
94
 
89
95
  if (data && typeof data === "object" && Object.isExtensible(data)) {
@@ -128,7 +128,7 @@ const globals = {
128
128
  encodeURIComponent,
129
129
  escape,
130
130
  eval,
131
- // fetch -- special case, see below
131
+ fetch,
132
132
  globalThis,
133
133
  isFinite,
134
134
  isNaN,
@@ -150,7 +150,6 @@ const globals = {
150
150
  true: true,
151
151
 
152
152
  // Special cases
153
- fetch: fetchWrapper,
154
153
  import: importWrapper,
155
154
  };
156
155
 
@@ -160,14 +159,6 @@ Object.defineProperty(globals, "globalThis", {
160
159
  value: globals,
161
160
  });
162
161
 
163
- async function fetchWrapper(resource, options) {
164
- console.warn(
165
- "Warning: A plain `fetch` reference will eventually call the standard JavaScript fetch() function. For Origami's fetch behavior, update your code to call Origami.fetch().",
166
- );
167
- const response = await fetch(resource, options);
168
- return response.ok ? await response.arrayBuffer() : undefined;
169
- }
170
-
171
162
  /**
172
163
  * @typedef {import("@weborigami/async-tree").AsyncMap} AsyncMap
173
164
  *
@@ -1,4 +1,4 @@
1
- import { Tree, keysFromPath } from "@weborigami/async-tree";
1
+ import { Tree, keysFromPath, pathFromKeys } from "@weborigami/async-tree";
2
2
  import projectRoot from "../project/projectRoot.js";
3
3
 
4
4
  /**
@@ -11,27 +11,36 @@ export default async function packageProtocol(...args) {
11
11
  const root = await projectRoot(state);
12
12
 
13
13
  // Identify the path to the package root
14
- const packageRootPath = ["node_modules"];
15
- const name = args.shift();
16
- packageRootPath.push(name);
14
+ const packageRootKeys = ["node_modules"];
15
+ let name = args.shift();
16
+ packageRootKeys.push(name);
17
17
  if (name.startsWith("@")) {
18
18
  // First key is an npm organization, add next key as name
19
- packageRootPath.push(args.shift());
19
+ const nameArg = args.shift();
20
+ name += nameArg;
21
+ packageRootKeys.push(nameArg);
20
22
  }
23
+ const packageRootPath = pathFromKeys(packageRootKeys);
21
24
 
22
25
  // Get the package root (top level folder of the package)
23
- const packageRoot = await Tree.traverse(root, ...packageRootPath);
26
+ let packageRoot = await Tree.traverse(root, ...packageRootKeys);
24
27
  if (!packageRoot) {
25
- throw new Error(`Can't find ${packageRootPath.join("/")}`);
28
+ // Can't find package -- are we *in* the package?
29
+ const packageJson = await Tree.traverse(root, "package.json");
30
+ const packageData = await packageJson?.unpack();
31
+ if (packageData?.name === name) {
32
+ // Yes, we're in the package itself
33
+ packageRoot = root;
34
+ } else {
35
+ throw new Error(`Can't find ${packageRootPath}`);
36
+ }
26
37
  }
27
38
 
28
39
  // Identify the main entry point
29
40
  const mainPath = await Tree.traverse(packageRoot, "package.json", "main");
30
41
  if (mainPath === undefined) {
31
42
  throw new Error(
32
- `${packageRootPath.join(
33
- "/",
34
- )} doesn't contain a package.json with a "main" entry.`,
43
+ `${packageRootPath} doesn't contain a package.json with a "main" entry.`,
35
44
  );
36
45
  }
37
46
 
@@ -20,6 +20,11 @@ const origamiSourceSignals = [
20
20
  * @param {Error} error
21
21
  */
22
22
  export async function formatError(error) {
23
+ // We want to display information for the root cause
24
+ while (error.cause instanceof Error) {
25
+ error = error.cause;
26
+ }
27
+
23
28
  // Get the original error message
24
29
  let originalMessage;
25
30
  // If the first line of the stack is just the error message, use that as the message
@@ -56,6 +56,9 @@ export default async function execute(code, state = {}) {
56
56
  if (fn.needsState) {
57
57
  // The function is an op that wants the runtime state
58
58
  args.push(state);
59
+ } else if (fn.needsContext) {
60
+ // The function is an op that wants the code context
61
+ args.push(context);
59
62
  } else if (fn.parentAsTarget && state.parent) {
60
63
  // The function wants the code's parent as the `this` target
61
64
  fn = fn.bind(state.parent);
@@ -26,6 +26,28 @@ export function addition(a, b) {
26
26
  }
27
27
  addOpLabel(addition, "«ops.addition»");
28
28
 
29
+ /**
30
+ * Flatten the arguments and then apply the function.
31
+ * This is used to handle spreads in function calls.
32
+ */
33
+ export async function apply(fn, args, state) {
34
+ // TODO: This is starting to recapitulate much of execute()
35
+ if (isUnpackable(fn)) {
36
+ fn = await fn.unpack();
37
+ }
38
+ if (fn.needsState) {
39
+ // The function is an op that wants the runtime state
40
+ args.push(state);
41
+ }
42
+ const result =
43
+ fn instanceof Function
44
+ ? await fn(...args) // Invoke the function
45
+ : await Tree.traverseOrThrow(fn, ...args); // Traverse the map.
46
+ return result;
47
+ }
48
+ addOpLabel(apply, "«ops.apply»");
49
+ apply.needsState = true;
50
+
29
51
  /**
30
52
  * Construct an array.
31
53
  *
@@ -157,11 +179,14 @@ addOpLabel(exponentiation, "«ops.exponentiation»");
157
179
  */
158
180
  export async function flat(...args) {
159
181
  const arrays = await Promise.all(
160
- args.map(async (arg) =>
161
- arg instanceof Array || typeof arg !== "object"
182
+ args.map(async (arg) => {
183
+ if (isUnpackable(arg)) {
184
+ arg = await arg.unpack();
185
+ }
186
+ return arg instanceof Array || typeof arg !== "object"
162
187
  ? arg
163
- : await Tree.values(arg),
164
- ),
188
+ : await Tree.values(arg);
189
+ }),
165
190
  );
166
191
 
167
192
  return arrays.flat();
@@ -569,8 +569,8 @@ describe("Origami parser", () => {
569
569
 
570
570
  test("parentheses arguments with spreads", () => {
571
571
  assertParse("callExpression", "fn(a, ...b, ...c)", [
572
- [ops.property, [markers.traverse, [markers.reference, "fn"]], "apply"],
573
- null,
572
+ ops.apply,
573
+ [markers.traverse, [markers.reference, "fn"]],
574
574
  [
575
575
  ops.flat,
576
576
  [ops.array, [markers.traverse, [markers.reference, "a"]]],
@@ -1126,12 +1126,8 @@ Body`,
1126
1126
  "implicitParenthesesCallExpression",
1127
1127
  "concat a.json, ...b.json, c.json",
1128
1128
  [
1129
- [
1130
- ops.property,
1131
- [markers.traverse, [markers.reference, "concat"]],
1132
- "apply",
1133
- ],
1134
- null,
1129
+ ops.apply,
1130
+ [markers.traverse, [markers.reference, "concat"]],
1135
1131
  [
1136
1132
  ops.flat,
1137
1133
  [ops.array, [markers.traverse, [markers.reference, "a.json"]]],
@@ -20,6 +20,12 @@ describe("ops", () => {
20
20
  );
21
21
  });
22
22
 
23
+ test("ops.apply applies a function to arguments", async () => {
24
+ const code = createCode([ops.apply, ops.addition, [ops.array, 1, 2]]);
25
+ const result = await execute(code);
26
+ assert.strictEqual(result, 3);
27
+ });
28
+
23
29
  test("ops.array creates an array", async () => {
24
30
  const code = createCode([ops.array, 1, 2, 3]);
25
31
  const result = await execute(code);