@weborigami/language 0.6.7 → 0.6.8

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/index.ts CHANGED
@@ -63,8 +63,8 @@ export type Position = {
63
63
  }
64
64
 
65
65
  export type RuntimeState = {
66
- /** The container (e.g., file system) that holds the code */
67
- container?: SyncOrAsyncMap | null;
66
+ /** The container (e.g., file system folder) that holds the code */
67
+ parent?: SyncOrAsyncMap | null;
68
68
  /** The object to which this code is attached */
69
69
  object?: SyncOrAsyncMap | null;
70
70
  /** The current stack of function parameter assignments */
package/main.js CHANGED
@@ -2,8 +2,9 @@ export * from "./src/runtime/internal.js";
2
2
 
3
3
  export * as compile from "./src/compiler/compile.js";
4
4
  export { default as isOrigamiFrontMatter } from "./src/compiler/isOrigamiFrontMatter.js";
5
+ export { parse } from "./src/compiler/parse.js";
6
+ export * from "./src/compiler/parserHelpers.js";
5
7
  export * as Handlers from "./src/handlers/handlers.js";
6
- export { default as builtins } from "./src/project/builtins.js";
7
8
  export { default as coreGlobals } from "./src/project/coreGlobals.js";
8
9
  export { default as jsGlobals } from "./src/project/jsGlobals.js";
9
10
  export { default as projectConfig } from "./src/project/projectConfig.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@weborigami/language",
3
- "version": "0.6.7",
3
+ "version": "0.6.8",
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.7",
14
+ "@weborigami/async-tree": "0.6.8",
15
15
  "exif-parser": "0.1.12",
16
16
  "watcher": "2.3.1",
17
17
  "yaml": "2.8.1"
@@ -6,6 +6,7 @@ function compile(source, options) {
6
6
  const { front, parent, startRule } = options;
7
7
  const mode = options.mode ?? "program";
8
8
  const globals = options.globals ?? {};
9
+
9
10
  if (typeof source === "string") {
10
11
  source = { text: source };
11
12
  }
@@ -853,8 +853,8 @@ relationalOperator
853
853
  / "<"
854
854
  / ">="
855
855
  / ">"
856
- / "instanceof"
857
- / "in"
856
+ / "instanceof" &[\s] // ensure `instanceof` is followed by whitespace
857
+ / "in" &[\s] // ensure `in` is followed by whitespace
858
858
 
859
859
  separator
860
860
  = __ "," __
@@ -455,13 +455,14 @@ function peg$parse(input, options) {
455
455
  const peg$r15 = /^[0-7]/;
456
456
  const peg$r16 = /^[gimuy]/;
457
457
  const peg$r17 = /^[^\/\n\r]/;
458
- const peg$r18 = /^[^\n\r]/;
459
- const peg$r19 = /^[!+]/;
460
- const peg$r20 = /^[\/)\]}]/;
461
- const peg$r21 = /^[^\/,)\]}]/;
462
- const peg$r22 = /^[a-z]/;
463
- const peg$r23 = /^[a-z0-9+-.]/;
464
- const peg$r24 = /^[:]/;
458
+ const peg$r18 = /^[s]/;
459
+ const peg$r19 = /^[^\n\r]/;
460
+ const peg$r20 = /^[!+]/;
461
+ const peg$r21 = /^[\/)\]}]/;
462
+ const peg$r22 = /^[^\/,)\]}]/;
463
+ const peg$r23 = /^[a-z]/;
464
+ const peg$r24 = /^[a-z0-9+-.]/;
465
+ const peg$r25 = /^[:]/;
465
466
 
466
467
  const peg$e0 = peg$literalExpectation("+", false);
467
468
  const peg$e1 = peg$literalExpectation("<", false);
@@ -566,32 +567,33 @@ function peg$parse(input, options) {
566
567
  const peg$e100 = peg$literalExpectation("<=", false);
567
568
  const peg$e101 = peg$literalExpectation(">=", false);
568
569
  const peg$e102 = peg$literalExpectation("instanceof", false);
569
- const peg$e103 = peg$literalExpectation("in", false);
570
- const peg$e104 = peg$literalExpectation("#!", false);
571
- const peg$e105 = peg$classExpectation(["\n", "\r"], true, false, false);
572
- const peg$e106 = peg$literalExpectation("<<", false);
573
- const peg$e107 = peg$literalExpectation(">>>", false);
574
- const peg$e108 = peg$literalExpectation(">>", false);
575
- const peg$e109 = peg$otherExpectation("lambda function");
576
- const peg$e110 = peg$literalExpectation("\u2192", false);
577
- const peg$e111 = peg$literalExpectation("->", false);
578
- const peg$e112 = peg$otherExpectation("single quote string");
579
- const peg$e113 = peg$otherExpectation("string");
580
- const peg$e114 = peg$literalExpectation("${", false);
581
- const peg$e115 = peg$otherExpectation("template document");
582
- const peg$e116 = peg$otherExpectation("template literal");
583
- const peg$e117 = peg$otherExpectation("template substitution");
584
- const peg$e118 = peg$classExpectation(["!", "+"], false, false, false);
585
- const peg$e119 = peg$literalExpectation("~", false);
586
- const peg$e120 = peg$classExpectation(["/", ")", "]", "}"], false, false, false);
587
- const peg$e121 = peg$literalExpectation("await", false);
588
- const peg$e122 = peg$literalExpectation("typeof", false);
589
- const peg$e123 = peg$literalExpectation("void", false);
590
- const peg$e124 = peg$classExpectation(["/", ",", ")", "]", "}"], true, false, false);
591
- const peg$e125 = peg$otherExpectation("slash-separated path");
592
- const peg$e126 = peg$classExpectation([["a", "z"]], false, false, false);
593
- const peg$e127 = peg$classExpectation([["a", "z"], ["0", "9"], ["+", "."]], false, false, false);
594
- const peg$e128 = peg$classExpectation([":"], false, false, false);
570
+ const peg$e103 = peg$classExpectation(["s"], false, false, false);
571
+ const peg$e104 = peg$literalExpectation("in", false);
572
+ const peg$e105 = peg$literalExpectation("#!", false);
573
+ const peg$e106 = peg$classExpectation(["\n", "\r"], true, false, false);
574
+ const peg$e107 = peg$literalExpectation("<<", false);
575
+ const peg$e108 = peg$literalExpectation(">>>", false);
576
+ const peg$e109 = peg$literalExpectation(">>", false);
577
+ const peg$e110 = peg$otherExpectation("lambda function");
578
+ const peg$e111 = peg$literalExpectation("\u2192", false);
579
+ const peg$e112 = peg$literalExpectation("->", false);
580
+ const peg$e113 = peg$otherExpectation("single quote string");
581
+ const peg$e114 = peg$otherExpectation("string");
582
+ const peg$e115 = peg$literalExpectation("${", false);
583
+ const peg$e116 = peg$otherExpectation("template document");
584
+ const peg$e117 = peg$otherExpectation("template literal");
585
+ const peg$e118 = peg$otherExpectation("template substitution");
586
+ const peg$e119 = peg$classExpectation(["!", "+"], false, false, false);
587
+ const peg$e120 = peg$literalExpectation("~", false);
588
+ const peg$e121 = peg$classExpectation(["/", ")", "]", "}"], false, false, false);
589
+ const peg$e122 = peg$literalExpectation("await", false);
590
+ const peg$e123 = peg$literalExpectation("typeof", false);
591
+ const peg$e124 = peg$literalExpectation("void", false);
592
+ const peg$e125 = peg$classExpectation(["/", ",", ")", "]", "}"], true, false, false);
593
+ const peg$e126 = peg$otherExpectation("slash-separated path");
594
+ const peg$e127 = peg$classExpectation([["a", "z"]], false, false, false);
595
+ const peg$e128 = peg$classExpectation([["a", "z"], ["0", "9"], ["+", "."]], false, false, false);
596
+ const peg$e129 = peg$classExpectation([":"], false, false, false);
595
597
 
596
598
  function peg$f0() {
597
599
  return null;
@@ -6353,7 +6355,7 @@ function peg$parse(input, options) {
6353
6355
  }
6354
6356
 
6355
6357
  function peg$parserelationalOperator() {
6356
- let s0;
6358
+ let s0, s1, s2, s3;
6357
6359
 
6358
6360
  if (input.substr(peg$currPos, 2) === peg$c59) {
6359
6361
  s0 = peg$c59;
@@ -6387,20 +6389,78 @@ function peg$parse(input, options) {
6387
6389
  if (peg$silentFails === 0) { peg$fail(peg$e3); }
6388
6390
  }
6389
6391
  if (s0 === peg$FAILED) {
6392
+ s0 = peg$currPos;
6390
6393
  if (input.substr(peg$currPos, 10) === peg$c61) {
6391
- s0 = peg$c61;
6394
+ s1 = peg$c61;
6392
6395
  peg$currPos += 10;
6393
6396
  } else {
6394
- s0 = peg$FAILED;
6397
+ s1 = peg$FAILED;
6395
6398
  if (peg$silentFails === 0) { peg$fail(peg$e102); }
6396
6399
  }
6400
+ if (s1 !== peg$FAILED) {
6401
+ s2 = peg$currPos;
6402
+ peg$silentFails++;
6403
+ s3 = input.charAt(peg$currPos);
6404
+ if (peg$r18.test(s3)) {
6405
+ peg$currPos++;
6406
+ } else {
6407
+ s3 = peg$FAILED;
6408
+ if (peg$silentFails === 0) { peg$fail(peg$e103); }
6409
+ }
6410
+ peg$silentFails--;
6411
+ if (s3 !== peg$FAILED) {
6412
+ peg$currPos = s2;
6413
+ s2 = undefined;
6414
+ } else {
6415
+ s2 = peg$FAILED;
6416
+ }
6417
+ if (s2 !== peg$FAILED) {
6418
+ s1 = [s1, s2];
6419
+ s0 = s1;
6420
+ } else {
6421
+ peg$currPos = s0;
6422
+ s0 = peg$FAILED;
6423
+ }
6424
+ } else {
6425
+ peg$currPos = s0;
6426
+ s0 = peg$FAILED;
6427
+ }
6397
6428
  if (s0 === peg$FAILED) {
6429
+ s0 = peg$currPos;
6398
6430
  if (input.substr(peg$currPos, 2) === peg$c62) {
6399
- s0 = peg$c62;
6431
+ s1 = peg$c62;
6400
6432
  peg$currPos += 2;
6401
6433
  } else {
6434
+ s1 = peg$FAILED;
6435
+ if (peg$silentFails === 0) { peg$fail(peg$e104); }
6436
+ }
6437
+ if (s1 !== peg$FAILED) {
6438
+ s2 = peg$currPos;
6439
+ peg$silentFails++;
6440
+ s3 = input.charAt(peg$currPos);
6441
+ if (peg$r18.test(s3)) {
6442
+ peg$currPos++;
6443
+ } else {
6444
+ s3 = peg$FAILED;
6445
+ if (peg$silentFails === 0) { peg$fail(peg$e103); }
6446
+ }
6447
+ peg$silentFails--;
6448
+ if (s3 !== peg$FAILED) {
6449
+ peg$currPos = s2;
6450
+ s2 = undefined;
6451
+ } else {
6452
+ s2 = peg$FAILED;
6453
+ }
6454
+ if (s2 !== peg$FAILED) {
6455
+ s1 = [s1, s2];
6456
+ s0 = s1;
6457
+ } else {
6458
+ peg$currPos = s0;
6459
+ s0 = peg$FAILED;
6460
+ }
6461
+ } else {
6462
+ peg$currPos = s0;
6402
6463
  s0 = peg$FAILED;
6403
- if (peg$silentFails === 0) { peg$fail(peg$e103); }
6404
6464
  }
6405
6465
  }
6406
6466
  }
@@ -6454,25 +6514,25 @@ function peg$parse(input, options) {
6454
6514
  peg$currPos += 2;
6455
6515
  } else {
6456
6516
  s1 = peg$FAILED;
6457
- if (peg$silentFails === 0) { peg$fail(peg$e104); }
6517
+ if (peg$silentFails === 0) { peg$fail(peg$e105); }
6458
6518
  }
6459
6519
  if (s1 !== peg$FAILED) {
6460
6520
  s2 = [];
6461
6521
  s3 = input.charAt(peg$currPos);
6462
- if (peg$r18.test(s3)) {
6522
+ if (peg$r19.test(s3)) {
6463
6523
  peg$currPos++;
6464
6524
  } else {
6465
6525
  s3 = peg$FAILED;
6466
- if (peg$silentFails === 0) { peg$fail(peg$e105); }
6526
+ if (peg$silentFails === 0) { peg$fail(peg$e106); }
6467
6527
  }
6468
6528
  while (s3 !== peg$FAILED) {
6469
6529
  s2.push(s3);
6470
6530
  s3 = input.charAt(peg$currPos);
6471
- if (peg$r18.test(s3)) {
6531
+ if (peg$r19.test(s3)) {
6472
6532
  peg$currPos++;
6473
6533
  } else {
6474
6534
  s3 = peg$FAILED;
6475
- if (peg$silentFails === 0) { peg$fail(peg$e105); }
6535
+ if (peg$silentFails === 0) { peg$fail(peg$e106); }
6476
6536
  }
6477
6537
  }
6478
6538
  peg$savedPos = s0;
@@ -6559,7 +6619,7 @@ function peg$parse(input, options) {
6559
6619
  peg$currPos += 2;
6560
6620
  } else {
6561
6621
  s0 = peg$FAILED;
6562
- if (peg$silentFails === 0) { peg$fail(peg$e106); }
6622
+ if (peg$silentFails === 0) { peg$fail(peg$e107); }
6563
6623
  }
6564
6624
  if (s0 === peg$FAILED) {
6565
6625
  if (input.substr(peg$currPos, 3) === peg$c65) {
@@ -6567,7 +6627,7 @@ function peg$parse(input, options) {
6567
6627
  peg$currPos += 3;
6568
6628
  } else {
6569
6629
  s0 = peg$FAILED;
6570
- if (peg$silentFails === 0) { peg$fail(peg$e107); }
6630
+ if (peg$silentFails === 0) { peg$fail(peg$e108); }
6571
6631
  }
6572
6632
  if (s0 === peg$FAILED) {
6573
6633
  if (input.substr(peg$currPos, 2) === peg$c66) {
@@ -6575,7 +6635,7 @@ function peg$parse(input, options) {
6575
6635
  peg$currPos += 2;
6576
6636
  } else {
6577
6637
  s0 = peg$FAILED;
6578
- if (peg$silentFails === 0) { peg$fail(peg$e108); }
6638
+ if (peg$silentFails === 0) { peg$fail(peg$e109); }
6579
6639
  }
6580
6640
  }
6581
6641
  }
@@ -6645,7 +6705,7 @@ function peg$parse(input, options) {
6645
6705
  peg$silentFails--;
6646
6706
  if (s0 === peg$FAILED) {
6647
6707
  s1 = peg$FAILED;
6648
- if (peg$silentFails === 0) { peg$fail(peg$e109); }
6708
+ if (peg$silentFails === 0) { peg$fail(peg$e110); }
6649
6709
  }
6650
6710
 
6651
6711
  return s0;
@@ -6659,7 +6719,7 @@ function peg$parse(input, options) {
6659
6719
  peg$currPos++;
6660
6720
  } else {
6661
6721
  s0 = peg$FAILED;
6662
- if (peg$silentFails === 0) { peg$fail(peg$e110); }
6722
+ if (peg$silentFails === 0) { peg$fail(peg$e111); }
6663
6723
  }
6664
6724
  if (s0 === peg$FAILED) {
6665
6725
  if (input.substr(peg$currPos, 2) === peg$c68) {
@@ -6667,7 +6727,7 @@ function peg$parse(input, options) {
6667
6727
  peg$currPos += 2;
6668
6728
  } else {
6669
6729
  s0 = peg$FAILED;
6670
- if (peg$silentFails === 0) { peg$fail(peg$e111); }
6730
+ if (peg$silentFails === 0) { peg$fail(peg$e112); }
6671
6731
  }
6672
6732
  }
6673
6733
 
@@ -6688,20 +6748,20 @@ function peg$parse(input, options) {
6688
6748
  if (s1 !== peg$FAILED) {
6689
6749
  s2 = [];
6690
6750
  s3 = input.charAt(peg$currPos);
6691
- if (peg$r18.test(s3)) {
6751
+ if (peg$r19.test(s3)) {
6692
6752
  peg$currPos++;
6693
6753
  } else {
6694
6754
  s3 = peg$FAILED;
6695
- if (peg$silentFails === 0) { peg$fail(peg$e105); }
6755
+ if (peg$silentFails === 0) { peg$fail(peg$e106); }
6696
6756
  }
6697
6757
  while (s3 !== peg$FAILED) {
6698
6758
  s2.push(s3);
6699
6759
  s3 = input.charAt(peg$currPos);
6700
- if (peg$r18.test(s3)) {
6760
+ if (peg$r19.test(s3)) {
6701
6761
  peg$currPos++;
6702
6762
  } else {
6703
6763
  s3 = peg$FAILED;
6704
- if (peg$silentFails === 0) { peg$fail(peg$e105); }
6764
+ if (peg$silentFails === 0) { peg$fail(peg$e106); }
6705
6765
  }
6706
6766
  }
6707
6767
  peg$savedPos = s0;
@@ -6748,7 +6808,7 @@ function peg$parse(input, options) {
6748
6808
  peg$silentFails--;
6749
6809
  if (s0 === peg$FAILED) {
6750
6810
  s1 = peg$FAILED;
6751
- if (peg$silentFails === 0) { peg$fail(peg$e112); }
6811
+ if (peg$silentFails === 0) { peg$fail(peg$e113); }
6752
6812
  }
6753
6813
 
6754
6814
  return s0;
@@ -6921,7 +6981,7 @@ function peg$parse(input, options) {
6921
6981
  peg$silentFails--;
6922
6982
  if (s0 === peg$FAILED) {
6923
6983
  s1 = peg$FAILED;
6924
- if (peg$silentFails === 0) { peg$fail(peg$e113); }
6984
+ if (peg$silentFails === 0) { peg$fail(peg$e114); }
6925
6985
  }
6926
6986
 
6927
6987
  return s0;
@@ -6975,7 +7035,7 @@ function peg$parse(input, options) {
6975
7035
  peg$currPos += 2;
6976
7036
  } else {
6977
7037
  s2 = peg$FAILED;
6978
- if (peg$silentFails === 0) { peg$fail(peg$e114); }
7038
+ if (peg$silentFails === 0) { peg$fail(peg$e115); }
6979
7039
  }
6980
7040
  peg$silentFails--;
6981
7041
  if (s2 === peg$FAILED) {
@@ -7056,7 +7116,7 @@ function peg$parse(input, options) {
7056
7116
  peg$silentFails--;
7057
7117
  if (s0 === peg$FAILED) {
7058
7118
  s1 = peg$FAILED;
7059
- if (peg$silentFails === 0) { peg$fail(peg$e115); }
7119
+ if (peg$silentFails === 0) { peg$fail(peg$e116); }
7060
7120
  }
7061
7121
 
7062
7122
  return s0;
@@ -7115,7 +7175,7 @@ function peg$parse(input, options) {
7115
7175
  peg$silentFails--;
7116
7176
  if (s0 === peg$FAILED) {
7117
7177
  s1 = peg$FAILED;
7118
- if (peg$silentFails === 0) { peg$fail(peg$e116); }
7178
+ if (peg$silentFails === 0) { peg$fail(peg$e117); }
7119
7179
  }
7120
7180
 
7121
7181
  return s0;
@@ -7140,7 +7200,7 @@ function peg$parse(input, options) {
7140
7200
  peg$currPos += 2;
7141
7201
  } else {
7142
7202
  s2 = peg$FAILED;
7143
- if (peg$silentFails === 0) { peg$fail(peg$e114); }
7203
+ if (peg$silentFails === 0) { peg$fail(peg$e115); }
7144
7204
  }
7145
7205
  }
7146
7206
  peg$silentFails--;
@@ -7193,7 +7253,7 @@ function peg$parse(input, options) {
7193
7253
  peg$currPos += 2;
7194
7254
  } else {
7195
7255
  s1 = peg$FAILED;
7196
- if (peg$silentFails === 0) { peg$fail(peg$e114); }
7256
+ if (peg$silentFails === 0) { peg$fail(peg$e115); }
7197
7257
  }
7198
7258
  if (s1 !== peg$FAILED) {
7199
7259
  s2 = peg$parseexpectExpression();
@@ -7223,7 +7283,7 @@ function peg$parse(input, options) {
7223
7283
  peg$silentFails--;
7224
7284
  if (s0 === peg$FAILED) {
7225
7285
  s1 = peg$FAILED;
7226
- if (peg$silentFails === 0) { peg$fail(peg$e117); }
7286
+ if (peg$silentFails === 0) { peg$fail(peg$e118); }
7227
7287
  }
7228
7288
 
7229
7289
  return s0;
@@ -7276,11 +7336,11 @@ function peg$parse(input, options) {
7276
7336
  let s0, s1, s2, s3;
7277
7337
 
7278
7338
  s0 = input.charAt(peg$currPos);
7279
- if (peg$r19.test(s0)) {
7339
+ if (peg$r20.test(s0)) {
7280
7340
  peg$currPos++;
7281
7341
  } else {
7282
7342
  s0 = peg$FAILED;
7283
- if (peg$silentFails === 0) { peg$fail(peg$e118); }
7343
+ if (peg$silentFails === 0) { peg$fail(peg$e119); }
7284
7344
  }
7285
7345
  if (s0 === peg$FAILED) {
7286
7346
  s0 = peg$currPos;
@@ -7289,17 +7349,17 @@ function peg$parse(input, options) {
7289
7349
  peg$currPos++;
7290
7350
  } else {
7291
7351
  s1 = peg$FAILED;
7292
- if (peg$silentFails === 0) { peg$fail(peg$e119); }
7352
+ if (peg$silentFails === 0) { peg$fail(peg$e120); }
7293
7353
  }
7294
7354
  if (s1 !== peg$FAILED) {
7295
7355
  s2 = peg$currPos;
7296
7356
  peg$silentFails++;
7297
7357
  s3 = input.charAt(peg$currPos);
7298
- if (peg$r20.test(s3)) {
7358
+ if (peg$r21.test(s3)) {
7299
7359
  peg$currPos++;
7300
7360
  } else {
7301
7361
  s3 = peg$FAILED;
7302
- if (peg$silentFails === 0) { peg$fail(peg$e120); }
7362
+ if (peg$silentFails === 0) { peg$fail(peg$e121); }
7303
7363
  }
7304
7364
  peg$silentFails--;
7305
7365
  if (s3 === peg$FAILED) {
@@ -7327,7 +7387,7 @@ function peg$parse(input, options) {
7327
7387
  peg$currPos += 5;
7328
7388
  } else {
7329
7389
  s1 = peg$FAILED;
7330
- if (peg$silentFails === 0) { peg$fail(peg$e121); }
7390
+ if (peg$silentFails === 0) { peg$fail(peg$e122); }
7331
7391
  }
7332
7392
  if (s1 !== peg$FAILED) {
7333
7393
  s2 = peg$currPos;
@@ -7357,7 +7417,7 @@ function peg$parse(input, options) {
7357
7417
  peg$currPos += 6;
7358
7418
  } else {
7359
7419
  s1 = peg$FAILED;
7360
- if (peg$silentFails === 0) { peg$fail(peg$e122); }
7420
+ if (peg$silentFails === 0) { peg$fail(peg$e123); }
7361
7421
  }
7362
7422
  if (s1 !== peg$FAILED) {
7363
7423
  s2 = peg$currPos;
@@ -7387,7 +7447,7 @@ function peg$parse(input, options) {
7387
7447
  peg$currPos += 4;
7388
7448
  } else {
7389
7449
  s1 = peg$FAILED;
7390
- if (peg$silentFails === 0) { peg$fail(peg$e123); }
7450
+ if (peg$silentFails === 0) { peg$fail(peg$e124); }
7391
7451
  }
7392
7452
  if (s1 !== peg$FAILED) {
7393
7453
  s2 = peg$currPos;
@@ -7563,11 +7623,11 @@ function peg$parse(input, options) {
7563
7623
 
7564
7624
  s0 = peg$currPos;
7565
7625
  s1 = input.charAt(peg$currPos);
7566
- if (peg$r21.test(s1)) {
7626
+ if (peg$r22.test(s1)) {
7567
7627
  peg$currPos++;
7568
7628
  } else {
7569
7629
  s1 = peg$FAILED;
7570
- if (peg$silentFails === 0) { peg$fail(peg$e124); }
7630
+ if (peg$silentFails === 0) { peg$fail(peg$e125); }
7571
7631
  }
7572
7632
  if (s1 !== peg$FAILED) {
7573
7633
  s2 = peg$currPos;
@@ -7630,7 +7690,7 @@ function peg$parse(input, options) {
7630
7690
  peg$silentFails--;
7631
7691
  if (s0 === peg$FAILED) {
7632
7692
  s1 = peg$FAILED;
7633
- if (peg$silentFails === 0) { peg$fail(peg$e125); }
7693
+ if (peg$silentFails === 0) { peg$fail(peg$e126); }
7634
7694
  }
7635
7695
 
7636
7696
  return s0;
@@ -7641,37 +7701,37 @@ function peg$parse(input, options) {
7641
7701
 
7642
7702
  s0 = peg$currPos;
7643
7703
  s1 = input.charAt(peg$currPos);
7644
- if (peg$r22.test(s1)) {
7704
+ if (peg$r23.test(s1)) {
7645
7705
  peg$currPos++;
7646
7706
  } else {
7647
7707
  s1 = peg$FAILED;
7648
- if (peg$silentFails === 0) { peg$fail(peg$e126); }
7708
+ if (peg$silentFails === 0) { peg$fail(peg$e127); }
7649
7709
  }
7650
7710
  if (s1 !== peg$FAILED) {
7651
7711
  s2 = [];
7652
7712
  s3 = input.charAt(peg$currPos);
7653
- if (peg$r23.test(s3)) {
7713
+ if (peg$r24.test(s3)) {
7654
7714
  peg$currPos++;
7655
7715
  } else {
7656
7716
  s3 = peg$FAILED;
7657
- if (peg$silentFails === 0) { peg$fail(peg$e127); }
7717
+ if (peg$silentFails === 0) { peg$fail(peg$e128); }
7658
7718
  }
7659
7719
  while (s3 !== peg$FAILED) {
7660
7720
  s2.push(s3);
7661
7721
  s3 = input.charAt(peg$currPos);
7662
- if (peg$r23.test(s3)) {
7722
+ if (peg$r24.test(s3)) {
7663
7723
  peg$currPos++;
7664
7724
  } else {
7665
7725
  s3 = peg$FAILED;
7666
- if (peg$silentFails === 0) { peg$fail(peg$e127); }
7726
+ if (peg$silentFails === 0) { peg$fail(peg$e128); }
7667
7727
  }
7668
7728
  }
7669
7729
  s3 = input.charAt(peg$currPos);
7670
- if (peg$r24.test(s3)) {
7730
+ if (peg$r25.test(s3)) {
7671
7731
  peg$currPos++;
7672
7732
  } else {
7673
7733
  s3 = peg$FAILED;
7674
- if (peg$silentFails === 0) { peg$fail(peg$e128); }
7734
+ if (peg$silentFails === 0) { peg$fail(peg$e129); }
7675
7735
  }
7676
7736
  if (s3 !== peg$FAILED) {
7677
7737
  peg$savedPos = s0;
@@ -18,7 +18,7 @@ export default {
18
18
 
19
19
  // Compile the source code as an Origami program
20
20
  const compiler = options.compiler ?? compile.program;
21
- const globals = options.globals ?? (await projectGlobals());
21
+ const globals = options.globals ?? (await projectGlobals(parent));
22
22
  const fn = compiler(source, {
23
23
  globals,
24
24
  mode: "program",
@@ -16,7 +16,7 @@ export default {
16
16
  const source = getSource(packed, options);
17
17
 
18
18
  // Compile the source code as an Origami template document
19
- const globals = options.globals ?? (await projectGlobals());
19
+ const globals = options.globals ?? (await projectGlobals(parent));
20
20
  const defineFn = compile.templateDocument(source, {
21
21
  front: options.front,
22
22
  globals,
@@ -80,7 +80,7 @@ export default {
80
80
  const { body, frontText, isOrigami } = parsed;
81
81
  let frontData;
82
82
  if (isOrigami) {
83
- const globals = await projectGlobals();
83
+ const globals = await projectGlobals(parent);
84
84
  const compiled = compile.expression(frontText.trim(), {
85
85
  globals,
86
86
  parent,
@@ -98,7 +98,7 @@ export default {
98
98
  };
99
99
 
100
100
  async function oriCallTagForParent(parent, options, yaml) {
101
- const globals = await projectGlobals();
101
+ const globals = await projectGlobals(parent);
102
102
  return {
103
103
  collection: "seq",
104
104
 
@@ -162,7 +162,7 @@ async function oriCallTagForParent(parent, options, yaml) {
162
162
  // Define the !ori tag for YAML parsing. This will run in the context of the
163
163
  // supplied parent.
164
164
  async function oriTagForParent(parent, options, yaml) {
165
- const globals = await projectGlobals();
165
+ const globals = await projectGlobals(parent);
166
166
  return {
167
167
  resolve(text) {
168
168
  hasOriTags = true;
@@ -1,17 +1,39 @@
1
1
  import { Tree } from "@weborigami/async-tree";
2
2
  import protocolGlobals from "../protocols/protocolGlobals.js";
3
3
  import * as protocols from "../protocols/protocols.js";
4
- import builtins from "./builtins.js";
5
4
  import jsGlobals from "./jsGlobals.js";
6
5
 
6
+ let globals;
7
+
7
8
  export default async function coreGlobals() {
9
+ if (globals) {
10
+ return globals;
11
+ }
12
+
13
+ // Dynamic import to avoid circular dependency
8
14
  const handlerGlobals = await import("../handlers/handlers.js");
9
- return {
15
+
16
+ let origamiBuiltins;
17
+ if (!origamiBuiltins) {
18
+ // This dynamic import is for a different reason. We want to load the
19
+ // Origami builtins if they're available, but not fail if they're not. This
20
+ // lets someone use the language package without installing the origami
21
+ // package. This arrangement is unorthodox but expedient.
22
+ try {
23
+ origamiBuiltins = await import("@weborigami/origami");
24
+ } catch {
25
+ origamiBuiltins = {};
26
+ }
27
+ }
28
+
29
+ globals = {
10
30
  ...jsGlobals,
11
31
  Tree,
12
32
  Protocol: protocols,
13
33
  ...protocolGlobals,
14
34
  ...handlerGlobals,
15
- ...builtins,
35
+ ...origamiBuiltins,
16
36
  };
37
+
38
+ return globals;
17
39
  }
@@ -1,23 +1,20 @@
1
- import { FileMap, toString } from "@weborigami/async-tree";
1
+ import { toString, Tree } from "@weborigami/async-tree";
2
2
  import ori_handler from "../handlers/ori_handler.js";
3
3
  import coreGlobals from "./coreGlobals.js";
4
- import projectRootFromPath from "./projectRootFromPath.js";
5
4
 
6
- const mapPathToConfig = new Map();
5
+ /**
6
+ * Given a container, return the Origami configuration for the associated
7
+ * project root. This will be the unpacked config.ori file if it exists, or an
8
+ * empty object otherwise.
9
+ *
10
+ * @typedef {import("@weborigami/async-tree").SyncOrAsyncMap} SyncOrAsyncMap
11
+ * @param {SyncOrAsyncMap} parent
12
+ */
13
+ export default async function config(parent) {
14
+ const projectRoot = await Tree.root(parent);
7
15
 
8
- export default async function config(dir = process.cwd()) {
9
- const root = await projectRootFromPath(dir);
10
-
11
- const rootPath = root.path;
12
- const cached = mapPathToConfig.get(rootPath);
13
- if (cached) {
14
- return cached;
15
- }
16
-
17
- // Use a plain FileMap to avoid loading extension handlers
18
- const rootFileMap = new FileMap(rootPath);
19
- const configBuffer = await rootFileMap.get("config.ori");
20
16
  let configObject = {};
17
+ const configBuffer = await projectRoot.get("config.ori");
21
18
  if (configBuffer) {
22
19
  const configText = toString(configBuffer);
23
20
  if (configText) {
@@ -26,11 +23,10 @@ export default async function config(dir = process.cwd()) {
26
23
  // Evaluate the config file to obtain the configuration object
27
24
  configObject = await ori_handler.unpack(configText, {
28
25
  globals,
29
- parent: root,
26
+ parent: projectRoot,
30
27
  });
31
28
  }
32
29
  }
33
30
 
34
- mapPathToConfig.set(rootPath, configObject);
35
31
  return configObject;
36
32
  }
@@ -1,19 +1,43 @@
1
+ import { Tree } from "@weborigami/async-tree";
2
+ import assignPropertyDescriptors from "../runtime/assignPropertyDescriptors.js";
1
3
  import coreGlobals from "./coreGlobals.js";
2
4
  import projectConfig from "./projectConfig.js";
3
5
 
4
- let globals;
6
+ /**
7
+ * Return the complete set of globals available to code running in the given
8
+ * container. This will be the core globals plus any configuration specified in
9
+ * the project's config.ori file.
10
+ *
11
+ * This destructively caches a `globals` property on the root of the given
12
+ * container.
13
+ *
14
+ * @typedef {import("@weborigami/async-tree").SyncOrAsyncMap} SyncOrAsyncMap
15
+ * @param {SyncOrAsyncMap|null} parent
16
+ */
17
+ export default async function projectGlobals(parent) {
18
+ if (!parent) {
19
+ return coreGlobals();
20
+ }
5
21
 
6
- // Core globals plus project config
7
- export default async function projectGlobals(dir = process.cwd()) {
8
- if (!globals) {
22
+ const projectRoot = await Tree.root(parent);
23
+ if (!projectRoot.globals) {
9
24
  // Start with core globals
10
- globals = await coreGlobals();
11
- // Now get config. The config.ori file may require access to globals,
12
- // which will obtain the core globals set above. Once we've got the
13
- // config, we add it to the globals.
14
- const config = await projectConfig(dir);
15
- Object.assign(globals, config);
25
+ const globals = await coreGlobals();
26
+
27
+ if (parent) {
28
+ // Get config for the given container and add it to the globals. During
29
+ // this read, we want to only rely on core globals to avoid circularities,
30
+ // so we temporarily set globals to coreGlobals.
31
+ projectRoot.globals = globals;
32
+ const config = await projectConfig(parent);
33
+
34
+ // Merge config into globals; don't invoke property getters.
35
+ assignPropertyDescriptors(globals, config);
36
+ }
37
+
38
+ // Cache globals on project root
39
+ projectRoot.globals = globals;
16
40
  }
17
41
 
18
- return globals;
42
+ return projectRoot.globals;
19
43
  }
@@ -4,6 +4,6 @@ import { Tree } from "@weborigami/async-tree";
4
4
  * Return an OrigamiFileMap object for the current code context.
5
5
  */
6
6
  export default async function projectRoot(state) {
7
- return Tree.root(state.container);
7
+ return Tree.root(state.parent);
8
8
  }
9
9
  projectRoot.needsState = true;
@@ -5,27 +5,19 @@ import OrigamiFileMap from "../runtime/OrigamiFileMap.js";
5
5
  const configFileName = "config.ori";
6
6
  const packageFileName = "package.json";
7
7
 
8
- const mapPathToRoot = new Map();
9
-
10
8
  /**
11
- * Return an OrigamiFileMap object for the current project root.
12
- *
13
- * This searches the current directory and its ancestors for an Origami file
14
- * called `config.ori`. If an Origami configuration file is found, the
15
- * containing folder is considered to be the project root.
9
+ * Return an OrigamiFileMap object for the given folder.
16
10
  *
17
- * Otherwise, this looks for a package.json file to determine the project root.
18
- * If no package.json is found, the current folder is used as the project root.
11
+ * This searches the given folder and its ancestors for an Origami file called
12
+ * `config.ori`. If an Origami configuration file is found, the containing
13
+ * folder is considered to be the project root.
19
14
  *
15
+ * Otherwise this looks for a package.json file to determine the project root.
16
+ * If none is found, the given folder itself is used as the project root.
20
17
  *
21
- * @param {string} [dirname]
18
+ * @param {string} dirname
22
19
  */
23
- export default async function projectRootFromPath(dirname = process.cwd()) {
24
- const cached = mapPathToRoot.get(dirname);
25
- if (cached) {
26
- return cached;
27
- }
28
-
20
+ export default async function projectRootFromPath(dirname) {
29
21
  let root;
30
22
  let value;
31
23
  // Use a plain FileMap to avoid loading extension handlers
@@ -63,6 +55,5 @@ export default async function projectRootFromPath(dirname = process.cwd()) {
63
55
  root = new OrigamiFileMap(dirname);
64
56
  }
65
57
 
66
- mapPathToRoot.set(dirname, root);
67
58
  return root;
68
59
  }
@@ -3,10 +3,12 @@ import handleExtension from "../runtime/handleExtension.js";
3
3
  /**
4
4
  * Fetch the resource at the given href.
5
5
  *
6
+ * @typedef {import("@weborigami/async-tree").SyncOrAsyncMap} SyncOrAsyncMap
6
7
  *
7
8
  * @param {string} href
9
+ * @param {SyncOrAsyncMap} parent
8
10
  */
9
- export default async function fetchAndHandleExtension(href) {
11
+ export default async function fetchAndHandleExtension(href, parent) {
10
12
  const response = await fetch(href);
11
13
  if (!response.ok) {
12
14
  return undefined;
@@ -22,7 +24,7 @@ export default async function fetchAndHandleExtension(href) {
22
24
  const url = new URL(href);
23
25
  const filename = url.pathname.split("/").pop();
24
26
  if (filename) {
25
- buffer = await handleExtension(buffer, filename);
27
+ buffer = await handleExtension(buffer, filename, parent);
26
28
  }
27
29
 
28
30
  return buffer;
@@ -17,8 +17,7 @@ export default async function files(...args) {
17
17
  basePath = os.homedir();
18
18
  relativePath = relativePath.slice(2);
19
19
  } else {
20
- const { container } = state;
21
- basePath = container.path;
20
+ basePath = state.parent.path;
22
21
  }
23
22
  const resolved = path.resolve(basePath, relativePath);
24
23
 
@@ -6,9 +6,11 @@ import fetchAndHandleExtension from "./fetchAndHandleExtension.js";
6
6
  *
7
7
  *
8
8
  * @param {string} host
9
- * @param {...string} keys
9
+ * @param {...any} keys
10
10
  */
11
11
  export default async function http(host, ...keys) {
12
+ const state = keys.pop();
12
13
  const href = constructHref("http:", host, ...keys);
13
- return fetchAndHandleExtension(href);
14
+ return fetchAndHandleExtension(href, state.parent);
14
15
  }
16
+ http.needsState = true;
@@ -6,9 +6,11 @@ import fetchAndHandleExtension from "./fetchAndHandleExtension.js";
6
6
  *
7
7
  *
8
8
  * @param {string} host
9
- * @param {...string} keys
9
+ * @param {...any} keys
10
10
  */
11
11
  export default async function https(host, ...keys) {
12
+ const state = keys.pop();
12
13
  const href = constructHref("https:", host, ...keys);
13
- return fetchAndHandleExtension(href);
14
+ return fetchAndHandleExtension(href, state.parent);
14
15
  }
16
+ https.needsState = true;
@@ -1,5 +1,4 @@
1
- import * as fs from "node:fs/promises";
2
- import path from "node:path";
1
+ import * as fs from "node:fs";
3
2
  import Watcher from "watcher";
4
3
  import TreeEvent from "./TreeEvent.js";
5
4
 
@@ -15,10 +14,10 @@ export default function WatchFilesMixin(Base) {
15
14
  }
16
15
  }
17
16
 
18
- onChange(key) {
17
+ onChange(filePath) {
19
18
  // Reset cached values.
20
19
  this.subfoldersMap = new Map();
21
- this.dispatchEvent(new TreeEvent("change", { key }));
20
+ this.dispatchEvent(new TreeEvent("change", { filePath }));
22
21
  }
23
22
 
24
23
  async unwatch() {
@@ -38,7 +37,7 @@ export default function WatchFilesMixin(Base) {
38
37
  this.watching = true;
39
38
 
40
39
  // Ensure the directory exists.
41
- await fs.mkdir(this.dirname, { recursive: true });
40
+ fs.mkdirSync(this.dirname, { recursive: true });
42
41
 
43
42
  this.watcher = new Watcher(this.dirname, {
44
43
  ignoreInitial: true,
@@ -46,8 +45,7 @@ export default function WatchFilesMixin(Base) {
46
45
  recursive: true,
47
46
  });
48
47
  this.watcher.on("all", (event, filePath) => {
49
- const key = path.basename(filePath);
50
- this.onChange(key);
48
+ this.onChange(filePath);
51
49
  });
52
50
 
53
51
  // Add to the list of FileTree instances watching this directory.
@@ -0,0 +1,23 @@
1
+ /**
2
+ * This is an analogue of Object.assign that destructively copies properties to
3
+ * a target object -- but avoids invoking property getters. Instead, it copies
4
+ * property descriptors over to the target object.
5
+ *
6
+ * @param {any} target
7
+ * @param {...any} sources
8
+ */
9
+ export default function assignPropertyDescriptors(target, ...sources) {
10
+ for (const source of sources) {
11
+ const descriptors = Object.getOwnPropertyDescriptors(source);
12
+ for (const [key, descriptor] of Object.entries(descriptors)) {
13
+ if (descriptor.value !== undefined) {
14
+ // Simple value, copy it
15
+ target[key] = descriptor.value;
16
+ } else {
17
+ // Getter and/or setter, copy the descriptor
18
+ Object.defineProperty(target, key, descriptor);
19
+ }
20
+ }
21
+ }
22
+ return target;
23
+ }
@@ -24,7 +24,7 @@ export default async function evaluate(code, state = {}) {
24
24
  } else {
25
25
  // Evaluate each instruction in the code.
26
26
  evaluated = await Promise.all(
27
- code.map((instruction) => evaluate(instruction, state))
27
+ code.map((instruction) => evaluate(instruction, state)),
28
28
  );
29
29
  }
30
30
 
@@ -34,7 +34,7 @@ export default async function evaluate(code, state = {}) {
34
34
  if (!fn) {
35
35
  // The code wants to invoke something that's couldn't be found in scope.
36
36
  const error = ReferenceError(
37
- `${codeFragment(code[0].location)} is not defined`
37
+ `${codeFragment(code[0].location)} is not defined`,
38
38
  );
39
39
  /** @type {any} */ (error).location = code[0].location;
40
40
  throw error;
@@ -48,9 +48,9 @@ export default async function evaluate(code, state = {}) {
48
48
  if (fn.needsState) {
49
49
  // The function is an op that wants the runtime state
50
50
  args.push(state);
51
- } else if (fn.containerAsTarget && state.container) {
51
+ } else if (fn.containerAsTarget && state.parent) {
52
52
  // The function wants the code's container as the `this` target
53
- fn = fn.bind(state.container);
53
+ fn = fn.bind(state.parent);
54
54
  }
55
55
 
56
56
  // Execute the function or traverse the tree.
@@ -10,9 +10,7 @@ import { evaluate } from "./internal.js";
10
10
  */
11
11
  export function createExpressionFunction(code, parent) {
12
12
  async function fn() {
13
- return evaluate(code, {
14
- container: parent,
15
- });
13
+ return evaluate(code, { parent });
16
14
  }
17
15
  fn.code = code;
18
16
  fn.toString = () => code.location.source.text;
@@ -7,9 +7,7 @@ import {
7
7
  setParent,
8
8
  trailingSlash,
9
9
  } from "@weborigami/async-tree";
10
- import globals from "../project/projectGlobals.js";
11
-
12
- let projectGlobals;
10
+ import projectGlobals from "../project/projectGlobals.js";
13
11
 
14
12
  /**
15
13
  * If the given value is packed (e.g., buffer) and the key is a string-like path
@@ -18,9 +16,9 @@ let projectGlobals;
18
16
  *
19
17
  * @param {any} value
20
18
  * @param {any} key
21
- * @param {import("@weborigami/async-tree").SyncOrAsyncMap} [parent]
19
+ * @param {import("@weborigami/async-tree").SyncOrAsyncMap|null} [parent]
22
20
  */
23
- export default async function handleExtension(value, key, parent) {
21
+ export default async function handleExtension(value, key, parent = null) {
24
22
  if (isPacked(value) && isStringlike(key) && value.unpack === undefined) {
25
23
  const hasSlash = trailingSlash.has(key);
26
24
  if (hasSlash) {
@@ -33,7 +31,7 @@ export default async function handleExtension(value, key, parent) {
33
31
  : extension.extname(key);
34
32
  if (extname) {
35
33
  const handlerName = `${extname.slice(1)}_handler`;
36
- const handlers = await getHandlers(parent);
34
+ const handlers = await projectGlobals(parent);
37
35
  let handler = await handlers[handlerName];
38
36
  if (handler) {
39
37
  if (isUnpackable(handler)) {
@@ -66,19 +64,3 @@ export default async function handleExtension(value, key, parent) {
66
64
  }
67
65
  return value;
68
66
  }
69
-
70
- async function getHandlers(parent) {
71
- // Walk up the parent chain to find first `handlers` property
72
- let current = parent;
73
-
74
- while (current) {
75
- if (current.handlers) {
76
- return current.handlers;
77
- }
78
- current = current.parent;
79
- }
80
-
81
- // Fall back to project globals
82
- projectGlobals ??= await globals();
83
- return projectGlobals;
84
- }
@@ -1,4 +1,5 @@
1
1
  import { isPlainObject, isUnpackable, Tree } from "@weborigami/async-tree";
2
+ import assignPropertyDescriptors from "./assignPropertyDescriptors.js";
2
3
 
3
4
  /**
4
5
  * Create a tree that's the result of merging the given trees.
@@ -21,26 +22,14 @@ export default async function mergeTrees(...trees) {
21
22
  // Unpack any packed objects.
22
23
  const unpacked = await Promise.all(
23
24
  filtered.map((obj) =>
24
- isUnpackable(obj) ? /** @type {any} */ (obj).unpack() : obj
25
- )
25
+ isUnpackable(obj) ? /** @type {any} */ (obj).unpack() : obj,
26
+ ),
26
27
  );
27
28
 
28
29
  // If all trees are plain objects, return a plain object.
29
30
  if (unpacked.every((tree) => isPlainObject(tree) && !Tree.isMap(tree))) {
30
- // If we do an Object.assign, we'd evaluate getters.
31
- // To avoid that, we'll merge property descriptors.
32
- const result = {};
33
- for (const obj of unpacked) {
34
- const descriptors = Object.getOwnPropertyDescriptors(obj);
35
- for (const [key, descriptor] of Object.entries(descriptors)) {
36
- if (descriptor.value !== undefined) {
37
- result[key] = descriptor.value;
38
- } else {
39
- Object.defineProperty(result, key, descriptor);
40
- }
41
- }
42
- }
43
- return result;
31
+ // Use our Object.assign variation that avoids invoking property getters.
32
+ return assignPropertyDescriptors({}, ...unpacked);
44
33
  }
45
34
 
46
35
  // Merge the trees.
@@ -1,28 +1,26 @@
1
+ import { FileMap } from "@weborigami/async-tree";
1
2
  import assert from "node:assert";
2
3
  import { describe, test } from "node:test";
3
- import { fileURLToPath } from "node:url";
4
4
  import projectConfig from "../../src/project/projectConfig.js";
5
5
 
6
6
  describe("projectConfig", () => {
7
- test("finds Origami configuration file", async () => {
8
- // Find the folder that represents the project root.
9
- const projectUrl = new URL("fixtures/withConfig/", import.meta.url);
10
- // Find subfolder inside project root.
11
- const subfolderUrl = new URL("./subfolder/", projectUrl);
12
- const subfolderPath = fileURLToPath(subfolderUrl);
13
-
14
- const config = await projectConfig(subfolderPath);
7
+ test("loads Origami configuration file if present", async () => {
8
+ // The folder that represents the project root
9
+ const projectFiles = new FileMap(
10
+ new URL("fixtures/withConfig/", import.meta.url),
11
+ );
12
+ const subfolder = await projectFiles.get("subfolder");
13
+ const config = await projectConfig(subfolder);
15
14
  assert.equal(config.message, "Hello");
16
15
  });
17
16
 
18
17
  test("defaults to an empty object", async () => {
19
- // Find the folder that represents the project root.
20
- const projectUrl = new URL("fixtures/withPackageJson/", import.meta.url);
21
- // Find subfolder inside project root.
22
- const subfolderUrl = new URL("./subfolder/", projectUrl);
23
- const subfolderPath = fileURLToPath(subfolderUrl);
24
-
25
- const config = await projectConfig(subfolderPath);
18
+ // The folder that represents the project root
19
+ const projectFiles = new FileMap(
20
+ new URL("fixtures/withPackageJson/", import.meta.url),
21
+ );
22
+ const subfolder = await projectFiles.get("subfolder");
23
+ const config = await projectConfig(subfolder);
26
24
  assert.deepEqual(config, {});
27
25
  });
28
26
  });
@@ -1,4 +1,5 @@
1
1
  import assert from "node:assert";
2
+ import process from "node:process";
2
3
  import { describe, test } from "node:test";
3
4
  import projectRootFromPath from "../../src/project/projectRootFromPath.js";
4
5
  import packageProtocol from "../../src/protocols/package.js";
@@ -6,10 +7,10 @@ import packageProtocol from "../../src/protocols/package.js";
6
7
  describe("package: protocol", () => {
7
8
  test("returns a package's main export(s)", async () => {
8
9
  // Reproduce the type of evaluation context object the runtime would create
9
- const projectRoot = await projectRootFromPath();
10
- const context = { container: projectRoot };
10
+ const projectRoot = await projectRootFromPath(process.cwd());
11
+ const state = { parent: projectRoot };
11
12
 
12
- const result = await packageProtocol("@weborigami", "async-tree", context);
13
+ const result = await packageProtocol("@weborigami", "async-tree", state);
13
14
  const { toString } = result;
14
15
  assert.equal(toString(123), "123");
15
16
  });
@@ -32,10 +32,10 @@ describe("evaluate", () => {
32
32
  return this;
33
33
  };
34
34
  fn.containerAsTarget = true;
35
- const container = new SyncMap();
36
- const state = { container };
35
+ const parent = new SyncMap();
36
+ const state = { parent };
37
37
  const code = createCode([fn]);
38
38
  const result = await evaluate(code, state);
39
- assert.equal(result, container);
39
+ assert.equal(result, parent);
40
40
  });
41
41
  });
@@ -1,5 +0,0 @@
1
- /**
2
- * Consumers of the compiler (e.g., the Origami CLI) should inject their own
3
- * builtin functions into this object.
4
- */
5
- export default {};