Skip to main content

Module quickjs_backend

Module quickjs_backend 

Source
Expand description

QuickJS JavaScript runtime backend for TypeScript plugins

This module provides a JavaScript runtime using QuickJS for executing TypeScript plugins. TypeScript is transpiled to JavaScript using oxc.

§Adding New API Methods

When adding a new method to JsEditorApi, follow these steps for full type safety:

§1. Define Types in fresh-core/src/api.rs

If your method needs custom types (parameters or return values), define them with:

#[derive(Debug, Clone, Serialize, Deserialize, TS)]
#[serde(rename_all = "camelCase")]  // Match JS naming conventions
#[ts(export)]  // Generates TypeScript type definition
pub struct MyConfig {
    pub field: String,
}

§2. Add PluginCommand Variant

In fresh-core/src/api.rs, add the command variant using typed structs:

pub enum PluginCommand {
    MyCommand {
        language: String,
        config: MyConfig,  // Use typed struct, not JsonValue
    },
}

§3. Implement the API Method

In JsEditorApi, use typed parameters for automatic deserialization:

/// Description of what this method does
pub fn my_method(&self, language: String, config: MyConfig) -> bool {
    self.command_sender
        .send(PluginCommand::MyCommand { language, config })
        .is_ok()
}

For methods returning complex types, use #[plugin_api(ts_return = "Type")]:

#[plugin_api(ts_return = "MyResult | null")]
pub fn get_data<'js>(&self, ctx: rquickjs::Ctx<'js>) -> rquickjs::Result<Value<'js>> {
    // Serialize result to JS value
}

For async methods:

#[plugin_api(async_promise, js_name = "myAsyncMethod", ts_return = "MyResult")]
#[qjs(rename = "_myAsyncMethodStart")]
pub fn my_async_method_start(&self, param: String) -> u64 {
    // Return callback ID, actual result sent via PluginResponse
}

§4. Register Types for Export

In ts_export.rs, add your types to get_type_decl():

"MyConfig" => Some(MyConfig::decl()),

And import them at the top of the file.

§5. Handle the Command

In fresh-editor/src/app/plugin_commands.rs, add the handler:

pub(super) fn handle_my_command(&mut self, language: String, config: MyConfig) {
    // Process the command
}

And dispatch it in fresh-editor/src/app/mod.rs.

§6. Regenerate TypeScript Definitions

Run: cargo test -p fresh-plugin-runtime write_fresh_dts_file -- --ignored

This validates TypeScript syntax and writes plugins/lib/fresh.d.ts.

Structs§

JsEditorApi
JavaScript-exposed Editor API using rquickjs class system This allows proper lifetime handling for methods returning JS values
PluginHandler
PluginTrackedState
Handler information for events and actions Tracks state created by a plugin for cleanup on unload.
QuickJsBackend
QuickJS-based JavaScript runtime for plugins
TsPluginInfo
Information about a loaded plugin

Constants§

JSEDITORAPI_JS_METHODS
List of all JavaScript method names exposed in the API
JSEDITORAPI_REFERENCED_TYPES
List of TypeScript types referenced in method signatures
JSEDITORAPI_TS_EDITOR_API
TypeScript EditorAPI interface (methods only)
JSEDITORAPI_TS_PREAMBLE
TypeScript preamble (header, getEditor, ProcessHandle, BufferId, SplitId)

Functions§

has_fatal_js_error
Check if a fatal JS error has occurred
set_panic_on_js_errors
Enable panicking on JS errors (call this from test setup)
take_fatal_js_error
Get and clear the fatal JS error message (returns None if no error)

Type Aliases§

AsyncResourceOwners
Type alias for the shared async resource owner map. Maps request_id → plugin_name for pending async resource creations (virtual buffers, composite buffers, terminals). Shared between QuickJsBackend (plugin thread) and PluginThreadHandle (main thread).
EventHandlerRegistry
Plugin event handler registry shared between the plugin thread (which reads + mutates on on / off / emit / cleanup_plugin) and the editor thread (which only does cheap “does anyone listen?” lookups via has_subscribers to gate expensive per-render hook arg building).
PendingResponses
Pending response senders type alias