fix(napi): module_exports binding (#2632)

This commit is contained in:
LongYinan 2025-05-13 19:54:25 +08:00 committed by GitHub
parent 8cd752db86
commit ce990542b9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 42 additions and 26 deletions

View File

@ -940,11 +940,14 @@ class Builder {
const workerPath = join(dir, 'wasi-worker.mjs')
const browserWorkerPath = join(dir, 'wasi-worker-browser.mjs')
const browserEntryPath = join(dir, 'browser.js')
const exportsCode = idents
.map(
(ident) => `module.exports.${ident} = __napiModule.exports.${ident}`,
)
.join('\n')
const exportsCode =
`module.exports = __napiModule.exports\n` +
idents
.map(
(ident) =>
`module.exports.${ident} = __napiModule.exports.${ident}`,
)
.join('\n')
await writeFileAsync(
bindingPath,
createWasiBinding(
@ -966,6 +969,7 @@ class Builder {
this.config.wasm?.browser?.fs,
this.config.wasm?.browser?.asyncInit,
) +
`export default __napiModule.exports\n` +
idents
.map(
(ident) =>

View File

@ -40,10 +40,10 @@ impl Symbol {
}
#[cfg(feature = "napi9")]
pub fn for_desc(desc: String) -> Self {
pub fn for_desc<S: AsRef<str>>(desc: S) -> Self {
Self {
desc: None,
for_desc: Some(desc.to_owned()),
for_desc: Some(desc.as_ref().to_owned()),
}
}
}

View File

@ -60,9 +60,6 @@ impl<K, V> Default for PersistedPerInstanceHashMap<K, V> {
type ModuleRegisterCallback =
RwLock<Vec<(Option<&'static str>, (&'static str, ExportRegisterCallback))>>;
#[cfg(not(feature = "noop"))]
type ModuleRegisterHookCallback = PersistedPerInstanceHashMap<ThreadId, ExportRegisterHookCallback>;
#[cfg(not(feature = "noop"))]
type ModuleClassProperty =
PersistedPerInstanceHashMap<TypeId, HashMap<Option<&'static str>, (&'static str, Vec<Property>)>>;
@ -77,7 +74,7 @@ type RegisteredClassesMap = PersistedPerInstanceHashMap<ThreadId, RegisteredClas
#[cfg(not(feature = "noop"))]
static MODULE_REGISTER_CALLBACK: LazyLock<ModuleRegisterCallback> = LazyLock::new(Default::default);
#[cfg(not(feature = "noop"))]
static MODULE_REGISTER_HOOK_CALLBACK: LazyLock<ModuleRegisterHookCallback> =
static MODULE_REGISTER_HOOK_CALLBACK: LazyLock<RwLock<Option<ExportRegisterHookCallback>>> =
LazyLock::new(Default::default);
#[cfg(not(feature = "noop"))]
static MODULE_CLASS_PROPERTIES: LazyLock<ModuleClassProperty> = LazyLock::new(Default::default);
@ -152,10 +149,10 @@ pub fn register_module_export(
#[cfg(not(feature = "noop"))]
#[doc(hidden)]
pub fn register_module_export_hook(cb: ExportRegisterHookCallback) {
let current_id = std::thread::current().id();
MODULE_REGISTER_HOOK_CALLBACK.borrow_mut(|inner| {
inner.insert(current_id, cb);
});
let mut inner = MODULE_REGISTER_HOOK_CALLBACK
.write()
.expect("Write MODULE_REGISTER_HOOK_CALLBACK failed");
*inner = Some(cb);
}
#[cfg(feature = "noop")]
@ -465,13 +462,14 @@ pub unsafe extern "C" fn napi_register_module_v1(
)
});
MODULE_REGISTER_HOOK_CALLBACK.borrow_mut(|inner| {
if let Some(cb) = inner.get(&current_thread_id) {
if let Err(e) = cb(env, exports) {
JsError::from(e).throw_into(env);
}
let module_register_hook_callback = MODULE_REGISTER_HOOK_CALLBACK
.read()
.expect("Read MODULE_REGISTER_HOOK_CALLBACK failed");
if let Some(cb) = module_register_hook_callback.as_ref() {
if let Err(e) = cb(env, exports) {
JsError::from(e).throw_into(env);
}
});
}
#[cfg(feature = "compat-mode")]
{

View File

@ -41,6 +41,8 @@ Generated by [AVA](https://avajs.dev).
type MaybePromise<T> = T | Promise<T>
export declare const NAPI_RS_SYMBOL: symbol␊
export declare class ExternalObject<T> {␊
readonly '': {␊
readonly '': unique symbol␊

View File

@ -233,6 +233,8 @@ import {
shutdownRuntime,
callAsyncWithUnknownReturnValue,
} from '../index.cjs'
// import other stuff in `#[napi(module_exports)]`
import nativeAddon from '../index.cjs'
import { test } from './test.framework.js'
@ -1634,3 +1636,7 @@ test('extends javascript error', (t) => {
t.true(typeof e.nativeStackTrace === 'string')
}
})
test('module exports', (t) => {
t.is(nativeAddon.NAPI_RS_SYMBOL, Symbol.for('NAPI_RS_SYMBOL'))
})

View File

@ -2,3 +2,5 @@
/* eslint-disable */
type MaybePromise<T> = T | Promise<T>
export declare const NAPI_RS_SYMBOL: symbol

View File

@ -60,6 +60,7 @@ const {
}
},
})
export default __napiModule.exports
export const Animal = __napiModule.exports.Animal
export const AnimalWithDefaultConstructor = __napiModule.exports.AnimalWithDefaultConstructor
export const AnotherClassForEither = __napiModule.exports.AnotherClassForEither

View File

@ -84,6 +84,7 @@ const { instance: __napiInstance, module: __wasiModule, napiModule: __napiModule
}
},
})
module.exports = __napiModule.exports
module.exports.Animal = __napiModule.exports.Animal
module.exports.AnimalWithDefaultConstructor = __napiModule.exports.AnimalWithDefaultConstructor
module.exports.AnotherClassForEither = __napiModule.exports.AnotherClassForEither

View File

@ -3,6 +3,8 @@
type MaybePromise<T> = T | Promise<T>
export declare const NAPI_RS_SYMBOL: symbol
export declare class ExternalObject<T> {
readonly '': {
readonly '': unique symbol

View File

@ -8,8 +8,8 @@
"scripts": {
"browser": "vite",
"build": "napi-raw build --platform --js index.cjs --dts index.d.cts",
"test": "cross-env TS_NODE_PROJECT=./tsconfig.json node --import @oxc-node/core/register ../../node_modules/ava/entrypoints/cli.mjs",
"test-js": "ava"
"test": "ava reset-cache && cross-env TS_NODE_PROJECT=./tsconfig.json node --import @oxc-node/core/register ../../node_modules/ava/entrypoints/cli.mjs",
"test-js": "ava reset-cache && ava"
},
"napi": {
"binaryName": "example",

View File

@ -8,7 +8,7 @@
#[cfg(not(target_family = "wasm"))]
use napi::bindgen_prelude::create_custom_tokio_runtime;
use napi::{
bindgen_prelude::{Env, Object, Result},
bindgen_prelude::{Object, Result, Symbol},
JsObjectValue,
};
@ -53,8 +53,8 @@ pub fn shutdown_runtime() {
}
#[napi(module_exports)]
pub fn exports(env: Env, mut export: Object) -> Result<()> {
let symbol = env.create_symbol(Some("NAPI_RS_SYMBOL"))?;
pub fn exports(mut export: Object) -> Result<()> {
let symbol = Symbol::for_desc("NAPI_RS_SYMBOL");
export.set_named_property("NAPI_RS_SYMBOL", symbol)?;
Ok(())
}