@weborigami/language 0.6.11 → 0.6.12
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/main.js +2 -4
- package/package.json +4 -4
- package/src/compiler/origami.pegjs +11 -5
- package/src/compiler/parse.js +56 -10
- package/src/handlers/sh_handler.js +7 -2
- package/src/runtime/WatchFilesMixin.d.ts +3 -1
- package/src/runtime/WatchFilesMixin.js +7 -0
- package/src/runtime/execute.js +14 -13
package/main.js
CHANGED
|
@@ -13,11 +13,9 @@ export { default as projectRoot } from "./src/project/projectRoot.js";
|
|
|
13
13
|
export { default as projectRootFromPath } from "./src/project/projectRootFromPath.js";
|
|
14
14
|
export * as Protocols from "./src/protocols/protocols.js";
|
|
15
15
|
export { formatError, highlightError, lineInfo } from "./src/runtime/errors.js";
|
|
16
|
+
export { default as evaluate } from "./src/runtime/evaluate.js";
|
|
16
17
|
export { default as EventTargetMixin } from "./src/runtime/EventTargetMixin.js";
|
|
17
|
-
export {
|
|
18
|
-
default as evaluate,
|
|
19
|
-
default as execute,
|
|
20
|
-
} from "./src/runtime/execute.js";
|
|
18
|
+
export { default as execute } from "./src/runtime/execute.js";
|
|
21
19
|
export * as expressionFunction from "./src/runtime/expressionFunction.js";
|
|
22
20
|
export { default as expressionObject } from "./src/runtime/expressionObject.js";
|
|
23
21
|
export * from "./src/runtime/handleExtension.js";
|
package/package.json
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@weborigami/language",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.12",
|
|
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": "
|
|
9
|
+
"@types/node": "25.3.2",
|
|
10
10
|
"peggy": "5.0.6",
|
|
11
11
|
"typescript": "5.9.3"
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@weborigami/async-tree": "0.6.
|
|
14
|
+
"@weborigami/async-tree": "0.6.12",
|
|
15
15
|
"exif-parser": "0.1.12",
|
|
16
16
|
"watcher": "2.3.1",
|
|
17
|
-
"yaml": "2.8.
|
|
17
|
+
"yaml": "2.8.2"
|
|
18
18
|
},
|
|
19
19
|
"scripts": {
|
|
20
20
|
"build": "peggy --allowed-start-rules=\"*\" --format es src/compiler/origami.pegjs --output src/compiler/parse.js",
|
|
@@ -845,8 +845,10 @@ relationalExpression
|
|
|
845
845
|
// We disallow a newline before the relational operator to support a newline
|
|
846
846
|
// as a separator in an object literal that has an object shorthand property
|
|
847
847
|
// with an angle bracket path. Otherwise the opening angle bracket would be
|
|
848
|
-
// interpreted as a relational operator.
|
|
849
|
-
|
|
848
|
+
// interpreted as a relational operator. In shell mode we require a space to
|
|
849
|
+
// avoid ambiguity with an angle bracket literal as an argument in an implicit
|
|
850
|
+
// parentheses call.
|
|
851
|
+
= head:shiftExpression tail:(inlineSpace @relationalOperator whitespaceRequiredForShell @shiftExpression)* {
|
|
850
852
|
return tail.reduce(makeBinaryOperation, head);
|
|
851
853
|
}
|
|
852
854
|
|
|
@@ -1039,9 +1041,9 @@ uriKey
|
|
|
1039
1041
|
|
|
1040
1042
|
// A single character in a URI key
|
|
1041
1043
|
uriKeyChar
|
|
1042
|
-
// Accept anything
|
|
1043
|
-
//
|
|
1044
|
-
= char:[^/,\)\]\}] !&{ return /\s/.test(char); } { return char; }
|
|
1044
|
+
// Accept anything except whitespace (see notes for `whitespace` term),
|
|
1045
|
+
// brackets, comma, or slash
|
|
1046
|
+
= char:[^/,\(\[\{\)\]\}] !&{ return /\s/.test(char); } { return char; }
|
|
1045
1047
|
/ escapedChar
|
|
1046
1048
|
|
|
1047
1049
|
// A slash-separated path of keys: `a/b/c`
|
|
@@ -1071,6 +1073,10 @@ whitespaceOptionalForProgram
|
|
|
1071
1073
|
= programMode __
|
|
1072
1074
|
/ shellMode
|
|
1073
1075
|
|
|
1076
|
+
whitespaceRequiredForShell
|
|
1077
|
+
= shellMode whitespace
|
|
1078
|
+
/ programMode __
|
|
1079
|
+
|
|
1074
1080
|
whitespaceOrParenthesis
|
|
1075
1081
|
= whitespace
|
|
1076
1082
|
/ "("
|
package/src/compiler/parse.js
CHANGED
|
@@ -357,6 +357,7 @@ function peg$parse(input, options) {
|
|
|
357
357
|
whitespace: peg$parsewhitespace,
|
|
358
358
|
whitespaceChar: peg$parsewhitespaceChar,
|
|
359
359
|
whitespaceOptionalForProgram: peg$parsewhitespaceOptionalForProgram,
|
|
360
|
+
whitespaceRequiredForShell: peg$parsewhitespaceRequiredForShell,
|
|
360
361
|
whitespaceOrParenthesis: peg$parsewhitespaceOrParenthesis,
|
|
361
362
|
whitespaceWithNewLine: peg$parsewhitespaceWithNewLine,
|
|
362
363
|
};
|
|
@@ -459,7 +460,7 @@ function peg$parse(input, options) {
|
|
|
459
460
|
const peg$r19 = /^[^\n\r]/;
|
|
460
461
|
const peg$r20 = /^[!+]/;
|
|
461
462
|
const peg$r21 = /^[\/)\]}]/;
|
|
462
|
-
const peg$r22 = /^[^\/,)\]}]/;
|
|
463
|
+
const peg$r22 = /^[^\/,([{)\]}]/;
|
|
463
464
|
const peg$r23 = /^[a-z]/;
|
|
464
465
|
const peg$r24 = /^[a-z0-9+-.]/;
|
|
465
466
|
const peg$r25 = /^[:]/;
|
|
@@ -589,7 +590,7 @@ function peg$parse(input, options) {
|
|
|
589
590
|
const peg$e122 = peg$literalExpectation("await", false);
|
|
590
591
|
const peg$e123 = peg$literalExpectation("typeof", false);
|
|
591
592
|
const peg$e124 = peg$literalExpectation("void", false);
|
|
592
|
-
const peg$e125 = peg$classExpectation(["/", ",", ")", "]", "}"], true, false, false);
|
|
593
|
+
const peg$e125 = peg$classExpectation(["/", ",", "(", "[", "{", ")", "]", "}"], true, false, false);
|
|
593
594
|
const peg$e126 = peg$otherExpectation("slash-separated path");
|
|
594
595
|
const peg$e127 = peg$classExpectation([["a", "z"]], false, false, false);
|
|
595
596
|
const peg$e128 = peg$classExpectation([["a", "z"], ["0", "9"], ["+", "."]], false, false, false);
|
|
@@ -6313,10 +6314,15 @@ function peg$parse(input, options) {
|
|
|
6313
6314
|
if (s4 !== peg$FAILED) {
|
|
6314
6315
|
s5 = peg$parserelationalOperator();
|
|
6315
6316
|
if (s5 !== peg$FAILED) {
|
|
6316
|
-
s6 = peg$
|
|
6317
|
-
|
|
6318
|
-
|
|
6319
|
-
|
|
6317
|
+
s6 = peg$parsewhitespaceRequiredForShell();
|
|
6318
|
+
if (s6 !== peg$FAILED) {
|
|
6319
|
+
s7 = peg$parseshiftExpression();
|
|
6320
|
+
if (s7 !== peg$FAILED) {
|
|
6321
|
+
s3 = [ s5, s7 ];
|
|
6322
|
+
} else {
|
|
6323
|
+
peg$currPos = s3;
|
|
6324
|
+
s3 = peg$FAILED;
|
|
6325
|
+
}
|
|
6320
6326
|
} else {
|
|
6321
6327
|
peg$currPos = s3;
|
|
6322
6328
|
s3 = peg$FAILED;
|
|
@@ -6336,10 +6342,15 @@ function peg$parse(input, options) {
|
|
|
6336
6342
|
if (s4 !== peg$FAILED) {
|
|
6337
6343
|
s5 = peg$parserelationalOperator();
|
|
6338
6344
|
if (s5 !== peg$FAILED) {
|
|
6339
|
-
s6 = peg$
|
|
6340
|
-
|
|
6341
|
-
|
|
6342
|
-
|
|
6345
|
+
s6 = peg$parsewhitespaceRequiredForShell();
|
|
6346
|
+
if (s6 !== peg$FAILED) {
|
|
6347
|
+
s7 = peg$parseshiftExpression();
|
|
6348
|
+
if (s7 !== peg$FAILED) {
|
|
6349
|
+
s3 = [ s5, s7 ];
|
|
6350
|
+
} else {
|
|
6351
|
+
peg$currPos = s3;
|
|
6352
|
+
s3 = peg$FAILED;
|
|
6353
|
+
}
|
|
6343
6354
|
} else {
|
|
6344
6355
|
peg$currPos = s3;
|
|
6345
6356
|
s3 = peg$FAILED;
|
|
@@ -7834,6 +7845,40 @@ function peg$parse(input, options) {
|
|
|
7834
7845
|
return s0;
|
|
7835
7846
|
}
|
|
7836
7847
|
|
|
7848
|
+
function peg$parsewhitespaceRequiredForShell() {
|
|
7849
|
+
let s0, s1, s2;
|
|
7850
|
+
|
|
7851
|
+
s0 = peg$currPos;
|
|
7852
|
+
s1 = peg$parseshellMode();
|
|
7853
|
+
if (s1 !== peg$FAILED) {
|
|
7854
|
+
s2 = peg$parsewhitespace();
|
|
7855
|
+
if (s2 !== peg$FAILED) {
|
|
7856
|
+
s1 = [s1, s2];
|
|
7857
|
+
s0 = s1;
|
|
7858
|
+
} else {
|
|
7859
|
+
peg$currPos = s0;
|
|
7860
|
+
s0 = peg$FAILED;
|
|
7861
|
+
}
|
|
7862
|
+
} else {
|
|
7863
|
+
peg$currPos = s0;
|
|
7864
|
+
s0 = peg$FAILED;
|
|
7865
|
+
}
|
|
7866
|
+
if (s0 === peg$FAILED) {
|
|
7867
|
+
s0 = peg$currPos;
|
|
7868
|
+
s1 = peg$parseprogramMode();
|
|
7869
|
+
if (s1 !== peg$FAILED) {
|
|
7870
|
+
s2 = peg$parse__();
|
|
7871
|
+
s1 = [s1, s2];
|
|
7872
|
+
s0 = s1;
|
|
7873
|
+
} else {
|
|
7874
|
+
peg$currPos = s0;
|
|
7875
|
+
s0 = peg$FAILED;
|
|
7876
|
+
}
|
|
7877
|
+
}
|
|
7878
|
+
|
|
7879
|
+
return s0;
|
|
7880
|
+
}
|
|
7881
|
+
|
|
7837
7882
|
function peg$parsewhitespaceOrParenthesis() {
|
|
7838
7883
|
let s0;
|
|
7839
7884
|
|
|
@@ -8075,6 +8120,7 @@ const peg$allowedStartRules = [
|
|
|
8075
8120
|
"whitespace",
|
|
8076
8121
|
"whitespaceChar",
|
|
8077
8122
|
"whitespaceOptionalForProgram",
|
|
8123
|
+
"whitespaceRequiredForShell",
|
|
8078
8124
|
"whitespaceOrParenthesis",
|
|
8079
8125
|
"whitespaceWithNewLine"
|
|
8080
8126
|
];
|
|
@@ -26,10 +26,15 @@ export default {
|
|
|
26
26
|
* Supports multiple commands, pipelines, redirects, etc.
|
|
27
27
|
*
|
|
28
28
|
* @param {string} scriptText - Shell code (may contain newlines/side effects)
|
|
29
|
-
* @param {
|
|
29
|
+
* @param {import("@weborigami/async-tree").Stringlike} inputText - Text to pipe to the script's stdin
|
|
30
30
|
* @returns {Promise<string>}
|
|
31
31
|
*/
|
|
32
32
|
function runShellScript(scriptText, inputText) {
|
|
33
|
+
if (inputText instanceof Function) {
|
|
34
|
+
throw new Error(
|
|
35
|
+
"A .sh file expects text input but got a function instead. Did you mean to invoke the function?",
|
|
36
|
+
);
|
|
37
|
+
}
|
|
33
38
|
return new Promise((resolve, reject) => {
|
|
34
39
|
// Use sh -c "<scriptText>" so stdin is free for inputText
|
|
35
40
|
const child = spawn("sh", ["-c", scriptText], {
|
|
@@ -49,7 +54,7 @@ function runShellScript(scriptText, inputText) {
|
|
|
49
54
|
if (code !== 0) {
|
|
50
55
|
/** @type {any} */
|
|
51
56
|
const err = new Error(
|
|
52
|
-
`Shell exited with code ${code}${stderr ? `: ${stderr}` : ""}
|
|
57
|
+
`Shell exited with code ${code}${stderr ? `: ${stderr}` : ""}`,
|
|
53
58
|
);
|
|
54
59
|
err.code = code;
|
|
55
60
|
err.stdout = stdout;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
2
3
|
import Watcher from "watcher";
|
|
3
4
|
import TreeEvent from "./TreeEvent.js";
|
|
4
5
|
|
|
@@ -17,6 +18,12 @@ export default function WatchFilesMixin(Base) {
|
|
|
17
18
|
onChange(filePath) {
|
|
18
19
|
// Reset cached values.
|
|
19
20
|
this.subfoldersMap = new Map();
|
|
21
|
+
|
|
22
|
+
// Special case: ignore events in .git folder
|
|
23
|
+
if (filePath.includes(`${path.sep}.git${path.sep}`)) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
20
27
|
this.dispatchEvent(new TreeEvent("change", { filePath }));
|
|
21
28
|
}
|
|
22
29
|
|
package/src/runtime/execute.js
CHANGED
|
@@ -19,23 +19,13 @@ export default async function execute(code, state = {}) {
|
|
|
19
19
|
return code;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
let evaluated;
|
|
23
|
-
if (code[0]?.unevaluatedArgs) {
|
|
24
|
-
// Don't evaluate instructions, use as is.
|
|
25
|
-
evaluated = code;
|
|
26
|
-
} else {
|
|
27
|
-
// Evaluate each instruction in the code.
|
|
28
|
-
evaluated = await Promise.all(
|
|
29
|
-
code.map((instruction) => execute(instruction, state)),
|
|
30
|
-
);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
22
|
// Add the code to the runtime state
|
|
34
23
|
/** @type {import("../../index.ts").CodeContext} */
|
|
35
24
|
const context = { state, code };
|
|
36
25
|
|
|
37
|
-
//
|
|
38
|
-
|
|
26
|
+
// Start by evaluating the head of the instruction
|
|
27
|
+
const [head, ...tail] = code;
|
|
28
|
+
let fn = await execute(head, state);
|
|
39
29
|
|
|
40
30
|
if (!fn) {
|
|
41
31
|
// The code wants to invoke something that's couldn't be found in scope.
|
|
@@ -53,6 +43,17 @@ export default async function execute(code, state = {}) {
|
|
|
53
43
|
fn = await fn.unpack();
|
|
54
44
|
}
|
|
55
45
|
|
|
46
|
+
let args;
|
|
47
|
+
if (fn?.unevaluatedArgs) {
|
|
48
|
+
// Don't evaluate instructions, use as is.
|
|
49
|
+
args = tail;
|
|
50
|
+
} else {
|
|
51
|
+
// Evaluate each instruction in the code.
|
|
52
|
+
args = await Promise.all(
|
|
53
|
+
tail.map((instruction) => execute(instruction, state)),
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
56
57
|
if (fn.needsState) {
|
|
57
58
|
// The function is an op that wants the runtime state
|
|
58
59
|
args.push(state);
|