Merge pull request #947 from h-a-n-a/feat/skip-ts-gen

feat(napi-derive): add `skip_typescript` attribute for macro
This commit is contained in:
LongYinan 2021-12-19 23:15:05 +08:00 committed by GitHub
commit b7b405a49b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 156 additions and 40 deletions

View file

@ -361,7 +361,10 @@ async function processIntermediateTypeFile(
return idents return idents
} }
const dtsHeader = `/* eslint-disable */ const dtsHeader = `/* tslint:disable */
/* eslint-disable */
/* auto-generated by NAPI-RS */
export class ExternalObject<T> { export class ExternalObject<T> {
readonly '': { readonly '': {

View file

@ -18,6 +18,7 @@ pub struct NapiFn {
pub js_mod: Option<String>, pub js_mod: Option<String>,
pub ts_args_type: Option<String>, pub ts_args_type: Option<String>,
pub ts_return_type: Option<String>, pub ts_return_type: Option<String>,
pub skip_typescript: bool,
pub comments: Vec<String>, pub comments: Vec<String>,
} }
@ -77,6 +78,7 @@ pub struct NapiStructField {
pub getter: bool, pub getter: bool,
pub setter: bool, pub setter: bool,
pub comments: Vec<String>, pub comments: Vec<String>,
pub skip_typescript: bool,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -96,6 +98,7 @@ pub struct NapiEnum {
pub variants: Vec<NapiEnumVariant>, pub variants: Vec<NapiEnumVariant>,
pub js_mod: Option<String>, pub js_mod: Option<String>,
pub comments: Vec<String>, pub comments: Vec<String>,
pub skip_typescript: bool,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -113,6 +116,7 @@ pub struct NapiConst {
pub value: Expr, pub value: Expr,
pub js_mod: Option<String>, pub js_mod: Option<String>,
pub comments: Vec<String>, pub comments: Vec<String>,
pub skip_typescript: bool,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]

View file

@ -38,7 +38,7 @@ macro_rules! napi_ast_impl {
#[cfg(feature = "type-def")] #[cfg(feature = "type-def")]
impl ToTypeDef for Napi { impl ToTypeDef for Napi {
fn to_type_def(&self) -> TypeDef { fn to_type_def(&self) -> Option<TypeDef> {
match self.item { match self.item {
$( NapiItem::$v(ref ast) => ast.to_type_def() ),* $( NapiItem::$v(ref ast) => ast.to_type_def() ),*
} }

View file

@ -90,7 +90,7 @@ impl ToString for TypeDef {
} }
pub trait ToTypeDef { pub trait ToTypeDef {
fn to_type_def(&self) -> TypeDef; fn to_type_def(&self) -> Option<TypeDef>;
} }
static KNOWN_TYPES: Lazy<HashMap<&'static str, &'static str>> = Lazy::new(|| { static KNOWN_TYPES: Lazy<HashMap<&'static str, &'static str>> = Lazy::new(|| {

View file

@ -3,9 +3,14 @@ use super::{ToTypeDef, TypeDef};
use crate::{js_doc_from_comments, ty_to_ts_type, typegen::add_alias, NapiConst}; use crate::{js_doc_from_comments, ty_to_ts_type, typegen::add_alias, NapiConst};
impl ToTypeDef for NapiConst { impl ToTypeDef for NapiConst {
fn to_type_def(&self) -> TypeDef { fn to_type_def(&self) -> Option<TypeDef> {
if self.skip_typescript {
return None;
}
add_alias(self.name.to_string(), self.js_name.to_string()); add_alias(self.name.to_string(), self.js_name.to_string());
TypeDef {
Some(TypeDef {
kind: "const".to_owned(), kind: "const".to_owned(),
name: self.js_name.to_owned(), name: self.js_name.to_owned(),
def: format!( def: format!(
@ -15,6 +20,6 @@ impl ToTypeDef for NapiConst {
), ),
js_mod: self.js_mod.to_owned(), js_mod: self.js_mod.to_owned(),
js_doc: js_doc_from_comments(&self.comments), js_doc: js_doc_from_comments(&self.comments),
} })
} }
} }

View file

@ -2,15 +2,20 @@ use super::{add_alias, ToTypeDef, TypeDef};
use crate::{js_doc_from_comments, NapiEnum}; use crate::{js_doc_from_comments, NapiEnum};
impl ToTypeDef for NapiEnum { impl ToTypeDef for NapiEnum {
fn to_type_def(&self) -> TypeDef { fn to_type_def(&self) -> Option<TypeDef> {
if self.skip_typescript {
return None;
}
add_alias(self.name.to_string(), self.js_name.to_string()); add_alias(self.name.to_string(), self.js_name.to_string());
TypeDef {
Some(TypeDef {
kind: "enum".to_owned(), kind: "enum".to_owned(),
name: self.js_name.to_owned(), name: self.js_name.to_owned(),
def: self.gen_ts_variants(), def: self.gen_ts_variants(),
js_doc: js_doc_from_comments(&self.comments), js_doc: js_doc_from_comments(&self.comments),
js_mod: self.js_mod.to_owned(), js_mod: self.js_mod.to_owned(),
} })
} }
} }

View file

@ -6,7 +6,11 @@ use super::{ty_to_ts_type, ToTypeDef, TypeDef};
use crate::{js_doc_from_comments, CallbackArg, FnKind, NapiFn}; use crate::{js_doc_from_comments, CallbackArg, FnKind, NapiFn};
impl ToTypeDef for NapiFn { impl ToTypeDef for NapiFn {
fn to_type_def(&self) -> TypeDef { fn to_type_def(&self) -> Option<TypeDef> {
if self.skip_typescript {
return None;
}
let def = format!( let def = format!(
r#"{prefix} {name}({args}){ret}"#, r#"{prefix} {name}({args}){ret}"#,
prefix = self.gen_ts_func_prefix(), prefix = self.gen_ts_func_prefix(),
@ -22,13 +26,13 @@ impl ToTypeDef for NapiFn {
.unwrap_or_else(|| self.gen_ts_func_ret()), .unwrap_or_else(|| self.gen_ts_func_ret()),
); );
TypeDef { Some(TypeDef {
kind: "fn".to_owned(), kind: "fn".to_owned(),
name: self.js_name.clone(), name: self.js_name.clone(),
def, def,
js_mod: self.js_mod.to_owned(), js_mod: self.js_mod.to_owned(),
js_doc: js_doc_from_comments(&self.comments), js_doc: js_doc_from_comments(&self.comments),
} })
} }
} }

View file

@ -10,13 +10,14 @@ thread_local! {
} }
impl ToTypeDef for NapiStruct { impl ToTypeDef for NapiStruct {
fn to_type_def(&self) -> TypeDef { fn to_type_def(&self) -> Option<TypeDef> {
CLASS_STRUCTS.with(|c| { CLASS_STRUCTS.with(|c| {
c.borrow_mut() c.borrow_mut()
.insert(self.name.to_string(), self.js_name.clone()); .insert(self.name.to_string(), self.js_name.clone());
}); });
add_alias(self.name.to_string(), self.js_name.to_string()); add_alias(self.name.to_string(), self.js_name.to_string());
TypeDef {
Some(TypeDef {
kind: String::from(if self.kind == NapiStructKind::Object { kind: String::from(if self.kind == NapiStructKind::Object {
"interface" "interface"
} else { } else {
@ -26,36 +27,42 @@ impl ToTypeDef for NapiStruct {
def: self.gen_ts_class(), def: self.gen_ts_class(),
js_mod: self.js_mod.to_owned(), js_mod: self.js_mod.to_owned(),
js_doc: js_doc_from_comments(&self.comments), js_doc: js_doc_from_comments(&self.comments),
} })
} }
} }
impl ToTypeDef for NapiImpl { impl ToTypeDef for NapiImpl {
fn to_type_def(&self) -> TypeDef { fn to_type_def(&self) -> Option<TypeDef> {
if let Some(output_type) = &self.task_output_type { if let Some(output_type) = &self.task_output_type {
TASK_STRUCTS.with(|t| { TASK_STRUCTS.with(|t| {
t.borrow_mut() t.borrow_mut()
.insert(self.js_name.clone(), ty_to_ts_type(output_type, false).0); .insert(self.js_name.clone(), ty_to_ts_type(output_type, false).0);
}); });
} }
TypeDef {
Some(TypeDef {
kind: "impl".to_owned(), kind: "impl".to_owned(),
name: self.js_name.to_owned(), name: self.js_name.to_owned(),
def: self def: self
.items .items
.iter() .iter()
.map(|f| { .filter_map(|f| {
format!( if f.skip_typescript {
"{}{}", None
js_doc_from_comments(&f.comments), } else {
f.to_type_def().def Some(format!(
) "{}{}",
js_doc_from_comments(&f.comments),
f.to_type_def()
.map_or(String::default(), |type_def| type_def.def)
))
}
}) })
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join("\\n"), .join("\\n"),
js_mod: self.js_mod.to_owned(), js_mod: self.js_mod.to_owned(),
js_doc: "".to_string(), js_doc: "".to_string(),
} })
} }
} }
@ -66,7 +73,11 @@ impl NapiStruct {
.fields .fields
.iter() .iter()
.filter(|f| f.getter) .filter(|f| f.getter)
.map(|f| { .filter_map(|f| {
if f.skip_typescript {
return None;
}
let mut field_str = String::from(""); let mut field_str = String::from("");
if !f.comments.is_empty() { if !f.comments.is_empty() {
@ -84,7 +95,7 @@ impl NapiStruct {
} }
field_str.push_str(&arg); field_str.push_str(&arg);
field_str Some(field_str)
}) })
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join("\\n"); .join("\\n");

View file

@ -141,15 +141,19 @@ fn expand(attr: TokenStream, input: TokenStream) -> BindgenResult<TokenStream> {
} }
#[cfg(all(feature = "type-def", not(feature = "noop")))] #[cfg(all(feature = "type-def", not(feature = "noop")))]
fn output_type_def(type_def_file: String, type_def: TypeDef) -> IOResult<()> { fn output_type_def(type_def_file: String, type_def: Option<TypeDef>) -> IOResult<()> {
let file = fs::OpenOptions::new() if type_def.is_some() {
.append(true) let file = fs::OpenOptions::new()
.create(true) .append(true)
.open(type_def_file)?; .create(true)
.open(type_def_file)?;
let mut writer = BufWriter::<fs::File>::new(file); let mut writer = BufWriter::<fs::File>::new(file);
writer.write_all(type_def.to_string().as_bytes())?; writer.write_all(type_def.unwrap().to_string().as_bytes())?;
writer.write_all("\n".as_bytes()) writer.write_all("\n".as_bytes())
} else {
IOResult::Ok(())
}
} }
#[cfg(feature = "compat-mode")] #[cfg(feature = "compat-mode")]

View file

@ -58,7 +58,7 @@ macro_rules! attrgen {
// impl later // impl later
// (inspectable, Inspectable(Span)), // (inspectable, Inspectable(Span)),
// (typescript_custom_section, TypescriptCustomSection(Span)), // (typescript_custom_section, TypescriptCustomSection(Span)),
// (skip_typescript, SkipTypescript(Span)), (skip_typescript, SkipTypescript(Span)),
// (getter_with_clone, GetterWithClone(Span)), // (getter_with_clone, GetterWithClone(Span)),
// For testing purposes only. // For testing purposes only.

View file

@ -578,6 +578,7 @@ fn napi_fn_from_decl(
js_mod: opts.namespace().map(|(m, _)| m.to_owned()), js_mod: opts.namespace().map(|(m, _)| m.to_owned()),
ts_args_type: opts.ts_args_type().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()), ts_return_type: opts.ts_return_type().map(|(m, _)| m.to_owned()),
skip_typescript: opts.skip_typescript().is_some(),
} }
}) })
} }
@ -606,10 +607,13 @@ impl ParseNapi for syn::ItemFn {
} }
impl ParseNapi for syn::ItemStruct { impl ParseNapi for syn::ItemStruct {
fn parse_napi(&mut self, tokens: &mut TokenStream, opts: BindgenAttrs) -> BindgenResult<Napi> { fn parse_napi(&mut self, tokens: &mut TokenStream, opts: BindgenAttrs) -> BindgenResult<Napi> {
if opts.ts_args_type().is_some() || opts.ts_return_type().is_some() { if opts.ts_args_type().is_some()
|| opts.ts_return_type().is_some()
|| opts.skip_typescript().is_some()
{
bail_span!( bail_span!(
self, self,
"#[napi] can't be applied to a struct with #[napi(ts_args_type)] or #[napi(ts_return_type)]" "#[napi] can't be applied to a struct with #[napi(ts_args_type)] or #[napi(ts_return_type)] or #[napi(skip_typescript)]"
); );
} }
let napi = self.convert_to_ast(opts); let napi = self.convert_to_ast(opts);
@ -620,10 +624,13 @@ impl ParseNapi for syn::ItemStruct {
} }
impl ParseNapi for syn::ItemImpl { impl ParseNapi for syn::ItemImpl {
fn parse_napi(&mut self, tokens: &mut TokenStream, opts: BindgenAttrs) -> BindgenResult<Napi> { fn parse_napi(&mut self, tokens: &mut TokenStream, opts: BindgenAttrs) -> BindgenResult<Napi> {
if opts.ts_args_type().is_some() || opts.ts_return_type().is_some() { if opts.ts_args_type().is_some()
|| opts.ts_return_type().is_some()
|| opts.skip_typescript().is_some()
{
bail_span!( bail_span!(
self, self,
"#[napi] can't be applied to impl with #[napi(ts_args_type)] or #[napi(ts_return_type)]" "#[napi] can't be applied to impl with #[napi(ts_args_type)] or #[napi(ts_return_type)] or #[napi(skip_typescript)]"
); );
} }
// #[napi] macro will be remove from impl items after converted to ast // #[napi] macro will be remove from impl items after converted to ast
@ -751,6 +758,7 @@ impl ConvertToAST for syn::ItemStruct {
let ignored = field_opts.skip().is_some(); let ignored = field_opts.skip().is_some();
let readonly = field_opts.readonly().is_some(); let readonly = field_opts.readonly().is_some();
let skip_typescript = field_opts.skip_typescript().is_some();
fields.push(NapiStructField { fields.push(NapiStructField {
name, name,
@ -759,6 +767,7 @@ impl ConvertToAST for syn::ItemStruct {
getter: !ignored, getter: !ignored,
setter: !(ignored || readonly), setter: !(ignored || readonly),
comments: extract_doc_comments(&field.attrs), comments: extract_doc_comments(&field.attrs),
skip_typescript,
}) })
} }
@ -943,6 +952,7 @@ impl ConvertToAST for syn::ItemEnum {
variants, variants,
js_mod: opts.namespace().map(|(m, _)| m.to_owned()), js_mod: opts.namespace().map(|(m, _)| m.to_owned()),
comments: extract_doc_comments(&self.attrs), comments: extract_doc_comments(&self.attrs),
skip_typescript: opts.skip_typescript().is_some(),
}), }),
}) })
} }
@ -961,6 +971,7 @@ impl ConvertToAST for syn::ItemConst {
value: *self.expr.clone(), value: *self.expr.clone(),
js_mod: opts.namespace().map(|(m, _)| m.to_owned()), js_mod: opts.namespace().map(|(m, _)| m.to_owned()),
comments: extract_doc_comments(&self.attrs), comments: extract_doc_comments(&self.attrs),
skip_typescript: opts.skip_typescript().is_some(),
}), }),
}), }),
_ => bail_span!(self, "only public const allowed"), _ => bail_span!(self, "only public const allowed"),

View file

@ -8,7 +8,10 @@ Generated by [AVA](https://avajs.dev).
> Snapshot 1 > Snapshot 1
`/* eslint-disable */␊ `/* tslint:disable */␊
/* eslint-disable */␊
/* auto-generated by NAPI-RS */␊
export class ExternalObject<T> {␊ export class ExternalObject<T> {␊
readonly '': {␊ readonly '': {␊
@ -156,6 +159,13 @@ Generated by [AVA](https://avajs.dev).
kind: number␊ kind: number␊
constructor(name: string, kind: number)␊ constructor(name: string, kind: number)␊
}␊ }␊
export class NinjaTurtle {␊
name: string␊
/** Create your ninja turtle! 🐢 */␊
static newRaph(): NinjaTurtle␊
getMaskColor(): string␊
getName(): string␊
}␊
export class ClassWithFactory {␊ export class ClassWithFactory {␊
name: string␊ name: string␊
static withName(name: string): ClassWithFactory␊ static withName(name: string): ClassWithFactory␊

View file

@ -1,5 +1,8 @@
/* tslint:disable */
/* eslint-disable */ /* eslint-disable */
/* auto-generated by NAPI-RS */
export class ExternalObject<T> { export class ExternalObject<T> {
readonly '': { readonly '': {
readonly '': unique symbol readonly '': unique symbol
@ -146,6 +149,13 @@ export class AnimalWithDefaultConstructor {
kind: number kind: number
constructor(name: string, kind: number) constructor(name: string, kind: number)
} }
export class NinjaTurtle {
name: string
/** Create your ninja turtle! 🐢 */
static newRaph(): NinjaTurtle
getMaskColor(): string
getName(): string
}
export class ClassWithFactory { export class ClassWithFactory {
name: string name: string
static withName(name: string): ClassWithFactory static withName(name: string): ClassWithFactory

View file

@ -120,3 +120,42 @@ pub struct AnimalWithDefaultConstructor {
pub name: String, pub name: String,
pub kind: u32, pub kind: u32,
} }
// Test for skip_typescript
#[napi]
pub struct NinjaTurtle {
pub name: String,
#[napi(skip_typescript)]
pub mask_color: String,
}
#[napi]
impl NinjaTurtle {
/// Create your ninja turtle! 🐢
#[napi(factory)]
pub fn new_raph() -> Self {
Self {
name: "Raphael".to_owned(),
mask_color: "Red".to_owned(),
}
}
/// We are not going to expose this character, so we just skip it...
#[napi(factory, skip_typescript)]
pub fn new_leo() -> Self {
Self {
name: "Leonardo".to_owned(),
mask_color: "Blue".to_owned(),
}
}
#[napi]
pub fn get_mask_color(&self) -> &str {
self.mask_color.as_str()
}
#[napi]
pub fn get_name(&self) -> &str {
self.name.as_str()
}
}

View file

@ -28,3 +28,10 @@ pub enum CustomNumEnum {
fn enum_to_i32(e: CustomNumEnum) -> i32 { fn enum_to_i32(e: CustomNumEnum) -> i32 {
e as i32 e as i32
} }
#[napi(skip_typescript)]
pub enum SkippedEnums {
One = 1,
Two,
Tree,
}

View file

@ -7,6 +7,9 @@ extern crate serde_derive;
/// This is a const /// This is a const
pub const DEFAULT_COST: u32 = 12; pub const DEFAULT_COST: u32 = 12;
#[napi(skip_typescript)]
pub const TYPE_SKIPPED_CONST: u32 = 12;
mod array; mod array;
mod r#async; mod r#async;
mod bigint; mod bigint;