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 |
x1 x3 x1 x9 x13 x13 x9 x9 x65 x9 x9 x9 |
// Imports
import type { Nullable, State } from "../engine/mod.ts"
import { Renderer } from "../engine/mod.ts"
/** 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 }))
* ```
*
* > [!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" } = {}): Promise<T> {
if (context === "function") {
expression = `async () =>{${expression}}`
}
variables ??= {}
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
}
|