feat(napi-derive): support const export

This commit is contained in:
LongYinan 2021-11-10 19:04:36 +08:00
parent 6bfaaebadc
commit 47da28adb4
No known key found for this signature in database
GPG key ID: C3666B7FC82ADAD7
12 changed files with 113 additions and 5 deletions

View file

@ -1,5 +1,5 @@
use proc_macro2::Ident;
use syn::{Attribute, Type};
use syn::{Attribute, Expr, Type};
#[derive(Debug, Clone)]
pub struct NapiFn {
@ -93,3 +93,11 @@ pub struct NapiEnumVariant {
pub val: i32,
pub comments: Vec<String>,
}
#[derive(Debug, Clone)]
pub struct NapiConst {
pub name: Ident,
pub js_name: String,
pub type_name: Type,
pub value: Expr,
}

View file

@ -2,6 +2,7 @@ use proc_macro2::{Ident, Span, TokenStream};
use crate::BindgenResult;
mod r#const;
mod r#enum;
mod r#fn;
mod r#struct;

View file

@ -0,0 +1,41 @@
use proc_macro2::{Literal, TokenStream};
use quote::ToTokens;
use crate::{codegen::get_register_ident, BindgenResult, NapiConst, TryToTokens};
impl TryToTokens for NapiConst {
fn try_to_tokens(&self, tokens: &mut TokenStream) -> BindgenResult<()> {
let register = self.gen_module_register();
(quote! {
#register
})
.to_tokens(tokens);
Ok(())
}
}
impl NapiConst {
fn gen_module_register(&self) -> TokenStream {
let name_str = self.name.to_string();
let name_ident = self.name.clone();
let js_name_lit = Literal::string(&self.js_name);
let register_name = get_register_ident(&name_str);
let type_name = &self.type_name;
quote! {
#[allow(non_snake_case)]
#[allow(clippy::all)]
#[napi::bindgen_prelude::ctor]
fn #register_name() {
use std::ffi::CString;
use std::ptr;
unsafe fn cb(env: napi::sys::napi_env) -> napi::Result<napi::sys::napi_value> {
<#type_name as napi::bindgen_prelude::ToNapiValue>::to_napi_value(env, #name_ident)
}
napi::bindgen_prelude::register_module_export(#js_name_lit, cb);
}
}
}
}

View file

@ -53,4 +53,5 @@ napi_ast_impl! {
(Struct, NapiStruct),
(Impl, NapiImpl),
(Enum, NapiEnum),
(Const, NapiConst),
}

View file

@ -1,3 +1,4 @@
mod r#const;
mod r#enum;
mod r#fn;
pub(crate) mod r#struct;

View file

@ -0,0 +1,17 @@
use super::{ToTypeDef, TypeDef};
use crate::{ty_to_ts_type, NapiConst};
impl ToTypeDef for NapiConst {
fn to_type_def(&self) -> TypeDef {
TypeDef {
kind: "const".to_owned(),
name: self.js_name.to_owned(),
def: format!(
"export const {}: {}",
&self.js_name,
ty_to_ts_type(&self.type_name, false).0
),
}
}
}

View file

@ -9,8 +9,9 @@ use attrs::{BindgenAttr, BindgenAttrs};
use convert_case::{Case, Casing};
use napi_derive_backend::{
BindgenResult, CallbackArg, Diagnostic, FnKind, FnSelf, Napi, NapiEnum, NapiEnumVariant, NapiFn,
NapiFnArgKind, NapiImpl, NapiItem, NapiStruct, NapiStructField, NapiStructKind,
BindgenResult, CallbackArg, Diagnostic, FnKind, FnSelf, Napi, NapiConst, NapiEnum,
NapiEnumVariant, NapiFn, NapiFnArgKind, NapiImpl, NapiItem, NapiStruct, NapiStructField,
NapiStructKind,
};
use proc_macro2::{Ident, TokenStream, TokenTree};
use quote::ToTokens;
@ -296,7 +297,7 @@ fn extract_doc_comments(attrs: &[syn::Attribute]) -> Vec<String> {
})
}
// Unescapes a quoted string. char::escape_debug() was used to escape the text.
// Unescaped a quoted string. char::escape_debug() was used to escape the text.
fn try_unescape(s: &str) -> Option<String> {
if s.is_empty() {
return Some(String::new());
@ -584,6 +585,7 @@ impl ParseNapi for syn::Item {
syn::Item::Struct(s) => s.parse_napi(tokens, opts),
syn::Item::Impl(i) => i.parse_napi(tokens, opts),
syn::Item::Enum(e) => e.parse_napi(tokens, opts),
syn::Item::Const(c) => c.parse_napi(tokens, opts),
_ => bail_span!(
self,
"#[napi] can only be applied to a function, struct, enum or impl."
@ -625,6 +627,14 @@ impl ParseNapi for syn::ItemEnum {
}
}
impl ParseNapi for syn::ItemConst {
fn parse_napi(&mut self, tokens: &mut TokenStream, opts: BindgenAttrs) -> BindgenResult<Napi> {
let napi = self.convert_to_ast(opts);
self.to_tokens(tokens);
napi
}
}
fn fn_kind(opts: &BindgenAttrs) -> FnKind {
let mut kind = FnKind::Normal;
@ -910,3 +920,22 @@ impl ConvertToAST for syn::ItemEnum {
})
}
}
impl ConvertToAST for syn::ItemConst {
fn convert_to_ast(&mut self, opts: BindgenAttrs) -> BindgenResult<Napi> {
match self.vis {
Visibility::Public(_) => Ok(Napi {
comments: vec![],
item: NapiItem::Const(NapiConst {
name: self.ident.clone(),
js_name: opts
.js_name()
.map_or_else(|| self.ident.to_string(), |(s, _)| s.to_string()),
type_name: *self.ty.clone(),
value: *self.expr.clone(),
}),
}),
_ => bail_span!(self, "only public const allowed"),
}
}
}

View file

@ -8,7 +8,8 @@ Generated by [AVA](https://avajs.dev).
> Snapshot 1
`export function getWords(): Array<string>
`export const DEFAULT_COST: number␊
export function getWords(): Array<string>
export function getNums(): Array<number>
export function sumNums(nums: Array<number>): number␊
export function readFileAsync(path: string): Promise<Buffer>

View file

@ -3,6 +3,7 @@ import { join } from 'path'
import test from 'ava'
import {
DEFAULT_COST,
add,
fibonacci,
contains,
@ -48,6 +49,10 @@ import {
createSymbol,
} from '../'
test('export const', (t) => {
t.is(DEFAULT_COST, 12)
})
test('number', (t) => {
t.is(add(1, 2), 3)
t.is(fibonacci(5), 5)

View file

@ -1,3 +1,4 @@
export const DEFAULT_COST: number
export function getWords(): Array<string>
export function getNums(): Array<number>
export function sumNums(nums: Array<number>): number

View file

@ -3,6 +3,9 @@ extern crate napi_derive;
#[macro_use]
extern crate serde_derive;
#[napi]
pub const DEFAULT_COST: u32 = 12;
mod array;
mod r#async;
mod bigint;