@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 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.11",
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.11",
15
- "@weborigami/types": "0.2.11",
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
- fnCall = [upgradeReference(target), ...args.slice(1)];
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
- const concat = annotate([ops.concat, value], value.location);
389
- values.push(concat);
412
+ values.push(value);
390
413
  strings.push(literal[1]);
391
414
  }
392
415
  const stringsCode = annotate(strings, location);
@@ -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 taggedTemplate(strings, ...values);
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
- return joinBlocks(modifiedStrings, values, blockIndentations);
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.only("error thrown for invalid Origami front matter expression", () => {
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.concat, [ops.scope, "foo"]],
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.concat, [ops.scope, "title"]],
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.concat, [ops.scope, "x"]],
1113
+ [ops.scope, "x"],
1122
1114
  ]);
1123
1115
  assertParse("templateLiteral", "`${`nested`}`", [
1124
1116
  ops.template,
1125
1117
  [ops.literal, ["", ""]],
1126
- [ops.concat, [ops.template, [ops.literal, ["nested"]]]],
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.concat,
1124
+ [ops.builtin, "map:"],
1125
+ [ops.scope, "people"],
1133
1126
  [
1134
- [ops.builtin, "map:"],
1135
- [ops.scope, "people"],
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.only("expressionObject", () => {
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.only("provides a symbols.keys method", async () => {
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
- });