@weborigami/language 0.2.11 → 0.2.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 +0 -1
- package/package.json +3 -3
- package/src/compiler/parserHelpers.js +26 -3
- package/src/runtime/ops.js +4 -4
- package/src/runtime/taggedTemplateIndent.js +10 -5
- package/test/compiler/parse.test.js +12 -27
- package/test/runtime/expressionObject.test.js +2 -2
- package/test/runtime/taggedTemplateIndent.test.js +6 -6
- package/src/runtime/taggedTemplate.js +0 -9
- package/test/runtime/taggedTemplate.test.js +0 -10
package/main.js
CHANGED
|
@@ -14,7 +14,6 @@ export { default as InvokeFunctionsTransform } from "./src/runtime/InvokeFunctio
|
|
|
14
14
|
export * as moduleCache from "./src/runtime/moduleCache.js";
|
|
15
15
|
export { default as OrigamiFiles } from "./src/runtime/OrigamiFiles.js";
|
|
16
16
|
export * as symbols from "./src/runtime/symbols.js";
|
|
17
|
-
export { default as taggedTemplate } from "./src/runtime/taggedTemplate.js";
|
|
18
17
|
export { default as taggedTemplateIndent } from "./src/runtime/taggedTemplateIndent.js";
|
|
19
18
|
export { default as TreeEvent } from "./src/runtime/TreeEvent.js";
|
|
20
19
|
export { default as WatchFilesMixin } from "./src/runtime/WatchFilesMixin.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@weborigami/language",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.12",
|
|
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.8.2"
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@weborigami/async-tree": "0.2.
|
|
15
|
-
"@weborigami/types": "0.2.
|
|
14
|
+
"@weborigami/async-tree": "0.2.12",
|
|
15
|
+
"@weborigami/types": "0.2.12",
|
|
16
16
|
"watcher": "2.3.1",
|
|
17
17
|
"yaml": "2.7.0"
|
|
18
18
|
},
|
|
@@ -226,7 +226,13 @@ export function makeCall(target, args) {
|
|
|
226
226
|
}
|
|
227
227
|
} else if (args[0] === ops.template) {
|
|
228
228
|
// Tagged template
|
|
229
|
-
|
|
229
|
+
const strings = args[1];
|
|
230
|
+
const values = args.slice(2);
|
|
231
|
+
fnCall = makeTaggedTemplateCall(
|
|
232
|
+
upgradeReference(target),
|
|
233
|
+
strings,
|
|
234
|
+
...values
|
|
235
|
+
);
|
|
230
236
|
} else {
|
|
231
237
|
// Function call with explicit or implicit parentheses
|
|
232
238
|
fnCall = [upgradeReference(target), ...args];
|
|
@@ -373,6 +379,24 @@ export function makeReference(identifier) {
|
|
|
373
379
|
return [op, identifier];
|
|
374
380
|
}
|
|
375
381
|
|
|
382
|
+
/**
|
|
383
|
+
* Make a tagged template call
|
|
384
|
+
*
|
|
385
|
+
* Because the tagged template function may not be an Origami function, we wrap
|
|
386
|
+
* each argument in a ops.concat call to convert it to a string.
|
|
387
|
+
*
|
|
388
|
+
* @param {AnnotatedCode} fn
|
|
389
|
+
* @param {AnnotatedCode} strings
|
|
390
|
+
* @param {AnnotatedCode[]} values
|
|
391
|
+
*/
|
|
392
|
+
function makeTaggedTemplateCall(fn, strings, ...values) {
|
|
393
|
+
const args = values.map((value) =>
|
|
394
|
+
// @ts-ignore
|
|
395
|
+
annotate([ops.concat, value], value.location)
|
|
396
|
+
);
|
|
397
|
+
return annotate([fn, strings, ...args], strings.location);
|
|
398
|
+
}
|
|
399
|
+
|
|
376
400
|
/**
|
|
377
401
|
* Make a template
|
|
378
402
|
*
|
|
@@ -385,8 +409,7 @@ export function makeTemplate(op, head, tail, location) {
|
|
|
385
409
|
const strings = [head[1]];
|
|
386
410
|
const values = [];
|
|
387
411
|
for (const [value, literal] of tail) {
|
|
388
|
-
|
|
389
|
-
values.push(concat);
|
|
412
|
+
values.push(value);
|
|
390
413
|
strings.push(literal[1]);
|
|
391
414
|
}
|
|
392
415
|
const stringsCode = annotate(strings, location);
|
package/src/runtime/ops.js
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import {
|
|
9
9
|
ObjectTree,
|
|
10
10
|
Tree,
|
|
11
|
+
concatTrees,
|
|
11
12
|
isUnpackable,
|
|
12
13
|
scope as scopeFn,
|
|
13
14
|
symbols,
|
|
@@ -21,7 +22,6 @@ import { evaluate } from "./internal.js";
|
|
|
21
22
|
import mergeTrees from "./mergeTrees.js";
|
|
22
23
|
import OrigamiFiles from "./OrigamiFiles.js";
|
|
23
24
|
import { codeSymbol } from "./symbols.js";
|
|
24
|
-
import taggedTemplate from "./taggedTemplate.js";
|
|
25
25
|
|
|
26
26
|
function addOpLabel(op, label) {
|
|
27
27
|
Object.defineProperty(op, "toString", {
|
|
@@ -532,15 +532,15 @@ addOpLabel(subtraction, "«ops.subtraction»");
|
|
|
532
532
|
/**
|
|
533
533
|
* Apply the default tagged template function.
|
|
534
534
|
*/
|
|
535
|
-
export function template(strings, ...values) {
|
|
536
|
-
return
|
|
535
|
+
export async function template(strings, ...values) {
|
|
536
|
+
return concatTrees(strings, ...values);
|
|
537
537
|
}
|
|
538
538
|
addOpLabel(template, "«ops.template»");
|
|
539
539
|
|
|
540
540
|
/**
|
|
541
541
|
* Apply the tagged template indent function.
|
|
542
542
|
*/
|
|
543
|
-
export function templateIndent(strings, ...values) {
|
|
543
|
+
export async function templateIndent(strings, ...values) {
|
|
544
544
|
return taggedTemplateIndent(strings, ...values);
|
|
545
545
|
}
|
|
546
546
|
addOpLabel(templateIndent, "«ops.templateIndent");
|
|
@@ -1,22 +1,27 @@
|
|
|
1
|
+
import { concat, toString, Tree } from "@weborigami/async-tree";
|
|
2
|
+
|
|
1
3
|
const lastLineWhitespaceRegex = /\n(?<indent>[ \t]*)$/;
|
|
2
4
|
|
|
3
5
|
const mapStringsToModifications = new Map();
|
|
4
6
|
|
|
5
7
|
/**
|
|
6
|
-
* Normalize indentation in a tagged template string
|
|
8
|
+
* Normalize indentation in a tagged template string
|
|
7
9
|
*
|
|
8
10
|
* @param {TemplateStringsArray} strings
|
|
9
11
|
* @param {...any} values
|
|
10
|
-
* @returns {string}
|
|
12
|
+
* @returns {Promise<string>}
|
|
11
13
|
*/
|
|
12
|
-
export default function indent(strings, ...values) {
|
|
14
|
+
export default async function indent(strings, ...values) {
|
|
13
15
|
let modified = mapStringsToModifications.get(strings);
|
|
14
16
|
if (!modified) {
|
|
15
17
|
modified = modifyStrings(strings);
|
|
16
18
|
mapStringsToModifications.set(strings, modified);
|
|
17
19
|
}
|
|
18
20
|
const { blockIndentations, strings: modifiedStrings } = modified;
|
|
19
|
-
|
|
21
|
+
const valueTexts = await Promise.all(
|
|
22
|
+
values.map((value) => (Tree.isTreelike(value) ? concat(value) : value))
|
|
23
|
+
);
|
|
24
|
+
return joinBlocks(modifiedStrings, valueTexts, blockIndentations);
|
|
20
25
|
}
|
|
21
26
|
|
|
22
27
|
// Join strings and values, applying the given block indentation to the lines of
|
|
@@ -24,7 +29,7 @@ export default function indent(strings, ...values) {
|
|
|
24
29
|
function joinBlocks(strings, values, blockIndentations) {
|
|
25
30
|
let result = strings[0];
|
|
26
31
|
for (let i = 0; i < values.length; i++) {
|
|
27
|
-
let text = values[i];
|
|
32
|
+
let text = toString(values[i]);
|
|
28
33
|
if (text) {
|
|
29
34
|
const blockIndentation = blockIndentations[i];
|
|
30
35
|
if (blockIndentation) {
|
|
@@ -298,7 +298,7 @@ describe("Origami parser", () => {
|
|
|
298
298
|
assertThrows("templateLiteral", "`foo", "Expected closing backtick");
|
|
299
299
|
});
|
|
300
300
|
|
|
301
|
-
test
|
|
301
|
+
test("error thrown for invalid Origami front matter expression", () => {
|
|
302
302
|
assertThrows(
|
|
303
303
|
"templateDocument",
|
|
304
304
|
`---
|
|
@@ -446,11 +446,7 @@ Body`,
|
|
|
446
446
|
[
|
|
447
447
|
ops.lambda,
|
|
448
448
|
[[ops.literal, "_"]],
|
|
449
|
-
[
|
|
450
|
-
ops.template,
|
|
451
|
-
[ops.literal, ["<li>", "</li>"]],
|
|
452
|
-
[ops.concat, [ops.scope, "_"]],
|
|
453
|
-
],
|
|
449
|
+
[ops.template, [ops.literal, ["<li>", "</li>"]], [ops.scope, "_"]],
|
|
454
450
|
],
|
|
455
451
|
]);
|
|
456
452
|
assertParse("expression", `https://example.com/about/`, [
|
|
@@ -991,11 +987,7 @@ Body`,
|
|
|
991
987
|
assertParse("shorthandFunction", "=`Hello, ${name}.`", [
|
|
992
988
|
ops.lambda,
|
|
993
989
|
[[ops.literal, "_"]],
|
|
994
|
-
[
|
|
995
|
-
ops.template,
|
|
996
|
-
[ops.literal, ["Hello, ", "."]],
|
|
997
|
-
[ops.concat, [ops.scope, "name"]],
|
|
998
|
-
],
|
|
990
|
+
[ops.template, [ops.literal, ["Hello, ", "."]], [ops.scope, "name"]],
|
|
999
991
|
]);
|
|
1000
992
|
assertParse("shorthandFunction", "=indent`hello`", [
|
|
1001
993
|
ops.lambda,
|
|
@@ -1037,7 +1029,7 @@ Body`,
|
|
|
1037
1029
|
[
|
|
1038
1030
|
ops.templateIndent,
|
|
1039
1031
|
[ops.literal, ["hello", "world"]],
|
|
1040
|
-
[ops.
|
|
1032
|
+
[ops.scope, "foo"],
|
|
1041
1033
|
],
|
|
1042
1034
|
]);
|
|
1043
1035
|
assertParse("templateBody", "Documents can contain ` backticks", [
|
|
@@ -1100,7 +1092,7 @@ Body text`,
|
|
|
1100
1092
|
[
|
|
1101
1093
|
ops.templateIndent,
|
|
1102
1094
|
[ops.literal, ["<h1>", "</h1>\n"]],
|
|
1103
|
-
[ops.
|
|
1095
|
+
[ops.scope, "title"],
|
|
1104
1096
|
],
|
|
1105
1097
|
],
|
|
1106
1098
|
undefined,
|
|
@@ -1118,30 +1110,23 @@ Body text`,
|
|
|
1118
1110
|
assertParse("templateLiteral", "`foo ${x} bar`", [
|
|
1119
1111
|
ops.template,
|
|
1120
1112
|
[ops.literal, ["foo ", " bar"]],
|
|
1121
|
-
[ops.
|
|
1113
|
+
[ops.scope, "x"],
|
|
1122
1114
|
]);
|
|
1123
1115
|
assertParse("templateLiteral", "`${`nested`}`", [
|
|
1124
1116
|
ops.template,
|
|
1125
1117
|
[ops.literal, ["", ""]],
|
|
1126
|
-
[ops.
|
|
1118
|
+
[ops.template, [ops.literal, ["nested"]]],
|
|
1127
1119
|
]);
|
|
1128
1120
|
assertParse("templateLiteral", "`${ map:(people, =`${name}`) }`", [
|
|
1129
1121
|
ops.template,
|
|
1130
1122
|
[ops.literal, ["", ""]],
|
|
1131
1123
|
[
|
|
1132
|
-
ops.
|
|
1124
|
+
[ops.builtin, "map:"],
|
|
1125
|
+
[ops.scope, "people"],
|
|
1133
1126
|
[
|
|
1134
|
-
|
|
1135
|
-
[ops.
|
|
1136
|
-
[
|
|
1137
|
-
ops.lambda,
|
|
1138
|
-
[[ops.literal, "_"]],
|
|
1139
|
-
[
|
|
1140
|
-
ops.template,
|
|
1141
|
-
[ops.literal, ["", ""]],
|
|
1142
|
-
[ops.concat, [ops.scope, "name"]],
|
|
1143
|
-
],
|
|
1144
|
-
],
|
|
1127
|
+
ops.lambda,
|
|
1128
|
+
[[ops.literal, "_"]],
|
|
1129
|
+
[ops.template, [ops.literal, ["", ""]], [ops.scope, "name"]],
|
|
1145
1130
|
],
|
|
1146
1131
|
],
|
|
1147
1132
|
]);
|
|
@@ -5,7 +5,7 @@ import { describe, test } from "node:test";
|
|
|
5
5
|
import expressionObject from "../../src/runtime/expressionObject.js";
|
|
6
6
|
import { ops } from "../../src/runtime/internal.js";
|
|
7
7
|
|
|
8
|
-
describe
|
|
8
|
+
describe("expressionObject", () => {
|
|
9
9
|
test("can instantiate an object", async () => {
|
|
10
10
|
const scope = new ObjectTree({
|
|
11
11
|
upper: (s) => s.toUpperCase(),
|
|
@@ -74,7 +74,7 @@ describe.only("expressionObject", () => {
|
|
|
74
74
|
assert.equal(object["hidden"], "shh");
|
|
75
75
|
});
|
|
76
76
|
|
|
77
|
-
test
|
|
77
|
+
test("provides a symbols.keys method", async () => {
|
|
78
78
|
const entries = [
|
|
79
79
|
// Will return a tree, should have a slash
|
|
80
80
|
["getter", [ops.getter, [ops.object, ["b", [ops.literal, 2]]]]],
|
|
@@ -3,13 +3,13 @@ import { describe, test } from "node:test";
|
|
|
3
3
|
import indent from "../../src/runtime/taggedTemplateIndent.js";
|
|
4
4
|
|
|
5
5
|
describe("taggedTemplateIndent", () => {
|
|
6
|
-
test("joins strings and values together if template isn't a block template", () => {
|
|
7
|
-
const result = indent`a ${"b"} c`;
|
|
6
|
+
test("joins strings and values together if template isn't a block template", async () => {
|
|
7
|
+
const result = await indent`a ${"b"} c`;
|
|
8
8
|
assert.equal(result, "a b c");
|
|
9
9
|
});
|
|
10
10
|
|
|
11
|
-
test("removes first and last lines if template is a block template", () => {
|
|
12
|
-
const actual = indent`
|
|
11
|
+
test("removes first and last lines if template is a block template", async () => {
|
|
12
|
+
const actual = await indent`
|
|
13
13
|
<p>
|
|
14
14
|
Hello, ${"Alice"}!
|
|
15
15
|
</p>
|
|
@@ -22,12 +22,12 @@ describe("taggedTemplateIndent", () => {
|
|
|
22
22
|
assert.equal(actual, expected);
|
|
23
23
|
});
|
|
24
24
|
|
|
25
|
-
test("indents all lines in a block substitution", () => {
|
|
25
|
+
test("indents all lines in a block substitution", async () => {
|
|
26
26
|
const lines = `
|
|
27
27
|
Line 1
|
|
28
28
|
Line 2
|
|
29
29
|
Line 3`.trimStart();
|
|
30
|
-
const actual = indent`
|
|
30
|
+
const actual = await indent`
|
|
31
31
|
<main>
|
|
32
32
|
${lines}
|
|
33
33
|
</main>
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
// Default JavaScript tagged template function splices strings and values
|
|
2
|
-
// together.
|
|
3
|
-
export default function taggedTemplate(strings, ...values) {
|
|
4
|
-
let result = strings[0];
|
|
5
|
-
for (let i = 0; i < values.length; i++) {
|
|
6
|
-
result += values[i] + strings[i + 1];
|
|
7
|
-
}
|
|
8
|
-
return result;
|
|
9
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert";
|
|
2
|
-
import { describe, test } from "node:test";
|
|
3
|
-
import taggedTemplate from "../../src/runtime/taggedTemplate.js";
|
|
4
|
-
|
|
5
|
-
describe("taggedTemplate", () => {
|
|
6
|
-
test("joins strings and values together", () => {
|
|
7
|
-
const result = taggedTemplate`a ${"b"} c`;
|
|
8
|
-
assert.equal(result, "a b c");
|
|
9
|
-
});
|
|
10
|
-
});
|