diff --git a/cli/src/build.ts b/cli/src/build.ts index ec7acc8e..0aea66f4 100644 --- a/cli/src/build.ts +++ b/cli/src/build.ts @@ -348,6 +348,7 @@ async function findUp(dir = process.cwd()): Promise { interface TypeDef { kind: 'fn' | 'struct' | 'impl' | 'enum' | 'interface' name: string + original_name?: string def: string js_mod?: string js_doc: string @@ -388,7 +389,10 @@ export class ExternalObject { const allDefs = lines.map((line) => JSON.parse(line) as TypeDef) function convertDefs(defs: TypeDef[], nested = false): string { - const classes = new Map() + const classes = new Map< + string, + { def: string; js_doc: string; original_name?: string } + >() const impls = new Map() let dts = '' const nest = nested ? 2 : 0 @@ -399,7 +403,11 @@ export class ExternalObject { if (!nested) { idents.push(def.name) } - classes.set(def.name, { def: def.def, js_doc: def.js_doc }) + classes.set(def.name, { + original_name: def.original_name, + def: def.def, + js_doc: def.js_doc, + }) break case 'impl': impls.set(def.name, `${def.js_doc}${def.def}`) @@ -429,9 +437,13 @@ export class ExternalObject { } }) - for (const [name, { js_doc, def }] of classes.entries()) { + for (const [name, { js_doc, def, original_name }] of classes.entries()) { const implDef = impls.get(name) + if (original_name && name !== original_name) { + dts += indentLines(`export type ${original_name} = ${name}\n`, nest) + } + dts += indentLines(`${js_doc}export class ${name} {`, nest) if (def) { diff --git a/crates/backend/src/codegen/fn.rs b/crates/backend/src/codegen/fn.rs index fed1373e..0c2300a0 100644 --- a/crates/backend/src/codegen/fn.rs +++ b/crates/backend/src/codegen/fn.rs @@ -47,10 +47,9 @@ impl TryToTokens for NapiFn { quote! { #native_call } } else if self.kind == FnKind::Constructor { quote! { - let call_from_factory = napi::bindgen_prelude::___CALL_FROM_FACTORY.load(std::sync::atomic::Ordering::Relaxed); // constructor function is called from class `factory` // so we should skip the original `constructor` logic - if call_from_factory { + if napi::bindgen_prelude::___CALL_FROM_FACTORY.load(std::sync::atomic::Ordering::Relaxed) { return std::ptr::null_mut(); } napi::bindgen_prelude::CallbackInfo::<#args_len>::new(env, cb, None).and_then(|mut cb| { diff --git a/crates/backend/src/codegen/struct.rs b/crates/backend/src/codegen/struct.rs index 539d46d5..06302a43 100644 --- a/crates/backend/src/codegen/struct.rs +++ b/crates/backend/src/codegen/struct.rs @@ -188,7 +188,6 @@ impl NapiStruct { "Failed to construct class `{}`", #js_name_str )?; - napi::bindgen_prelude::___CALL_FROM_FACTORY.store(false, std::sync::atomic::Ordering::Relaxed); napi::check_status!( napi::sys::napi_wrap( env, @@ -201,6 +200,7 @@ impl NapiStruct { "Failed to wrap native object of class `{}`", #js_name_str )?; + napi::bindgen_prelude::___CALL_FROM_FACTORY.store(false, std::sync::atomic::Ordering::Relaxed); Ok(result) } else { Err(napi::bindgen_prelude::Error::new( diff --git a/crates/backend/src/typegen.rs b/crates/backend/src/typegen.rs index 58f9caa5..b17907a6 100644 --- a/crates/backend/src/typegen.rs +++ b/crates/backend/src/typegen.rs @@ -12,6 +12,7 @@ use syn::Type; pub struct TypeDef { pub kind: String, pub name: String, + pub original_name: Option, pub def: String, pub js_mod: Option, pub js_doc: String, @@ -78,12 +79,18 @@ impl ToString for TypeDef { } else { "".to_owned() }; + let original_name = if let Some(original_name) = &self.original_name { + format!(", \"original_name\": \"{}\"", original_name) + } else { + "".to_owned() + }; format!( - r#"{{"kind": "{}", "name": "{}", "js_doc": "{}", "def": "{}"{}}}"#, + r#"{{"kind": "{}", "name": "{}", "js_doc": "{}", "def": "{}"{}{}}}"#, self.kind, self.name, escape_json(&self.js_doc), escape_json(&self.def), + original_name, js_mod, ) } diff --git a/crates/backend/src/typegen/const.rs b/crates/backend/src/typegen/const.rs index 1ae5b808..7def2c14 100644 --- a/crates/backend/src/typegen/const.rs +++ b/crates/backend/src/typegen/const.rs @@ -13,6 +13,7 @@ impl ToTypeDef for NapiConst { Some(TypeDef { kind: "const".to_owned(), name: self.js_name.to_owned(), + original_name: Some(self.name.to_string()), def: format!( "export const {}: {}", &self.js_name, diff --git a/crates/backend/src/typegen/enum.rs b/crates/backend/src/typegen/enum.rs index c22666f8..6f3c1efe 100644 --- a/crates/backend/src/typegen/enum.rs +++ b/crates/backend/src/typegen/enum.rs @@ -12,6 +12,7 @@ impl ToTypeDef for NapiEnum { Some(TypeDef { kind: "enum".to_owned(), name: self.js_name.to_owned(), + original_name: Some(self.name.to_string()), def: self.gen_ts_variants(), js_doc: js_doc_from_comments(&self.comments), js_mod: self.js_mod.to_owned(), diff --git a/crates/backend/src/typegen/fn.rs b/crates/backend/src/typegen/fn.rs index 45297793..65a7e2ff 100644 --- a/crates/backend/src/typegen/fn.rs +++ b/crates/backend/src/typegen/fn.rs @@ -29,6 +29,7 @@ impl ToTypeDef for NapiFn { Some(TypeDef { kind: "fn".to_owned(), name: self.js_name.clone(), + original_name: None, def, js_mod: self.js_mod.to_owned(), js_doc: js_doc_from_comments(&self.comments), diff --git a/crates/backend/src/typegen/struct.rs b/crates/backend/src/typegen/struct.rs index afbe5227..d1bee1ca 100644 --- a/crates/backend/src/typegen/struct.rs +++ b/crates/backend/src/typegen/struct.rs @@ -24,6 +24,7 @@ impl ToTypeDef for NapiStruct { "struct" }), name: self.js_name.to_owned(), + original_name: Some(self.name.to_string()), def: self.gen_ts_class(), js_mod: self.js_mod.to_owned(), js_doc: js_doc_from_comments(&self.comments), @@ -43,6 +44,7 @@ impl ToTypeDef for NapiImpl { Some(TypeDef { kind: "impl".to_owned(), name: self.js_name.to_owned(), + original_name: None, def: self .items .iter() diff --git a/examples/napi/__test__/typegen.spec.ts.md b/examples/napi/__test__/typegen.spec.ts.md index b8d3c8dd..23907a13 100644 --- a/examples/napi/__test__/typegen.spec.ts.md +++ b/examples/napi/__test__/typegen.spec.ts.md @@ -156,10 +156,12 @@ Generated by [AVA](https://avajs.dev). constructor(name: string)␊ getCount(): number␊ }␊ + export type Blake2bHasher = Blake2BHasher␊ /** Smoking test for type generation */␊ export class Blake2BHasher {␊ static withKey(key: Blake2bKey): Blake2BHasher␊ }␊ + export type Blake2bKey = Blake2BKey␊ export class Blake2BKey { }␊ export class Context {␊ maybeNeed?: boolean | undefined | null␊ @@ -179,6 +181,16 @@ Generated by [AVA](https://avajs.dev). getMaskColor(): string␊ getName(): string␊ }␊ + export type JsAssets = Assets␊ + export class Assets {␊ + constructor()␊ + get(id: number): JsAsset | undefined | null␊ + }␊ + export type JsAsset = Asset␊ + export class Asset {␊ + constructor()␊ + get filePath(): number␊ + }␊ export class ClassWithFactory {␊ name: string␊ static withName(name: string): ClassWithFactory␊ diff --git a/examples/napi/__test__/typegen.spec.ts.snap b/examples/napi/__test__/typegen.spec.ts.snap index ff404ed7..fbdb529d 100644 Binary files a/examples/napi/__test__/typegen.spec.ts.snap and b/examples/napi/__test__/typegen.spec.ts.snap differ diff --git a/examples/napi/__test__/values.spec.ts b/examples/napi/__test__/values.spec.ts index 99ce7fa2..c0a010fc 100644 --- a/examples/napi/__test__/values.spec.ts +++ b/examples/napi/__test__/values.spec.ts @@ -71,6 +71,7 @@ import { returnUndefined, Dog, Bird, + Assets, } from '../' test('export const', (t) => { @@ -130,6 +131,8 @@ test('class', (t) => { t.deepEqual(dog.returnOtherClass(), new Dog('Doge')) t.deepEqual(dog.returnOtherClassWithCustomConstructor(), new Bird('parrot')) t.is(dog.returnOtherClassWithCustomConstructor().getCount(), 1234) + const assets = new Assets() + t.is(assets.get(1)?.filePath, 1) }) test('class factory', (t) => { diff --git a/examples/napi/index.d.ts b/examples/napi/index.d.ts index a64af8ad..3453c31c 100644 --- a/examples/napi/index.d.ts +++ b/examples/napi/index.d.ts @@ -146,10 +146,12 @@ export class Bird { constructor(name: string) getCount(): number } +export type Blake2bHasher = Blake2BHasher /** Smoking test for type generation */ export class Blake2BHasher { static withKey(key: Blake2bKey): Blake2BHasher } +export type Blake2bKey = Blake2BKey export class Blake2BKey { } export class Context { maybeNeed?: boolean | undefined | null @@ -169,6 +171,16 @@ export class NinjaTurtle { getMaskColor(): string getName(): string } +export type JsAssets = Assets +export class Assets { + constructor() + get(id: number): JsAsset | undefined | null +} +export type JsAsset = Asset +export class Asset { + constructor() + get filePath(): number +} export class ClassWithFactory { name: string static withName(name: string): ClassWithFactory diff --git a/examples/napi/src/class.rs b/examples/napi/src/class.rs index f1d5e720..554d417d 100644 --- a/examples/napi/src/class.rs +++ b/examples/napi/src/class.rs @@ -202,3 +202,35 @@ impl NinjaTurtle { self.name.as_str() } } + +#[napi(js_name = "Assets")] +pub struct JsAssets {} + +#[napi] +impl JsAssets { + #[napi(constructor)] + pub fn new() -> Self { + JsAssets {} + } + + #[napi] + pub fn get(&mut self, _id: u32) -> Option { + Some(JsAsset {}) + } +} + +#[napi(js_name = "Asset")] +pub struct JsAsset {} + +#[napi] +impl JsAsset { + #[napi(constructor)] + pub fn new() -> Self { + Self {} + } + + #[napi(getter)] + pub fn get_file_path(&self) -> u32 { + return 1; + } +} diff --git a/tsconfig.json b/tsconfig.json index 16988746..5de32776 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -27,5 +27,5 @@ "lib": ["dom", "DOM.Iterable", "ES2019", "ES2020", "esnext"] }, "include": ["."], - "exclude": ["node_modules", "bench", "cli/scripts", "scripts"] + "exclude": ["node_modules", "bench", "cli/scripts", "scripts", "examples"] }