diff --git a/Cargo.toml b/Cargo.toml index 1b6d31f2..060f0091 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ members = [ "./crates/napi", "./crates/sys", "./examples/napi", + "./examples/napi-shared", "./examples/napi-compat-mode", "./examples/binary", "./bench", diff --git a/cli/package.json b/cli/package.json index 4fe7533a..977c24a9 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,6 +1,6 @@ { "name": "@napi-rs/cli", - "version": "2.15.0", + "version": "2.15.1-alpha.0", "description": "Cli tools for napi-rs", "keywords": [ "cli", diff --git a/cli/src/build.ts b/cli/src/build.ts index 466c20d8..d5b78a59 100644 --- a/cli/src/build.ts +++ b/cli/src/build.ts @@ -9,6 +9,8 @@ import * as chalk from 'colorette' import envPaths from 'env-paths' import { groupBy } from 'lodash-es' +import { version } from '../package.json' + import { ARM_FEATURES_H } from './arm-features.h' import { getNapiConfig } from './consts' import { debugFactory } from './debug' @@ -493,6 +495,7 @@ export class BuildCommand extends Command { } const cwdSha = createHash('sha256') .update(process.cwd()) + .update(version) .digest('hex') .substring(0, 8) const intermediateTypeFile = join( @@ -698,6 +701,15 @@ async function processIntermediateTypeFile( .split('\n') .map((line) => line.trim()) .filter(Boolean) + .map((line) => { + // compatible with old version + if (line.startsWith('{')) { + return line + } else { + const [_crateName, ...rest] = line.split(':') + return rest.join(':') + } + }) if (!lines.length) { return idents diff --git a/crates/backend/src/typegen.rs b/crates/backend/src/typegen.rs index 2f0efdf7..4da7eead 100644 --- a/crates/backend/src/typegen.rs +++ b/crates/backend/src/typegen.rs @@ -92,6 +92,7 @@ fn escape_json(src: &str) -> String { impl ToString for TypeDef { fn to_string(&self) -> String { + let pkg_name = std::env::var("CARGO_PKG_NAME").expect("CARGO_PKG_NAME is not set"); let js_mod = if let Some(js_mod) = &self.js_mod { format!(", \"js_mod\": \"{}\"", js_mod) } else { @@ -103,7 +104,8 @@ impl ToString for TypeDef { "".to_owned() }; format!( - r#"{{"kind": "{}", "name": "{}", "js_doc": "{}", "def": "{}"{}{}}}"#, + r#"{}:{{"kind": "{}", "name": "{}", "js_doc": "{}", "def": "{}"{}{}}}"#, + pkg_name, self.kind, self.name, escape_json(&self.js_doc), diff --git a/crates/macro/src/lib.rs b/crates/macro/src/lib.rs index f9fc6dd9..af5770f5 100644 --- a/crates/macro/src/lib.rs +++ b/crates/macro/src/lib.rs @@ -67,8 +67,8 @@ pub fn napi(attr: RawStream, input: RawStream) -> RawStream { { // logic on first macro expansion #[cfg(feature = "type-def")] - if let Ok(type_def_file) = env::var("TYPE_DEF_TMP_PATH") { - if let Err(_e) = fs::remove_file(type_def_file) { + if let Ok(ref type_def_file) = env::var("TYPE_DEF_TMP_PATH") { + if let Err(_e) = remove_existed_type_def(type_def_file) { #[cfg(debug_assertions)] { println!("Failed to manipulate type def file: {:?}", _e); @@ -432,3 +432,38 @@ fn replace_napi_attr_in_mod( None } } + +#[cfg(all(feature = "type-def", not(feature = "noop")))] +fn remove_existed_type_def(type_def_file: &str) -> std::io::Result<()> { + use std::io::{BufRead, BufReader}; + + let pkg_name = std::env::var("CARGO_PKG_NAME").expect("CARGO_PKG_NAME is not set"); + if let Ok(content) = std::fs::File::open(type_def_file) { + let reader = BufReader::new(content); + let cleaned_content = reader + .lines() + .filter_map(|line| { + if let Ok(line) = line { + if let Some((package_name, _)) = line.split_once(':') { + if pkg_name == package_name { + return None; + } + } + Some(line) + } else { + None + } + }) + .collect::>() + .join("\n"); + let mut content = std::fs::OpenOptions::new() + .read(true) + .write(true) + .truncate(true) + .open(type_def_file)?; + + content.write_all(cleaned_content.as_bytes())?; + content.write_all(b"\n")?; + } + Ok(()) +} diff --git a/examples/napi-shared/Cargo.toml b/examples/napi-shared/Cargo.toml new file mode 100644 index 00000000..a2106ee6 --- /dev/null +++ b/examples/napi-shared/Cargo.toml @@ -0,0 +1,22 @@ +[package] +authors = ["LongYinan "] +edition = "2021" +name = "napi-shared" +publish = false +version = "0.1.0" + +[dependencies] +napi = { path = "../../crates/napi", default-features = false, features = [ + "tokio_fs", + "napi8", + "tokio_rt", + "serde-json", + "async", + "experimental", + "latin1", + "chrono_date", +] } +napi-derive = { path = "../../crates/macro", features = ["type-def"] } + +[build-dependencies] +napi-build = { path = "../../crates/build" } diff --git a/examples/napi-shared/src/lib.rs b/examples/napi-shared/src/lib.rs new file mode 100644 index 00000000..ef4b2251 --- /dev/null +++ b/examples/napi-shared/src/lib.rs @@ -0,0 +1,6 @@ +use napi_derive::napi; + +#[napi(object)] +pub struct Shared { + pub value: u32, +} diff --git a/examples/napi/Cargo.toml b/examples/napi/Cargo.toml index 1a7ee3bc..7ccf12a6 100644 --- a/examples/napi/Cargo.toml +++ b/examples/napi/Cargo.toml @@ -25,6 +25,7 @@ napi = { path = "../../crates/napi", default-features = false, features = [ "chrono_date", ] } napi-derive = { path = "../../crates/macro", features = ["type-def"] } +napi-shared = { path = "../napi-shared" } serde = "1" serde_derive = "1" serde_json = "1" diff --git a/examples/napi/__test__/typegen.spec.ts.md b/examples/napi/__test__/typegen.spec.ts.md index 1a513ae4..dcb71156 100644 --- a/examples/napi/__test__/typegen.spec.ts.md +++ b/examples/napi/__test__/typegen.spec.ts.md @@ -19,6 +19,9 @@ Generated by [AVA](https://avajs.dev). [K: symbol]: T␊ }␊ }␊ + export interface Shared {␊ + value: number␊ + }␊ /** This is a const */␊ export const DEFAULT_COST: number␊ export function getWords(): Array␊ @@ -192,6 +195,7 @@ Generated by [AVA](https://avajs.dev). export function readPackageJson(): PackageJson␊ export function getPackageJsonName(packageJson: PackageJson): string␊ export function testSerdeRoundtrip(data: any): any␊ + export function returnFromSharedCrate(): Shared␊ export function contains(source: string, target: string): boolean␊ export function concatStr(s: string): string␊ export function concatUtf16(s: string): string␊ diff --git a/examples/napi/__test__/typegen.spec.ts.snap b/examples/napi/__test__/typegen.spec.ts.snap index 85740da6..51bfd98d 100644 Binary files a/examples/napi/__test__/typegen.spec.ts.snap and b/examples/napi/__test__/typegen.spec.ts.snap differ diff --git a/examples/napi/__test__/values.spec.ts b/examples/napi/__test__/values.spec.ts index b5bb7847..12cf64e8 100644 --- a/examples/napi/__test__/values.spec.ts +++ b/examples/napi/__test__/values.spec.ts @@ -126,6 +126,7 @@ import { runScript, tsfnReturnPromise, tsfnReturnPromiseTimeout, + returnFromSharedCrate, } from '../' test('export const', (t) => { @@ -650,6 +651,12 @@ test('should be able to run script', async (t) => { t.is(await runScript(`Promise.resolve(1)`), 1) }) +test('should be able to return object from shared crate', (t) => { + t.deepEqual(returnFromSharedCrate(), { + value: 42, + }) +}) + const AbortSignalTest = typeof AbortController !== 'undefined' ? test : test.skip diff --git a/examples/napi/index.d.ts b/examples/napi/index.d.ts index 3dc1da08..1d04fd43 100644 --- a/examples/napi/index.d.ts +++ b/examples/napi/index.d.ts @@ -9,6 +9,9 @@ export class ExternalObject { [K: symbol]: T } } +export interface Shared { + value: number +} /** This is a const */ export const DEFAULT_COST: number export function getWords(): Array @@ -182,6 +185,7 @@ export interface PackageJson { export function readPackageJson(): PackageJson export function getPackageJsonName(packageJson: PackageJson): string export function testSerdeRoundtrip(data: any): any +export function returnFromSharedCrate(): Shared export function contains(source: string, target: string): boolean export function concatStr(s: string): string export function concatUtf16(s: string): string diff --git a/examples/napi/src/lib.rs b/examples/napi/src/lib.rs index e03619e1..9fd31d27 100644 --- a/examples/napi/src/lib.rs +++ b/examples/napi/src/lib.rs @@ -43,6 +43,7 @@ mod object; mod promise; mod reference; mod serde; +mod shared; mod string; mod symbol; mod task; diff --git a/examples/napi/src/shared.rs b/examples/napi/src/shared.rs new file mode 100644 index 00000000..4a892518 --- /dev/null +++ b/examples/napi/src/shared.rs @@ -0,0 +1,7 @@ +use napi_derive::napi; +use napi_shared::Shared; + +#[napi] +pub fn return_from_shared_crate() -> Shared { + Shared { value: 42 } +} diff --git a/examples/napi/src/threadsafe_function.rs b/examples/napi/src/threadsafe_function.rs index d54ee7b6..468c6e51 100644 --- a/examples/napi/src/threadsafe_function.rs +++ b/examples/napi/src/threadsafe_function.rs @@ -140,10 +140,10 @@ pub async fn tsfn_return_promise_timeout(func: ThreadsafeFunction) -> Resul let sleep = time::sleep(Duration::from_nanos(1)); tokio::select! { _ = sleep => { - return Err(Error::new(Status::GenericFailure, "Timeout".to_owned())); + Err(Error::new(Status::GenericFailure, "Timeout".to_owned())) } value = promise => { - return Ok(value? + 2); + Ok(value? + 2) } } } diff --git a/memory-testing/test-util.mjs b/memory-testing/test-util.mjs index e2744bb9..1e442c4e 100644 --- a/memory-testing/test-util.mjs +++ b/memory-testing/test-util.mjs @@ -50,9 +50,9 @@ export async function createSuite(testFile, maxMemoryUsage) { stats.on('data', (d) => { const { memory_stats } = JSON.parse(d.toString('utf8')) if (Date.now() - initialDate > 10000 && !shouldAssertMemoryUsage) { - resolve() initialMemoryUsage = memory_stats.usage shouldAssertMemoryUsage = true + resolve() } if (shouldAssertMemoryUsage && memory_stats?.usage) { const memoryGrowth = memory_stats.usage - initialMemoryUsage