neon

Attribute Macro export

#[export]
Expand description

Register an item to be exported by the Neon addon

§Exporting constants and statics

#[neon::export]
static GREETING: &str = "Hello, Neon!";

#[neon::export]
const ANSWER: u8 = 42;

§Renaming an export

By default, items will be exported with their Rust name. Exports may be renamed by providing the name attribute.

#[neon::export(name = "myGreeting")]
static GREETING: &str = "Hello, Neon!";

§JSON exports

Complex values may be exported by automatically serializing to JSON and parsing in JavaScript. Any type that implements serde::Serialize may be used.

#[neon::export(json)]
static MESSAGES: &[&str] = &["hello", "goodbye"];

§Exporting functions

Functions may take any type that implements TryFromJs as an argument and return any type that implements TryIntoJs.

#[neon::export]
fn add(a: f64, b: f64) -> f64 {
    a + b
}

§Naming exported functions

Conventionally, Rust uses snake_case for function identifiers and JavaScript uses camelCase. By default, Neon will attempt to convert function names to camel case. For example:

#[neon::export]
fn add_one(n: f64) -> f64 {
    n + 1.0
}

The add_one function will be exported as addOne in JavaScript.

import { addOne } from ".";

Similar to globals, exported functions can be overridden with the name attribute.

#[neon::export(name = "addOneSync")]
fn add_one(n: f64) -> f64 {
    n + 1.0
}

Neon uses the following rules when converting snake_case to camelCase:

  • All leading and trailing underscores (_) are preserved
  • Characters immediately following a non-leading underscore are converted to uppercase
  • If the identifier contains an unexpected character, no conversion is performed and the identifier is used unchanged. Unexpected characters include:
    • Uppercase characters
    • Duplicate interior (non-leading, non-trailing underscores)

§Exporting a function that uses JSON

The Json wrapper allows ergonomically handling complex types that implement serde::Deserialize and serde::Serialize.

#[neon::export]
fn sort(Json(mut items): Json<Vec<String>>) -> Json<Vec<String>> {
    items.sort();
    Json(items)
}

As a convenience, macro uses may add the json attribute to automatically wrap arguments and return values with Json.

#[neon::export(json)]
fn sort(mut items: Vec<String>) -> Vec<String> {
    items.sort();
    items
}

§Tasks

Neon provides an API for spawning tasks to execute asynchronously on Node’s worker pool. JavaScript may await a promise for completion of the task.

#[neon::export]
fn add<'cx>(cx: &mut FunctionContext<'cx>, a: f64, b: f64) -> JsResult<'cx, JsPromise> {
    let promise = cx
        .task(move || a + b)
        .promise(|mut cx, res| Ok(cx.number(res)));

    Ok(promise)
}

As a convenience, macro users may indicate that a function should be executed asynchronously on the worker pool by adding the task attribute.

#[neon::export(task)]
fn add(a: f64, b: f64) -> f64 {
    a + b
}

§Async Functions

The export macro can export async fn, converting to a JavaScript Promise, if a global future executor is registered. See neon::set_global_executor for more details.

#[neon::export]
async fn add(a: f64, b: f64) -> f64 {
    a + b
}

A fn that returns a Future can be annotated with #[neon::export(async)] if it needs to perform some setup on the JavaScript main thread before running asynchronously.

#[neon::export(async)]
fn add(a: f64, b: f64) -> impl Future<Output = f64> {
    println!("Hello from the JavaScript main thread!");

    async move {
        a + b
    }
}

If work needs to be performed on the JavaScript main thread after the asynchronous operation, the With extractor can be used to execute a closure before returning.

#[neon::export]
async fn add(a: f64, b: f64) -> impl for<'cx> TryIntoJs<'cx> {
    let sum = a + b;

    With(move |_cx| {
        println!("Hello from the JavaScript main thread!");

        sum
    })
}

§Error Handling

If an exported function returns a Result, a JavaScript exception will be thrown with the Err. Any error type that implements TryIntoJs may be used.

#[neon::export]
fn throw(msg: String) -> Result<(), String> {
    Err(msg)
}

The Error type is provided for ergonomic error conversions from most error types using the ? operator.

use neon::types::extract::Error;

#[neon::export]
fn read_file(path: String) -> Result<String, Error> {
    let contents = std::fs::read_to_string(path)?;
    Ok(contents)
}

§Interact with the JavaScript runtime

More complex functions may need to interact directly with the JavaScript runtime, for example with Context or handles to JavaScript values.

Functions may optionally include a Cx or FunctionContext argument. Note that unlike functions created with JsFunction::new, exported function receive a borrowed context and may require explicit lifetimes.

#[neon::export]
fn add<'cx>(
    cx: &mut Cx<'cx>,
    a: Handle<JsNumber>,
    b: Handle<JsNumber>,
) -> JsResult<'cx, JsNumber> {
    let a = a.value(cx);
    let b = b.value(cx);

    Ok(cx.number(a + b))
}

§Advanced

The following attributes are for advanced configuration and may not be necessary for most users.

§context

The #[neon::export] uses a heuristic to determine if the first argument to a function is a context argument.

  • In a function executed on the JavaScript main thread, it looks for &mut Cx or &mut FunctionContext to determine if the Context should be passed.
  • In a function executed on another thread, it looks for Channel.

If the type has been renamed when importing, the context attribute can be added to force it to be passed.

use neon::event::Channel as Ch;
use neon::context::FunctionContext as FnCtx;

#[neon::export(context)]
fn add(_cx: &mut FnCtx, a: f64, b: f64) -> f64 {
    a + b
}

#[neon::export(context)]
async fn div(_ch: Ch, a: f64, b: f64) -> f64 {
    a / b
}
§this

The #[neon::export] uses a heuristic to determine if an argument to this function is referring to this.

  1. If the first argument is a context, use the 0th argument, otherwise use the 1st.
  2. If the argument binding is named this
  3. Or if it is a tuple struct pattern with an element named this
use neon::types::extract::Boxed;

#[neon::export]
fn buffer_clone(this: Vec<u8>) -> Vec<u8> {
    this
}

#[neon::export]
fn box_to_string(Boxed(this): Boxed<String>) -> String {
    this
}

If the function uses a variable name other than this, the this attribute may be added.

#[neon::export(this)]
fn buffer_clone(me: Vec<u8>) -> Vec<u8> {
    me
}