Skip to content

Commit

Permalink
feat(napi): output Rust doc comments in definitions as jsdoc comments
Browse files Browse the repository at this point in the history
  • Loading branch information
timfish committed Nov 29, 2021
1 parent a25f0b9 commit 18d2743
Show file tree
Hide file tree
Showing 19 changed files with 250 additions and 72 deletions.
69 changes: 43 additions & 26 deletions cli/src/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,7 @@ interface TypeDef {
name: string
def: string
js_mod?: string
js_doc: string
}

async function processIntermediateTypeFile(
Expand Down Expand Up @@ -358,49 +359,65 @@ export class ExternalObject<T> {
const allDefs = lines.map((line) => JSON.parse(line) as TypeDef)

function convertDefs(defs: TypeDef[], nested = false): string {
const classes = new Map<string, string>()
const classes = new Map<string, { def: string; js_doc: string }>()
const impls = new Map<string, string>()
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 (def) {
dts += '\n' + indentLines(def, nest + 2)
}

if (implDef) {
dts += `\n${indentLines(implDef, nested ? 4 : 2)}`
dts += '\n' + indentLines(implDef, nest + 2)
}

if (def || implDef) {
dts += '\n'
} else {
dts += ` `
}

dts += `\n${lineStart}}\n`
dts += indentLines(`}`, nest) + '\n'
}

return dts
}

Expand All @@ -413,12 +430,7 @@ export class ExternalObject<T> {
),
).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)
Expand All @@ -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')
}

Expand All @@ -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',
Expand Down
6 changes: 6 additions & 0 deletions crates/backend/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pub struct NapiFn {
pub js_mod: Option<String>,
pub ts_args_type: Option<String>,
pub ts_return_type: Option<String>,
pub comments: Vec<String>,
}

#[derive(Debug, Clone)]
Expand Down Expand Up @@ -58,6 +59,7 @@ pub struct NapiStruct {
pub is_tuple: bool,
pub kind: NapiStructKind,
pub js_mod: Option<String>,
pub comments: Vec<String>,
}

#[derive(Debug, Clone, PartialEq)]
Expand All @@ -74,6 +76,7 @@ pub struct NapiStructField {
pub ty: syn::Type,
pub getter: bool,
pub setter: bool,
pub comments: Vec<String>,
}

#[derive(Debug, Clone)]
Expand All @@ -83,6 +86,7 @@ pub struct NapiImpl {
pub items: Vec<NapiFn>,
pub task_output_type: Option<Type>,
pub js_mod: Option<String>,
pub comments: Vec<String>,
}

#[derive(Debug, Clone)]
Expand All @@ -91,6 +95,7 @@ pub struct NapiEnum {
pub js_name: String,
pub variants: Vec<NapiEnumVariant>,
pub js_mod: Option<String>,
pub comments: Vec<String>,
}

#[derive(Debug, Clone)]
Expand All @@ -107,6 +112,7 @@ pub struct NapiConst {
pub type_name: Type,
pub value: Expr,
pub js_mod: Option<String>,
pub comments: Vec<String>,
}

#[derive(Debug, Clone)]
Expand Down
1 change: 0 additions & 1 deletion crates/backend/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ pub use typegen::*;

#[derive(Debug)]
pub struct Napi {
pub comments: Vec<String>,
pub item: NapiItem,
}

Expand Down
53 changes: 51 additions & 2 deletions crates/backend/src/typegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,51 @@ pub struct TypeDef {
pub name: String,
pub def: String,
pub js_mod: Option<String>,
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::<Vec<String>>()
.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 {
Expand All @@ -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,
)
}
}
Expand Down
3 changes: 2 additions & 1 deletion crates/backend/src/typegen/const.rs
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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),
}
}
}
20 changes: 12 additions & 8 deletions crates/backend/src/typegen/enum.rs
Original file line number Diff line number Diff line change
@@ -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(),
}
}
Expand All @@ -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::<Vec<_>>()
.join(", ")
.join(",\n ")
}
}
3 changes: 2 additions & 1 deletion crates/backend/src/typegen/fn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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),
}
}
}
Expand Down
1 change: 1 addition & 0 deletions crates/backend/src/typegen/js_mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ impl ToTypeDef for NapiMod {
name: self.js_name.clone(),
def: "".to_owned(),
js_mod: None,
js_doc: "".to_owned(),
}
}
}
16 changes: 14 additions & 2 deletions crates/backend/src/typegen/struct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<HashMap<String, String>> = Default::default();
Expand All @@ -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),
}
}
}
Expand All @@ -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::<Vec<_>>()
.join("\\n"),
js_mod: self.js_mod.to_owned(),
js_doc: "".to_string(),
}
}
}
Expand All @@ -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 ")
}
Expand Down

0 comments on commit 18d2743

Please sign in to comment.