jevstrudel.git / packages / core / evaluate.mjs

evaluate.mjs - <short description TODO> Copyright (C) 2022 Strudel contributors - see https://codeberg.org/uzu/strudel/src/branch/main/packages/core/evaluate.mjs This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see https://www.gnu.org/licenses/.

7export const strudelScope = {};

Make strudelScope available globally so transpiled code can access it

9globalThis.strudelScope = strudelScope;

Track user-defined keys (from block-based eval) so we can clear them without removing strudel functions

12export const userDefinedKeys = new Set();
13globalThis.userDefinedKeys = userDefinedKeys;

Clears all user-defined variables and functions from the scope. This removes variables created during block-based evaluation. @name clearScope @example // After defining variables in blocks: // let myVar = 5 // function myFunc() { return 10; } clearScope() // removes myVar and myFunc from scope

25export const clearScope = () => {
26  for (const key of userDefinedKeys) {
27    delete strudelScope[key];
28    delete globalThis[key];
29  }
30  userDefinedKeys.clear();
31  // Return silence if available (for use in pattern expressions), otherwise undefined
32  return globalThis.silence;
33};

Make clearScope available globally

35globalThis.clearScope = clearScope;
37export const evalScope = async (...args) => {
38  const results = await Promise.allSettled(args);
39  const modules = results.filter((result) => result.status === 'fulfilled').map((r) => r.value);
40  results.forEach((result, i) => {
41    if (result.status === 'rejected') {
42      console.warn(`evalScope: module with index ${i} could not be loaded:`, result.reason);
43    }
44  });
45  // Object.assign(globalThis, ...modules);
46  // below is a fix for above commented out line
47  // same error as https://github.com/vitest-dev/vitest/issues/1807 when running this on astro server
48  modules.forEach((module) => {
49    Object.entries(module).forEach(([name, value]) => {
50      globalThis[name] = value;
51      strudelScope[name] = value;
52    });
53  });
54  return modules;
55};
56
57function safeEval(str, options = {}) {
58  const { wrapExpression = true, wrapAsync = true } = options;
59  if (wrapExpression) {
60    str = `{${str}}`;
61  }
62  if (wrapAsync) {
63    str = `(async ()=>${str})()`;
64  }
65  const body = `"use strict";return (${str})`;
66  return Function(body)();
67}
68
69export const evaluate = async (code, transpiler, transpilerOptions) => {
70  let meta = {};
71
72  if (transpiler) {
73    // transform syntactically correct js code to semantically usable code
74    const transpiled = transpiler(code, transpilerOptions);
75    code = transpiled.output;
76    meta = transpiled;
77  }
78  // if no transpiler is given, we expect a single instruction (!wrapExpression)
79  const options = { wrapExpression: !!transpiler };
80  let evaluated = await safeEval(code, options);
81  return { mode: 'javascript', pattern: evaluated, meta };
82};