From 5be415d3d90cf1a5ce440d3c6ac639ab0ea3e93f Mon Sep 17 00:00:00 2001 From: Jose L Date: Sun, 22 May 2022 01:43:11 -0400 Subject: [PATCH] feat(napi-derive): add ts_arg_type attribute to override individual args on functions (#1192) --- crates/backend/src/ast.rs | 19 +++- crates/backend/src/codegen/fn.rs | 2 +- crates/backend/src/typegen/fn.rs | 8 +- crates/macro/src/parser/mod.rs | 108 ++++++++++++++++---- examples/napi/__test__/typegen.spec.ts.md | 3 + examples/napi/__test__/typegen.spec.ts.snap | Bin 3151 -> 3242 bytes examples/napi/__test__/values.spec.ts | 20 ++++ examples/napi/index.d.ts | 3 + examples/napi/src/class.rs | 12 +++ examples/napi/src/fn_ts_override.rs | 30 ++++++ 10 files changed, 180 insertions(+), 25 deletions(-) diff --git a/crates/backend/src/ast.rs b/crates/backend/src/ast.rs index 18d17f1d..33641687 100644 --- a/crates/backend/src/ast.rs +++ b/crates/backend/src/ast.rs @@ -6,7 +6,7 @@ pub struct NapiFn { pub name: Ident, pub js_name: String, pub attrs: Vec, - pub args: Vec, + pub args: Vec, pub ret: Option, pub is_ret_result: bool, pub is_async: bool, @@ -31,6 +31,23 @@ pub struct CallbackArg { pub ret: Option, } +#[derive(Debug, Clone)] +pub struct NapiFnArg { + pub kind: NapiFnArgKind, + pub ts_arg_type: Option, +} + +impl NapiFnArg { + /// if type was overridden with `#[napi(ts_arg_type = "...")]` use that instead + pub fn use_overridden_type_or(&self, default: impl FnOnce() -> String) -> String { + self + .ts_arg_type + .as_ref() + .map(|ts| ts.clone()) + .unwrap_or_else(default) + } +} + #[derive(Debug, Clone)] pub enum NapiFnArgKind { PatType(Box), diff --git a/crates/backend/src/codegen/fn.rs b/crates/backend/src/codegen/fn.rs index 31d0e1c1..d4d6a5db 100644 --- a/crates/backend/src/codegen/fn.rs +++ b/crates/backend/src/codegen/fn.rs @@ -121,7 +121,7 @@ impl NapiFn { let i = i - skipped_arg_count; let ident = Ident::new(&format!("arg{}", i), Span::call_site()); - match arg { + match &arg.kind { NapiFnArgKind::PatType(path) => { if &path.ty.to_token_stream().to_string() == "Env" { args.push(quote! { napi::bindgen_prelude::Env::from(env) }); diff --git a/crates/backend/src/typegen/fn.rs b/crates/backend/src/typegen/fn.rs index eee33a5b..a5541c14 100644 --- a/crates/backend/src/typegen/fn.rs +++ b/crates/backend/src/typegen/fn.rs @@ -119,7 +119,7 @@ impl NapiFn { self .args .iter() - .filter_map(|arg| match arg { + .filter_map(|arg| match &arg.kind { crate::NapiFnArgKind::PatType(path) => { let ty_string = path.ty.to_token_stream().to_string(); if ty_string == "Env" { @@ -142,8 +142,10 @@ impl NapiFn { if let Pat::Ident(i) = path.pat.as_mut() { i.mutability = None; } - let arg = path.pat.to_token_stream().to_string().to_case(Case::Camel); + let (ts_type, is_optional) = ty_to_ts_type(&path.ty, false, false); + let ts_type = arg.use_overridden_type_or(|| ts_type); + let arg = path.pat.to_token_stream().to_string().to_case(Case::Camel); Some(FnArg { arg, @@ -152,8 +154,8 @@ impl NapiFn { }) } crate::NapiFnArgKind::Callback(cb) => { + let ts_type = arg.use_overridden_type_or(|| gen_callback_type(cb)); let arg = cb.pat.to_token_stream().to_string().to_case(Case::Camel); - let ts_type = gen_callback_type(cb); Some(FnArg { arg, diff --git a/crates/macro/src/parser/mod.rs b/crates/macro/src/parser/mod.rs index aae59dc9..34fa64fb 100644 --- a/crates/macro/src/parser/mod.rs +++ b/crates/macro/src/parser/mod.rs @@ -10,14 +10,14 @@ use attrs::{BindgenAttr, BindgenAttrs}; use convert_case::{Case, Casing}; use napi_derive_backend::{ BindgenResult, CallbackArg, Diagnostic, FnKind, FnSelf, Napi, NapiConst, NapiEnum, - NapiEnumVariant, NapiFn, NapiFnArgKind, NapiImpl, NapiItem, NapiStruct, NapiStructField, - NapiStructKind, + NapiEnumVariant, NapiFn, NapiFnArg, NapiFnArgKind, NapiImpl, NapiItem, NapiStruct, + NapiStructField, NapiStructKind, }; -use proc_macro2::{Ident, TokenStream, TokenTree}; +use proc_macro2::{Ident, Span, TokenStream, TokenTree}; use quote::ToTokens; use syn::ext::IdentExt; use syn::parse::{Parse, ParseStream, Result as SynResult}; -use syn::{Attribute, PathSegment, Signature, Type, Visibility}; +use syn::{Attribute, Meta, NestedMeta, PatType, PathSegment, Signature, Type, Visibility}; use crate::parser::attrs::{check_recorded_struct_for_impl, record_struct}; @@ -155,6 +155,60 @@ pub trait ParseNapi { fn parse_napi(&mut self, tokens: &mut TokenStream, opts: BindgenAttrs) -> BindgenResult; } +/// This function does a few things: +/// - parses the tokens for the given argument `p` to find the `#[napi(ts_arg_type = "MyType")]` +/// attribute and return the manually overridden type. +/// - If both the `ts_args_type` override and the `ts_arg_type` override are present, bail +/// since it should only allow one at a time. +/// - Bails if it finds the `#[napi...]` attribute but it has the wrong data. +/// - Removes the attribute from the output token stream so this +/// `pub fn add(u: u32, #[napi(ts_arg_type = "MyType")] f: String)` +/// turns into +/// `pub fn add(u: u32, f: String)` +/// otherwise it won't compile +fn find_ts_arg_type_and_remove_attribute( + p: &mut PatType, + ts_args_type: Option<&(&str, Span)>, +) -> BindgenResult> { + for (idx, attr) in p.attrs.iter().enumerate() { + if let Ok(Meta::List(meta_list)) = attr.parse_meta() { + if meta_list.path.get_ident() != Some(&format_ident!("napi")) { + // If this attribute is not for `napi` ignore it. + continue; + } + + if let Some((ts_args_type, _)) = ts_args_type { + bail_span!( + meta_list, + "Found a 'ts_args_type'=\"{}\" override. Cannot use 'ts_arg_type' at the same time since they are mutually exclusive.", + ts_args_type + ); + } + + let nested = meta_list.nested.first(); + + let nm = if let Some(NestedMeta::Meta(Meta::NameValue(nm))) = nested { + nm + } else { + bail_span!(meta_list.nested, "Expected Name Value"); + }; + + if Some(&format_ident!("ts_arg_type")) != nm.path.get_ident() { + bail_span!(nm.path, "Did not find 'ts_arg_type'"); + } + + if let syn::Lit::Str(lit) = &nm.lit { + p.attrs.remove(idx); + return Ok(Some(lit.value())); + } else { + bail_span!(nm.lit, "Expected a string literal"); + } + } + } + + Ok(None) +} + fn get_ty(mut ty: &syn::Type) -> &syn::Type { while let syn::Type::Group(g) = ty { ty = &g.elem; @@ -462,7 +516,7 @@ fn extract_fn_closure_generics( } fn napi_fn_from_decl( - sig: Signature, + sig: &mut Signature, opts: &BindgenAttrs, attrs: Vec, vis: Visibility, @@ -473,36 +527,48 @@ fn napi_fn_from_decl( let syn::Signature { ident, asyncness, - inputs, output, generics, .. - } = sig; + } = sig.clone(); let mut fn_self = None; let callback_traits = extract_fn_closure_generics(&generics)?; - let args = inputs - .into_iter() + let args = sig + .inputs + .iter_mut() .filter_map(|arg| match arg { - syn::FnArg::Typed(mut p) => { + syn::FnArg::Typed(ref mut p) => { + let ts_arg_type = find_ts_arg_type_and_remove_attribute(p, opts.ts_args_type().as_ref()) + .unwrap_or_else(|e| { + errors.push(e); + None + }); + let ty_str = p.ty.to_token_stream().to_string(); if let Some(path_arguments) = callback_traits.get(&ty_str) { match extract_callback_trait_types(path_arguments) { - Ok((fn_args, fn_ret)) => Some(NapiFnArgKind::Callback(Box::new(CallbackArg { - pat: p.pat, - args: fn_args, - ret: fn_ret, - }))), + Ok((fn_args, fn_ret)) => Some(NapiFnArg { + kind: NapiFnArgKind::Callback(Box::new(CallbackArg { + pat: p.pat.clone(), + args: fn_args, + ret: fn_ret, + })), + ts_arg_type, + }), Err(e) => { errors.push(e); None } } } else { - let ty = replace_self(*p.ty, parent); + let ty = replace_self(p.ty.as_ref().clone(), parent); p.ty = Box::new(ty); - Some(NapiFnArgKind::PatType(Box::new(p))) + Some(NapiFnArg { + kind: NapiFnArgKind::PatType(Box::new(p.clone())), + ts_arg_type, + }) } } syn::FnArg::Receiver(r) => { @@ -638,8 +704,10 @@ impl ParseNapi for syn::ItemFn { "#[napi] can't be applied to a function with #[napi(ts_type)]" ); } + let napi = self.convert_to_ast(opts); self.to_tokens(tokens); - self.convert_to_ast(opts) + + napi } } impl ParseNapi for syn::ItemStruct { @@ -734,7 +802,7 @@ fn fn_kind(opts: &BindgenAttrs) -> FnKind { impl ConvertToAST for syn::ItemFn { fn convert_to_ast(&mut self, opts: BindgenAttrs) -> BindgenResult { let func = napi_fn_from_decl( - self.sig.clone(), + &mut self.sig, &opts, self.attrs.clone(), self.vis.clone(), @@ -912,7 +980,7 @@ impl ConvertToAST for syn::ItemImpl { } let func = napi_fn_from_decl( - method.sig.clone(), + &mut method.sig, &opts, method.attrs.clone(), vis, diff --git a/examples/napi/__test__/typegen.spec.ts.md b/examples/napi/__test__/typegen.spec.ts.md index 8b46b46a..f4698cc0 100644 --- a/examples/napi/__test__/typegen.spec.ts.md +++ b/examples/napi/__test__/typegen.spec.ts.md @@ -105,6 +105,8 @@ Generated by [AVA](https://avajs.dev). export function validateString(s: string): string␊ export function validateSymbol(s: symbol): boolean␊ export function tsRename(a: { foo: number }): string[]␊ + export function overrideIndividualArgOnFunction(notOverridden: string, f: () => string, notOverridden2: number): string␊ + export function overrideIndividualArgOnFunctionWithCbArg(callback: (town: string, name?: string | undefined | null) => string, notOverridden: number): object␊ export function xxh64Alias(input: Buffer): bigint␊ export function getMapping(): Record␊ export function sumMapping(nums: Record): number␊ @@ -208,6 +210,7 @@ Generated by [AVA](https://avajs.dev). */␊ returnOtherClass(): Dog␊ returnOtherClassWithCustomConstructor(): Bird␊ + overrideIndividualArgOnMethod(normalTy: string, overriddenTy: {n: string}): Bird␊ }␊ export class Dog {␊ name: string␊ diff --git a/examples/napi/__test__/typegen.spec.ts.snap b/examples/napi/__test__/typegen.spec.ts.snap index ca1b89b0c23f46311216a7f6183a8d7eb4f52863..40c7c246f46b866e6a7b11712d3cc88c713f51b9 100644 GIT binary patch literal 3242 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 literal 3151 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 { @@ -157,6 +159,10 @@ test('class', (t) => { t.is(dog.name, '可乐') t.deepEqual(dog.returnOtherClass(), new Dog('Doge')) t.deepEqual(dog.returnOtherClassWithCustomConstructor(), new Bird('parrot')) + t.is( + dog.overrideIndividualArgOnMethod('Jafar', { n: 'Iago' }).name, + 'Jafar-Iago', + ) t.is(dog.returnOtherClassWithCustomConstructor().getCount(), 1234) t.is(dog.type, Kind.Dog) dog.type = Kind.Cat @@ -321,6 +327,20 @@ test('function ts type override', (t) => { t.deepEqual(tsRename({ foo: 1, bar: 2, baz: 2 }), ['foo', 'bar', 'baz']) }) +test('function individual ts arg type override', (t) => { + t.is( + overrideIndividualArgOnFunction('someStr', () => 'anotherStr', 42), + 'oia: someStr-42-anotherStr', + ) + t.deepEqual( + overrideIndividualArgOnFunctionWithCbArg( + (town, opt) => `im: ${town}-${opt}`, + 89, + ), + 'im: World(89)-null', + ) +}) + test('option object', (t) => { t.notThrows(() => receiveAllOptionalObject()) t.notThrows(() => receiveAllOptionalObject({})) diff --git a/examples/napi/index.d.ts b/examples/napi/index.d.ts index cd564af2..b59573d7 100644 --- a/examples/napi/index.d.ts +++ b/examples/napi/index.d.ts @@ -95,6 +95,8 @@ export function validatePromise(p: Promise): Promise export function validateString(s: string): string export function validateSymbol(s: symbol): boolean export function tsRename(a: { foo: number }): string[] +export function overrideIndividualArgOnFunction(notOverridden: string, f: () => string, notOverridden2: number): string +export function overrideIndividualArgOnFunctionWithCbArg(callback: (town: string, name?: string | undefined | null) => string, notOverridden: number): object export function xxh64Alias(input: Buffer): bigint export function getMapping(): Record export function sumMapping(nums: Record): number @@ -198,6 +200,7 @@ export class Animal { */ returnOtherClass(): Dog returnOtherClassWithCustomConstructor(): Bird + overrideIndividualArgOnMethod(normalTy: string, overriddenTy: {n: string}): Bird } export class Dog { name: string diff --git a/examples/napi/src/class.rs b/examples/napi/src/class.rs index 63405e02..2f4bec89 100644 --- a/examples/napi/src/class.rs +++ b/examples/napi/src/class.rs @@ -85,6 +85,18 @@ impl Animal { pub fn return_other_class_with_custom_constructor(&self) -> Bird { Bird::new("parrot".to_owned()) } + + #[napi] + pub fn override_individual_arg_on_method( + &self, + normal_ty: String, + #[napi(ts_arg_type = "{n: string}")] overridden_ty: napi::JsObject, + ) -> Bird { + let obj = overridden_ty.coerce_to_object().unwrap(); + let the_n: Option = obj.get("n").unwrap(); + + Bird::new(format!("{}-{}", normal_ty, the_n.unwrap())) + } } #[napi(constructor)] diff --git a/examples/napi/src/fn_ts_override.rs b/examples/napi/src/fn_ts_override.rs index 7793325f..9cc2ad6b 100644 --- a/examples/napi/src/fn_ts_override.rs +++ b/examples/napi/src/fn_ts_override.rs @@ -1,6 +1,36 @@ use napi::bindgen_prelude::{Object, Result}; +use napi::JsFunction; #[napi(ts_args_type = "a: { foo: number }", ts_return_type = "string[]")] fn ts_rename(a: Object) -> Result { a.get_property_names() } + +#[napi] +fn override_individual_arg_on_function( + not_overridden: String, + #[napi(ts_arg_type = "() => string")] f: JsFunction, + not_overridden2: u32, +) -> String { + let u = f.call_without_args(None).unwrap(); + let s = u + .coerce_to_string() + .unwrap() + .into_utf8() + .unwrap() + .as_str() + .unwrap() + .to_string(); + + format!("oia: {}-{}-{}", not_overridden, not_overridden2, s) +} + +#[napi] +fn override_individual_arg_on_function_with_cb_arg< + T: Fn(String, Option) -> Result, +>( + #[napi(ts_arg_type = "(town: string, name?: string | undefined | null) => string")] callback: T, + not_overridden: u32, +) -> Result { + callback(format!("World({})", not_overridden), None) +}