commit
11a96c65fd
30 changed files with 744 additions and 374 deletions
76
.github/workflows/linux.yaml
vendored
Normal file
76
.github/workflows/linux.yaml
vendored
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
name: Linux
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build_and_test:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
node: ['10', '12', '13']
|
||||||
|
version:
|
||||||
|
- stable
|
||||||
|
- nightly
|
||||||
|
|
||||||
|
name: ${{ matrix.version }} - x86_64-unknown-linux-gnu - node@${{ matrix.node }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Setup node
|
||||||
|
uses: actions/setup-node@v1
|
||||||
|
with:
|
||||||
|
node-version: ${{ matrix.node }}
|
||||||
|
|
||||||
|
- name: Install ${{ matrix.version }}
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: ${{ matrix.version }}-x86_64-unknown-linux-gnu
|
||||||
|
profile: minimal
|
||||||
|
override: true
|
||||||
|
|
||||||
|
- name: Generate Cargo.lock
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: generate-lockfile
|
||||||
|
- name: Cache cargo registry
|
||||||
|
uses: actions/cache@v1
|
||||||
|
with:
|
||||||
|
path: ~/.cargo/registry
|
||||||
|
key: ${{ matrix.version }}-x86_64-unknown-linux-gnu-cargo-registry-trimmed-${{ hashFiles('**/Cargo.lock') }}
|
||||||
|
- name: Cache cargo index
|
||||||
|
uses: actions/cache@v1
|
||||||
|
with:
|
||||||
|
path: ~/.cargo/git
|
||||||
|
key: ${{ matrix.version }}-x86_64-unknown-linux-gnu-cargo-index-trimmed-${{ hashFiles('**/Cargo.lock') }}
|
||||||
|
- name: Cache cargo build
|
||||||
|
uses: actions/cache@v1
|
||||||
|
with:
|
||||||
|
path: target
|
||||||
|
key: ${{ matrix.version }}-x86_64-unknown-linux-gnu-cargo-build-trimmed-${{ hashFiles('**/Cargo.lock') }}
|
||||||
|
|
||||||
|
- name: check build
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: check
|
||||||
|
args: --all --bins --examples --tests -vvv
|
||||||
|
|
||||||
|
- name: tests
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
timeout-minutes: 40
|
||||||
|
with:
|
||||||
|
command: test
|
||||||
|
args: -p napi-rs --lib -- --nocapture
|
||||||
|
|
||||||
|
- name: test scripts
|
||||||
|
run: |
|
||||||
|
cd test_module
|
||||||
|
cargo build
|
||||||
|
cp target/debug/libtest_module.so target/debug/libtest_module.node
|
||||||
|
node tests.js
|
||||||
|
|
||||||
|
- name: Clear the cargo caches
|
||||||
|
run: |
|
||||||
|
cargo install cargo-cache --no-default-features --features ci-autoclean
|
||||||
|
cargo-cache
|
76
.github/workflows/macos.yaml
vendored
Normal file
76
.github/workflows/macos.yaml
vendored
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
name: macOS
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build_and_test:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
node: ['10', '12', '13']
|
||||||
|
version:
|
||||||
|
- stable
|
||||||
|
- nightly
|
||||||
|
|
||||||
|
name: ${{ matrix.version }} - x86_64-apple-darwin - node@${{ matrix.node }}
|
||||||
|
runs-on: macos-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Setup node
|
||||||
|
uses: actions/setup-node@v1
|
||||||
|
with:
|
||||||
|
node-version: ${{ matrix.node }}
|
||||||
|
|
||||||
|
- name: Install ${{ matrix.version }}
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: ${{ matrix.version }}-x86_64-apple-darwin
|
||||||
|
profile: minimal
|
||||||
|
override: true
|
||||||
|
|
||||||
|
- name: Generate Cargo.lock
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: generate-lockfile
|
||||||
|
- name: Cache cargo registry
|
||||||
|
uses: actions/cache@v1
|
||||||
|
with:
|
||||||
|
path: ~/.cargo/registry
|
||||||
|
key: ${{ matrix.version }}-x86_64-apple-darwin-cargo-registry-trimmed-${{ hashFiles('**/Cargo.lock') }}
|
||||||
|
- name: Cache cargo index
|
||||||
|
uses: actions/cache@v1
|
||||||
|
with:
|
||||||
|
path: ~/.cargo/git
|
||||||
|
key: ${{ matrix.version }}-x86_64-apple-darwin-cargo-index-trimmed-${{ hashFiles('**/Cargo.lock') }}
|
||||||
|
- name: Cache cargo build
|
||||||
|
uses: actions/cache@v1
|
||||||
|
with:
|
||||||
|
path: target
|
||||||
|
key: ${{ matrix.version }}-x86_64-apple-darwin-cargo-build-trimmed-${{ hashFiles('**/Cargo.lock') }}
|
||||||
|
|
||||||
|
- name: check build
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: check
|
||||||
|
args: --all --bins --examples --tests -vvv
|
||||||
|
|
||||||
|
- name: tests
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
timeout-minutes: 40
|
||||||
|
with:
|
||||||
|
command: test
|
||||||
|
args: -p napi-rs --lib -- --nocapture
|
||||||
|
|
||||||
|
- name: test scripts
|
||||||
|
run: |
|
||||||
|
cd test_module
|
||||||
|
cargo build
|
||||||
|
cp target/debug/libtest_module.dylib target/debug/libtest_module.node
|
||||||
|
node tests.js
|
||||||
|
|
||||||
|
- name: Clear the cargo caches
|
||||||
|
run: |
|
||||||
|
cargo install cargo-cache --no-default-features --features ci-autoclean
|
||||||
|
cargo-cache
|
82
.github/workflows/windows.yaml
vendored
Normal file
82
.github/workflows/windows.yaml
vendored
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
name: Windows
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build_and_test:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
node: ['10', '12', '13']
|
||||||
|
version:
|
||||||
|
- stable
|
||||||
|
- nightly
|
||||||
|
target:
|
||||||
|
- x86_64-pc-windows-msvc
|
||||||
|
|
||||||
|
name: ${{ matrix.version }} - ${{ matrix.target }} - node@${{ matrix.node }}
|
||||||
|
runs-on: windows-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Setup node
|
||||||
|
uses: actions/setup-node@v1
|
||||||
|
with:
|
||||||
|
node-version: ${{ matrix.node }}
|
||||||
|
- name: Install ${{ matrix.version }}
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: ${{ matrix.version }}-${{ matrix.target }}
|
||||||
|
profile: minimal
|
||||||
|
override: true
|
||||||
|
- name: Install llvm
|
||||||
|
run: choco install -y llvm
|
||||||
|
- name: set environment variables
|
||||||
|
uses: allenevans/set-env@v1.0.0
|
||||||
|
with:
|
||||||
|
LIBCLANG_PATH: 'C:\Program Files\LLVM\bin'
|
||||||
|
- name: Generate Cargo.lock
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: generate-lockfile
|
||||||
|
- name: Cache cargo registry
|
||||||
|
uses: actions/cache@v1
|
||||||
|
with:
|
||||||
|
path: ~/.cargo/registry
|
||||||
|
key: ${{ matrix.version }}-${{ matrix.target }}-cargo-registry-trimmed-${{ hashFiles('**/Cargo.lock') }}
|
||||||
|
- name: Cache cargo index
|
||||||
|
uses: actions/cache@v1
|
||||||
|
with:
|
||||||
|
path: ~/.cargo/git
|
||||||
|
key: ${{ matrix.version }}-${{ matrix.target }}-cargo-index-trimmed-${{ hashFiles('**/Cargo.lock') }}
|
||||||
|
- name: Cache cargo build
|
||||||
|
uses: actions/cache@v1
|
||||||
|
with:
|
||||||
|
path: target
|
||||||
|
key: ${{ matrix.version }}-${{ matrix.target }}-cargo-build-trimmed-${{ hashFiles('**/Cargo.lock') }}
|
||||||
|
|
||||||
|
- name: check build
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: check
|
||||||
|
args: -p napi-rs -vvv
|
||||||
|
|
||||||
|
- name: tests
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
timeout-minutes: 40
|
||||||
|
with:
|
||||||
|
command: test
|
||||||
|
args: -p napi-rs --lib -- --nocapture
|
||||||
|
|
||||||
|
- name: test scripts
|
||||||
|
run: |
|
||||||
|
cd test_module
|
||||||
|
cargo build
|
||||||
|
cp target/debug/test_module.dll target/debug/libtest_module.node
|
||||||
|
node tests.js
|
||||||
|
|
||||||
|
- name: Clear the cargo caches
|
||||||
|
run: |
|
||||||
|
cargo install cargo-cache --no-default-features --features ci-autoclean
|
||||||
|
cargo-cache
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -3,4 +3,5 @@ target/
|
||||||
.DS_Store
|
.DS_Store
|
||||||
Cargo.lock
|
Cargo.lock
|
||||||
node_modules
|
node_modules
|
||||||
src/sys/bindings.rs
|
napi/src/sys/bindings.rs
|
||||||
|
*.node
|
||||||
|
|
1
.yarnrc
Normal file
1
.yarnrc
Normal file
|
@ -0,0 +1 @@
|
||||||
|
registry "https://registry.npmjs.org"
|
20
Cargo.toml
20
Cargo.toml
|
@ -1,14 +1,6 @@
|
||||||
[package]
|
[workspace]
|
||||||
name = "napi-rs"
|
members = [
|
||||||
version = "0.1.0"
|
"./build",
|
||||||
authors = ["Nathan Sobo <nathan@github.com>", "Yinan Long <lynweklm@gmail.com>"]
|
"./napi"
|
||||||
license = "MIT"
|
]
|
||||||
|
exclude = ["./test_module"]
|
||||||
[dependencies]
|
|
||||||
futures = "0.3.1"
|
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
bindgen = "0.52"
|
|
||||||
cc = "1.0"
|
|
||||||
glob = "0.3"
|
|
||||||
semver = "0.9"
|
|
||||||
|
|
20
README.md
20
README.md
|
@ -1,7 +1,25 @@
|
||||||
# napi
|
# napi-rs
|
||||||
|
|
||||||
> This project was initialized from [xray](https://github.com/atom/xray)
|
> This project was initialized from [xray](https://github.com/atom/xray)
|
||||||
|
|
||||||
|
# Platform Support
|
||||||
|
|
||||||
|
![](https://github.com/Brooooooklyn/napi-rs/workflows/macOS/badge.svg)
|
||||||
|
![](https://github.com/Brooooooklyn/napi-rs/workflows/Linux/badge.svg)
|
||||||
|
![](https://github.com/Brooooooklyn/napi-rs/workflows/Windows/badge.svg)
|
||||||
|
|
||||||
|
## Operating Systems
|
||||||
|
|
||||||
|
| Linux | macOS | Windows x64 MSVC |
|
||||||
|
| ----- | ----- | ---------------- |
|
||||||
|
| ✓ | ✓ | ✓ |
|
||||||
|
|
||||||
|
## Python
|
||||||
|
|
||||||
|
| Node10 | Node 12 | Node13 |
|
||||||
|
| --------- | --------- | --------- |
|
||||||
|
| ✓ | ✓ | ✓ |
|
||||||
|
|
||||||
A minimal library for building compiled Node add-ons in Rust.
|
A minimal library for building compiled Node add-ons in Rust.
|
||||||
|
|
||||||
This library depends on N-API and requires Node 8.9 or later. It is still pretty raw and has not been tested in a production setting.
|
This library depends on N-API and requires Node 8.9 or later. It is still pretty raw and has not been tested in a production setting.
|
||||||
|
|
87
build.rs
87
build.rs
|
@ -1,87 +0,0 @@
|
||||||
extern crate bindgen;
|
|
||||||
extern crate cc;
|
|
||||||
extern crate glob;
|
|
||||||
extern crate semver;
|
|
||||||
|
|
||||||
use glob::glob;
|
|
||||||
use std::env;
|
|
||||||
use std::path::{Path, PathBuf};
|
|
||||||
use std::process::Command;
|
|
||||||
|
|
||||||
fn find_it<P>(exe_name: P) -> Option<PathBuf>
|
|
||||||
where
|
|
||||||
P: AsRef<Path>,
|
|
||||||
{
|
|
||||||
env::var_os("PATH").and_then(|paths| {
|
|
||||||
env::split_paths(&paths)
|
|
||||||
.filter_map(|dir| {
|
|
||||||
let full_path = dir.join(&exe_name);
|
|
||||||
if full_path.is_file() {
|
|
||||||
Some(full_path)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.next()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let node_include_path = find_it("node")
|
|
||||||
.expect("can not find executable node")
|
|
||||||
.parent()
|
|
||||||
.unwrap()
|
|
||||||
.parent()
|
|
||||||
.unwrap()
|
|
||||||
.join("include/node");
|
|
||||||
let node_version = semver::Version::parse(
|
|
||||||
String::from_utf8(Command::new("node").arg("-v").output().unwrap().stdout)
|
|
||||||
.unwrap()
|
|
||||||
.as_str()
|
|
||||||
.get(1..)
|
|
||||||
.unwrap(),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let node_major_version = node_version.major;
|
|
||||||
|
|
||||||
println!("cargo:rerun-if-env-changed=NODE_INCLUDE_PATH");
|
|
||||||
for entry in glob("./src/sys/**/*.*").unwrap() {
|
|
||||||
println!(
|
|
||||||
"cargo:rerun-if-changed={}",
|
|
||||||
entry.unwrap().to_str().unwrap()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Activate the "node8" or "nodestable" feature for compatibility with
|
|
||||||
// different versions of Node.js/N-API.
|
|
||||||
println!(
|
|
||||||
"cargo:rustc-cfg=node{}",
|
|
||||||
if node_major_version > 8 {
|
|
||||||
"stable"
|
|
||||||
} else if node_major_version == 8 {
|
|
||||||
"8"
|
|
||||||
} else {
|
|
||||||
panic!("node version is too low")
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
bindgen::Builder::default()
|
|
||||||
.header("src/sys/bindings.h")
|
|
||||||
.clang_arg(String::from("-I") + node_include_path.to_str().unwrap())
|
|
||||||
.rustified_enum("(napi_|uv_).+")
|
|
||||||
.whitelist_function("(napi_|uv_|extras_).+")
|
|
||||||
.whitelist_type("(napi_|uv_|extras_).+")
|
|
||||||
.generate()
|
|
||||||
.expect("Unable to generate napi bindings")
|
|
||||||
.write_to_file("src/sys/bindings.rs")
|
|
||||||
.expect("Unable to write napi bindings");
|
|
||||||
|
|
||||||
cc::Build::new()
|
|
||||||
.cpp(true)
|
|
||||||
.include(&node_include_path)
|
|
||||||
.file("src/sys/bindings.cc")
|
|
||||||
.flag("-std=c++0x")
|
|
||||||
.flag("-Wno-unused-parameter")
|
|
||||||
.compile("extras");
|
|
||||||
}
|
|
13
build/Cargo.toml
Normal file
13
build/Cargo.toml
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
[package]
|
||||||
|
name = "napi-build"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["LongYinan <lynweklm@gmail.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cfg-if = "0.1"
|
||||||
|
|
||||||
|
[target.'cfg(windows)'.dependencies]
|
||||||
|
reqwest = { version = "0.10", features = ["native-tls", "blocking"] }
|
1
build/build.rs
Normal file
1
build/build.rs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
fn main() {}
|
67
build/src/lib.rs
Normal file
67
build/src/lib.rs
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
extern crate cfg_if;
|
||||||
|
#[cfg(windows)]
|
||||||
|
extern crate reqwest;
|
||||||
|
|
||||||
|
use cfg_if::cfg_if;
|
||||||
|
|
||||||
|
cfg_if! {
|
||||||
|
if #[cfg(windows)] {
|
||||||
|
use std::env::var;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::copy;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
pub fn setup() {
|
||||||
|
let node_full_version =
|
||||||
|
String::from_utf8(Command::new("node").arg("-v").output().unwrap().stdout).unwrap();
|
||||||
|
let node_exec_path = String::from_utf8(
|
||||||
|
Command::new("node")
|
||||||
|
.arg("-e")
|
||||||
|
.arg("console.log(process.execPath)")
|
||||||
|
.output()
|
||||||
|
.unwrap()
|
||||||
|
.stdout,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let node_exec_path = node_exec_path.trim_end();
|
||||||
|
let mut node_lib_file_dir = PathBuf::from(node_exec_path)
|
||||||
|
.parent()
|
||||||
|
.unwrap()
|
||||||
|
.to_path_buf();
|
||||||
|
let node_lib_dir = PathBuf::from(&node_lib_file_dir);
|
||||||
|
node_lib_file_dir.push(format!("node-{}.lib", node_full_version.trim_end()));
|
||||||
|
if !node_lib_file_dir.exists() {
|
||||||
|
let lib_file_download_url = format!(
|
||||||
|
"https://nodejs.org/dist/{}/win-x64/node.lib",
|
||||||
|
node_full_version
|
||||||
|
);
|
||||||
|
let mut resp =
|
||||||
|
reqwest::blocking::get(&lib_file_download_url).expect("Download node.lib file failed");
|
||||||
|
let mut node_lib_file = File::create(&node_lib_file_dir).unwrap();
|
||||||
|
copy(&mut resp, &mut node_lib_file).expect("Save node.lib file failed");
|
||||||
|
}
|
||||||
|
println!(
|
||||||
|
"cargo:rustc-link-lib={}",
|
||||||
|
&node_lib_file_dir.file_stem().unwrap().to_str().unwrap()
|
||||||
|
);
|
||||||
|
println!("cargo:rustc-link-search={}", node_lib_dir.to_str().unwrap());
|
||||||
|
// Link `win_delay_load_hook.obj` for windows electron
|
||||||
|
let node_runtime_env = "npm_config_runtime";
|
||||||
|
println!("cargo:rerun-if-env-changed={}", node_runtime_env);
|
||||||
|
if var(node_runtime_env).map(|s| s == "electron") == Ok(true) {
|
||||||
|
println!("cargo:rustc-cdylib-link-arg=win_delay_load_hook.obj");
|
||||||
|
println!("cargo:rustc-cdylib-link-arg=delayimp.lib");
|
||||||
|
println!("cargo:rustc-cdylib-link-arg=/DELAYLOAD:node.exe");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if #[cfg(target_os = "macos")] {
|
||||||
|
/// Set up the build environment by setting Cargo configuration variables.
|
||||||
|
pub fn setup() {
|
||||||
|
println!("cargo:rustc-cdylib-link-arg=-undefined");
|
||||||
|
println!("cargo:rustc-cdylib-link-arg=dynamic_lookup");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pub fn setup() { }
|
||||||
|
}
|
||||||
|
}
|
21
napi/Cargo.toml
Normal file
21
napi/Cargo.toml
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
[package]
|
||||||
|
name = "napi-rs"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Nathan Sobo <nathan@github.com>", "Yinan Long <lynweklm@gmail.com>"]
|
||||||
|
license = "MIT"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
futures = { version = "0.3", features = ["default", "thread-pool"] }
|
||||||
|
|
||||||
|
[target.'cfg(windows)'.build-dependencies]
|
||||||
|
flate2 = "1.0"
|
||||||
|
reqwest = { version = "0.10", features = ["native-tls", "blocking"] }
|
||||||
|
tar = "0.4"
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
bindgen = "0.53.1"
|
||||||
|
cc = "1.0"
|
||||||
|
glob = "0.3"
|
||||||
|
napi-build = { path = "../build" }
|
||||||
|
regex = "1.3"
|
||||||
|
semver = "0.9"
|
170
napi/build.rs
Normal file
170
napi/build.rs
Normal file
|
@ -0,0 +1,170 @@
|
||||||
|
extern crate bindgen;
|
||||||
|
extern crate cc;
|
||||||
|
#[cfg(windows)]
|
||||||
|
extern crate flate2;
|
||||||
|
extern crate glob;
|
||||||
|
extern crate napi_build;
|
||||||
|
#[cfg(windows)]
|
||||||
|
extern crate reqwest;
|
||||||
|
extern crate semver;
|
||||||
|
#[cfg(windows)]
|
||||||
|
extern crate tar;
|
||||||
|
|
||||||
|
use glob::glob;
|
||||||
|
|
||||||
|
use std::borrow::Cow;
|
||||||
|
use std::env;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
// https://stackoverflow.com/questions/37498864/finding-executable-in-path-with-rust
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "windows"))]
|
||||||
|
fn enhance_exe_name(exe_name: &Path) -> Cow<Path> {
|
||||||
|
exe_name.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
fn enhance_exe_name(exe_name: &Path) -> Cow<Path> {
|
||||||
|
use std::ffi::OsStr;
|
||||||
|
use std::os::windows::ffi::OsStrExt;
|
||||||
|
|
||||||
|
let raw_input: Vec<_> = exe_name.as_os_str().encode_wide().collect();
|
||||||
|
let raw_extension: Vec<_> = OsStr::new(".exe").encode_wide().collect();
|
||||||
|
|
||||||
|
if raw_input.ends_with(&raw_extension) {
|
||||||
|
exe_name.into()
|
||||||
|
} else {
|
||||||
|
let mut with_exe = exe_name.as_os_str().to_owned();
|
||||||
|
with_exe.push(".exe");
|
||||||
|
PathBuf::from(with_exe).into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_it<P>(exe_name: P) -> Option<PathBuf>
|
||||||
|
where
|
||||||
|
P: AsRef<Path>,
|
||||||
|
{
|
||||||
|
let exe_name = enhance_exe_name(exe_name.as_ref());
|
||||||
|
env::var_os("PATH").and_then(|paths| {
|
||||||
|
env::split_paths(&paths)
|
||||||
|
.filter_map(|dir| {
|
||||||
|
let full_path = dir.join(&exe_name);
|
||||||
|
if full_path.is_file() {
|
||||||
|
Some(full_path)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.next()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
napi_build::setup();
|
||||||
|
let node_full_version =
|
||||||
|
String::from_utf8(Command::new("node").arg("-v").output().unwrap().stdout).unwrap();
|
||||||
|
let node_version = semver::Version::parse(node_full_version.as_str().get(1..).unwrap()).unwrap();
|
||||||
|
|
||||||
|
let node_major_version = node_version.major;
|
||||||
|
|
||||||
|
println!("cargo:rerun-if-env-changed=NODE_INCLUDE_PATH");
|
||||||
|
for entry in glob("./src/sys/**/*.*").unwrap() {
|
||||||
|
println!(
|
||||||
|
"cargo:rerun-if-changed={}",
|
||||||
|
entry.unwrap().to_str().unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
env::set_var("CARGO_RUSTC_FLAGS", "-Clink-args=-export_dynamic");
|
||||||
|
|
||||||
|
if node_major_version < 10 {
|
||||||
|
panic!("node version is too low")
|
||||||
|
}
|
||||||
|
|
||||||
|
let node_include_path = find_node_include_path(node_full_version.trim_end());
|
||||||
|
|
||||||
|
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
|
||||||
|
|
||||||
|
let mut sys_bindigs_path = PathBuf::from("src");
|
||||||
|
sys_bindigs_path.push("sys");
|
||||||
|
sys_bindigs_path.push("bindings.h");
|
||||||
|
|
||||||
|
bindgen::Builder::default()
|
||||||
|
.header(sys_bindigs_path.to_str().unwrap().to_owned())
|
||||||
|
.clang_arg(String::from("-I") + node_include_path.to_str().unwrap())
|
||||||
|
.rustified_enum("(napi_|uv_).+")
|
||||||
|
.whitelist_function("(napi_|uv_|extras_).+")
|
||||||
|
.whitelist_type("(napi_|uv_|extras_).+")
|
||||||
|
.generate()
|
||||||
|
.expect("Unable to generate napi bindings")
|
||||||
|
.write_to_file(out_path.join("bindings.rs"))
|
||||||
|
.expect("Unable to write napi bindings");
|
||||||
|
|
||||||
|
let mut bindings_path = PathBuf::from("src");
|
||||||
|
bindings_path.push("sys");
|
||||||
|
bindings_path.push("bindings.cc");
|
||||||
|
|
||||||
|
let mut cc_builder = cc::Build::new();
|
||||||
|
|
||||||
|
cc_builder
|
||||||
|
.cpp(true)
|
||||||
|
.include(&node_include_path)
|
||||||
|
.file(bindings_path);
|
||||||
|
if !cfg!(windows) {
|
||||||
|
cc_builder.flag("-Wno-unused-parameter");
|
||||||
|
};
|
||||||
|
|
||||||
|
if cfg!(target_os = "macos") {
|
||||||
|
cc_builder.flag("-std=c++0x");
|
||||||
|
} else if cfg!(linux) || cfg!(target_env = "gnu") {
|
||||||
|
cc_builder.flag("-std=c++14");
|
||||||
|
}
|
||||||
|
|
||||||
|
cc_builder
|
||||||
|
.cargo_metadata(true)
|
||||||
|
.out_dir(&out_path)
|
||||||
|
.compile("napi-bindings");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
fn find_node_include_path(node_full_version: &str) -> PathBuf {
|
||||||
|
let mut node_exec_path = PathBuf::from(
|
||||||
|
find_it("node")
|
||||||
|
.expect("can not find executable node")
|
||||||
|
.parent()
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
node_exec_path.push(format!("node-headers-{}.tar.gz", node_full_version));
|
||||||
|
let mut header_dist_path = PathBuf::from(&PathBuf::from(&node_exec_path).parent().unwrap());
|
||||||
|
let unpack_path = PathBuf::from(&header_dist_path);
|
||||||
|
header_dist_path.push(format!("node-{}", node_full_version));
|
||||||
|
header_dist_path.push("include");
|
||||||
|
header_dist_path.push("node");
|
||||||
|
if !header_dist_path.exists() {
|
||||||
|
let header_file_download_url = String::from_utf8(
|
||||||
|
Command::new("node")
|
||||||
|
.args(vec!["-e", "console.log(process.release.headersUrl)"])
|
||||||
|
.output()
|
||||||
|
.unwrap()
|
||||||
|
.stdout,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let resp = reqwest::blocking::get(&header_file_download_url).expect("request failed");
|
||||||
|
tar::Archive::new(flate2::read::GzDecoder::new(resp))
|
||||||
|
.unpack(&unpack_path)
|
||||||
|
.expect("Unpack headers file failed");
|
||||||
|
};
|
||||||
|
header_dist_path
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "windows"))]
|
||||||
|
fn find_node_include_path(_node_full_version: &str) -> PathBuf {
|
||||||
|
let node_exec_path = find_it("node").expect("can not find executable node");
|
||||||
|
node_exec_path
|
||||||
|
.parent()
|
||||||
|
.unwrap()
|
||||||
|
.parent()
|
||||||
|
.unwrap()
|
||||||
|
.join("include/node")
|
||||||
|
}
|
106
napi/src/executor.rs
Normal file
106
napi/src/executor.rs
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
use super::sys;
|
||||||
|
use futures::task::Poll;
|
||||||
|
use std::future::Future;
|
||||||
|
use std::mem;
|
||||||
|
use std::os::raw::c_void;
|
||||||
|
use std::pin::Pin;
|
||||||
|
use std::task::{Context, RawWaker, RawWakerVTable, Waker};
|
||||||
|
|
||||||
|
pub struct LibuvExecutor {
|
||||||
|
event_loop: *mut sys::uv_loop_s,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
struct LibuvWaker(*mut sys::uv_async_t);
|
||||||
|
|
||||||
|
unsafe impl Send for LibuvWaker {}
|
||||||
|
|
||||||
|
unsafe impl Sync for LibuvWaker {}
|
||||||
|
|
||||||
|
const UV_ASYNC_V_TABLE: RawWakerVTable = RawWakerVTable::new(
|
||||||
|
clone_executor,
|
||||||
|
wake_uv_async,
|
||||||
|
wake_uv_async_by_ref,
|
||||||
|
drop_uv_async,
|
||||||
|
);
|
||||||
|
|
||||||
|
unsafe fn clone_executor(uv_async_t: *const ()) -> RawWaker {
|
||||||
|
RawWaker::new(uv_async_t, &UV_ASYNC_V_TABLE)
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn wake_uv_async(uv_async_t: *const ()) {
|
||||||
|
let status = sys::uv_async_send(uv_async_t as *mut sys::uv_async_t);
|
||||||
|
assert!(status == 0, "wake_uv_async failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn wake_uv_async_by_ref(uv_async_t: *const ()) {
|
||||||
|
let status = sys::uv_async_send(uv_async_t as *mut sys::uv_async_t);
|
||||||
|
assert!(status == 0, "wake_uv_async_by_ref failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn drop_uv_async(uv_async_t_ptr: *const ()) {
|
||||||
|
sys::uv_unref(uv_async_t_ptr as *mut sys::uv_handle_t);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Task<'a> {
|
||||||
|
future: Pin<Box<dyn Future<Output = ()>>>,
|
||||||
|
context: Context<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LibuvExecutor {
|
||||||
|
pub fn new(event_loop: *mut sys::uv_loop_s) -> Self {
|
||||||
|
Self { event_loop }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn execute<F: 'static + Future<Output = ()>>(&self, future: F) {
|
||||||
|
let uninit = mem::MaybeUninit::<sys::uv_async_t>::uninit();
|
||||||
|
let uv_async_t: Box<sys::uv_async_t> = unsafe { Box::new(uninit.assume_init()) };
|
||||||
|
let uv_async_t_ref = Box::leak(uv_async_t);
|
||||||
|
unsafe {
|
||||||
|
let status = sys::uv_async_init(self.event_loop, uv_async_t_ref, Some(poll_future));
|
||||||
|
assert!(status == 0, "Non-zero status returned from uv_async_init");
|
||||||
|
};
|
||||||
|
unsafe {
|
||||||
|
let waker = Waker::from_raw(RawWaker::new(
|
||||||
|
uv_async_t_ref as *mut _ as *const (),
|
||||||
|
&UV_ASYNC_V_TABLE,
|
||||||
|
));
|
||||||
|
let context = Context::from_waker(&waker);
|
||||||
|
let task = Box::leak(Box::new(Task {
|
||||||
|
future: Box::pin(future),
|
||||||
|
context,
|
||||||
|
}));
|
||||||
|
sys::uv_handle_set_data(
|
||||||
|
uv_async_t_ref as *mut _ as *mut sys::uv_handle_t,
|
||||||
|
task as *mut _ as *mut c_void,
|
||||||
|
);
|
||||||
|
task.poll_future();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Task<'a> {
|
||||||
|
fn poll_future(&mut self) -> bool {
|
||||||
|
match self.future.as_mut().poll(&mut self.context) {
|
||||||
|
Poll::Ready(_) => true,
|
||||||
|
Poll::Pending => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "C" fn poll_future(handle: *mut sys::uv_async_t) {
|
||||||
|
let data_ptr = sys::uv_handle_get_data(handle as *mut sys::uv_handle_t) as *mut Task;
|
||||||
|
let mut task = Box::from_raw(data_ptr);
|
||||||
|
if !task.as_mut().poll_future() {
|
||||||
|
Box::leak(task);
|
||||||
|
} else {
|
||||||
|
sys::uv_close(
|
||||||
|
handle as *mut sys::uv_handle_t,
|
||||||
|
Some(drop_handle_after_close),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "C" fn drop_handle_after_close(handle: *mut sys::uv_handle_t) {
|
||||||
|
Box::from_raw(handle);
|
||||||
|
}
|
|
@ -54,7 +54,7 @@ pub struct Function;
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct Buffer {
|
pub struct Buffer {
|
||||||
data: *const u8,
|
data: *const u8,
|
||||||
size: usize,
|
size: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
@ -160,14 +160,15 @@ macro_rules! callback {
|
||||||
) -> sys::napi_value {
|
) -> sys::napi_value {
|
||||||
const MAX_ARGC: usize = 8;
|
const MAX_ARGC: usize = 8;
|
||||||
let mut argc = MAX_ARGC;
|
let mut argc = MAX_ARGC;
|
||||||
let mut raw_args: [$crate::sys::napi_value; MAX_ARGC] = unsafe { mem::uninitialized() };
|
let mut raw_args =
|
||||||
|
unsafe { mem::MaybeUninit::<[$crate::sys::napi_value; MAX_ARGC]>::uninit().assume_init() };
|
||||||
let mut raw_this = ptr::null_mut();
|
let mut raw_this = ptr::null_mut();
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let status = sys::napi_get_cb_info(
|
let status = sys::napi_get_cb_info(
|
||||||
raw_env,
|
raw_env,
|
||||||
cb_info,
|
cb_info,
|
||||||
&mut argc,
|
&mut argc as *mut usize as *mut u64,
|
||||||
&mut raw_args[0],
|
&mut raw_args[0],
|
||||||
&mut raw_this,
|
&mut raw_this,
|
||||||
ptr::null_mut(),
|
ptr::null_mut(),
|
||||||
|
@ -177,8 +178,8 @@ macro_rules! callback {
|
||||||
|
|
||||||
let env = Env::from_raw(raw_env);
|
let env = Env::from_raw(raw_env);
|
||||||
let this = Value::from_raw(&env, raw_this);
|
let this = Value::from_raw(&env, raw_this);
|
||||||
let mut args: [Value<Any>; 8] = unsafe { mem::uninitialized() };
|
let mut args = unsafe { mem::MaybeUninit::<[Value<Any>; 8]>::uninit().assume_init() };
|
||||||
for (i, raw_arg) in raw_args.into_iter().enumerate() {
|
for (i, raw_arg) in raw_args.iter().enumerate() {
|
||||||
args[i] = Value::from_raw(&env, *raw_arg)
|
args[i] = Value::from_raw(&env, *raw_arg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,7 +248,12 @@ impl Env {
|
||||||
pub fn create_string<'a, 'b>(&'a self, s: &'b str) -> Value<'a, String> {
|
pub fn create_string<'a, 'b>(&'a self, s: &'b str) -> Value<'a, String> {
|
||||||
let mut raw_value = ptr::null_mut();
|
let mut raw_value = ptr::null_mut();
|
||||||
let status = unsafe {
|
let status = unsafe {
|
||||||
sys::napi_create_string_utf8(self.0, s.as_ptr() as *const c_char, s.len(), &mut raw_value)
|
sys::napi_create_string_utf8(
|
||||||
|
self.0,
|
||||||
|
s.as_ptr() as *const c_char,
|
||||||
|
s.len() as u64,
|
||||||
|
&mut raw_value,
|
||||||
|
)
|
||||||
};
|
};
|
||||||
debug_assert!(Status::from(status) == Status::Ok);
|
debug_assert!(Status::from(status) == Status::Ok);
|
||||||
Value::from_raw_value(self, raw_value, String)
|
Value::from_raw_value(self, raw_value, String)
|
||||||
|
@ -255,8 +261,9 @@ impl Env {
|
||||||
|
|
||||||
pub fn create_string_utf16<'a, 'b>(&'a self, chars: &[u16]) -> Value<'a, String> {
|
pub fn create_string_utf16<'a, 'b>(&'a self, chars: &[u16]) -> Value<'a, String> {
|
||||||
let mut raw_value = ptr::null_mut();
|
let mut raw_value = ptr::null_mut();
|
||||||
let status =
|
let status = unsafe {
|
||||||
unsafe { sys::napi_create_string_utf16(self.0, chars.as_ptr(), chars.len(), &mut raw_value) };
|
sys::napi_create_string_utf16(self.0, chars.as_ptr(), chars.len() as u64, &mut raw_value)
|
||||||
|
};
|
||||||
debug_assert!(Status::from(status) == Status::Ok);
|
debug_assert!(Status::from(status) == Status::Ok);
|
||||||
Value::from_raw_value(self, raw_value, String)
|
Value::from_raw_value(self, raw_value, String)
|
||||||
}
|
}
|
||||||
|
@ -270,12 +277,13 @@ impl Env {
|
||||||
|
|
||||||
pub fn create_array_with_length(&self, length: usize) -> Value<Object> {
|
pub fn create_array_with_length(&self, length: usize) -> Value<Object> {
|
||||||
let mut raw_value = ptr::null_mut();
|
let mut raw_value = ptr::null_mut();
|
||||||
let status = unsafe { sys::napi_create_array_with_length(self.0, length, &mut raw_value) };
|
let status =
|
||||||
|
unsafe { sys::napi_create_array_with_length(self.0, length as u64, &mut raw_value) };
|
||||||
debug_assert!(Status::from(status) == Status::Ok);
|
debug_assert!(Status::from(status) == Status::Ok);
|
||||||
Value::from_raw_value(self, raw_value, Object)
|
Value::from_raw_value(self, raw_value, Object)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_buffer(&self, length: usize) -> Value<Buffer> {
|
pub fn create_buffer(&self, length: u64) -> Value<Buffer> {
|
||||||
let mut raw_value = ptr::null_mut();
|
let mut raw_value = ptr::null_mut();
|
||||||
let mut data = ptr::null_mut();
|
let mut data = ptr::null_mut();
|
||||||
let status = unsafe { sys::napi_create_buffer(self.0, length, &mut data, &mut raw_value) };
|
let status = unsafe { sys::napi_create_buffer(self.0, length, &mut data, &mut raw_value) };
|
||||||
|
@ -290,7 +298,7 @@ impl Env {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_buffer_with_data(&self, data_ptr: *const u8, length: usize) -> Value<Buffer> {
|
pub fn create_buffer_with_data(&self, data_ptr: *const u8, length: u64) -> Value<Buffer> {
|
||||||
let mut raw_value = ptr::null_mut();
|
let mut raw_value = ptr::null_mut();
|
||||||
let mut data_raw_ptr = data_ptr as *mut c_void;
|
let mut data_raw_ptr = data_ptr as *mut c_void;
|
||||||
let status =
|
let status =
|
||||||
|
@ -316,7 +324,7 @@ impl Env {
|
||||||
sys::napi_create_function(
|
sys::napi_create_function(
|
||||||
self.0,
|
self.0,
|
||||||
name.as_ptr() as *const c_char,
|
name.as_ptr() as *const c_char,
|
||||||
name.len(),
|
name.len() as u64,
|
||||||
Some(callback),
|
Some(callback),
|
||||||
callback as *mut c_void,
|
callback as *mut c_void,
|
||||||
&mut raw_result,
|
&mut raw_result,
|
||||||
|
@ -368,10 +376,10 @@ impl Env {
|
||||||
sys::napi_define_class(
|
sys::napi_define_class(
|
||||||
self.0,
|
self.0,
|
||||||
name.as_ptr() as *const c_char,
|
name.as_ptr() as *const c_char,
|
||||||
name.len(),
|
name.len() as u64,
|
||||||
Some(constructor_cb),
|
Some(constructor_cb),
|
||||||
ptr::null_mut(),
|
ptr::null_mut(),
|
||||||
raw_properties.len(),
|
raw_properties.len() as u64,
|
||||||
raw_properties.as_ptr(),
|
raw_properties.as_ptr(),
|
||||||
&mut raw_result,
|
&mut raw_result,
|
||||||
)
|
)
|
||||||
|
@ -559,7 +567,7 @@ impl ValueType for Object {
|
||||||
impl ValueType for Buffer {
|
impl ValueType for Buffer {
|
||||||
fn from_raw(env: sys::napi_env, raw: sys::napi_value) -> Self {
|
fn from_raw(env: sys::napi_env, raw: sys::napi_value) -> Self {
|
||||||
let mut data = ptr::null_mut();
|
let mut data = ptr::null_mut();
|
||||||
let mut size: usize = 0;
|
let mut size: u64 = 0;
|
||||||
let status = unsafe { sys::napi_get_buffer_info(env, raw, &mut data, &mut size) };
|
let status = unsafe { sys::napi_get_buffer_info(env, raw, &mut data, &mut size) };
|
||||||
debug_assert!(Status::from(status) == Status::Ok);
|
debug_assert!(Status::from(status) == Status::Ok);
|
||||||
Buffer {
|
Buffer {
|
||||||
|
@ -664,10 +672,10 @@ impl<'env, T: ValueType> Value<'env, T> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn get_raw_type(env: sys::napi_env, raw_value: sys::napi_value) -> sys::napi_valuetype {
|
fn get_raw_type(env: sys::napi_env, raw_value: sys::napi_value) -> sys::napi_valuetype {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut value_type: sys::napi_valuetype = mem::uninitialized();
|
let value_type = ptr::null_mut();
|
||||||
let status = sys::napi_typeof(env, raw_value, &mut value_type);
|
let status = sys::napi_typeof(env, raw_value, value_type);
|
||||||
debug_assert!(Status::from(status) == Status::Ok);
|
debug_assert!(Status::from(status) == Status::Ok);
|
||||||
value_type
|
*value_type
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -692,7 +700,7 @@ impl<'env> Deref for Value<'env, String> {
|
||||||
type Target = [u8];
|
type Target = [u8];
|
||||||
|
|
||||||
fn deref(&self) -> &[u8] {
|
fn deref(&self) -> &[u8] {
|
||||||
let mut written_char_count: usize = 0;
|
let mut written_char_count: u64 = 0;
|
||||||
let len = self.len() + 1;
|
let len = self.len() + 1;
|
||||||
let mut result = Vec::with_capacity(len);
|
let mut result = Vec::with_capacity(len);
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -700,14 +708,14 @@ impl<'env> Deref for Value<'env, String> {
|
||||||
self.env.0,
|
self.env.0,
|
||||||
self.raw_value,
|
self.raw_value,
|
||||||
result.as_mut_ptr(),
|
result.as_mut_ptr(),
|
||||||
len,
|
len as u64,
|
||||||
&mut written_char_count as *mut usize,
|
&mut written_char_count,
|
||||||
);
|
);
|
||||||
|
|
||||||
debug_assert!(Status::from(status) == Status::Ok);
|
debug_assert!(Status::from(status) == Status::Ok);
|
||||||
let ptr = result.as_ptr();
|
let ptr = result.as_ptr();
|
||||||
mem::forget(result);
|
mem::forget(result);
|
||||||
slice::from_raw_parts(ptr as *const u8, written_char_count)
|
slice::from_raw_parts(ptr as *const u8, written_char_count as usize)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -722,11 +730,11 @@ impl<'env> Into<Vec<u16>> for Value<'env, String> {
|
||||||
self.env.0,
|
self.env.0,
|
||||||
self.raw_value,
|
self.raw_value,
|
||||||
result.as_mut_ptr(),
|
result.as_mut_ptr(),
|
||||||
result.capacity(),
|
result.capacity() as u64,
|
||||||
&mut written_char_count,
|
&mut written_char_count,
|
||||||
);
|
);
|
||||||
debug_assert!(Status::from(status) == Status::Ok);
|
debug_assert!(Status::from(status) == Status::Ok);
|
||||||
result.set_len(written_char_count);
|
result.set_len(written_char_count as usize);
|
||||||
}
|
}
|
||||||
|
|
||||||
result
|
result
|
||||||
|
@ -871,7 +879,7 @@ impl<'env> Deref for Value<'env, Buffer> {
|
||||||
type Target = [u8];
|
type Target = [u8];
|
||||||
|
|
||||||
fn deref(&self) -> &[u8] {
|
fn deref(&self) -> &[u8] {
|
||||||
unsafe { slice::from_raw_parts(self.value.data, self.value.size) }
|
unsafe { slice::from_raw_parts(self.value.data, self.value.size as usize) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -882,7 +890,7 @@ impl<'env> DerefMut for Value<'env, Buffer> {
|
||||||
let status =
|
let status =
|
||||||
unsafe { sys::napi_get_buffer_info(self.env.0, self.raw_value, &mut data, &mut size) };
|
unsafe { sys::napi_get_buffer_info(self.env.0, self.raw_value, &mut data, &mut size) };
|
||||||
debug_assert!(Status::from(status) == Status::Ok);
|
debug_assert!(Status::from(status) == Status::Ok);
|
||||||
unsafe { slice::from_raw_parts_mut(data as *mut _, size) }
|
unsafe { slice::from_raw_parts_mut(data as *mut _, size as usize) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -895,7 +903,7 @@ impl<'env> Value<'env, Function> {
|
||||||
let raw_this = this
|
let raw_this = this
|
||||||
.map(|v| v.into_raw())
|
.map(|v| v.into_raw())
|
||||||
.unwrap_or_else(|| self.env.get_undefined().into_raw());
|
.unwrap_or_else(|| self.env.get_undefined().into_raw());
|
||||||
let mut raw_args: [sys::napi_value; 8] = unsafe { mem::uninitialized() };
|
let mut raw_args = unsafe { mem::MaybeUninit::<[sys::napi_value; 8]>::uninit().assume_init() };
|
||||||
for (i, arg) in args.into_iter().enumerate() {
|
for (i, arg) in args.into_iter().enumerate() {
|
||||||
raw_args[i] = arg.raw_value;
|
raw_args[i] = arg.raw_value;
|
||||||
}
|
}
|
||||||
|
@ -905,7 +913,7 @@ impl<'env> Value<'env, Function> {
|
||||||
self.env.0,
|
self.env.0,
|
||||||
raw_this,
|
raw_this,
|
||||||
self.raw_value,
|
self.raw_value,
|
||||||
args.len(),
|
args.len() as u64,
|
||||||
&raw_args[0],
|
&raw_args[0],
|
||||||
&mut return_value,
|
&mut return_value,
|
||||||
)
|
)
|
|
@ -10,7 +10,9 @@ static v8::Local<v8::Value> V8LocalValueFromJsValue(napi_value v)
|
||||||
return local;
|
return local;
|
||||||
}
|
}
|
||||||
|
|
||||||
void extras_open_callback_scope(napi_async_context napi_async_context,
|
EXTERN_C_START
|
||||||
|
|
||||||
|
NAPI_EXTERN void extras_open_callback_scope(napi_async_context napi_async_context,
|
||||||
napi_value napi_resource_object,
|
napi_value napi_resource_object,
|
||||||
extras_callback_scope *result)
|
extras_callback_scope *result)
|
||||||
{
|
{
|
||||||
|
@ -21,7 +23,9 @@ void extras_open_callback_scope(napi_async_context napi_async_context,
|
||||||
*result = reinterpret_cast<extras_callback_scope>(new node::CallbackScope(isolate, resource_object, *node_async_context));
|
*result = reinterpret_cast<extras_callback_scope>(new node::CallbackScope(isolate, resource_object, *node_async_context));
|
||||||
}
|
}
|
||||||
|
|
||||||
void extras_close_callback_scope(extras_callback_scope callback_scope)
|
NAPI_EXTERN void extras_close_callback_scope(extras_callback_scope callback_scope)
|
||||||
{
|
{
|
||||||
delete reinterpret_cast<node::CallbackScope *>(callback_scope);
|
delete reinterpret_cast<node::CallbackScope *>(callback_scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EXTERN_C_END
|
|
@ -3,14 +3,7 @@
|
||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
include!("bindings.rs");
|
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
|
||||||
|
|
||||||
#[cfg(node8)]
|
|
||||||
mod node8;
|
|
||||||
#[cfg(node8)]
|
|
||||||
pub use self::node8::Status;
|
|
||||||
|
|
||||||
#[cfg(nodestable)]
|
|
||||||
mod stable;
|
mod stable;
|
||||||
#[cfg(nodestable)]
|
|
||||||
pub use self::stable::Status;
|
pub use self::stable::Status;
|
10
package.json
10
package.json
|
@ -5,9 +5,6 @@
|
||||||
"bin": {
|
"bin": {
|
||||||
"napi": "scripts/napi.js"
|
"napi": "scripts/napi.js"
|
||||||
},
|
},
|
||||||
"scripts": {
|
|
||||||
"check": "scripts/napi.js check"
|
|
||||||
},
|
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git@github.com:Brooooooklyn/napi-rs.git"
|
"url": "git@github.com:Brooooooklyn/napi-rs.git"
|
||||||
|
@ -35,13 +32,10 @@
|
||||||
"arrowParens": "always"
|
"arrowParens": "always"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"scripts/napi.js",
|
"scripts/napi.js"
|
||||||
"src/**",
|
|
||||||
"build.rs",
|
|
||||||
"Cargo.toml",
|
|
||||||
"Cargo.lock"
|
|
||||||
],
|
],
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/node": "^13.7.1",
|
||||||
"prettier": "^1.12.1"
|
"prettier": "^1.12.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
15
scripts/napi.js
Executable file → Normal file
15
scripts/napi.js
Executable file → Normal file
|
@ -6,10 +6,9 @@ const path = require('path')
|
||||||
const os = require('os')
|
const os = require('os')
|
||||||
const parsedNodeVersion = process.versions.node.match(/^(\d+)\.(\d+)\.(\d+)$/)
|
const parsedNodeVersion = process.versions.node.match(/^(\d+)\.(\d+)\.(\d+)$/)
|
||||||
const nodeMajorVersion = parseInt(parsedNodeVersion[1])
|
const nodeMajorVersion = parseInt(parsedNodeVersion[1])
|
||||||
const nodeMinorVersion = parseInt(parsedNodeVersion[2])
|
|
||||||
|
|
||||||
if (nodeMajorVersion < 8 || (nodeMajorVersion === 8 && nodeMinorVersion < 9)) {
|
if (nodeMajorVersion < 10) {
|
||||||
console.error('This build script should be run on Node 8.9 or greater')
|
console.error('This build script should be run on Node 10 or greater')
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,17 +18,7 @@ const argv = parseArgs(process.argv.slice(2), {
|
||||||
|
|
||||||
const subcommand = argv._[0] || 'build'
|
const subcommand = argv._[0] || 'build'
|
||||||
|
|
||||||
const nodeIncludePath = path.join(
|
|
||||||
process.argv[0],
|
|
||||||
'..',
|
|
||||||
'..',
|
|
||||||
'include',
|
|
||||||
'node',
|
|
||||||
)
|
|
||||||
|
|
||||||
const moduleName = path.basename(process.cwd()).replace(/-/g, '_')
|
const moduleName = path.basename(process.cwd()).replace(/-/g, '_')
|
||||||
process.env.NODE_INCLUDE_PATH = nodeIncludePath
|
|
||||||
process.env.NODE_MAJOR_VERSION = nodeMajorVersion > 8 ? 'stable' : 8
|
|
||||||
|
|
||||||
const platform = os.platform()
|
const platform = os.platform()
|
||||||
let libExt, platformArgs
|
let libExt, platformArgs
|
||||||
|
|
125
src/executor.rs
125
src/executor.rs
|
@ -1,125 +0,0 @@
|
||||||
use super::sys;
|
|
||||||
use futures::executor::{self, Notify, Spawn};
|
|
||||||
use futures::future::{ExecuteError, Executor};
|
|
||||||
use futures::{Async, Future};
|
|
||||||
use std::mem;
|
|
||||||
use std::os::raw::c_void;
|
|
||||||
use std::ptr;
|
|
||||||
use std::sync::{Arc, RwLock};
|
|
||||||
|
|
||||||
pub struct LibuvExecutor {
|
|
||||||
event_loop: *mut sys::uv_loop_t,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Task<T: 'static + Future> {
|
|
||||||
spawn: Spawn<T>,
|
|
||||||
notify_handle: Arc<TaskNotifyHandle>,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct TaskNotifyHandle(RwLock<Option<UvAsyncHandle>>);
|
|
||||||
|
|
||||||
struct UvAsyncHandle(Box<sys::uv_async_t>);
|
|
||||||
|
|
||||||
impl LibuvExecutor {
|
|
||||||
pub fn new(event_loop: *mut sys::uv_loop_t) -> Self {
|
|
||||||
Self { event_loop }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<F> Executor<F> for LibuvExecutor
|
|
||||||
where
|
|
||||||
F: 'static + Future<Item = (), Error = ()>,
|
|
||||||
{
|
|
||||||
fn execute(&self, future: F) -> Result<(), ExecuteError<F>> {
|
|
||||||
let spawn = executor::spawn(future);
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
let mut task = Box::new(Task {
|
|
||||||
spawn,
|
|
||||||
notify_handle: mem::uninitialized(),
|
|
||||||
});
|
|
||||||
|
|
||||||
ptr::write(
|
|
||||||
&mut task.notify_handle,
|
|
||||||
Arc::new(TaskNotifyHandle::new(
|
|
||||||
self.event_loop,
|
|
||||||
Some(poll_future_on_main_thread::<F>),
|
|
||||||
mem::transmute_copy(&task),
|
|
||||||
)),
|
|
||||||
);
|
|
||||||
|
|
||||||
if !task.poll_future() {
|
|
||||||
mem::forget(task)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: 'static + Future> Task<T> {
|
|
||||||
fn poll_future(&mut self) -> bool {
|
|
||||||
match self.spawn.poll_future_notify(&self.notify_handle, 0) {
|
|
||||||
Ok(Async::Ready(_)) => {
|
|
||||||
let handle = self.notify_handle.0.write().unwrap().take().unwrap();
|
|
||||||
handle.close();
|
|
||||||
true
|
|
||||||
}
|
|
||||||
Ok(Async::NotReady) => false,
|
|
||||||
Err(_) => panic!("Future yielded an error"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TaskNotifyHandle {
|
|
||||||
fn new(event_loop: *mut sys::uv_loop_t, callback: sys::uv_async_cb, data: *mut c_void) -> Self {
|
|
||||||
TaskNotifyHandle(RwLock::new(Some(UvAsyncHandle::new(
|
|
||||||
event_loop, callback, data,
|
|
||||||
))))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Notify for TaskNotifyHandle {
|
|
||||||
fn notify(&self, _id: usize) {
|
|
||||||
if let Some(ref uv_handle) = *self.0.read().unwrap() {
|
|
||||||
unsafe {
|
|
||||||
sys::uv_async_send(mem::transmute_copy(&uv_handle.0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UvAsyncHandle {
|
|
||||||
fn new(event_loop: *mut sys::uv_loop_t, callback: sys::uv_async_cb, data: *mut c_void) -> Self {
|
|
||||||
unsafe {
|
|
||||||
let mut handle = UvAsyncHandle(Box::new(mem::uninitialized()));
|
|
||||||
let status = sys::uv_async_init(event_loop, mem::transmute_copy(&handle.0), callback);
|
|
||||||
assert!(status == 0, "Non-zero status returned from uv_async_init");
|
|
||||||
handle.0.data = data;
|
|
||||||
handle
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn close(self) {
|
|
||||||
unsafe {
|
|
||||||
sys::uv_close(mem::transmute_copy(&self.0), Some(drop_handle_after_close));
|
|
||||||
mem::forget(self.0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Send for UvAsyncHandle {}
|
|
||||||
unsafe impl Sync for UvAsyncHandle {}
|
|
||||||
|
|
||||||
extern "C" fn drop_handle_after_close(handle: *mut sys::uv_handle_t) {
|
|
||||||
unsafe {
|
|
||||||
Box::from_raw(handle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" fn poll_future_on_main_thread<T: 'static + Future>(handle: *mut sys::uv_async_t) {
|
|
||||||
let mut task: Box<Task<T>> = unsafe { Box::from_raw((*handle).data as *mut Task<T>) };
|
|
||||||
if !task.poll_future() {
|
|
||||||
mem::forget(task); // Don't drop task if it isn't complete.
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,46 +0,0 @@
|
||||||
use super::napi_status;
|
|
||||||
|
|
||||||
#[derive(Eq, PartialEq, Debug)]
|
|
||||||
pub enum Status {
|
|
||||||
Ok,
|
|
||||||
InvalidArg,
|
|
||||||
ObjectExpected,
|
|
||||||
StringExpected,
|
|
||||||
NameExpected,
|
|
||||||
FunctionExpected,
|
|
||||||
NumberExpected,
|
|
||||||
BooleanExpected,
|
|
||||||
ArrayExpected,
|
|
||||||
GenericFailure,
|
|
||||||
PendingException,
|
|
||||||
Cancelled,
|
|
||||||
EscapeCalledTwice,
|
|
||||||
HandleScopeMismatch,
|
|
||||||
StringContainsNull,
|
|
||||||
CallbackScopeMismatch,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<napi_status> for Status {
|
|
||||||
fn from(code: napi_status) -> Self {
|
|
||||||
use self::napi_status::*;
|
|
||||||
use Status::*;
|
|
||||||
|
|
||||||
match code {
|
|
||||||
napi_ok => Ok,
|
|
||||||
napi_invalid_arg => InvalidArg,
|
|
||||||
napi_object_expected => ObjectExpected,
|
|
||||||
napi_string_expected => StringExpected,
|
|
||||||
napi_name_expected => NameExpected,
|
|
||||||
napi_function_expected => FunctionExpected,
|
|
||||||
napi_number_expected => NumberExpected,
|
|
||||||
napi_boolean_expected => BooleanExpected,
|
|
||||||
napi_array_expected => ArrayExpected,
|
|
||||||
napi_generic_failure => GenericFailure,
|
|
||||||
napi_pending_exception => PendingException,
|
|
||||||
napi_cancelled => Cancelled,
|
|
||||||
napi_escape_called_twice => EscapeCalledTwice,
|
|
||||||
napi_handle_scope_mismatch => HandleScopeMismatch,
|
|
||||||
napi_callback_scope_mismatch => CallbackScopeMismatch,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,9 +2,13 @@
|
||||||
name = "test-module"
|
name = "test-module"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
authors = ["LongYinan <lynweklm@gmail.com>"]
|
authors = ["LongYinan <lynweklm@gmail.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
crate-type = ["dylib"]
|
crate-type = ["cdylib"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
napi-rs = {path = ".."}
|
napi-rs = {path = "../napi"}
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
napi-build = { path = "../build" }
|
||||||
|
|
7
test_module/build.rs
Normal file
7
test_module/build.rs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
extern crate napi_build;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
use napi_build::setup;
|
||||||
|
|
||||||
|
setup();
|
||||||
|
}
|
|
@ -2,9 +2,9 @@
|
||||||
"name": "test-module",
|
"name": "test-module",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "napi build",
|
"build": "../scripts/napi.js build",
|
||||||
"build-release": "napi build --release",
|
"build-release": "../scripts/napi.js build --release",
|
||||||
"check": "napi check",
|
"check": "../scripts/napi.js check",
|
||||||
"test": "node ./tests.js"
|
"test": "node ./tests.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
|
@ -25,41 +25,29 @@ fn test_spawn<'a>(
|
||||||
_this: Value<'a, Any>,
|
_this: Value<'a, Any>,
|
||||||
_args: &[Value<'a, Any>],
|
_args: &[Value<'a, Any>],
|
||||||
) -> Result<Option<Value<'a, Any>>> {
|
) -> Result<Option<Value<'a, Any>>> {
|
||||||
use futures::future::Executor;
|
use futures::executor::ThreadPool;
|
||||||
use futures::{Future, Stream};
|
use futures::StreamExt;
|
||||||
use std::{thread, time};
|
|
||||||
|
|
||||||
let async_context = env.async_init(None, "test_spawn");
|
let async_context = env.async_init(None, "test_spawn");
|
||||||
|
let pool = ThreadPool::new().expect("Failed to build pool");
|
||||||
let (promise, deferred) = env.create_promise();
|
let (promise, deferred) = env.create_promise();
|
||||||
let (tx, rx) = futures::sync::mpsc::unbounded();
|
let (tx, rx) = futures::channel::mpsc::unbounded::<i32>();
|
||||||
|
let fut_values = async move {
|
||||||
let future = rx.for_each(|n: usize| {
|
let fut_tx_result = async move {
|
||||||
println!("Received value {:?}", n);
|
(0..100).for_each(|v| {
|
||||||
futures::future::ok(())
|
tx.unbounded_send(v).expect("Failed to send");
|
||||||
}).and_then(move |_| {
|
})
|
||||||
|
};
|
||||||
|
pool.spawn_ok(fut_tx_result);
|
||||||
|
let fut_values = rx.map(|v| v * 2).collect::<Vec<i32>>();
|
||||||
|
let results = fut_values.await;
|
||||||
|
println!("Collected result lenght {}", results.len());
|
||||||
async_context.enter(|env| {
|
async_context.enter(|env| {
|
||||||
env.resolve_deferred(deferred, env.get_undefined());
|
env.resolve_deferred(deferred, env.get_undefined());
|
||||||
});
|
});
|
||||||
futures::future::ok(())
|
};
|
||||||
});
|
|
||||||
|
|
||||||
env.create_executor().execute(future).unwrap();
|
env.create_executor().execute(fut_values);
|
||||||
|
|
||||||
for _i in 0..10 {
|
|
||||||
let thread_tx = tx.clone();
|
|
||||||
thread::spawn(move || {
|
|
||||||
let mut n = 0;
|
|
||||||
loop {
|
|
||||||
println!("send {:?}", n);
|
|
||||||
thread_tx.unbounded_send(n).unwrap();
|
|
||||||
n += 1;
|
|
||||||
thread::sleep(time::Duration::from_millis(50));
|
|
||||||
if n == 10 {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Some(promise.try_into().unwrap()))
|
Ok(Some(promise.try_into().unwrap()))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
const testModule = require('./target/debug/test_module')
|
const testModule = require(`./target/debug/libtest_module.node`)
|
||||||
|
|
||||||
function testSpawn() {
|
function testSpawn() {
|
||||||
console.log('=== Test spawning a future on libuv event loop')
|
console.log('=== Test spawning a future on libuv event loop')
|
||||||
|
@ -16,4 +16,12 @@ function testThrow() {
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
testSpawn().then(testThrow)
|
const future = testSpawn()
|
||||||
|
|
||||||
|
// https://github.com/nodejs/node/issues/29355
|
||||||
|
setTimeout(() => {
|
||||||
|
future.then(testThrow).catch((e) => {
|
||||||
|
console.error(e)
|
||||||
|
process.exit(1)
|
||||||
|
})
|
||||||
|
}, 10)
|
||||||
|
|
|
@ -4,10 +4,12 @@
|
||||||
|
|
||||||
minimist@^1.2.0:
|
minimist@^1.2.0:
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
resolved "http://registry.npm.taobao.org/minimist/download/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
|
resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
|
||||||
|
integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=
|
||||||
|
|
||||||
napi-rs@^0.1.0:
|
napi-rs@^0.1.0:
|
||||||
version "0.1.0"
|
version "0.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/napi-rs/-/napi-rs-0.1.0.tgz#32c8b57045e939d66bbf1a0d5d36dc0cf188d70c"
|
resolved "https://registry.npmjs.org/napi-rs/-/napi-rs-0.1.1.tgz#00c823423be5f070e4d5a01b143e4e848f4a2512"
|
||||||
|
integrity sha512-eYHtHpqneoL3coE0kLr5hbRR9Z3hx97qfNdzXneuUWR3eFs8qNaJZRX2dln2gWieJaYrqUWs+rvR4+jdBlse6Q==
|
||||||
dependencies:
|
dependencies:
|
||||||
minimist "^1.2.0"
|
minimist "^1.2.0"
|
||||||
|
|
13
yarn.lock
13
yarn.lock
|
@ -2,10 +2,17 @@
|
||||||
# yarn lockfile v1
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
|
"@types/node@^13.7.1":
|
||||||
|
version "13.7.1"
|
||||||
|
resolved "https://registry.npmjs.org/@types/node/-/node-13.7.1.tgz#238eb34a66431b71d2aaddeaa7db166f25971a0d"
|
||||||
|
integrity sha512-Zq8gcQGmn4txQEJeiXo/KiLpon8TzAl0kmKH4zdWctPj05nWwp1ClMdAVEloqrQKfaC48PNLdgN/aVaLqUrluA==
|
||||||
|
|
||||||
minimist@^1.2.0:
|
minimist@^1.2.0:
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
resolved "http://registry.npm.taobao.org/minimist/download/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
|
resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
|
||||||
|
integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=
|
||||||
|
|
||||||
prettier@^1.12.1:
|
prettier@^1.12.1:
|
||||||
version "1.12.1"
|
version "1.19.1"
|
||||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.12.1.tgz#c1ad20e803e7749faf905a409d2367e06bbe7325"
|
resolved "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb"
|
||||||
|
integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==
|
||||||
|
|
Loading…
Reference in a new issue