@weborigami/language 0.0.59 → 0.0.61

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.
@@ -1,20 +1,6 @@
1
- import { ObjectTree } from "@weborigami/async-tree";
1
+ import { DeepObjectTree } from "@weborigami/async-tree";
2
2
  import InvokeFunctionsTransform from "./InvokeFunctionsTransform.js";
3
- import { expressionFunction } from "./internal.js";
4
3
 
5
4
  export default class ExpressionTree extends InvokeFunctionsTransform(
6
- ObjectTree
7
- ) {
8
- // Return the unevaluated expressions in the original object.
9
- expressions() {
10
- const obj = /** @type {any} */ (this).object;
11
- const result = {};
12
- for (const key in obj) {
13
- const value = obj[key];
14
- if (expressionFunction.isExpressionFunction(value)) {
15
- result[key] = value.code;
16
- }
17
- }
18
- return result;
19
- }
20
- }
5
+ DeepObjectTree
6
+ ) {}
@@ -1,5 +1,4 @@
1
1
  import { isStringLike } from "@weborigami/async-tree";
2
- import Scope from "./Scope.js";
3
2
  import handleExtension from "./handleExtension.js";
4
3
 
5
4
  /**
@@ -17,8 +16,7 @@ export default function HandleExtensionsTransform(Base) {
17
16
  // If the key is string-like and has an extension, attach a loader (if one
18
17
  // exists) that handles that extension.
19
18
  if (value && isStringLike(key)) {
20
- const scope = Scope.getScope(this);
21
- value = await handleExtension(scope, String(key), value, this);
19
+ value = await handleExtension(this, String(key), value);
22
20
  }
23
21
 
24
22
  return value;
@@ -1,5 +1,4 @@
1
1
  import { Tree } from "@weborigami/async-tree";
2
- import Scope from "./Scope.js";
3
2
 
4
3
  /**
5
4
  * When using `get` to retrieve a value from a tree, if the value is a
@@ -14,10 +13,9 @@ export default function InvokeFunctionsTransform(Base) {
14
13
  async get(key) {
15
14
  let value = await super.get(key);
16
15
  if (typeof value === "function") {
17
- const scope = Scope.getScope(this);
18
- value = await value.call(scope);
16
+ value = await value.call(this);
19
17
 
20
- if (Tree.isAsyncTree(value)) {
18
+ if (Tree.isAsyncTree(value) && !value.parent) {
21
19
  value.parent = this;
22
20
  }
23
21
  }
@@ -1,11 +1,5 @@
1
1
  import { Mixin } from "../../index.ts";
2
2
 
3
- import type { AsyncTree } from "@weborigami/types";
4
-
5
- // TODO: Figure out how to import declarations from InheritScopeMixin and
6
- // HandleExtensionsTransform and apply them here.
7
- declare const OrigamiTransform: Mixin<{
8
- scope: AsyncTree|null;
9
- }>;
3
+ declare const OrigamiTransform: Mixin<{}>;
10
4
 
11
5
  export default OrigamiTransform;
@@ -1,13 +1,3 @@
1
+ // Right now this just applies HandleExtensionsTransform.
1
2
  import HandleExtensionsTransform from "./HandleExtensionsTransform.js";
2
- import InheritScopeMixin from "./InheritScopeMixin.js";
3
-
4
- /**
5
- * @typedef {import("@weborigami/types").AsyncTree} AsyncTree
6
- * @typedef {import("../../index.js").Constructor<AsyncTree>} AsyncTreeConstructor
7
- * @param {AsyncTreeConstructor} Base
8
- */
9
- export default function OrigamiTransform(Base) {
10
- return class Origami extends InheritScopeMixin(
11
- HandleExtensionsTransform(Base)
12
- ) {};
13
- }
3
+ export default HandleExtensionsTransform;
@@ -1,26 +1,27 @@
1
- import { Tree, isPlainObject, isUnpackable } from "@weborigami/async-tree";
1
+ import {
2
+ Tree,
3
+ isPlainObject,
4
+ isUnpackable,
5
+ scope,
6
+ } from "@weborigami/async-tree";
2
7
  import { ops } from "./internal.js";
3
8
 
4
9
  const codeSymbol = Symbol("code");
10
+ const scopeSymbol = Symbol("scope");
5
11
  const sourceSymbol = Symbol("source");
6
12
 
7
13
  /**
8
14
  * Evaluate the given code and return the result.
9
15
  *
10
- * `this` should be the scope used to look up references found in the code.
16
+ * `this` should be the tree used as the context for the evaluation.
11
17
  *
12
- * @typedef {import("@weborigami/async-tree").Treelike} Treelike
13
- *
14
- * @this {Treelike|null}
18
+ * @this {import("@weborigami/types").AsyncTree|null}
15
19
  * @param {any} code
16
20
  */
17
21
  export default async function evaluate(code) {
18
- const scope = this;
22
+ const tree = this;
19
23
 
20
- if (code === ops.scope) {
21
- // ops.scope is a placeholder for the context's scope.
22
- return scope;
23
- } else if (!(code instanceof Array)) {
24
+ if (!(code instanceof Array)) {
24
25
  // Simple scalar; return as is.
25
26
  return code;
26
27
  }
@@ -33,7 +34,7 @@ export default async function evaluate(code) {
33
34
  } else {
34
35
  // Evaluate each instruction in the code.
35
36
  evaluated = await Promise.all(
36
- code.map((instruction) => evaluate.call(scope, instruction))
37
+ code.map((instruction) => evaluate.call(tree, instruction))
37
38
  );
38
39
  }
39
40
 
@@ -61,7 +62,7 @@ export default async function evaluate(code) {
61
62
  try {
62
63
  result =
63
64
  fn instanceof Function
64
- ? await fn.call(scope, ...args) // Invoke the function
65
+ ? await fn.call(tree, ...args) // Invoke the function
65
66
  : await Tree.traverseOrThrow(fn, ...args); // Traverse the tree.
66
67
  } catch (/** @type {any} */ error) {
67
68
  if (!error.location) {
@@ -71,6 +72,12 @@ export default async function evaluate(code) {
71
72
  throw error;
72
73
  }
73
74
 
75
+ // If the result is a tree, then the default parent of the tree is the current
76
+ // tree.
77
+ if (Tree.isAsyncTree(result) && !result.parent) {
78
+ result.parent = tree;
79
+ }
80
+
74
81
  // To aid debugging, add the code to the result.
75
82
  if (
76
83
  result &&
@@ -83,6 +90,14 @@ export default async function evaluate(code) {
83
90
  if (/** @type {any} */ (code).location) {
84
91
  result[sourceSymbol] = codeFragment(code);
85
92
  }
93
+ if (!result[scopeSymbol]) {
94
+ Object.defineProperty(result, scopeSymbol, {
95
+ get() {
96
+ return scope(result).trees;
97
+ },
98
+ enumerable: false,
99
+ });
100
+ }
86
101
  } catch (/** @type {any} */ error) {
87
102
  // Ignore errors.
88
103
  }
@@ -1,5 +1,4 @@
1
1
  import { map, Tree } from "@weborigami/async-tree";
2
- import Scope from "./Scope.js";
3
2
 
4
3
  /**
5
4
  * When using `get` to retrieve a value from a tree, if the value is a
@@ -12,9 +11,8 @@ export default function functionResultsMap(treelike) {
12
11
  value: async (sourceValue, sourceKey, tree) => {
13
12
  let resultValue;
14
13
  if (typeof sourceValue === "function") {
15
- const scope = Scope.getScope(tree);
16
- resultValue = await sourceValue.call(scope);
17
- if (Tree.isAsyncTree(resultValue)) {
14
+ resultValue = await sourceValue.call(tree);
15
+ if (Tree.isAsyncTree(resultValue) && !resultValue.parent) {
18
16
  resultValue.parent = tree;
19
17
  }
20
18
  } else {
@@ -1,4 +1,4 @@
1
- import { isUnpackable, symbols } from "@weborigami/async-tree";
1
+ import { isUnpackable, scope, symbols } from "@weborigami/async-tree";
2
2
  import extname from "./extname.js";
3
3
 
4
4
  /**
@@ -8,18 +8,18 @@ import extname from "./extname.js";
8
8
  *
9
9
  * @typedef {import("@weborigami/types").AsyncTree} AsyncTree
10
10
  *
11
- * @param {AsyncTree|null} scope
11
+ * @param {AsyncTree} parent
12
12
  * @param {any} key
13
13
  * @param {any} value
14
- * @param {AsyncTree|null} parent
15
14
  */
16
- export default async function handleExtension(scope, key, value, parent) {
15
+ export default async function handleExtension(parent, key, value) {
17
16
  const extension = extname(key);
18
17
  let result = value;
19
18
  if (extension) {
20
19
  const handlerName = `${extension.slice(1)}_handler`;
20
+ const parentScope = scope(parent);
21
21
  /** @type {import("../../index.ts").ExtensionHandler} */
22
- let extensionHandler = await scope?.get(handlerName);
22
+ let extensionHandler = await parentScope?.get(handlerName);
23
23
  if (isUnpackable(extensionHandler)) {
24
24
  // The extension handler itself needs to be unpacked. E.g., if it's a
25
25
  // buffer containing JavaScript file, we need to unpack it to get its
@@ -1,5 +1,4 @@
1
1
  import { isPlainObject, isUnpackable, merge } from "@weborigami/async-tree";
2
- import Scope from "./Scope.js";
3
2
 
4
3
  /**
5
4
  * Create a tree that's the result of merging the given trees.
@@ -38,21 +37,7 @@ export default async function mergeTrees(...trees) {
38
37
  return unpacked.flat();
39
38
  }
40
39
 
41
- // If a tree can take a scope, give it one that includes the other trees and
42
- // the current scope.
43
- const scopedTrees = unpacked.map((tree) => {
44
- const otherTrees = unpacked.filter((g) => g !== tree);
45
- const scope = new Scope(...otherTrees, this);
46
- // Each tree will be included first in its own scope.
47
- return Scope.treeWithScope(tree, scope);
48
- });
49
-
50
40
  // Merge the trees.
51
- const result = merge(...scopedTrees);
52
-
53
- // Give the overall mixed tree a scope that includes the component trees and
54
- // the current scope.
55
- /** @type {any} */ (result).scope = new Scope(result, this);
56
-
41
+ const result = merge(...unpacked);
57
42
  return result;
58
43
  }
@@ -8,11 +8,11 @@ import {
8
8
  SiteTree,
9
9
  Tree,
10
10
  isUnpackable,
11
+ scope as scopeFn,
11
12
  concat as treeConcat,
12
13
  } from "@weborigami/async-tree";
13
14
  import HandleExtensionsTransform from "./HandleExtensionsTransform.js";
14
15
  import OrigamiFiles from "./OrigamiFiles.js";
15
- import Scope from "./Scope.js";
16
16
  import handleExtension from "./handleExtension.js";
17
17
  import { OrigamiTree, evaluate, expressionFunction } from "./internal.js";
18
18
  import mergeTrees from "./mergeTrees.js";
@@ -72,12 +72,18 @@ function constructHref(protocol, host, ...keys) {
72
72
  * @param {...any} keys
73
73
  */
74
74
  export async function constructor(...keys) {
75
- const scope = this;
75
+ const tree = this;
76
+ const scope = scopeFn(tree);
76
77
  let constructor = await Tree.traverseOrThrow(scope, ...keys);
77
78
  if (isUnpackable(constructor)) {
78
79
  constructor = await constructor.unpack();
79
80
  }
80
- return (...args) => new constructor(...args);
81
+ // Origami may pass `undefined` as the first argument to the constructor. We
82
+ // don't pass that along, because constructors like `Date` don't like it.
83
+ return (...args) =>
84
+ args.length === 1 && args[0] === undefined
85
+ ? new constructor()
86
+ : new constructor(...args);
81
87
  }
82
88
  constructor.toString = () => "«ops.constructor»";
83
89
 
@@ -97,8 +103,8 @@ async function fetchResponse(href) {
97
103
  // Attach any loader defined for the file type.
98
104
  const url = new URL(href);
99
105
  const filename = url.pathname.split("/").pop();
100
- if (filename) {
101
- buffer = await handleExtension(this, filename, buffer, null);
106
+ if (this && filename) {
107
+ buffer = await handleExtension(this, filename, buffer);
102
108
  }
103
109
 
104
110
  return buffer;
@@ -110,13 +116,12 @@ async function fetchResponse(href) {
110
116
  * @this {AsyncTree|null}
111
117
  */
112
118
  export async function filesRoot() {
113
- /** @type {AsyncTree} */
114
119
  let root = new OrigamiFiles("/");
115
120
 
116
- // The root itself needs a scope so that expressions evaluated within it
121
+ // The root itself needs a parent so that expressions evaluated within it
117
122
  // (e.g., Origami expressions loaded from .ori files) will have access to
118
123
  // things like the built-in functions.
119
- root = Scope.treeWithScope(root, this);
124
+ root.parent = this;
120
125
 
121
126
  return root;
122
127
  }
@@ -148,17 +153,18 @@ export function https(host, ...keys) {
148
153
  https.toString = () => "«ops.https»";
149
154
 
150
155
  /**
151
- * Search the inherited scope -- i.e., exclude the current tree -- for the
152
- * given key.
156
+ * Search the parent's scope -- i.e., exclude the current tree -- for the given
157
+ * key.
153
158
  *
154
159
  * @this {AsyncTree|null}
155
160
  * @param {*} key
156
161
  */
157
162
  export async function inherited(key) {
158
- const scope = this;
159
- const scopeTrees = /** @type {any} */ (scope).trees ?? scope;
160
- const inheritedScope = new Scope(...scopeTrees.slice(1));
161
- return inheritedScope.get(key);
163
+ if (!this?.parent) {
164
+ return undefined;
165
+ }
166
+ const parentScope = scopeFn(this.parent);
167
+ return parentScope.get(key);
162
168
  }
163
169
  inherited.toString = () => "«ops.inherited»";
164
170
 
@@ -170,6 +176,7 @@ inherited.toString = () => "«ops.inherited»";
170
176
  * @param {string[]} parameters
171
177
  * @param {Code} code
172
178
  */
179
+
173
180
  export function lambda(parameters, code) {
174
181
  if (lambdaFnMap.has(code)) {
175
182
  return lambdaFnMap.get(code);
@@ -186,15 +193,16 @@ export function lambda(parameters, code) {
186
193
  ambients[parameter] = args.shift();
187
194
  }
188
195
  ambients["@recurse"] = invoke;
189
- const scope = new Scope(new ObjectTree(ambients), this);
196
+ const ambientTree = new ObjectTree(ambients);
197
+ ambientTree.parent = this;
190
198
 
191
- let result = await evaluate.call(scope, code);
199
+ let result = await evaluate.call(ambientTree, code);
192
200
 
193
- // Bind a function result to the scope so that it has access to the
201
+ // Bind a function result to the ambients so that it has access to the
194
202
  // parameter values -- i.e., like a closure.
195
203
  if (result instanceof Function) {
196
204
  const resultCode = result.code;
197
- result = result.bind(scope);
205
+ result = result.bind(ambientTree);
198
206
  if (code) {
199
207
  // Copy over Origami code
200
208
  result.code = resultCode;
@@ -216,7 +224,7 @@ export function lambda(parameters, code) {
216
224
  lambdaFnMap.set(code, invoke);
217
225
  return invoke;
218
226
  }
219
- lambda.toString = () => "«ops.lambda»";
227
+ lambda.toString = () => "«ops.lambda";
220
228
 
221
229
  /**
222
230
  * Merge the given trees. If they are all plain objects, return a plain object.
@@ -238,16 +246,30 @@ merge.toString = () => "«ops.merge»";
238
246
  * @param {any[]} entries
239
247
  */
240
248
  export async function object(...entries) {
241
- const scope = this;
249
+ const tree = this;
242
250
  const promises = entries.map(async ([key, value]) => [
243
251
  key,
244
- await evaluate.call(scope, value),
252
+ await evaluate.call(tree, value),
245
253
  ]);
246
254
  const evaluated = await Promise.all(promises);
247
255
  return Object.fromEntries(evaluated);
248
256
  }
249
257
  object.toString = () => "«ops.object»";
250
258
 
259
+ /**
260
+ * Look up the given key in the scope for the current tree.
261
+ *
262
+ * @this {AsyncTree|null}
263
+ */
264
+ export async function scope(key) {
265
+ if (!this) {
266
+ throw new Error("Tried to get the scope of a null or undefined tree.");
267
+ }
268
+ const scope = scopeFn(this);
269
+ return scope.get(key);
270
+ }
271
+ scope.toString = () => "«ops.scope»";
272
+
251
273
  /**
252
274
  * The spread operator is a placeholder during parsing. It should be replaced
253
275
  * with an object merge.
@@ -282,7 +304,7 @@ export async function tree(...entries) {
282
304
  });
283
305
  const object = Object.fromEntries(fns);
284
306
  const result = new OrigamiTree(object);
285
- result.scope = new Scope(result, this);
307
+ result.parent = this;
286
308
  return result;
287
309
  }
288
310
  tree.toString = () => "«ops.tree»";
@@ -296,9 +318,8 @@ tree.toString = () => "«ops.tree»";
296
318
  */
297
319
  export function treeHttp(host, ...keys) {
298
320
  const href = constructHref("http:", host, ...keys);
299
- /** @type {AsyncTree} */
300
321
  let result = new (HandleExtensionsTransform(SiteTree))(href);
301
- result = Scope.treeWithScope(result, this);
322
+ result.parent = this;
302
323
  return result;
303
324
  }
304
325
  treeHttp.toString = () => "«ops.treeHttp»";
@@ -312,12 +333,8 @@ treeHttp.toString = () => "«ops.treeHttp»";
312
333
  */
313
334
  export function treeHttps(host, ...keys) {
314
335
  const href = constructHref("https:", host, ...keys);
315
- /** @type {AsyncTree} */
316
336
  let result = new (HandleExtensionsTransform(SiteTree))(href);
317
- result = Scope.treeWithScope(result, this);
337
+ result.parent = this;
318
338
  return result;
319
339
  }
320
340
  treeHttps.toString = () => "«ops.treeHttps»";
321
-
322
- // The scope op is a placeholder for the tree's scope.
323
- export const scope = "«ops.scope»";
@@ -3,7 +3,7 @@ import assert from "node:assert";
3
3
  import { describe, test } from "node:test";
4
4
  import * as compile from "../../src/compiler/compile.js";
5
5
 
6
- const scope = new ObjectTree({
6
+ const shared = new ObjectTree({
7
7
  greet: (name) => `Hello, ${name}!`,
8
8
  name: "Alice",
9
9
  });
@@ -24,7 +24,7 @@ describe("compile", () => {
24
24
  test("tree", async () => {
25
25
  const fn = compile.expression("{ message = greet(name) }");
26
26
  const tree = await fn.call(null);
27
- tree.scope = scope;
27
+ tree.parent = shared;
28
28
  assert.deepEqual(await Tree.plain(tree), {
29
29
  message: "Hello, Alice!",
30
30
  });
@@ -43,7 +43,7 @@ describe("compile", () => {
43
43
 
44
44
  test("templateDocument", async () => {
45
45
  const fn = compile.templateDocument("Documents can contain ` backticks");
46
- const templateFn = await fn.call(scope);
46
+ const templateFn = await fn.call(shared);
47
47
  const value = await templateFn.call(null);
48
48
  assert.deepEqual(value, "Documents can contain ` backticks");
49
49
  });
@@ -59,6 +59,6 @@ describe("compile", () => {
59
59
 
60
60
  async function assertCompile(text, expected) {
61
61
  const fn = compile.expression(text);
62
- const result = await fn.call(scope);
62
+ const result = await fn.call(shared);
63
63
  assert.deepEqual(result, expected);
64
64
  }
@@ -438,6 +438,7 @@ describe("Origami parser", () => {
438
438
  assertParse("string", "'bar baz'", "bar baz");
439
439
  assertParse("string", `"foo\\"s bar"`, `foo"s bar`);
440
440
  assertParse("string", `'bar\\'s baz'`, `bar's baz`);
441
+ assertParse("string", `«string»`, "string");
441
442
  });
442
443
 
443
444
  test("templateDocument", () => {
@@ -2,7 +2,6 @@ import { ObjectTree, Tree } from "@weborigami/async-tree";
2
2
  import assert from "node:assert";
3
3
  import { describe, test } from "node:test";
4
4
  import HandleExtensionsTransform from "../../src/runtime/HandleExtensionsTransform.js";
5
- import Scope from "../../src/runtime/Scope.js";
6
5
 
7
6
  describe("HandleExtensionsTransform", () => {
8
7
  test("invokes an appropriate loader for a .json file extension", async () => {
@@ -25,17 +24,17 @@ describe("HandleExtensionsTransform", () => {
25
24
  });
26
25
 
27
26
  function createFixture() {
28
- /** @type {import("@weborigami/types").AsyncTree} */
29
- let tree = new (HandleExtensionsTransform(ObjectTree))({
30
- foo: 1, // No extension, should be left alone
31
- "bar.json": `{ "bar": 2 }`,
32
- });
33
27
  /** @type {any} */
34
- const scope = new ObjectTree({
28
+ const parent = new ObjectTree({
35
29
  json_handler: {
36
30
  unpack: (buffer) => JSON.parse(String(buffer)),
37
31
  },
38
32
  });
39
- tree = Scope.treeWithScope(tree, scope);
33
+ /** @type {import("@weborigami/types").AsyncTree} */
34
+ let tree = new (HandleExtensionsTransform(ObjectTree))({
35
+ foo: 1, // No extension, should be left alone
36
+ "bar.json": `{ "bar": 2 }`,
37
+ });
38
+ tree.parent = parent;
40
39
  return tree;
41
40
  }
@@ -8,10 +8,12 @@ import evaluate from "../../src/runtime/evaluate.js";
8
8
  describe("evaluate", () => {
9
9
  test("can retrieve values from scope", async () => {
10
10
  const code = [ops.scope, "message"];
11
- const scope = {
11
+ const parent = new ObjectTree({
12
12
  message: "Hello",
13
- };
14
- const result = await evaluate.call(scope, code);
13
+ });
14
+ const tree = new ObjectTree({});
15
+ tree.parent = parent;
16
+ const result = await evaluate.call(tree, code);
15
17
  assert.equal(result, "Hello");
16
18
  });
17
19
 
@@ -22,25 +24,25 @@ describe("evaluate", () => {
22
24
  [ops.scope, "name"],
23
25
  ];
24
26
 
25
- const scope = new ObjectTree({
27
+ const tree = new ObjectTree({
26
28
  async greet(name) {
27
29
  return `Hello ${name}`;
28
30
  },
29
31
  name: "world",
30
32
  });
31
33
 
32
- const result = await evaluate.call(scope, code);
34
+ const result = await evaluate.call(tree, code);
33
35
  assert.equal(result, "Hello world");
34
36
  });
35
37
 
36
38
  test("passes context to invoked functions", async () => {
37
39
  const code = [ops.scope, "fn"];
38
- const scope = {
40
+ const tree = new ObjectTree({
39
41
  async fn() {
40
- assert.equal(this, scope);
42
+ assert.equal(this, tree);
41
43
  },
42
- };
43
- await evaluate.call(scope, code);
44
+ });
45
+ await evaluate.call(tree, code);
44
46
  });
45
47
 
46
48
  test("evaluates a function with fixed number of arguments", async () => {
@@ -59,4 +61,12 @@ describe("evaluate", () => {
59
61
  const result = await evaluate.call(null, code);
60
62
  assert.equal(result, "a,b,c");
61
63
  });
64
+
65
+ test("by defalut sets the parent of a returned tree to the current tree", async () => {
66
+ const fn = () => new ObjectTree({});
67
+ const code = [fn];
68
+ const tree = new ObjectTree({});
69
+ const result = await evaluate.call(tree, code);
70
+ assert.equal(result.parent, tree);
71
+ });
62
72
  });
@@ -1,23 +1,20 @@
1
- import { Tree } from "@weborigami/async-tree";
1
+ import { ObjectTree, Tree, scope } from "@weborigami/async-tree";
2
2
  import assert from "node:assert";
3
3
  import { describe, test } from "node:test";
4
- import Scope from "../../src/runtime/Scope.js";
5
4
  import functionResultsMap from "../../src/runtime/functionResultsMap.js";
6
5
 
7
6
  describe("functionResultsMap", () => {
8
7
  test("get() invokes functions using scope, returns other values as is", async () => {
9
- const scope = {
8
+ const parent = new ObjectTree({
10
9
  message: "Hello",
11
- };
12
- const tree = Scope.treeWithScope(
13
- {
14
- fn: /** @this {import("@weborigami/types").AsyncTree} */ function () {
15
- return this.get("message");
16
- },
17
- string: "string",
10
+ });
11
+ const tree = new ObjectTree({
12
+ fn: /** @this {import("@weborigami/types").AsyncTree} */ function () {
13
+ return scope(this).get("message");
18
14
  },
19
- scope
20
- );
15
+ string: "string",
16
+ });
17
+ tree.parent = parent;
21
18
  const fixture = functionResultsMap(tree);
22
19
  assert.deepEqual(await Tree.plain(fixture), {
23
20
  fn: "Hello",
@@ -1,11 +1,6 @@
1
1
  import { Tree } from "@weborigami/async-tree";
2
2
  import assert from "node:assert";
3
3
  import { describe, test } from "node:test";
4
- import {
5
- ExpressionTree,
6
- expressionFunction,
7
- ops,
8
- } from "../../src/runtime/internal.js";
9
4
  import mergeTrees from "../../src/runtime/mergeTrees.js";
10
5
 
11
6
  describe("mergeTrees", () => {
@@ -29,27 +24,6 @@ describe("mergeTrees", () => {
29
24
  });
30
25
  });
31
26
 
32
- test("puts all trees in scope", async () => {
33
- const tree = await mergeTrees.call(
34
- null,
35
- new ExpressionTree({
36
- a: 1,
37
- b: expressionFunction.createExpressionFunction([ops.scope, "c"]),
38
- }),
39
- new ExpressionTree({
40
- c: 2,
41
- d: expressionFunction.createExpressionFunction([ops.scope, "a"]),
42
- })
43
- );
44
- // @ts-ignore
45
- assert.deepEqual(await Tree.plain(tree), {
46
- a: 1,
47
- b: 2,
48
- c: 2,
49
- d: 1,
50
- });
51
- });
52
-
53
27
  test("if all arguments are plain objects, result is a plain object", async () => {
54
28
  const result = await mergeTrees.call(
55
29
  null,