1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
x1 x1 x1 x1 x1 x1 x10 x4 x4 x10 x10 x2 x2 x2 x2 x2 x2 x8 x8 x10 x10 x10 |
I
|
// Imports
import type { Nullable, State } from "@mizu/internal/engine"
import { Renderer } from "@mizu/internal/engine"
/** Default renderer instance. */
// deno-lint-ignore no-explicit-any
const renderer = await new Renderer(null as any, { directives: [] }).ready
/**
* Evaluate an expression with given variables and arguments.
*
* This is a convenience function built upon Mizu {@linkcode Renderer.evaluate} and does not require a DOM or an already instantiated renderer.
*
* Both `variables` and `imports` are exposed through {@linkcode https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/with | with} statements,
* meaning that their properties can be accessed directly in the expression without prefixing them.
*
* It is possible to specify the `context` (either `"expression"` or `"function"`).
*
* If set to `"expression"` (default), the expression is evaluated as a regular JavaScript expression.
* ```ts
* console.assert(await evaluate("1 + 1") === 2)
* ```
*
* If set to `"function"`, the expression is automatically wrapped in an async function, effectively treating the expression as a function body.
* It also means that no implicit value is returned, requiring an explicit `return` statement to return a value.
*
* ```ts
* console.assert(await evaluate("return 1 + 1", null, { context: "function" }) === 2)
* ```
*
* > [!IMPORTANT]
* > Note that because evaluation is not performed within a ESM module, it is not possible to use {@linkcode https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/import | import} statement directly (and other ESM-only features).
* > Instead, use the `imports` option which will dynamically import the specified modules and expose them in the evaluation context.
* Both of these examples are equivalent:
* ```ts
* console.assert(await evaluate("foo.default", null, { imports: { foo: "data:application/javascript,export default true" }}) === true)
* ```
* ```ts
* const foo = await import("data:application/javascript,export default true")
* console.assert(await evaluate("foo.default", { foo }))
* ```
*
* The `permissions` option can be used to specify the permissions for the evaluation.
* If specified, the evaluation will be performed in a separate worker with the given permissions.
* This option is only supported in Deno, and requires the `--unstable-worker-options` flag to be enabled.
* Note that some results may not be transferable between workers, in which case an error will be thrown.
* See {@link https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm#supported_types | structured clone algorithm} for more details.
*
* > [!NOTE]
* > The root {@linkcode Renderer.internal} prefix is used internally to manage evaluation state, and thus cannot be used as a variable name.
*/
export async function evaluate<T = unknown>(
expression: string,
variables = null as Nullable<Record<PropertyKey, unknown>>,
{ imports = {} as Record<string, string>, context = "expression" as "expression" | "function", permissions = null as Nullable<Deno.PermissionOptions>, sandbox = null as Nullable<string> } = {},
): Promise<T> {
if (context === "function") {
expression = `async () =>{${expression}}`
}
variables ??= {}
if (permissions !== null) {
const { promise, resolve, reject } = Promise.withResolvers<T>()
const worker = new Worker(sandbox ?? import.meta.resolve("./sandbox.ts"), { name: "mizu-sandbox", type: "module", deno: { permissions } } as WorkerOptions)
worker.onmessage = ({ data: { error, result } }) => error ? reject(error) : resolve(result)
worker.postMessage({ expression, variables, imports, context })
return await promise
}
return await renderer.evaluate(null, expression, {
state: { ...Object.fromEntries(await Promise.all(Object.entries(imports).map(async ([name, value]) => [name, await import(value)]))), ...variables } as State,
args: context === "function" ? [] : undefined,
}) as T
}
|