From 18d2743862819a35261cc70556e44fbcfe8bb47d Mon Sep 17 00:00:00 2001 From: Tim Fish Date: Mon, 29 Nov 2021 04:54:45 +0000 Subject: [PATCH] feat(napi): output Rust doc comments in definitions as jsdoc comments --- cli/src/build.ts | 71 ++++++++++++-------- crates/backend/src/ast.rs | 6 ++ crates/backend/src/lib.rs | 1 - crates/backend/src/typegen.rs | 53 ++++++++++++++- crates/backend/src/typegen/const.rs | 3 +- crates/backend/src/typegen/enum.rs | 20 +++--- crates/backend/src/typegen/fn.rs | 3 +- crates/backend/src/typegen/js_mod.rs | 1 + crates/backend/src/typegen/struct.rs | 16 ++++- crates/macro/src/parser/mod.rs | 18 ++--- examples/napi/__test__/typegen.spec.ts.md | 57 +++++++++++++--- examples/napi/__test__/typegen.spec.ts.snap | Bin 1392 -> 1789 bytes examples/napi/index.d.ts | 57 +++++++++++++--- examples/napi/src/array.rs | 1 + examples/napi/src/class.rs | 8 +++ examples/napi/src/enum.rs | 3 + examples/napi/src/js_mod.rs | 3 + examples/napi/src/lib.rs | 1 + examples/napi/src/serde.rs | 2 + 19 files changed, 251 insertions(+), 73 deletions(-) diff --git a/cli/src/build.ts b/cli/src/build.ts index 0b0d9010..1d512310 100644 --- a/cli/src/build.ts +++ b/cli/src/build.ts @@ -329,6 +329,7 @@ interface TypeDef { name: string def: string js_mod?: string + js_doc: string } async function processIntermediateTypeFile( @@ -358,49 +359,65 @@ 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() const impls = new Map() let dts = '' - const lineStart = nested ? ' ' : '' + const nest = nested ? 2 : 0 + defs.forEach((def) => { switch (def.kind) { case 'struct': if (!nested) { idents.push(def.name) } - classes.set(def.name, def.def) + classes.set(def.name, { def: def.def, js_doc: def.js_doc }) break case 'impl': - impls.set(def.name, def.def) + impls.set(def.name, `${def.js_doc}${def.def}`) break case 'interface': - dts += `${lineStart}interface ${def.name} {\n${indentLines( - def.def, - nested ? 4 : 2, - )}\n}\n` + dts += + indentLines(`${def.js_doc}export interface ${def.name} {`, nest) + + '\n' + dts += indentLines(def.def, nest + 2) + '\n' + dts += indentLines(`}`, nest) + '\n' + break + case 'enum': + dts += + indentLines(`${def.js_doc}export enum ${def.name} {`, nest) + '\n' + dts += indentLines(def.def, nest + 2) + '\n' + dts += indentLines(`}`, nest) + '\n' break default: if (!nested) { idents.push(def.name) } - dts += lineStart + def.def + '\n' + dts += indentLines(`${def.js_doc}${def.def}`, nest) + '\n' } }) - for (const [name, classDef] of classes.entries()) { + for (const [name, { js_doc, def }] of classes.entries()) { const implDef = impls.get(name) - dts += `${lineStart}export class ${name} {\n${indentLines( - classDef, - nested ? 4 : 2, - )}` + dts += indentLines(`${js_doc}export class ${name} {`, nest) - if (implDef) { - dts += `\n${indentLines(implDef, nested ? 4 : 2)}` + if (def) { + dts += '\n' + indentLines(def, nest + 2) } - dts += `\n${lineStart}}\n` + if (implDef) { + dts += '\n' + indentLines(implDef, nest + 2) + } + + if (def || implDef) { + dts += '\n' + } else { + dts += ` ` + } + + dts += indentLines(`}`, nest) + '\n' } + return dts } @@ -413,12 +430,7 @@ export class ExternalObject { ), ).reduce((acc, [mod, defs]) => { idents.push(mod) - return ( - acc + - `export namespace ${mod} { -${convertDefs(defs, true)} -}\n` - ) + return acc + `export namespace ${mod} {\n${convertDefs(defs, true)}}\n` }, '') await unlinkAsync(source) @@ -429,7 +441,11 @@ ${convertDefs(defs, true)} function indentLines(input: string, spaces: number) { return input .split('\n') - .map((line) => ''.padEnd(spaces, ' ') + line.trim()) + .map( + (line) => + ''.padEnd(spaces, ' ') + + (line.startsWith(' *') ? line.trimEnd() : line.trim()), + ) .join('\n') } @@ -442,9 +458,10 @@ async function writeJsBinding( if (distFileName) { const template = createJsBinding(localName, packageName) const declareCodes = `const { ${idents.join(', ')} } = nativeBinding\n` - const exportsCode = idents.reduce((acc, cur) => { - return `${acc}\nmodule.exports.${cur} = ${cur}` - }, '') + const exportsCode = idents.reduce( + (acc, cur) => `${acc}\nmodule.exports.${cur} = ${cur}`, + '', + ) await writeFileAsync( distFileName, template + declareCodes + exportsCode + '\n', diff --git a/crates/backend/src/ast.rs b/crates/backend/src/ast.rs index cbacc189..7f3cd6bf 100644 --- a/crates/backend/src/ast.rs +++ b/crates/backend/src/ast.rs @@ -18,6 +18,7 @@ pub struct NapiFn { pub js_mod: Option, pub ts_args_type: Option, pub ts_return_type: Option, + pub comments: Vec, } #[derive(Debug, Clone)] @@ -58,6 +59,7 @@ pub struct NapiStruct { pub is_tuple: bool, pub kind: NapiStructKind, pub js_mod: Option, + pub comments: Vec, } #[derive(Debug, Clone, PartialEq)] @@ -74,6 +76,7 @@ pub struct NapiStructField { pub ty: syn::Type, pub getter: bool, pub setter: bool, + pub comments: Vec, } #[derive(Debug, Clone)] @@ -83,6 +86,7 @@ pub struct NapiImpl { pub items: Vec, pub task_output_type: Option, pub js_mod: Option, + pub comments: Vec, } #[derive(Debug, Clone)] @@ -91,6 +95,7 @@ pub struct NapiEnum { pub js_name: String, pub variants: Vec, pub js_mod: Option, + pub comments: Vec, } #[derive(Debug, Clone)] @@ -107,6 +112,7 @@ pub struct NapiConst { pub type_name: Type, pub value: Expr, pub js_mod: Option, + pub comments: Vec, } #[derive(Debug, Clone)] diff --git a/crates/backend/src/lib.rs b/crates/backend/src/lib.rs index c7567372..9e8c10c3 100644 --- a/crates/backend/src/lib.rs +++ b/crates/backend/src/lib.rs @@ -18,7 +18,6 @@ pub use typegen::*; #[derive(Debug)] pub struct Napi { - pub comments: Vec, pub item: NapiItem, } diff --git a/crates/backend/src/typegen.rs b/crates/backend/src/typegen.rs index 60ef40fc..4c005fb1 100644 --- a/crates/backend/src/typegen.rs +++ b/crates/backend/src/typegen.rs @@ -15,6 +15,51 @@ pub struct TypeDef { pub name: String, pub def: String, pub js_mod: Option, + pub js_doc: String, +} + +pub fn js_doc_from_comments(comments: &[String]) -> String { + if comments.is_empty() { + return "".to_owned(); + } + + if comments.len() == 1 { + return format!("/**{} */\n", comments[0]); + } + + format!( + "/**\n{} */\n", + comments + .iter() + .map(|c| format!(" *{}\n", c)) + .collect::>() + .join("") + ) +} + +fn escape_json(src: &str) -> String { + use std::fmt::Write; + let mut escaped = String::with_capacity(src.len()); + let mut utf16_buf = [0u16; 2]; + for c in src.chars() { + match c { + '\x08' => escaped += "\\b", + '\x0c' => escaped += "\\f", + '\n' => escaped += "\\n", + '\r' => escaped += "\\r", + '\t' => escaped += "\\t", + '"' => escaped += "\\\"", + '\\' => escaped += "\\", + c if c.is_ascii_graphic() => escaped.push(c), + c => { + let encoded = c.encode_utf16(&mut utf16_buf); + for utf16 in encoded { + write!(&mut escaped, "\\u{:04X}", utf16).unwrap(); + } + } + } + } + escaped } impl ToString for TypeDef { @@ -25,8 +70,12 @@ impl ToString for TypeDef { "".to_owned() }; format!( - r#"{{"kind": "{}", "name": "{}", "def": "{}"{}}}"#, - self.kind, self.name, self.def, js_mod, + r#"{{"kind": "{}", "name": "{}", "js_doc": "{}", "def": "{}"{}}}"#, + self.kind, + self.name, + escape_json(&self.js_doc), + escape_json(&self.def), + js_mod, ) } } diff --git a/crates/backend/src/typegen/const.rs b/crates/backend/src/typegen/const.rs index 46bad1c1..5ac266be 100644 --- a/crates/backend/src/typegen/const.rs +++ b/crates/backend/src/typegen/const.rs @@ -1,6 +1,6 @@ use super::{ToTypeDef, TypeDef}; -use crate::{ty_to_ts_type, NapiConst}; +use crate::{js_doc_from_comments, ty_to_ts_type, NapiConst}; impl ToTypeDef for NapiConst { fn to_type_def(&self) -> TypeDef { @@ -13,6 +13,7 @@ impl ToTypeDef for NapiConst { ty_to_ts_type(&self.type_name, false).0 ), js_mod: self.js_mod.to_owned(), + js_doc: js_doc_from_comments(&self.comments), } } } diff --git a/crates/backend/src/typegen/enum.rs b/crates/backend/src/typegen/enum.rs index 59b060df..3046c648 100644 --- a/crates/backend/src/typegen/enum.rs +++ b/crates/backend/src/typegen/enum.rs @@ -1,16 +1,13 @@ use super::{ToTypeDef, TypeDef}; -use crate::NapiEnum; +use crate::{js_doc_from_comments, NapiEnum}; impl ToTypeDef for NapiEnum { fn to_type_def(&self) -> TypeDef { TypeDef { kind: "enum".to_owned(), name: self.js_name.to_owned(), - def: format!( - r"export enum {js_name} {{ {variants} }}", - js_name = &self.js_name, - variants = self.gen_ts_variants() - ), + def: self.gen_ts_variants(), + js_doc: js_doc_from_comments(&self.comments), js_mod: self.js_mod.to_owned(), } } @@ -21,8 +18,15 @@ impl NapiEnum { self .variants .iter() - .map(|v| format!("{} = {}", v.name, v.val)) + .map(|v| { + format!( + "{}{} = {}", + js_doc_from_comments(&v.comments), + v.name, + v.val, + ) + }) .collect::>() - .join(", ") + .join(",\n ") } } diff --git a/crates/backend/src/typegen/fn.rs b/crates/backend/src/typegen/fn.rs index 24e0cc82..0199823d 100644 --- a/crates/backend/src/typegen/fn.rs +++ b/crates/backend/src/typegen/fn.rs @@ -3,7 +3,7 @@ use quote::ToTokens; use syn::Pat; use super::{ty_to_ts_type, ToTypeDef, TypeDef}; -use crate::{CallbackArg, FnKind, NapiFn}; +use crate::{js_doc_from_comments, CallbackArg, FnKind, NapiFn}; impl ToTypeDef for NapiFn { fn to_type_def(&self) -> TypeDef { @@ -27,6 +27,7 @@ impl ToTypeDef for NapiFn { name: self.js_name.clone(), def, js_mod: self.js_mod.to_owned(), + js_doc: js_doc_from_comments(&self.comments), } } } diff --git a/crates/backend/src/typegen/js_mod.rs b/crates/backend/src/typegen/js_mod.rs index ec08dbf0..01f7c8c5 100644 --- a/crates/backend/src/typegen/js_mod.rs +++ b/crates/backend/src/typegen/js_mod.rs @@ -7,6 +7,7 @@ impl ToTypeDef for NapiMod { name: self.js_name.clone(), def: "".to_owned(), js_mod: None, + js_doc: "".to_owned(), } } } diff --git a/crates/backend/src/typegen/struct.rs b/crates/backend/src/typegen/struct.rs index 5493ca0f..d44a8b24 100644 --- a/crates/backend/src/typegen/struct.rs +++ b/crates/backend/src/typegen/struct.rs @@ -2,7 +2,7 @@ use std::cell::RefCell; use std::collections::HashMap; use super::{ToTypeDef, TypeDef}; -use crate::{ty_to_ts_type, NapiImpl, NapiStruct, NapiStructKind}; +use crate::{js_doc_from_comments, ty_to_ts_type, NapiImpl, NapiStruct, NapiStructKind}; thread_local! { pub(crate) static TASK_STRUCTS: RefCell> = Default::default(); @@ -24,6 +24,7 @@ impl ToTypeDef for NapiStruct { name: self.js_name.to_owned(), def: self.gen_ts_class(), js_mod: self.js_mod.to_owned(), + js_doc: js_doc_from_comments(&self.comments), } } } @@ -42,10 +43,17 @@ impl ToTypeDef for NapiImpl { def: self .items .iter() - .map(|f| f.to_type_def().def) + .map(|f| { + format!( + "{}{}", + js_doc_from_comments(&f.comments), + f.to_type_def().def + ) + }) .collect::>() .join("\\n"), js_mod: self.js_mod.to_owned(), + js_doc: "".to_string(), } } } @@ -60,6 +68,10 @@ impl NapiStruct { .map(|f| { let mut field_str = String::from(""); + if !f.comments.is_empty() { + field_str.push_str(&js_doc_from_comments(&f.comments)) + } + if !f.setter { field_str.push_str("readonly ") } diff --git a/crates/macro/src/parser/mod.rs b/crates/macro/src/parser/mod.rs index fa0b109b..737068bf 100644 --- a/crates/macro/src/parser/mod.rs +++ b/crates/macro/src/parser/mod.rs @@ -572,6 +572,7 @@ fn napi_fn_from_decl( kind: fn_kind(opts), fn_self, parent: parent.cloned(), + comments: extract_doc_comments(&attrs), attrs, strict: opts.strict().is_some(), js_mod: opts.namespace().map(|(m, _)| m.to_owned()), @@ -693,7 +694,6 @@ impl ConvertToAST for syn::ItemFn { )?; Ok(Napi { - comments: vec![], item: NapiItem::Fn(func), }) } @@ -758,12 +758,13 @@ impl ConvertToAST for syn::ItemStruct { ty: field.ty.clone(), getter: !ignored, setter: !(ignored || readonly), + comments: extract_doc_comments(&field.attrs), }) } + record_struct(&struct_name, js_name.clone(), &opts); Diagnostic::from_vec(errors).map(|()| Napi { - comments: vec![], item: NapiItem::Struct(NapiStruct { js_name, name: struct_name, @@ -772,6 +773,7 @@ impl ConvertToAST for syn::ItemStruct { is_tuple, kind: struct_kind, js_mod: opts.namespace().map(|(m, _)| m.to_owned()), + comments: extract_doc_comments(&self.attrs), }), }) } @@ -842,13 +844,13 @@ impl ConvertToAST for syn::ItemImpl { } Ok(Napi { - comments: vec![], item: NapiItem::Impl(NapiImpl { name: struct_name, js_name: struct_js_name, items, task_output_type, js_mod: impl_opts.namespace().map(|(m, _)| m.to_owned()), + comments: extract_doc_comments(&self.attrs), }), }) } @@ -925,24 +927,22 @@ impl ConvertToAST for syn::ItemEnum { }; last_variant_val = val; - let comments = extract_doc_comments(&v.attrs); + Ok(NapiEnumVariant { name: v.ident.clone(), val, - comments, + comments: extract_doc_comments(&v.attrs), }) }) .collect::>>()?; - let comments = extract_doc_comments(&self.attrs); - Ok(Napi { - comments, item: NapiItem::Enum(NapiEnum { name: self.ident.clone(), js_name, variants, js_mod: opts.namespace().map(|(m, _)| m.to_owned()), + comments: extract_doc_comments(&self.attrs), }), }) } @@ -952,7 +952,6 @@ impl ConvertToAST for syn::ItemConst { fn convert_to_ast(&mut self, opts: BindgenAttrs) -> BindgenResult { match self.vis { Visibility::Public(_) => Ok(Napi { - comments: vec![], item: NapiItem::Const(NapiConst { name: self.ident.clone(), js_name: opts @@ -961,6 +960,7 @@ impl ConvertToAST for syn::ItemConst { type_name: *self.ty.clone(), value: *self.expr.clone(), js_mod: opts.namespace().map(|(m, _)| m.to_owned()), + comments: extract_doc_comments(&self.attrs), }), }), _ => bail_span!(self, "only public const allowed"), diff --git a/examples/napi/__test__/typegen.spec.ts.md b/examples/napi/__test__/typegen.spec.ts.md index d2d2f385..fe909b6d 100644 --- a/examples/napi/__test__/typegen.spec.ts.md +++ b/examples/napi/__test__/typegen.spec.ts.md @@ -16,8 +16,10 @@ Generated by [AVA](https://avajs.dev). [K: symbol]: T␊ }␊ }␊ + /** This is a const */␊ export const DEFAULT_COST: number␊ export function getWords(): Array␊ + /** Gets some numbers */␊ export function getNums(): Array␊ export function sumNums(nums: Array): number␊ export function readFileAsync(path: string): Promise␊ @@ -26,16 +28,35 @@ Generated by [AVA](https://avajs.dev). export function createBigInt(): BigInt␊ export function createBigIntI64(): BigInt␊ export function getCwd(callback: (arg0: string) => void): void␊ + /** napi = { version = 2, features = ["serde-json"] } */␊ export function readFile(callback: (arg0: Error | undefined, arg1?: string | undefined | null) => void): void␊ export function eitherStringOrNumber(input: string | number): number␊ export function returnEither(input: number): string | number␊ export function either3(input: string | number | boolean): number␊ - interface Obj {␊ + export interface Obj {␊ v: string | number␊ }␊ export function either4(input: string | number | boolean | Obj): number␊ - export enum Kind { Dog = 0, Cat = 1, Duck = 2 }␊ - export enum CustomNumEnum { One = 1, Two = 2, Three = 3, Four = 4, Six = 6, Eight = 8, Nine = 9, Ten = 10 }␊ + /** default enum values are continuos i32s start from 0 */␊ + export enum Kind {␊ + /** Barks */␊ + Dog = 0,␊ + /** Kills birds */␊ + Cat = 1,␊ + /** Tasty */␊ + Duck = 2␊ + }␊ + /** You could break the step and for an new continuous value. */␊ + export enum CustomNumEnum {␊ + One = 1,␊ + Two = 2,␊ + Three = 3,␊ + Four = 4,␊ + Six = 6,␊ + Eight = 8,␊ + Nine = 9,␊ + Ten = 10␊ + }␊ export function enumToI32(e: CustomNumEnum): number␊ export function throwError(): void␊ export function createExternal(size: number): ExternalObject␊ @@ -53,8 +74,10 @@ Generated by [AVA](https://avajs.dev). export function getUndefined(): void␊ export function getNull(): JsNull␊ export function asyncPlus100(p: Promise): Promise␊ - interface PackageJson {␊ + /** This is an interface for package.json */␊ + export interface PackageJson {␊ name: string␊ + /** The version of the package */␊ version: string␊ dependencies?: Record | undefined | null␊ devDependencies?: Record | undefined | null␊ @@ -73,22 +96,34 @@ Generated by [AVA](https://avajs.dev). export function threadsafeFunctionThrowError(cb: (...args: any[]) => any): void␊ export function threadsafeFunctionFatalMode(cb: (...args: any[]) => any): void␊ export function getBuffer(): Buffer␊ + /**␊ + * \`constructor\` option for \`struct\` requires all fields to be public,␊ + * otherwise tag impl fn as constructor␊ + * #[napi(constructor)]␊ + */␊ export class Animal {␊ + /** Kind of animal */␊ readonly kind: Kind␊ + /** This is the constructor */␊ constructor(kind: Kind, name: string)␊ + /** This is a factory method */␊ static withKind(kind: Kind): Animal␊ get name(): string␊ set name(name: string)␊ + /**␊ + * This is a␊ + * multi-line comment␊ + * with an emoji 🚀␊ + */␊ whoami(): string␊ + /** This is static... */␊ static getDogKind(): Kind␊ }␊ + /** Smoking test for type generation */␊ export class Blake2BHasher {␊ - ␊ static withKey(key: Blake2bKey): Blake2BHasher␊ }␊ - export class Blake2BKey {␊ - ␊ - }␊ + export class Blake2BKey { }␊ export class Context {␊ maybeNeed?: boolean | undefined | null␊ constructor()␊ @@ -103,18 +138,18 @@ Generated by [AVA](https://avajs.dev). export namespace xxh3 {␊ export const ALIGNMENT: number␊ export function xxh3_64(input: Buffer): BigInt␊ + /** xxh128 function */␊ export function xxh128(input: Buffer): BigInt␊ + /** Xxh3 class */␊ export class Xxh3 {␊ - ␊ constructor()␊ + /** update */␊ update(input: Buffer): void␊ digest(): BigInt␊ }␊ - ␊ }␊ export namespace xxh2 {␊ export function xxh2Plus(a: number, b: number): number␊ export function xxh3Xxh64Alias(input: Buffer): BigInt␊ - ␊ }␊ ` diff --git a/examples/napi/__test__/typegen.spec.ts.snap b/examples/napi/__test__/typegen.spec.ts.snap index ac8bd40a11c40784bb04006022b1b690081e754b..f8409d6b4631780212cb599d10d4d9025570c98a 100644 GIT binary patch literal 1789 zcmV1@u08hdHc>@YCG+vV=FM+@?`=q}R=ZZa{{1I^{_*## zUw{7W^MC*P{kLDP)$resH)^%(wU5mB+K07|Kltv(odv>Fz=gi;b4A^Nk%c?;Iy;YK ztce#;rAX&ovslnz-#uZTzSr9zZ|j7_jQUapV{+?O%Ph<-C4#?67*XTUm4Vql+-qgi zqZaAG(nbBEerI8U^oCp!_)m!^h0+*79)Ou|cXry(ANO8w?H}}7L?ofh;#{Ji2v2h< z$bjh=GWM0zXpwdt)A2o}V=e|8#>GRX6;U!|nYyx^*e1G3Xjw>QHwuC(35_7^t2x<5 zWq_ES9Uic@8Vk>fs2)N(Oo*WKG?pP(?A~V5@0WR`sPrTWH1C~BhsJ~4jip-^t;+{+ zzir=lXp3y}!LHCv;^xCfJ}BsU5V2iIU>x5xFS4*F5t@ z&z(4yG5H3->a#u<%x@CdSbmVlWN*P(BtcLJbKV={ddT7fqrV?_&80Y8M2WVnPA;z! zEO0>4F{X0miO!sriU?LKoWj7BGGJ5`+yhKl+@~HR@YcqqWC{Z-Pa8n=~Go$4$t# z8~{L;n%T}C4*~^`0XQPv-=Z4!m-GD|ReGFhCf*2A;1d_1G$L?X2#7_5 zg5y2_2#zM$*(Byfr6*aOk8~?hS|W9HaE2l83zo+K58I#{hr^iR`YKL#WD>)84aW!k z9ER&S?C`-5MR*5|Gi)bqF_E*B2C(va6ZHdc;wPg+EpBp1qhIecUARk-(c_58J{g#WGSTOGCMuC{I)=bld!)Lh$-=_@G)UC)(vlMuigii(p6Q5$ z#mUGt5mYZaV2fDpEOwa+Jxv83;XV=!;w;BhPIN$Dc{zpiBb#MrQ})aDEG?f!400hn z&J@7t8S|ji$}R#brWkLO;Rf|ix8F;(NL^UNt+Qdqt!dfCSleBEY|(T+qdR$WMMMGn zP%f0CfP%eA8k+=)5je0|DJyVg7Cfqf%p7G&SGxn@=eobVes%H36iUwWRpb@Z2d4Yp z75GTfPCW0(tDj8ztWr(Pde+d-i=9CaAQRnoVRZ}g7Rw-j)}?|cE^ArQ+Eq+7iQ)sp z$VU3WOs-%b8$m3x)K|36cCux=KU`c~giD6B0z?mwOw$LWN=7f(>P^e0=l&1&cBrPo z6X~<}F$Z8xOND99%?N3xP8P_q`NWD7Ps{k2$SCD&V4CA}^%!2>n}lPt4aor}X94t2 zS`rsnBXI-nHPMX34)6?84sT;Xco=~wk{_|mqui&5*wH)IYU8MGfAE4r81F%6lC7mb{ZC)s(i7;D}E_kuG49VAAYw3Z1OxAhR_kE>*yS71PkQ} zC;$BP!?$J~!#f*F8uA&p_LR^)-{59IM{H&DOR4}D<|6HUv9U8^<5ni+!o1O%Rhd?R8G9fJ9n;(%as;$y#!E=Y zF6%PpKWJHB9VMm720~*tylr@Djt@U9PR)Fv2ZI4CvGdX3&lg~3Cj(7M6}Fsp3pi*M zOr(%0lhkmX(*ST&Mc4&EtGH?$&-@*w{do6b_erNa{Ugdmz-}^EU$3uKb(krNKxld8 z?o`QOsKgQoz1z@Bj5E!fZ6jTInIEEfTd8&u!7XJ?3&~~$e})io-{%8BuVaUBQIWe9 fJ9qYhRq+>B~!-_lx>}7cK^iyAuEadN5_D 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 { [K: symbol]: T } } +/** This is a const */ export const DEFAULT_COST: number export function getWords(): Array +/** Gets some numbers */ export function getNums(): Array export function sumNums(nums: Array): number export function readFileAsync(path: string): Promise @@ -16,16 +18,35 @@ export function bigintAdd(a: BigInt, b: BigInt): BigInt export function createBigInt(): BigInt export function createBigIntI64(): BigInt export function getCwd(callback: (arg0: string) => void): void +/** napi = { version = 2, features = ["serde-json"] } */ export function readFile(callback: (arg0: Error | undefined, arg1?: string | undefined | null) => void): void export function eitherStringOrNumber(input: string | number): number export function returnEither(input: number): string | number export function either3(input: string | number | boolean): number -interface Obj { +export interface Obj { v: string | number } export function either4(input: string | number | boolean | Obj): number -export enum Kind { Dog = 0, Cat = 1, Duck = 2 } -export enum CustomNumEnum { One = 1, Two = 2, Three = 3, Four = 4, Six = 6, Eight = 8, Nine = 9, Ten = 10 } +/** default enum values are continuos i32s start from 0 */ +export enum Kind { + /** Barks */ + Dog = 0, + /** Kills birds */ + Cat = 1, + /** Tasty */ + Duck = 2 +} +/** You could break the step and for an new continuous value. */ +export enum CustomNumEnum { + One = 1, + Two = 2, + Three = 3, + Four = 4, + Six = 6, + Eight = 8, + Nine = 9, + Ten = 10 +} export function enumToI32(e: CustomNumEnum): number export function throwError(): void export function createExternal(size: number): ExternalObject @@ -43,8 +64,10 @@ export function getGlobal(): typeof global export function getUndefined(): void export function getNull(): JsNull export function asyncPlus100(p: Promise): Promise -interface PackageJson { +/** This is an interface for package.json */ +export interface PackageJson { name: string + /** The version of the package */ version: string dependencies?: Record | undefined | null devDependencies?: Record | undefined | null @@ -63,22 +86,34 @@ export function callThreadsafeFunction(callback: (...args: any[]) => any): void export function threadsafeFunctionThrowError(cb: (...args: any[]) => any): void export function threadsafeFunctionFatalMode(cb: (...args: any[]) => any): void export function getBuffer(): Buffer +/** + * `constructor` option for `struct` requires all fields to be public, + * otherwise tag impl fn as constructor + * #[napi(constructor)] + */ export class Animal { + /** Kind of animal */ readonly kind: Kind + /** This is the constructor */ constructor(kind: Kind, name: string) + /** This is a factory method */ static withKind(kind: Kind): Animal get name(): string set name(name: string) + /** + * This is a + * multi-line comment + * with an emoji 🚀 + */ whoami(): string + /** This is static... */ static getDogKind(): Kind } +/** Smoking test for type generation */ export class Blake2BHasher { - static withKey(key: Blake2bKey): Blake2BHasher } -export class Blake2BKey { - -} +export class Blake2BKey { } export class Context { maybeNeed?: boolean | undefined | null constructor() @@ -93,17 +128,17 @@ export class ClassWithFactory { export namespace xxh3 { export const ALIGNMENT: number export function xxh3_64(input: Buffer): BigInt + /** xxh128 function */ export function xxh128(input: Buffer): BigInt + /** Xxh3 class */ export class Xxh3 { - constructor() + /** update */ update(input: Buffer): void digest(): BigInt } - } export namespace xxh2 { export function xxh2Plus(a: number, b: number): number export function xxh3Xxh64Alias(input: Buffer): BigInt - } diff --git a/examples/napi/src/array.rs b/examples/napi/src/array.rs index 04b55d31..9dda022d 100644 --- a/examples/napi/src/array.rs +++ b/examples/napi/src/array.rs @@ -4,6 +4,7 @@ fn get_words() -> Vec<&'static str> { } #[napi] +/// Gets some numbers fn get_nums() -> Vec { vec![1, 1, 2, 3, 5, 8] } diff --git a/examples/napi/src/class.rs b/examples/napi/src/class.rs index 3cd699c2..a8271527 100644 --- a/examples/napi/src/class.rs +++ b/examples/napi/src/class.rs @@ -8,17 +8,21 @@ use crate::r#enum::Kind; #[napi] pub struct Animal { #[napi(readonly)] + /// Kind of animal pub kind: Kind, + name: String, } #[napi] impl Animal { + /// This is the constructor #[napi(constructor)] pub fn new(kind: Kind, name: String) -> Self { Animal { kind, name } } + /// This is a factory method #[napi(factory)] pub fn with_kind(kind: Kind) -> Self { Animal { @@ -37,6 +41,9 @@ impl Animal { self.name = name; } + /// This is a + /// multi-line comment + /// with an emoji 🚀 #[napi] pub fn whoami(&self) -> String { match self.kind { @@ -49,6 +56,7 @@ impl Animal { } #[napi] + /// This is static... pub fn get_dog_kind() -> Kind { Kind::Dog } diff --git a/examples/napi/src/enum.rs b/examples/napi/src/enum.rs index 4f76f99b..76ab296b 100644 --- a/examples/napi/src/enum.rs +++ b/examples/napi/src/enum.rs @@ -3,8 +3,11 @@ use napi::bindgen_prelude::*; /// default enum values are continuos i32s start from 0 #[napi] pub enum Kind { + /// Barks Dog, + /// Kills birds Cat, + /// Tasty Duck, } diff --git a/examples/napi/src/js_mod.rs b/examples/napi/src/js_mod.rs index b0037b9a..5c812567 100644 --- a/examples/napi/src/js_mod.rs +++ b/examples/napi/src/js_mod.rs @@ -15,6 +15,7 @@ mod xxh3 { } #[napi] + /// xxh128 function pub fn xxh128(input: Buffer) -> u128 { let mut h: u128 = 0; for i in input.as_ref() { @@ -24,6 +25,7 @@ mod xxh3 { } #[napi] + /// Xxh3 class pub struct Xxh3 { inner: BigInt, } @@ -41,6 +43,7 @@ mod xxh3 { } #[napi] + /// update pub fn update(&mut self, input: Buffer) { for i in input.as_ref() { self.inner = BigInt { diff --git a/examples/napi/src/lib.rs b/examples/napi/src/lib.rs index 9cf239a1..c8b76a5d 100644 --- a/examples/napi/src/lib.rs +++ b/examples/napi/src/lib.rs @@ -4,6 +4,7 @@ extern crate napi_derive; extern crate serde_derive; #[napi] +/// This is a const pub const DEFAULT_COST: u32 = 12; mod array; diff --git a/examples/napi/src/serde.rs b/examples/napi/src/serde.rs index 4ff31c1f..f6121182 100644 --- a/examples/napi/src/serde.rs +++ b/examples/napi/src/serde.rs @@ -4,8 +4,10 @@ use std::fs; #[napi(object)] #[derive(Serialize, Deserialize, Debug)] +/// This is an interface for package.json struct PackageJson { pub name: String, + /// The version of the package pub version: String, pub dependencies: Option>, #[serde(rename = "devDependencies")]