From e2e3ef95f845052c28070b1891f82c9e058ea71b Mon Sep 17 00:00:00 2001 From: LongYinan Date: Fri, 26 Nov 2021 17:26:14 +0800 Subject: [PATCH] feat(napi-derive): support renmae function args and return type --- crates/backend/src/ast.rs | 2 ++ crates/backend/src/typegen.rs | 1 + crates/backend/src/typegen/fn.rs | 11 ++++++-- crates/macro/src/parser/attrs.rs | 3 ++- crates/macro/src/parser/mod.rs | 28 ++++++++++++++++++-- examples/napi/__test__/typegen.spec.ts.md | 1 + examples/napi/__test__/typegen.spec.ts.snap | Bin 1374 -> 1392 bytes examples/napi/__test__/values.spec.ts | 5 ++++ examples/napi/index.d.ts | 1 + examples/napi/src/fn_ts_override.rs | 6 +++++ examples/napi/src/lib.rs | 1 + 11 files changed, 54 insertions(+), 5 deletions(-) create mode 100644 examples/napi/src/fn_ts_override.rs diff --git a/crates/backend/src/ast.rs b/crates/backend/src/ast.rs index 7d5c1e0c..cbacc189 100644 --- a/crates/backend/src/ast.rs +++ b/crates/backend/src/ast.rs @@ -16,6 +16,8 @@ pub struct NapiFn { pub parent: Option, pub strict: bool, pub js_mod: Option, + pub ts_args_type: Option, + pub ts_return_type: Option, } #[derive(Debug, Clone)] diff --git a/crates/backend/src/typegen.rs b/crates/backend/src/typegen.rs index 1d7ca257..60ef40fc 100644 --- a/crates/backend/src/typegen.rs +++ b/crates/backend/src/typegen.rs @@ -67,6 +67,7 @@ static KNOWN_TYPES: Lazy> = Lazy::new(|| { ("char", "string"), ("JsObject", "object"), ("Object", "object"), + ("Array", "unknown[]"), ("Value", "any"), ("Map", "Record"), ("HashMap", "Record<{}, {}>"), diff --git a/crates/backend/src/typegen/fn.rs b/crates/backend/src/typegen/fn.rs index 6f2ed754..24e0cc82 100644 --- a/crates/backend/src/typegen/fn.rs +++ b/crates/backend/src/typegen/fn.rs @@ -11,8 +11,15 @@ impl ToTypeDef for NapiFn { r#"{prefix} {name}({args}){ret}"#, prefix = self.gen_ts_func_prefix(), name = &self.js_name, - args = self.gen_ts_func_args(), - ret = self.gen_ts_func_ret(), + args = self + .ts_args_type + .clone() + .unwrap_or_else(|| self.gen_ts_func_args()), + ret = self + .ts_return_type + .clone() + .map(|t| format!(": {}", t)) + .unwrap_or_else(|| self.gen_ts_func_ret()), ); TypeDef { diff --git a/crates/macro/src/parser/attrs.rs b/crates/macro/src/parser/attrs.rs index a53e8272..a04da0ef 100644 --- a/crates/macro/src/parser/attrs.rs +++ b/crates/macro/src/parser/attrs.rs @@ -53,12 +53,13 @@ macro_rules! attrgen { (strict, Strict(Span)), (object, Object(Span)), (namespace, Namespace(Span, String, Span)), + (ts_args_type, TsArgsType(Span, String, Span)), + (ts_return_type, TsReturnType(Span, String, Span)), // impl later // (inspectable, Inspectable(Span)), // (typescript_custom_section, TypescriptCustomSection(Span)), // (skip_typescript, SkipTypescript(Span)), - // (typescript_type, TypeScriptType(Span, String, Span)), // (getter_with_clone, GetterWithClone(Span)), // For testing purposes only. diff --git a/crates/macro/src/parser/mod.rs b/crates/macro/src/parser/mod.rs index 9d6a2b65..fa0b109b 100644 --- a/crates/macro/src/parser/mod.rs +++ b/crates/macro/src/parser/mod.rs @@ -575,6 +575,8 @@ fn napi_fn_from_decl( attrs, strict: opts.strict().is_some(), js_mod: opts.namespace().map(|(m, _)| m.to_owned()), + ts_args_type: opts.ts_args_type().map(|(m, _)| m.to_owned()), + ts_return_type: opts.ts_return_type().map(|(m, _)| m.to_owned()), } }) } @@ -603,6 +605,12 @@ impl ParseNapi for syn::ItemFn { } impl ParseNapi for syn::ItemStruct { fn parse_napi(&mut self, tokens: &mut TokenStream, opts: BindgenAttrs) -> BindgenResult { + if opts.ts_args_type().is_some() || opts.ts_return_type().is_some() { + bail_span!( + self, + "#[napi] can't be applied to a struct with #[napi(ts_args_type)] or #[napi(ts_return_type)]" + ); + } let napi = self.convert_to_ast(opts); self.to_tokens(tokens); @@ -611,6 +619,12 @@ impl ParseNapi for syn::ItemStruct { } impl ParseNapi for syn::ItemImpl { fn parse_napi(&mut self, tokens: &mut TokenStream, opts: BindgenAttrs) -> BindgenResult { + if opts.ts_args_type().is_some() || opts.ts_return_type().is_some() { + bail_span!( + self, + "#[napi] can't be applied to impl with #[napi(ts_args_type)] or #[napi(ts_return_type)]" + ); + } // #[napi] macro will be remove from impl items after converted to ast let napi = self.convert_to_ast(opts); self.to_tokens(tokens); @@ -618,18 +632,28 @@ impl ParseNapi for syn::ItemImpl { napi } } - impl ParseNapi for syn::ItemEnum { fn parse_napi(&mut self, tokens: &mut TokenStream, opts: BindgenAttrs) -> BindgenResult { + if opts.ts_args_type().is_some() || opts.ts_return_type().is_some() { + bail_span!( + self, + "#[napi] can't be applied to a enum with #[napi(ts_args_type)] or #[napi(ts_return_type)]" + ); + } let napi = self.convert_to_ast(opts); self.to_tokens(tokens); napi } } - impl ParseNapi for syn::ItemConst { fn parse_napi(&mut self, tokens: &mut TokenStream, opts: BindgenAttrs) -> BindgenResult { + if opts.ts_args_type().is_some() || opts.ts_return_type().is_some() { + bail_span!( + self, + "#[napi] can't be applied to a const with #[napi(ts_args_type)] or #[napi(ts_return_type)]" + ); + } let napi = self.convert_to_ast(opts); self.to_tokens(tokens); napi diff --git a/examples/napi/__test__/typegen.spec.ts.md b/examples/napi/__test__/typegen.spec.ts.md index 31303f56..d2d2f385 100644 --- a/examples/napi/__test__/typegen.spec.ts.md +++ b/examples/napi/__test__/typegen.spec.ts.md @@ -42,6 +42,7 @@ Generated by [AVA](https://avajs.dev). export function createExternalString(content: string): ExternalObject␊ export function getExternal(external: ExternalObject): number␊ export function mutateExternal(external: ExternalObject, newVal: number): void␊ + export function tsRename(a: { foo: number }): string[]␊ export function xxh64Alias(input: Buffer): BigInt␊ export function mapOption(val?: number | undefined | null): number | undefined | null␊ export function add(a: number, b: number): number␊ diff --git a/examples/napi/__test__/typegen.spec.ts.snap b/examples/napi/__test__/typegen.spec.ts.snap index 1357fa794d6020d01417bb220adc8bb6bb958bd9..ac8bd40a11c40784bb04006022b1b690081e754b 100644 GIT binary patch literal 1392 zcmV-$1&{hcRzVC`9;p0l>QROp3uY6kh{F95v&z=&H5##F1kV)!CK%PEZU4`3$h!qJUDkW*$ zDu_9aPW=xMs2A-Wa<@uI3^Wux8j&YYJhQN{lyLS#0z{7bzKG2B<&ih}yz)pJm&U8( zsU^XsA_t9T{o=U&e*g5m?Gc{zeTXxTPQnAl1SefkZ$%tRr|OY<9MjPYsba>vJ4KwW zq;D~%8uN@Y=^IAemkYerQa|)*n=FF59Pz*zP}RfZ8Bb8=EEatx;l*Ck=@eO{TIeK+ z6l)KKL*s5{#*!`b)@NPJS3L|J>XALxJ><$IenwU^A}1H1#|qL-=V5Gz+gtZ=Vtn_9 zp%c(3@@a7G5q*&LX%xuI9daXBhzj(-(g5ZaG~!sq-TOAKu-HG~xE&7BtTlN>P~k~?xBy5uEUcga3gNYq?%kObFAZIJOC z|9&Es(6^|ezwXE>hm;@dTeBn4i$Sk#x}+(R7^y9noU>abwq4R--JZ7a+$Ak$WWPZX z;9AK#DL5mVX^X?n4F|k=FCk`{l4c#dR0f|cO~BrLn2^Gec^a}ePa zaAje{g0_25Z0Zk8h%#5o&)!dzeXwPUT*6`ao!*}wzA#=&z5!1AprPoFbcDzR$as1q zF0XQ8x3|6Rt$M_$%!)Jx%5tpf)4}OLQ_c;IcD-3G=YZgzy*v!3Aesso5M71<9p(#8 zgMc}FIdjCM!nhv6NIJs*;1OYhytHwp1%#5iPE(npvUn8?aV9%cf47&jz?G)u#%iSD&lxQAebbqq^){(J3ivnnExAdhL%)aMj zr&lANc|-}yxO5~!WH1epOB6Ohu+d|J;YAt(st|M>X>pgEL*@(Bsck=;{g^7oYY*X< zpw7*}cgS@yr^8=11eTX}TK=h|j{0PVTx1(!n-htu`?$J~af?M1VOuV_&&#GNt?ePI zTq0T5AgAFzHy;nsuZJAn0%<5|2bz;*d-hpdTf-z{Sg}MeugpM$R4MZ2bhT&gE%5&b zeNC!pbRt6d7N7ZKh0qAYMeDzkPH1Wl1|kE8JR6E>Kyh2?X!8_)C}sunaCKbPFx+&A8aq8*RZko zEtNQgne#0;2_xqkM&5+c$M32)FIA!vIa7TL+qz=0p_{!v9r@4#2zNbe%qsv&JG={F zJix>|A^uo5d}2de0K9gg6`MtG^zU0#)1+GYs0i;ZW362Ly_||PRYjbt$K=cby(tH} z-{63;X$;T3FYCvLuUaRK*6eXPWw1w+&G*||WphcPKyBmsQvNhJ?=rtkRphfkNHW04 yLr$H!WO_ejU6A(sO~0c{La>rB=00000000B6 zSIur4HxQN^6a{=rPrVhqWfg23OLpCK;ntFB+8 zuh2e3Z$*LLd+sar*mH+mtwd>eEhDH8@{s)Ihcm;OSyHJ~Rw@sE{^GAc|M}s!U%vYK z-@pI({nwR>{(JJUQh8AM%+M>JRzCma%ZE=N6Oa+(>Pg5X^&=pUpRTUL^-#o$1QC^z zG_MuJoJObqdkEBaXNTOZ5)uOq1&_w$(Id}nENms5eMo@F@xT|6Iev5GO_pyx(!s6U z)!V5i!KESx%~s>$xbtrR^t|H{o(z15GekGxfntJ_9;jC$4y9A|NF$Evcw4HN@!n1m zvYiYpU@9>Wl*zyV@mwxotEG16+qPH)4LRn4Go-4I*E5zN&si)6Ov3hF((M*$q?+j@ zi4^OMghS(ArpDqev({%lj8`KJ9qN%i);r|NC4NR$Ga|!Fbkm%q2 zQRoCTihLSedPHAjed-1Be1}{K7NP+CuhfBg0nIoTG5HB|D1qb+xFko6hvbGFh#q-P)?Kns6%sX<93;UdQgw1W zhu=@668aT2_3DP4a!B!*-x`iYKL)+G;gXg}Vx%@*a?Y-i*m6me_4-=FGncfP;r#|# zfNLS^q~MOIrXvnF>JE7G%FCmt`mq?9+;YmYBu)OT;yIH22v&p_Dq-0Jofnd(oP!lk z0asRySdex{6`R@v6QazN^1BZbWnXNWAeV3$eXq}_moN0!^>u%1vk@^WvusN%UU|_N z(BbJ&Q_dBQcD-3r=3ew2XZd=XR>PFRRKsOYxyyXPX%H}nF9%0VDs<2hjHM&|_Z|_( zTT44onhD6M6E5W`itmd^_!tz#sPPa)m-Ng=5%ePSL}?0}t``|;FJ-&XS-Sb0MTxAf zuRFtB!zpM%vqE%+b(;3zr9?TpAaOc?ET?sSf>@#oYbS&upv^pBATiwEL4eJB;xH`h ze7sW>U(I)Q@KLP!TA>?_Ww-W>wJdU=b=<}aVt1de=Xj@8KOcE~?v!!qNUU|i)I}~) z*vY^y1CwH0Bq5**tA`^kbh$bNU#M;CX#su-``2QNXLo5}5v>ts%+xNMp$Pwh1Q zQ%>#m$uPJG8)3f^iE8+`yN`a0MHFFwEqTw&#v?85KB8PASJLi8dCn6115mNP;oEe}GfJ_>{+L-ai4#F { @@ -194,6 +195,10 @@ test('Result', (t) => { t.throws(() => throwError(), null, 'Manual Error') }) +test('function ts type override', (t) => { + t.deepEqual(tsRename({ foo: 1, bar: 2, baz: 2 }), ['foo', 'bar', 'baz']) +}) + test('serde-json', (t) => { const packageJson = readPackageJson() t.is(packageJson.name, 'napi-rs') diff --git a/examples/napi/index.d.ts b/examples/napi/index.d.ts index d9afc5fd..40eadb14 100644 --- a/examples/napi/index.d.ts +++ b/examples/napi/index.d.ts @@ -32,6 +32,7 @@ export function createExternal(size: number): ExternalObject export function createExternalString(content: string): ExternalObject export function getExternal(external: ExternalObject): number export function mutateExternal(external: ExternalObject, newVal: number): void +export function tsRename(a: { foo: number }): string[] export function xxh64Alias(input: Buffer): BigInt export function mapOption(val?: number | undefined | null): number | undefined | null export function add(a: number, b: number): number diff --git a/examples/napi/src/fn_ts_override.rs b/examples/napi/src/fn_ts_override.rs new file mode 100644 index 00000000..7793325f --- /dev/null +++ b/examples/napi/src/fn_ts_override.rs @@ -0,0 +1,6 @@ +use napi::bindgen_prelude::{Object, Result}; + +#[napi(ts_args_type = "a: { foo: number }", ts_return_type = "string[]")] +fn ts_rename(a: Object) -> Result { + a.get_property_names() +} diff --git a/examples/napi/src/lib.rs b/examples/napi/src/lib.rs index 751d16c9..9cf239a1 100644 --- a/examples/napi/src/lib.rs +++ b/examples/napi/src/lib.rs @@ -16,6 +16,7 @@ mod either; mod r#enum; mod error; mod external; +mod fn_ts_override; mod js_mod; mod nullable; mod number;