@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 +2 -2
- package/src/compiler/parserHelpers.js +3 -4
- package/src/handlers/yaml_handler.js +8 -2
- package/src/project/jsGlobals.js +1 -10
- package/src/protocols/package.js +19 -10
- package/src/runtime/errors.js +5 -0
- package/src/runtime/execute.js +3 -0
- package/src/runtime/ops.js +29 -4
- package/test/compiler/parse.test.js +4 -8
- package/test/runtime/ops.test.js +6 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@weborigami/language",
|
|
3
|
-
"version": "0.6.
|
|
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.
|
|
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
|
-
//
|
|
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
|
|
647
|
-
return [
|
|
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
|
-
//
|
|
86
|
-
|
|
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)) {
|
package/src/project/jsGlobals.js
CHANGED
|
@@ -128,7 +128,7 @@ const globals = {
|
|
|
128
128
|
encodeURIComponent,
|
|
129
129
|
escape,
|
|
130
130
|
eval,
|
|
131
|
-
|
|
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
|
*
|
package/src/protocols/package.js
CHANGED
|
@@ -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
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
|
|
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
|
-
|
|
26
|
+
let packageRoot = await Tree.traverse(root, ...packageRootKeys);
|
|
24
27
|
if (!packageRoot) {
|
|
25
|
-
|
|
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.
|
|
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
|
|
package/src/runtime/errors.js
CHANGED
|
@@ -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
|
package/src/runtime/execute.js
CHANGED
|
@@ -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);
|
package/src/runtime/ops.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
573
|
-
|
|
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
|
-
|
|
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"]]],
|
package/test/runtime/ops.test.js
CHANGED
|
@@ -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);
|