feat(cli): brand new cli tool with both cli and programmatical usage (#1492)
BREAKING CHANGE: requires node >= 16 and some cli options have been renamed
This commit is contained in:
parent
7c4dc2a2bd
commit
a781a4f27e
194 changed files with 8805 additions and 4158 deletions
|
@ -14,3 +14,4 @@ target
|
||||||
scripts
|
scripts
|
||||||
triples/index.js
|
triples/index.js
|
||||||
rollup.config.js
|
rollup.config.js
|
||||||
|
crates/cli/index.js
|
|
@ -192,7 +192,7 @@ rules:
|
||||||
|
|
||||||
overrides:
|
overrides:
|
||||||
- files:
|
- files:
|
||||||
- ./cli/**/*.ts
|
- ./**/*.ts
|
||||||
plugins:
|
plugins:
|
||||||
- '@typescript-eslint'
|
- '@typescript-eslint'
|
||||||
parserOptions:
|
parserOptions:
|
||||||
|
|
5
.github/workflows/android-armv7.yml
vendored
5
.github/workflows/android-armv7.yml
vendored
|
@ -47,13 +47,10 @@ jobs:
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: yarn install --immutable --mode=skip-build
|
run: yarn install --immutable --mode=skip-build
|
||||||
|
|
||||||
- name: 'Build TypeScript'
|
|
||||||
run: yarn build
|
|
||||||
|
|
||||||
- name: Cross build
|
- name: Cross build
|
||||||
run: |
|
run: |
|
||||||
export CARGO_TARGET_ARMV7_LINUX_ANDROIDEABI_LINKER="${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi24-clang"
|
export CARGO_TARGET_ARMV7_LINUX_ANDROIDEABI_LINKER="${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi24-clang"
|
||||||
yarn build:test:android:armv7
|
yarn build:test -- --target armv7-linux-androideabi
|
||||||
du -sh examples/napi/index.node
|
du -sh examples/napi/index.node
|
||||||
${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip examples/napi/index.node
|
${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip examples/napi/index.node
|
||||||
du -sh examples/napi/index.node
|
du -sh examples/napi/index.node
|
||||||
|
|
5
.github/workflows/android.yml
vendored
5
.github/workflows/android.yml
vendored
|
@ -47,10 +47,7 @@ jobs:
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: yarn install --immutable --mode=skip-build
|
run: yarn install --immutable --mode=skip-build
|
||||||
|
|
||||||
- name: 'Build TypeScript'
|
|
||||||
run: yarn build
|
|
||||||
|
|
||||||
- name: Cross build native tests
|
- name: Cross build native tests
|
||||||
run: |
|
run: |
|
||||||
export CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER="${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android24-clang"
|
export CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER="${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android24-clang"
|
||||||
yarn build:test:android
|
yarn build:test -- --target aarch64-linux-android
|
||||||
|
|
6
.github/workflows/asan.yml
vendored
6
.github/workflows/asan.yml
vendored
|
@ -44,12 +44,10 @@ jobs:
|
||||||
- name: 'Install dependencies'
|
- name: 'Install dependencies'
|
||||||
run: yarn install --immutable --mode=skip-build
|
run: yarn install --immutable --mode=skip-build
|
||||||
|
|
||||||
- name: 'Build TypeScript'
|
|
||||||
run: yarn build
|
|
||||||
|
|
||||||
- name: Unit tests with address sanitizer
|
- name: Unit tests with address sanitizer
|
||||||
run: |
|
run: |
|
||||||
yarn build:test:asan
|
yarn workspace @examples/napi build -- -Z build-std
|
||||||
|
yarn workspace @examples/compat-mode build -- -Z build-std
|
||||||
LD_PRELOAD=/usr/lib/gcc/x86_64-linux-gnu/9/libasan.so yarn test
|
LD_PRELOAD=/usr/lib/gcc/x86_64-linux-gnu/9/libasan.so yarn test
|
||||||
env:
|
env:
|
||||||
RUST_TARGET: x86_64-unknown-linux-gnu
|
RUST_TARGET: x86_64-unknown-linux-gnu
|
||||||
|
|
3
.github/workflows/bench.yaml
vendored
3
.github/workflows/bench.yaml
vendored
|
@ -46,9 +46,6 @@ jobs:
|
||||||
- name: 'Install dependencies'
|
- name: 'Install dependencies'
|
||||||
run: yarn install --immutable --mode=skip-build
|
run: yarn install --immutable --mode=skip-build
|
||||||
|
|
||||||
- name: 'Build ts'
|
|
||||||
run: yarn build
|
|
||||||
|
|
||||||
- name: 'Build bench'
|
- name: 'Build bench'
|
||||||
run: yarn build:bench
|
run: yarn build:bench
|
||||||
|
|
||||||
|
|
10
.github/workflows/cli-binary.yml
vendored
10
.github/workflows/cli-binary.yml
vendored
|
@ -44,9 +44,6 @@ jobs:
|
||||||
- name: 'Install dependencies'
|
- name: 'Install dependencies'
|
||||||
run: yarn install --mode=skip-build --immutable
|
run: yarn install --mode=skip-build --immutable
|
||||||
|
|
||||||
- name: 'Build TypeScript'
|
|
||||||
run: yarn build
|
|
||||||
|
|
||||||
- name: Build and run binary
|
- name: Build and run binary
|
||||||
run: |
|
run: |
|
||||||
yarn workspace binary build
|
yarn workspace binary build
|
||||||
|
@ -54,13 +51,6 @@ jobs:
|
||||||
env:
|
env:
|
||||||
RUST_BACKTRACE: 1
|
RUST_BACKTRACE: 1
|
||||||
|
|
||||||
- name: Pass -p and --cargo-name to build
|
|
||||||
run: |
|
|
||||||
node ./cli/scripts/index.js build -p napi-examples-binary --cargo-name napi-examples-binary
|
|
||||||
./napi-examples-binary
|
|
||||||
env:
|
|
||||||
RUST_BACKTRACE: 1
|
|
||||||
|
|
||||||
- name: Clear the cargo caches
|
- name: Clear the cargo caches
|
||||||
run: |
|
run: |
|
||||||
cargo install cargo-cache --no-default-features --features ci-autoclean
|
cargo install cargo-cache --no-default-features --features ci-autoclean
|
||||||
|
|
6
.github/workflows/linux-aarch64-musl.yaml
vendored
6
.github/workflows/linux-aarch64-musl.yaml
vendored
|
@ -30,9 +30,6 @@ jobs:
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: yarn install --immutable --mode=skip-build
|
run: yarn install --immutable --mode=skip-build
|
||||||
|
|
||||||
- name: 'Build TypeScript'
|
|
||||||
run: yarn build
|
|
||||||
|
|
||||||
- name: Cross build native tests
|
- name: Cross build native tests
|
||||||
uses: addnab/docker-run-action@v3
|
uses: addnab/docker-run-action@v3
|
||||||
with:
|
with:
|
||||||
|
@ -40,8 +37,7 @@ jobs:
|
||||||
options: -v ${{ github.workspace }}:/napi-rs -w /napi-rs
|
options: -v ${{ github.workspace }}:/napi-rs -w /napi-rs
|
||||||
run: |
|
run: |
|
||||||
export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER=aarch64-linux-musl-gcc
|
export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER=aarch64-linux-musl-gcc
|
||||||
yarn workspace compat-mode-examples build --target aarch64-unknown-linux-musl
|
yarn build:test -- --target aarch64-unknown-linux-musl
|
||||||
yarn workspace examples build --target aarch64-unknown-linux-musl
|
|
||||||
|
|
||||||
- run: docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
|
- run: docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
|
||||||
|
|
||||||
|
|
5
.github/workflows/linux-aarch64.yaml
vendored
5
.github/workflows/linux-aarch64.yaml
vendored
|
@ -50,11 +50,8 @@ jobs:
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: yarn install --immutable --mode=skip-build
|
run: yarn install --immutable --mode=skip-build
|
||||||
|
|
||||||
- name: 'Build TypeScript'
|
|
||||||
run: yarn build
|
|
||||||
|
|
||||||
- name: Cross build native tests
|
- name: Cross build native tests
|
||||||
run: yarn build:test:aarch64
|
run: yarn build:test -- --target aarch64-unknown-linux-gnu --cross-compile
|
||||||
|
|
||||||
- run: docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
|
- run: docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
|
||||||
|
|
||||||
|
|
5
.github/workflows/linux-armv7.yaml
vendored
5
.github/workflows/linux-armv7.yaml
vendored
|
@ -51,11 +51,8 @@ jobs:
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: yarn install --immutable --mode=skip-build
|
run: yarn install --immutable --mode=skip-build
|
||||||
|
|
||||||
- name: 'Build TypeScript'
|
|
||||||
run: yarn build
|
|
||||||
|
|
||||||
- name: Cross build native tests
|
- name: Cross build native tests
|
||||||
run: yarn build:test:armv7
|
run: yarn build:test -- --target armv7-unknown-linux-gnueabihf --cross-compile
|
||||||
|
|
||||||
- run: docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
|
- run: docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
|
||||||
|
|
||||||
|
|
5
.github/workflows/linux-musl.yaml
vendored
5
.github/workflows/linux-musl.yaml
vendored
|
@ -30,9 +30,6 @@ jobs:
|
||||||
- name: 'Install dependencies'
|
- name: 'Install dependencies'
|
||||||
run: yarn install --immutable --mode=skip-build
|
run: yarn install --immutable --mode=skip-build
|
||||||
|
|
||||||
- name: 'Build TypeScript'
|
|
||||||
run: yarn build
|
|
||||||
|
|
||||||
- name: Setup and run tests
|
- name: Setup and run tests
|
||||||
uses: addnab/docker-run-action@v3
|
uses: addnab/docker-run-action@v3
|
||||||
with:
|
with:
|
||||||
|
@ -40,5 +37,5 @@ jobs:
|
||||||
options: -v ${{ github.workspace }}:/napi-rs -w /napi-rs
|
options: -v ${{ github.workspace }}:/napi-rs -w /napi-rs
|
||||||
run: |
|
run: |
|
||||||
cargo check -vvv
|
cargo check -vvv
|
||||||
yarn build:test
|
yarn build:test -- --target x86_64-unknown-linux-musl
|
||||||
yarn test
|
yarn test
|
||||||
|
|
3
.github/workflows/memory-test.yml
vendored
3
.github/workflows/memory-test.yml
vendored
|
@ -49,9 +49,6 @@ jobs:
|
||||||
- name: 'Install dependencies'
|
- name: 'Install dependencies'
|
||||||
run: yarn install --immutable
|
run: yarn install --immutable
|
||||||
|
|
||||||
- name: 'Build TypeScript'
|
|
||||||
run: yarn build
|
|
||||||
|
|
||||||
- name: 'Pull docker image'
|
- name: 'Pull docker image'
|
||||||
run: docker pull node:lts-slim
|
run: docker pull node:lts-slim
|
||||||
|
|
||||||
|
|
3
.github/workflows/msrv.yml
vendored
3
.github/workflows/msrv.yml
vendored
|
@ -43,9 +43,6 @@ jobs:
|
||||||
- name: 'Install dependencies'
|
- name: 'Install dependencies'
|
||||||
run: yarn install --mode=skip-build --immutable
|
run: yarn install --mode=skip-build --immutable
|
||||||
|
|
||||||
- name: 'Build TypeScript'
|
|
||||||
run: yarn build
|
|
||||||
|
|
||||||
- name: Check build
|
- name: Check build
|
||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
|
|
6
.github/workflows/test.yaml
vendored
6
.github/workflows/test.yaml
vendored
|
@ -19,7 +19,7 @@ jobs:
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
node: ['14', '16', '18']
|
node: ['16', '18']
|
||||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||||
|
|
||||||
name: stable - ${{ matrix.os }} - node@${{ matrix.node }}
|
name: stable - ${{ matrix.os }} - node@${{ matrix.node }}
|
||||||
|
@ -51,9 +51,6 @@ jobs:
|
||||||
- name: 'Install dependencies'
|
- name: 'Install dependencies'
|
||||||
run: yarn install --mode=skip-build --immutable
|
run: yarn install --mode=skip-build --immutable
|
||||||
|
|
||||||
- name: 'Build TypeScript'
|
|
||||||
run: yarn build
|
|
||||||
|
|
||||||
- name: Check build
|
- name: Check build
|
||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
|
@ -62,6 +59,7 @@ jobs:
|
||||||
|
|
||||||
- name: Unit tests
|
- name: Unit tests
|
||||||
run: |
|
run: |
|
||||||
|
yarn test:cli
|
||||||
yarn build:test
|
yarn build:test
|
||||||
yarn test --verbose
|
yarn test --verbose
|
||||||
yarn tsc -p examples/napi/tsconfig.json --noEmit
|
yarn tsc -p examples/napi/tsconfig.json --noEmit
|
||||||
|
|
7
.github/workflows/windows-arm.yml
vendored
7
.github/workflows/windows-arm.yml
vendored
|
@ -30,9 +30,6 @@ jobs:
|
||||||
- name: 'Install dependencies'
|
- name: 'Install dependencies'
|
||||||
run: yarn install --mode=skip-build --immutable
|
run: yarn install --mode=skip-build --immutable
|
||||||
|
|
||||||
- name: 'Build TypeScript'
|
|
||||||
run: yarn build
|
|
||||||
|
|
||||||
- name: Install
|
- name: Install
|
||||||
uses: dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@stable
|
||||||
with:
|
with:
|
||||||
|
@ -55,7 +52,9 @@ jobs:
|
||||||
args: --all --bins --examples --tests --target aarch64-pc-windows-msvc -vvv
|
args: --all --bins --examples --tests --target aarch64-pc-windows-msvc -vvv
|
||||||
|
|
||||||
- name: Build release target
|
- name: Build release target
|
||||||
run: cargo build --release --target aarch64-pc-windows-msvc
|
run: |
|
||||||
|
yarn workspace @examples/napi build --target aarch64-pc-windows-msvc --release
|
||||||
|
yarn workspace @examples/compat-mode build --target aarch64-pc-windows-msvc --release
|
||||||
|
|
||||||
- name: Clear the cargo caches
|
- name: Clear the cargo caches
|
||||||
run: |
|
run: |
|
||||||
|
|
10
.github/workflows/windows-i686.yml
vendored
10
.github/workflows/windows-i686.yml
vendored
|
@ -31,9 +31,6 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
yarn install --mode=skip-build --immutable
|
yarn install --mode=skip-build --immutable
|
||||||
|
|
||||||
- name: 'Build TypeScript'
|
|
||||||
run: yarn build
|
|
||||||
|
|
||||||
- name: Install
|
- name: Install
|
||||||
uses: dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@stable
|
||||||
with:
|
with:
|
||||||
|
@ -55,6 +52,11 @@ jobs:
|
||||||
command: check
|
command: check
|
||||||
args: --all --bins --examples --tests --target i686-pc-windows-msvc -vvv
|
args: --all --bins --examples --tests --target i686-pc-windows-msvc -vvv
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: |
|
||||||
|
yarn workspace @examples/napi build --target i686-pc-windows-msvc --release
|
||||||
|
yarn workspace @examples/compat-mode build --target i686-pc-windows-msvc --release
|
||||||
|
|
||||||
- name: Setup node
|
- name: Setup node
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
|
@ -64,8 +66,6 @@ jobs:
|
||||||
|
|
||||||
- name: Build Tests
|
- name: Build Tests
|
||||||
run: |
|
run: |
|
||||||
yarn workspace compat-mode-examples build-i686 --release
|
|
||||||
yarn workspace examples build-i686 --release
|
|
||||||
yarn test --verbose
|
yarn test --verbose
|
||||||
node ./node_modules/electron/install.js
|
node ./node_modules/electron/install.js
|
||||||
yarn test:electron
|
yarn test:electron
|
||||||
|
|
18
.github/workflows/zig.yaml
vendored
18
.github/workflows/zig.yaml
vendored
|
@ -56,17 +56,15 @@ jobs:
|
||||||
version: 0.10.1
|
version: 0.10.1
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: yarn install --immutable --mode=skip-build
|
run: yarn install --immutable --mode=skip-build
|
||||||
- name: 'Build TypeScript'
|
- name: install MacOS SDK
|
||||||
run: yarn build
|
if: contains(matrix.target, 'apple')
|
||||||
- name: Setup node
|
|
||||||
uses: actions/setup-node@v3
|
|
||||||
with:
|
|
||||||
# Testing for compatibility with node v12.x
|
|
||||||
node-version: 12
|
|
||||||
- name: Cross build native tests
|
|
||||||
run: |
|
run: |
|
||||||
yarn workspace compat-mode-examples build --target ${{ matrix.target }} --zig
|
curl -L "https://github.com/phracker/MacOSX-SDKs/releases/download/11.3/MacOSX11.3.sdk.tar.xz" | tar -J -x -C /opt
|
||||||
yarn workspace examples build --target ${{ matrix.target }} --zig
|
- name: Cross build native tests
|
||||||
|
env:
|
||||||
|
SDKROOT: /opt/MacOSX11.3.sdk
|
||||||
|
run: |
|
||||||
|
yarn build:test -- --target ${{ matrix.target }} --cross-compile
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
import { cpus } from 'os'
|
|
||||||
|
|
||||||
const configuration = {
|
|
||||||
extensions: ['ts', 'tsx'],
|
|
||||||
files: ['cli/**/*.spec.ts', 'examples/**/__test__/**/*.spec.ts'],
|
|
||||||
require: ['ts-node/register/transpile-only'],
|
|
||||||
environmentVariables: {
|
|
||||||
TS_NODE_PROJECT: './examples/tsconfig.json',
|
|
||||||
},
|
|
||||||
timeout: '5m',
|
|
||||||
workerThreads: true,
|
|
||||||
concurrency: process.env.CI ? 2 : cpus().length,
|
|
||||||
failFast: false,
|
|
||||||
verbose: !!process.env.CI,
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parseInt(process.versions.napi, 10) < 4) {
|
|
||||||
configuration.compileEnhancements = false
|
|
||||||
}
|
|
||||||
|
|
||||||
export default configuration
|
|
|
@ -3,9 +3,10 @@
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "node ../cli/scripts/index.js build --js false --release"
|
"build": "napi-raw build --js false --release"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@napi-rs/cli": "workspace:*",
|
||||||
"benny": "^3.7.1"
|
"benny": "^3.7.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,88 +9,27 @@
|
||||||
|
|
||||||
> Cli tools for napi-rs
|
> Cli tools for napi-rs
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# or npm, pnpm
|
||||||
|
yarn add @napi-rs/cli -D
|
||||||
|
yarn napi build
|
||||||
|
```
|
||||||
|
|
||||||
## Commands
|
## Commands
|
||||||
|
|
||||||
|
| Command | desc | docs |
|
||||||
|
| --------------- | -------------------------------------------------------------- | --------------------------------------------------- |
|
||||||
|
| new | create new napi-rs project | [./docs/new.md](./docs/new.md) |
|
||||||
|
| build | build napi-rs project | [./docs/build.md](./docs/build.md) |
|
||||||
|
| create-npm-dirs | Create npm package dirs for different platforms | [./docs/create-npm-dirs](./docs/create-npm-dirs.md) |
|
||||||
|
| artifacts | Copy artifacts from Github Actions into specified dir | [./docs/artifacts.md](./docs/artifacts.md) |
|
||||||
|
| rename | Rename the napi-rs project | [./docs/rename.md](./docs/rename.md) |
|
||||||
|
| universalize | Combile built binaries into one universal binary | [./docs/universalize.md](./docs/universalize.md) |
|
||||||
|
| version | Update version in created npm packages by `create-npm-dirs` | [./docs/version.md](./docs/version.md) |
|
||||||
|
| pre-publish | Update package.json and copy addons into per platform packages | [./docs/pre-publish.md](./docs/pre-publish.md) |
|
||||||
|
|
||||||
### Debug mode
|
### Debug mode
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
DEBUG="napi:*" napi [command]
|
DEBUG="napi:*" napi [command]
|
||||||
```
|
```
|
||||||
|
|
||||||
### `napi build`
|
|
||||||
|
|
||||||
> Build command. Build rust codes and copy the dynamic lib binary file to the dist dir.
|
|
||||||
|
|
||||||
#### `--platform`
|
|
||||||
|
|
||||||
> default `false`
|
|
||||||
|
|
||||||
Append `platform-arch-[abi]` name to dist file. eg: `index.darwin-x64.node`.
|
|
||||||
|
|
||||||
#### `--release`
|
|
||||||
|
|
||||||
> default `false`
|
|
||||||
|
|
||||||
Is release build. This flag will be passed to `Cargo` directly.
|
|
||||||
|
|
||||||
#### `--features`
|
|
||||||
|
|
||||||
> default `''`
|
|
||||||
|
|
||||||
Cargo features, passthrough to `cargo build` command.
|
|
||||||
|
|
||||||
#### `--config,-c`
|
|
||||||
|
|
||||||
> default `package.json`
|
|
||||||
|
|
||||||
`napi-rs` config file name. `napi-rs` config example :
|
|
||||||
|
|
||||||
```js
|
|
||||||
{
|
|
||||||
"name": "@native-binding/fib",
|
|
||||||
"version": "0.1.0",
|
|
||||||
"napi": {
|
|
||||||
"name": "fib", // binary name
|
|
||||||
"triples": {
|
|
||||||
"defaults": true, // default true, if this value is true, will build `x86_64-pc-windows-msvc`, `x86_64-apple-darwin` and `x86_64-unknown-linux-gnu`
|
|
||||||
"additional": [
|
|
||||||
"x86_64-unknown-linux-musl",
|
|
||||||
"x86_64-unknown-freebsd",
|
|
||||||
"aarch64-unknown-linux-gnu"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### `--cargo-name`
|
|
||||||
|
|
||||||
> default `undefined`
|
|
||||||
|
|
||||||
If not set, cli will read the `package.name` field in `Cargo.toml` under `process.cwd()`. The `-` in the name will be replaced with `_`.
|
|
||||||
|
|
||||||
#### `--target`
|
|
||||||
|
|
||||||
> default `undefined`
|
|
||||||
|
|
||||||
> Note you should have `rustup` installed if omit the `--target` flag. The `@napi-rs/cli` will try to find the default target on your system via `rustup` if no `--target` specified.
|
|
||||||
|
|
||||||
You can also define this value using the `RUST_TARGET` environment variable.
|
|
||||||
|
|
||||||
This value will be passed to `Cargo build` command directly. eg: `napi build --target x86_64-unknown-linux-musl`
|
|
||||||
|
|
||||||
#### `--cargo-flags`
|
|
||||||
|
|
||||||
> default `undefined`
|
|
||||||
|
|
||||||
Other flags you want pass to `Cargo build`.
|
|
||||||
|
|
||||||
#### `--cargo-cwd`
|
|
||||||
|
|
||||||
> default `undefined`
|
|
||||||
|
|
||||||
This flag can be used to build binaries that are not in the current directory. The path that is passed to this flag should be relative to the current directory.
|
|
||||||
|
|
||||||
### `napi artifacts`
|
|
||||||
|
|
||||||
> Copy artifact files in Github actions.
|
|
||||||
|
|
10
cli/ava.config.mjs
Normal file
10
cli/ava.config.mjs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
export default {
|
||||||
|
extensions: {
|
||||||
|
ts: 'module',
|
||||||
|
},
|
||||||
|
files: ['**/__tests__/**/*.spec.ts'],
|
||||||
|
nodeArguments: ['--loader=ts-node/esm/transpile-only'],
|
||||||
|
environmentVariables: {
|
||||||
|
TS_NODE_PROJECT: './tsconfig.json',
|
||||||
|
},
|
||||||
|
}
|
12
cli/cli.mjs
Executable file
12
cli/cli.mjs
Executable file
|
@ -0,0 +1,12 @@
|
||||||
|
import { execSync } from 'child_process'
|
||||||
|
import { resolve } from 'path'
|
||||||
|
import { fileURLToPath } from 'url'
|
||||||
|
|
||||||
|
execSync(
|
||||||
|
`node --loader ts-node/esm/transpile-only ${resolve(fileURLToPath(import.meta.url), '../src/cli.ts')} ${process.argv
|
||||||
|
.slice(2)
|
||||||
|
.join(' ')}`,
|
||||||
|
{
|
||||||
|
stdio: 'inherit',
|
||||||
|
},
|
||||||
|
)
|
505
cli/codegen/commands.ts
Normal file
505
cli/codegen/commands.ts
Normal file
|
@ -0,0 +1,505 @@
|
||||||
|
export interface ArgSchema {
|
||||||
|
name: string
|
||||||
|
type: 'string'
|
||||||
|
description: string
|
||||||
|
required?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OptionSchema {
|
||||||
|
name: string
|
||||||
|
type: string
|
||||||
|
description: string
|
||||||
|
required?: boolean
|
||||||
|
default?: any
|
||||||
|
short?: string
|
||||||
|
long?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CommandSchema {
|
||||||
|
name: string
|
||||||
|
description: string
|
||||||
|
args: ArgSchema[]
|
||||||
|
options: OptionSchema[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export type CommandDefineSchema = CommandSchema[]
|
||||||
|
|
||||||
|
const NEW_OPTIONS: CommandSchema = {
|
||||||
|
name: 'new',
|
||||||
|
description: 'Create a new project with pre-configured boilerplate',
|
||||||
|
args: [
|
||||||
|
{
|
||||||
|
name: 'path',
|
||||||
|
type: 'string',
|
||||||
|
description: 'The path where the napi-rs project will be created.',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'name',
|
||||||
|
type: 'string',
|
||||||
|
description:
|
||||||
|
'The name of the project, default to the name of the directory if not provided',
|
||||||
|
short: 'n',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'minNodeApiVersion',
|
||||||
|
type: 'number',
|
||||||
|
description: 'The minimum Node-API version to support',
|
||||||
|
default: 4,
|
||||||
|
short: 'v',
|
||||||
|
long: 'min-node-api',
|
||||||
|
},
|
||||||
|
// will support it later
|
||||||
|
// {
|
||||||
|
// name: 'packageManager',
|
||||||
|
// type: 'string',
|
||||||
|
// description: 'The package manager to use',
|
||||||
|
// default: "'yarn'",
|
||||||
|
// },
|
||||||
|
{
|
||||||
|
name: 'license',
|
||||||
|
type: 'string',
|
||||||
|
description: 'License for open-sourced project',
|
||||||
|
short: 'l',
|
||||||
|
default: "'MIT'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'targets',
|
||||||
|
type: 'string[]',
|
||||||
|
description: 'All targets the crate will be compiled for.',
|
||||||
|
short: 't',
|
||||||
|
default: '[]',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'enableDefaultTargets',
|
||||||
|
type: 'boolean',
|
||||||
|
description: 'Whether enable default targets',
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'enableAllTargets',
|
||||||
|
type: 'boolean',
|
||||||
|
description: 'Whether enable all targets',
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'enableTypeDef',
|
||||||
|
type: 'boolean',
|
||||||
|
description:
|
||||||
|
'Whether enable the `type-def` feature for typescript definitions auto-generation',
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'enableGithubActions',
|
||||||
|
type: 'boolean',
|
||||||
|
description: 'Whether generate preconfigured GitHub Actions workflow',
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'dryRun',
|
||||||
|
type: 'boolean',
|
||||||
|
description: 'Whether to run the command in dry-run mode',
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
const BUILD_OPTIONS: CommandSchema = {
|
||||||
|
name: 'build',
|
||||||
|
description: 'Build the napi-rs project',
|
||||||
|
args: [],
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'target',
|
||||||
|
type: 'string',
|
||||||
|
description:
|
||||||
|
'Build for the target triple, bypassed to `cargo build --target`',
|
||||||
|
short: 't',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'cwd',
|
||||||
|
type: 'string',
|
||||||
|
description:
|
||||||
|
'The working directory of where napi command will be executed in, all other paths options are relative to this path',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'manifestPath',
|
||||||
|
type: 'string',
|
||||||
|
description: 'Path to `Cargo.toml`',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'packageJsonPath',
|
||||||
|
type: 'string',
|
||||||
|
description: 'Path to `package.json`',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'targetDir',
|
||||||
|
type: 'string',
|
||||||
|
description:
|
||||||
|
'Directory for all crate generated artifacts, see `cargo build --target-dir`',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'outputDir',
|
||||||
|
type: 'string',
|
||||||
|
description:
|
||||||
|
'Path to where all the built files would be put. Default to the crate folder',
|
||||||
|
short: 'o',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'platform',
|
||||||
|
type: 'boolean',
|
||||||
|
description:
|
||||||
|
'Add platform triple to the generated nodejs binding file, eg: `[name].linux-x64-gnu.node`',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'jsPackageName',
|
||||||
|
type: 'string',
|
||||||
|
description:
|
||||||
|
'Package name in generated js binding file. Only works with `--platform` flag',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'jsBinding',
|
||||||
|
type: 'string',
|
||||||
|
description:
|
||||||
|
'Path and filename of generated JS binding file. Only works with `--platform` flag. Relative to `--output_dir`.',
|
||||||
|
long: 'js',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'noJsBinding',
|
||||||
|
type: 'boolean',
|
||||||
|
description:
|
||||||
|
'Whether to disable the generation JS binding file. Only works with `--platform` flag.',
|
||||||
|
long: 'no-js',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'dts',
|
||||||
|
type: 'string',
|
||||||
|
description:
|
||||||
|
'Path and filename of generated type def file. Relative to `--output_dir`',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'dtsHeader',
|
||||||
|
type: 'string',
|
||||||
|
description:
|
||||||
|
'Custom file header for generated type def file. Only works when `typedef` feature enabled.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'noDtsHeader',
|
||||||
|
type: 'boolean',
|
||||||
|
description:
|
||||||
|
'Whether to disable the default file header for generated type def file. Only works when `typedef` feature enabled.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'strip',
|
||||||
|
type: 'boolean',
|
||||||
|
description: 'Whether strip the library to achieve the minimum file size',
|
||||||
|
short: 's',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'release',
|
||||||
|
type: 'boolean',
|
||||||
|
description: 'Build in release mode',
|
||||||
|
short: 'r',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'verbose',
|
||||||
|
type: 'boolean',
|
||||||
|
description: 'Verbosely log build command trace',
|
||||||
|
short: 'v',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'bin',
|
||||||
|
type: 'string',
|
||||||
|
description: 'Build only the specified binary',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'package',
|
||||||
|
type: 'string',
|
||||||
|
description: 'Build the specified library or the one at cwd',
|
||||||
|
short: 'p',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'crossCompile',
|
||||||
|
type: 'boolean',
|
||||||
|
description:
|
||||||
|
'[experimental] cross-compile for the specified target with `cargo-xwin` on windows and `cargo-zigbuild` on other platform',
|
||||||
|
short: 'x',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'watch',
|
||||||
|
type: 'boolean',
|
||||||
|
description:
|
||||||
|
'watch the crate changes and build continiously with `cargo-watch` crates',
|
||||||
|
short: 'w',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'features',
|
||||||
|
type: 'string[]',
|
||||||
|
description: 'Space-separated list of features to activate',
|
||||||
|
short: 'F',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'allFeatures',
|
||||||
|
type: 'boolean',
|
||||||
|
description: 'Activate all available features',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'noDefaultFeatures',
|
||||||
|
type: 'boolean',
|
||||||
|
description: 'Do not activate the `default` feature',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
const ARTIFACTS_OPTIONS: CommandSchema = {
|
||||||
|
name: 'artifacts',
|
||||||
|
description:
|
||||||
|
'Copy artifacts from Github Actions into npm packages and ready to publish',
|
||||||
|
args: [],
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'cwd',
|
||||||
|
type: 'string',
|
||||||
|
description:
|
||||||
|
'The working directory of where napi command will be executed in, all other paths options are relative to this path',
|
||||||
|
default: 'process.cwd()',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'packageJsonPath',
|
||||||
|
type: 'string',
|
||||||
|
description: 'Path to `package.json`',
|
||||||
|
default: "'package.json'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'outputDir',
|
||||||
|
type: 'string',
|
||||||
|
description:
|
||||||
|
'Path to the folder where all built `.node` files put, same as `--output-dir` of build command',
|
||||||
|
short: 'o',
|
||||||
|
default: "'./'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'npmDir',
|
||||||
|
type: 'string',
|
||||||
|
description: 'Path to the folder where the npm packages put',
|
||||||
|
default: "'npm'",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
const CREATE_NPM_DIRS_OPTIONS: CommandSchema = {
|
||||||
|
name: 'createNpmDirs',
|
||||||
|
description: 'Create npm package dirs for different platforms',
|
||||||
|
args: [],
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'cwd',
|
||||||
|
type: 'string',
|
||||||
|
description:
|
||||||
|
'The working directory of where napi command will be executed in, all other paths options are relative to this path',
|
||||||
|
default: 'process.cwd()',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'packageJsonPath',
|
||||||
|
type: 'string',
|
||||||
|
description: 'Path to `package.json`',
|
||||||
|
default: "'package.json'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'npmDir',
|
||||||
|
type: 'string',
|
||||||
|
description: 'Path to the folder where the npm packages put',
|
||||||
|
default: "'npm'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'dryRun',
|
||||||
|
type: 'boolean',
|
||||||
|
description: 'Dry run without touching file system',
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
const RENAME_OPTIONS: CommandSchema = {
|
||||||
|
name: 'rename',
|
||||||
|
description: 'Rename the napi-rs project',
|
||||||
|
args: [],
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'cwd',
|
||||||
|
type: 'string',
|
||||||
|
description:
|
||||||
|
'The working directory of where napi command will be executed in, all other paths options are relative to this path',
|
||||||
|
default: 'process.cwd()',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'packageJsonPath',
|
||||||
|
type: 'string',
|
||||||
|
description: 'Path to `package.json`',
|
||||||
|
default: "'package.json'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'npmDir',
|
||||||
|
type: 'string',
|
||||||
|
description: 'Path to the folder where the npm packages put',
|
||||||
|
default: "'npm'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'name',
|
||||||
|
type: 'string',
|
||||||
|
description: 'The new name of the project',
|
||||||
|
short: 'n',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'binaryName',
|
||||||
|
type: 'string',
|
||||||
|
description: 'The new binary name *.node files',
|
||||||
|
short: 'b',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'packageName',
|
||||||
|
type: 'string',
|
||||||
|
description: 'The new package name of the project',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'manifestPath',
|
||||||
|
type: 'string',
|
||||||
|
description: 'Path to `Cargo.toml`',
|
||||||
|
default: "'Cargo.toml'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'repository',
|
||||||
|
type: 'string',
|
||||||
|
description: 'The new repository of the project',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'description',
|
||||||
|
type: 'string',
|
||||||
|
description: 'The new description of the project',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
const UNIVERSALIZE_OPTIONS: CommandSchema = {
|
||||||
|
name: 'universalize',
|
||||||
|
description: 'Combile built binaries into one universal binary',
|
||||||
|
args: [],
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'cwd',
|
||||||
|
type: 'string',
|
||||||
|
description:
|
||||||
|
'The working directory of where napi command will be executed in, all other paths options are relative to this path',
|
||||||
|
default: 'process.cwd()',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'packageJsonPath',
|
||||||
|
type: 'string',
|
||||||
|
description: 'Path to `package.json`',
|
||||||
|
default: "'package.json'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'outputDir',
|
||||||
|
type: 'string',
|
||||||
|
description:
|
||||||
|
'Path to the folder where all built `.node` files put, same as `--output-dir` of build command',
|
||||||
|
short: 'o',
|
||||||
|
default: "'./'",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
const VERSION_OPTIONS: CommandSchema = {
|
||||||
|
name: 'version',
|
||||||
|
description: 'Update version in created npm packages',
|
||||||
|
args: [],
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'cwd',
|
||||||
|
type: 'string',
|
||||||
|
description:
|
||||||
|
'The working directory of where napi command will be executed in, all other paths options are relative to this path',
|
||||||
|
default: 'process.cwd()',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'packageJsonPath',
|
||||||
|
type: 'string',
|
||||||
|
description: 'Path to `package.json`',
|
||||||
|
default: "'package.json'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'npmDir',
|
||||||
|
type: 'string',
|
||||||
|
description: 'Path to the folder where the npm packages put',
|
||||||
|
default: "'npm'",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
const PRE_PUBLISH_OPTIONS: CommandSchema = {
|
||||||
|
name: 'prePublish',
|
||||||
|
description: 'Update package.json and copy addons into per platform packages',
|
||||||
|
args: [],
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'cwd',
|
||||||
|
type: 'string',
|
||||||
|
description:
|
||||||
|
'The working directory of where napi command will be executed in, all other paths options are relative to this path',
|
||||||
|
default: 'process.cwd()',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'packageJsonPath',
|
||||||
|
type: 'string',
|
||||||
|
description: 'Path to `package.json`',
|
||||||
|
default: "'package.json'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'npmDir',
|
||||||
|
type: 'string',
|
||||||
|
description: 'Path to the folder where the npm packages put',
|
||||||
|
default: "'npm'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'tagStyle',
|
||||||
|
type: "'npm' | 'lerna'",
|
||||||
|
description: 'git tag style, `npm` or `lerna`',
|
||||||
|
default: "'lerna'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'ghRelease',
|
||||||
|
type: 'boolean',
|
||||||
|
description: 'Whether create GitHub release',
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'ghReleaseName',
|
||||||
|
type: 'string',
|
||||||
|
description: 'GitHub release name',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'ghReleaseId',
|
||||||
|
type: 'string',
|
||||||
|
description: 'Existing GitHub release id',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'dryRun',
|
||||||
|
type: 'boolean',
|
||||||
|
description: 'Dry run without touching file system',
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
export const commandDefines: CommandDefineSchema = [
|
||||||
|
NEW_OPTIONS,
|
||||||
|
BUILD_OPTIONS,
|
||||||
|
ARTIFACTS_OPTIONS,
|
||||||
|
CREATE_NPM_DIRS_OPTIONS,
|
||||||
|
RENAME_OPTIONS,
|
||||||
|
UNIVERSALIZE_OPTIONS,
|
||||||
|
VERSION_OPTIONS,
|
||||||
|
PRE_PUBLISH_OPTIONS,
|
||||||
|
]
|
279
cli/codegen/index.ts
Normal file
279
cli/codegen/index.ts
Normal file
|
@ -0,0 +1,279 @@
|
||||||
|
import { execSync } from 'child_process'
|
||||||
|
import fs from 'fs'
|
||||||
|
import path from 'path'
|
||||||
|
import { fileURLToPath } from 'url'
|
||||||
|
|
||||||
|
import { kebabCase, startCase } from 'lodash-es'
|
||||||
|
|
||||||
|
import { commandDefines, CommandSchema, OptionSchema } from './commands.js'
|
||||||
|
|
||||||
|
const __filename = fileURLToPath(import.meta.url)
|
||||||
|
const defFolder = path.join(__filename, '../../src/def')
|
||||||
|
const docsTargetFolder = path.join(__filename, '../../docs')
|
||||||
|
|
||||||
|
function PascalCase(str: string) {
|
||||||
|
return startCase(str).replace(/\s/g, '')
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* convert command definition to command options interface
|
||||||
|
*/
|
||||||
|
function generateOptionsDef(command: CommandSchema) {
|
||||||
|
const optionsName = `${PascalCase(command.name)}Options`
|
||||||
|
|
||||||
|
const optLines: string[] = []
|
||||||
|
|
||||||
|
optLines.push('/**')
|
||||||
|
optLines.push(` * ${command.description}`)
|
||||||
|
optLines.push(' */')
|
||||||
|
optLines.push(`export interface ${optionsName} {`)
|
||||||
|
command.args.forEach((arg) => {
|
||||||
|
optLines.push(' /**')
|
||||||
|
optLines.push(` * ${arg.description}`)
|
||||||
|
optLines.push(' */')
|
||||||
|
optLines.push(` ${arg.name}${arg.required ? '' : '?'}: ${arg.type}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
command.options.forEach((opt) => {
|
||||||
|
optLines.push(' /**')
|
||||||
|
optLines.push(` * ${opt.description}`)
|
||||||
|
if (typeof opt.default !== 'undefined') {
|
||||||
|
optLines.push(' *')
|
||||||
|
optLines.push(` * @default ${opt.default}`)
|
||||||
|
}
|
||||||
|
optLines.push(' */')
|
||||||
|
optLines.push(` ${opt.name}${opt.required ? '' : '?'}: ${opt.type}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
optLines.push('}\n')
|
||||||
|
|
||||||
|
if (command.options.some((opt) => typeof opt.default !== 'undefined')) {
|
||||||
|
optLines.push(
|
||||||
|
`export function applyDefault${optionsName}(options: ${optionsName}) {`,
|
||||||
|
)
|
||||||
|
optLines.push(` return {`)
|
||||||
|
command.options.forEach((opt) => {
|
||||||
|
if (typeof opt.default !== 'undefined') {
|
||||||
|
optLines.push(` ${opt.name}: ${opt.default},`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
optLines.push(' ...options,')
|
||||||
|
optLines.push(' }')
|
||||||
|
optLines.push('}\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
return optLines.join('\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
function getOptionDescriptor(opt: OptionSchema) {
|
||||||
|
let desc = `--${opt.long ?? kebabCase(opt.name)}`
|
||||||
|
if (opt.short) {
|
||||||
|
desc += `,-${opt.short}`
|
||||||
|
}
|
||||||
|
|
||||||
|
return desc
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateCommandDef(command: CommandSchema) {
|
||||||
|
const commandPath = kebabCase(command.name)
|
||||||
|
const avoidList = ['path', 'name']
|
||||||
|
|
||||||
|
const avoidName = (name: string) => {
|
||||||
|
return avoidList.includes(name) ? '$$' + name : name
|
||||||
|
}
|
||||||
|
|
||||||
|
const prepare: string[] = []
|
||||||
|
const cmdLines: string[] = []
|
||||||
|
|
||||||
|
cmdLines.push(`
|
||||||
|
export abstract class Base${PascalCase(command.name)}Command extends Command {
|
||||||
|
static paths = [['${commandPath}']]
|
||||||
|
|
||||||
|
static usage = Command.Usage({
|
||||||
|
description: '${command.description}',
|
||||||
|
})\n`)
|
||||||
|
|
||||||
|
command.args.forEach((arg) => {
|
||||||
|
cmdLines.push(
|
||||||
|
` ${avoidName(arg.name)} = Option.String({ required: ${
|
||||||
|
arg.required ?? false
|
||||||
|
} })`,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
cmdLines.push('')
|
||||||
|
|
||||||
|
command.options.forEach((opt) => {
|
||||||
|
const optName = avoidName(opt.name)
|
||||||
|
let optionType = ''
|
||||||
|
|
||||||
|
switch (opt.type) {
|
||||||
|
case 'number':
|
||||||
|
optionType = 'String'
|
||||||
|
prepare.push("import * as typanion from 'typanion'")
|
||||||
|
break
|
||||||
|
case 'boolean':
|
||||||
|
optionType = 'Boolean'
|
||||||
|
break
|
||||||
|
case 'string[]':
|
||||||
|
optionType = 'Array'
|
||||||
|
break
|
||||||
|
case 'string':
|
||||||
|
default:
|
||||||
|
optionType = 'String'
|
||||||
|
}
|
||||||
|
|
||||||
|
const optionDesc = getOptionDescriptor(opt)
|
||||||
|
|
||||||
|
if (opt.required) {
|
||||||
|
cmdLines.push(` ${optName} = Option.${optionType}('${optionDesc}', {`)
|
||||||
|
cmdLines.push(' required: true,')
|
||||||
|
} else if (typeof opt.default !== 'undefined') {
|
||||||
|
const defaultValue =
|
||||||
|
typeof opt.default === 'number'
|
||||||
|
? `'${opt.default.toString()}'`
|
||||||
|
: opt.default
|
||||||
|
cmdLines.push(` ${optName} = Option.${optionType}(`)
|
||||||
|
cmdLines.push(` '${optionDesc}',`)
|
||||||
|
cmdLines.push(` ${defaultValue},`)
|
||||||
|
cmdLines.push(` {`)
|
||||||
|
} else {
|
||||||
|
cmdLines.push(
|
||||||
|
` ${optName}?: ${opt.type} = Option.${optionType}('${optionDesc}', {`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opt.type === 'number') {
|
||||||
|
cmdLines.push(' validator: typanion.isNumber(),')
|
||||||
|
}
|
||||||
|
|
||||||
|
cmdLines.push(` description: '${opt.description}'`)
|
||||||
|
cmdLines.push(' })\n')
|
||||||
|
})
|
||||||
|
|
||||||
|
cmdLines.push(` getOptions() {`)
|
||||||
|
cmdLines.push(` return {`)
|
||||||
|
command.args
|
||||||
|
.map(({ name }) => name)
|
||||||
|
.concat(command.options.map(({ name }) => name))
|
||||||
|
.forEach((name) => {
|
||||||
|
cmdLines.push(` ${name}: this.${avoidName(name)},`)
|
||||||
|
})
|
||||||
|
cmdLines.push(' }')
|
||||||
|
cmdLines.push(' }')
|
||||||
|
|
||||||
|
cmdLines.push('}\n')
|
||||||
|
|
||||||
|
return prepare.join('\n') + '\n' + cmdLines.join('\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateDocs(command: CommandSchema, targetFolder: string): string {
|
||||||
|
const docsFileName = kebabCase(command.name)
|
||||||
|
const docsFile = path.join(targetFolder, `${docsFileName}.md`)
|
||||||
|
|
||||||
|
const options: string[] = []
|
||||||
|
|
||||||
|
command.args.forEach((arg) => {
|
||||||
|
options.push(
|
||||||
|
[
|
||||||
|
'',
|
||||||
|
arg.name,
|
||||||
|
`<${kebabCase(arg.name)}>`,
|
||||||
|
arg.required ? 'true' : 'false',
|
||||||
|
arg.type,
|
||||||
|
'',
|
||||||
|
arg.description,
|
||||||
|
'',
|
||||||
|
].join('|'),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
command.options.forEach((opt) => {
|
||||||
|
options.push(
|
||||||
|
[
|
||||||
|
'',
|
||||||
|
opt.name,
|
||||||
|
getOptionDescriptor(opt),
|
||||||
|
opt.type.replace(/\|/g, '\\|'),
|
||||||
|
opt.required ? 'true' : 'false',
|
||||||
|
opt.default ?? '',
|
||||||
|
opt.description,
|
||||||
|
'',
|
||||||
|
].join('|'),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
const content = `# ${startCase(command.name)}
|
||||||
|
|
||||||
|
> This file is generated by cli/codegen. Do not edit this file manually.
|
||||||
|
|
||||||
|
${command.description}
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
\`\`\`sh
|
||||||
|
# CLI
|
||||||
|
napi ${kebabCase(command.name)}${command.args.reduce(
|
||||||
|
(h, arg) => h + ` <${arg.name}>`,
|
||||||
|
'',
|
||||||
|
)} [--options]
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
\`\`\`typescript
|
||||||
|
// Programatically
|
||||||
|
import { NapiCli } from '@napi-rs/cli'
|
||||||
|
|
||||||
|
new NapiCli().${command.name}({
|
||||||
|
// options
|
||||||
|
})
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
| Options | CLI Options | type | required | default | description |
|
||||||
|
| ------- | ----------- | ---- | -------- | ------- | ----------- |
|
||||||
|
| | --help,-h | | | | get help |
|
||||||
|
${options.join('\n')}
|
||||||
|
`
|
||||||
|
|
||||||
|
// make sure the target folder exists
|
||||||
|
fs.mkdirSync(targetFolder, { recursive: true })
|
||||||
|
// write file
|
||||||
|
fs.writeFileSync(docsFile, content)
|
||||||
|
|
||||||
|
return docsFile
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateDef(cmd: CommandSchema, folder: string): string {
|
||||||
|
const defFileName = kebabCase(cmd.name)
|
||||||
|
const defFilePath = path.join(folder, `${defFileName}.ts`)
|
||||||
|
|
||||||
|
const def = `// This file is generated by codegen/index.ts
|
||||||
|
// Do not edit this file manually
|
||||||
|
import { Command, Option } from 'clipanion'
|
||||||
|
${generateCommandDef(cmd)}
|
||||||
|
|
||||||
|
${generateOptionsDef(cmd)}
|
||||||
|
`
|
||||||
|
|
||||||
|
// make sure the target folder exists
|
||||||
|
fs.mkdirSync(folder, { recursive: true })
|
||||||
|
// write file
|
||||||
|
fs.writeFileSync(defFilePath, def)
|
||||||
|
|
||||||
|
return defFilePath
|
||||||
|
}
|
||||||
|
|
||||||
|
function codegen() {
|
||||||
|
const outputs: string[] = []
|
||||||
|
commandDefines.forEach((command) => {
|
||||||
|
outputs.push(generateDef(command, defFolder))
|
||||||
|
outputs.push(generateDocs(command, docsTargetFolder))
|
||||||
|
})
|
||||||
|
|
||||||
|
outputs.forEach((output) => {
|
||||||
|
execSync(`yarn prettier -w ${output}`)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
codegen()
|
31
cli/docs/artifacts.md
Normal file
31
cli/docs/artifacts.md
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
# Artifacts
|
||||||
|
|
||||||
|
> This file is generated by cli/codegen. Do not edit this file manually.
|
||||||
|
|
||||||
|
Copy artifacts from Github Actions into npm packages and ready to publish
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# CLI
|
||||||
|
napi artifacts [--options]
|
||||||
|
```
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Programatically
|
||||||
|
import { NapiCli } from '@napi-rs/cli'
|
||||||
|
|
||||||
|
new NapiCli().artifacts({
|
||||||
|
// options
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
| Options | CLI Options | type | required | default | description |
|
||||||
|
| --------------- | ------------------- | ------ | -------- | -------------- | ------------------------------------------------------------------------------------------------------------------ |
|
||||||
|
| | --help,-h | | | | get help |
|
||||||
|
| cwd | --cwd | string | false | process.cwd() | The working directory of where napi command will be executed in, all other paths options are relative to this path |
|
||||||
|
| packageJsonPath | --package-json-path | string | false | 'package.json' | Path to `package.json` |
|
||||||
|
| outputDir | --output-dir,-o | string | false | './' | Path to the folder where all built `.node` files put, same as `--output-dir` of build command |
|
||||||
|
| npmDir | --npm-dir | string | false | 'npm' | Path to the folder where the npm packages put |
|
50
cli/docs/build.md
Normal file
50
cli/docs/build.md
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
# Build
|
||||||
|
|
||||||
|
> This file is generated by cli/codegen. Do not edit this file manually.
|
||||||
|
|
||||||
|
Build the napi-rs project
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# CLI
|
||||||
|
napi build [--options]
|
||||||
|
```
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Programatically
|
||||||
|
import { NapiCli } from '@napi-rs/cli'
|
||||||
|
|
||||||
|
new NapiCli().build({
|
||||||
|
// options
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
| Options | CLI Options | type | required | default | description |
|
||||||
|
| ----------------- | --------------------- | -------- | -------- | ------- | ------------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| | --help,-h | | | | get help |
|
||||||
|
| target | --target,-t | string | false | | Build for the target triple, bypassed to `cargo build --target` |
|
||||||
|
| cwd | --cwd | string | false | | The working directory of where napi command will be executed in, all other paths options are relative to this path |
|
||||||
|
| manifestPath | --manifest-path | string | false | | Path to `Cargo.toml` |
|
||||||
|
| packageJsonPath | --package-json-path | string | false | | Path to `package.json` |
|
||||||
|
| targetDir | --target-dir | string | false | | Directory for all crate generated artifacts, see `cargo build --target-dir` |
|
||||||
|
| outputDir | --output-dir,-o | string | false | | Path to where all the built files would be put. Default to the crate folder |
|
||||||
|
| platform | --platform | boolean | false | | Add platform triple to the generated nodejs binding file, eg: `[name].linux-x64-gnu.node` |
|
||||||
|
| jsPackageName | --js-package-name | string | false | | Package name in generated js binding file. Only works with `--platform` flag |
|
||||||
|
| jsBinding | --js | string | false | | Path and filename of generated JS binding file. Only works with `--platform` flag. Relative to `--output_dir`. |
|
||||||
|
| noJsBinding | --no-js | boolean | false | | Whether to disable the generation JS binding file. Only works with `--platform` flag. |
|
||||||
|
| dts | --dts | string | false | | Path and filename of generated type def file. Relative to `--output_dir` |
|
||||||
|
| dtsHeader | --dts-header | string | false | | Custom file header for generated type def file. Only works when `typedef` feature enabled. |
|
||||||
|
| noDtsHeader | --no-dts-header | boolean | false | | Whether to disable the default file header for generated type def file. Only works when `typedef` feature enabled. |
|
||||||
|
| strip | --strip,-s | boolean | false | | Whether strip the library to achieve the minimum file size |
|
||||||
|
| release | --release,-r | boolean | false | | Build in release mode |
|
||||||
|
| verbose | --verbose,-v | boolean | false | | Verbosely log build command trace |
|
||||||
|
| bin | --bin | string | false | | Build only the specified binary |
|
||||||
|
| package | --package,-p | string | false | | Build the specified library or the one at cwd |
|
||||||
|
| crossCompile | --cross-compile,-x | boolean | false | | [experimental] cross-compile for the specified target with `cargo-xwin` on windows and `cargo-zigbuild` on other platform |
|
||||||
|
| watch | --watch,-w | boolean | false | | watch the crate changes and build continiously with `cargo-watch` crates |
|
||||||
|
| features | --features,-F | string[] | false | | Space-separated list of features to activate |
|
||||||
|
| allFeatures | --all-features | boolean | false | | Activate all available features |
|
||||||
|
| noDefaultFeatures | --no-default-features | boolean | false | | Do not activate the `default` feature |
|
31
cli/docs/create-npm-dirs.md
Normal file
31
cli/docs/create-npm-dirs.md
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
# Create Npm Dirs
|
||||||
|
|
||||||
|
> This file is generated by cli/codegen. Do not edit this file manually.
|
||||||
|
|
||||||
|
Create npm package dirs for different platforms
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# CLI
|
||||||
|
napi create-npm-dirs [--options]
|
||||||
|
```
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Programatically
|
||||||
|
import { NapiCli } from '@napi-rs/cli'
|
||||||
|
|
||||||
|
new NapiCli().createNpmDirs({
|
||||||
|
// options
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
| Options | CLI Options | type | required | default | description |
|
||||||
|
| --------------- | ------------------- | ------- | -------- | -------------- | ------------------------------------------------------------------------------------------------------------------ |
|
||||||
|
| | --help,-h | | | | get help |
|
||||||
|
| cwd | --cwd | string | false | process.cwd() | The working directory of where napi command will be executed in, all other paths options are relative to this path |
|
||||||
|
| packageJsonPath | --package-json-path | string | false | 'package.json' | Path to `package.json` |
|
||||||
|
| npmDir | --npm-dir | string | false | 'npm' | Path to the folder where the npm packages put |
|
||||||
|
| dryRun | --dry-run | boolean | false | false | Dry run without touching file system |
|
37
cli/docs/new.md
Normal file
37
cli/docs/new.md
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
# New
|
||||||
|
|
||||||
|
> This file is generated by cli/codegen. Do not edit this file manually.
|
||||||
|
|
||||||
|
Create a new project with pre-configured boilerplate
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# CLI
|
||||||
|
napi new <path> [--options]
|
||||||
|
```
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Programatically
|
||||||
|
import { NapiCli } from '@napi-rs/cli'
|
||||||
|
|
||||||
|
new NapiCli().new({
|
||||||
|
// options
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
| Options | CLI Options | type | required | default | description |
|
||||||
|
| -------------------- | ------------------------ | -------- | -------- | ------- | -------------------------------------------------------------------------------- |
|
||||||
|
| | --help,-h | | | | get help |
|
||||||
|
| path | <path> | true | string | | The path where the napi-rs project will be created. |
|
||||||
|
| name | --name,-n | string | false | | The name of the project, default to the name of the directory if not provided |
|
||||||
|
| minNodeApiVersion | --min-node-api,-v | number | false | 4 | The minimum Node-API version to support |
|
||||||
|
| license | --license,-l | string | false | 'MIT' | License for open-sourced project |
|
||||||
|
| targets | --targets,-t | string[] | false | [] | All targets the crate will be compiled for. |
|
||||||
|
| enableDefaultTargets | --enable-default-targets | boolean | false | true | Whether enable default targets |
|
||||||
|
| enableAllTargets | --enable-all-targets | boolean | false | false | Whether enable all targets |
|
||||||
|
| enableTypeDef | --enable-type-def | boolean | false | true | Whether enable the `type-def` feature for typescript definitions auto-generation |
|
||||||
|
| enableGithubActions | --enable-github-actions | boolean | false | true | Whether generate preconfigured GitHub Actions workflow |
|
||||||
|
| dryRun | --dry-run | boolean | false | false | Whether to run the command in dry-run mode |
|
35
cli/docs/pre-publish.md
Normal file
35
cli/docs/pre-publish.md
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
# Pre Publish
|
||||||
|
|
||||||
|
> This file is generated by cli/codegen. Do not edit this file manually.
|
||||||
|
|
||||||
|
Update package.json and copy addons into per platform packages
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# CLI
|
||||||
|
napi pre-publish [--options]
|
||||||
|
```
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Programatically
|
||||||
|
import { NapiCli } from '@napi-rs/cli'
|
||||||
|
|
||||||
|
new NapiCli().prePublish({
|
||||||
|
// options
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
| Options | CLI Options | type | required | default | description |
|
||||||
|
| --------------- | ------------------- | ---------------- | -------- | -------------- | ------------------------------------------------------------------------------------------------------------------ |
|
||||||
|
| | --help,-h | | | | get help |
|
||||||
|
| cwd | --cwd | string | false | process.cwd() | The working directory of where napi command will be executed in, all other paths options are relative to this path |
|
||||||
|
| packageJsonPath | --package-json-path | string | false | 'package.json' | Path to `package.json` |
|
||||||
|
| npmDir | --npm-dir | string | false | 'npm' | Path to the folder where the npm packages put |
|
||||||
|
| tagStyle | --tag-style | 'npm' \| 'lerna' | false | 'lerna' | git tag style, `npm` or `lerna` |
|
||||||
|
| ghRelease | --gh-release | boolean | false | true | Whether create GitHub release |
|
||||||
|
| ghReleaseName | --gh-release-name | string | false | | GitHub release name |
|
||||||
|
| ghReleaseId | --gh-release-id | string | false | | Existing GitHub release id |
|
||||||
|
| dryRun | --dry-run | boolean | false | false | Dry run without touching file system |
|
36
cli/docs/rename.md
Normal file
36
cli/docs/rename.md
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
# Rename
|
||||||
|
|
||||||
|
> This file is generated by cli/codegen. Do not edit this file manually.
|
||||||
|
|
||||||
|
Rename the napi-rs project
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# CLI
|
||||||
|
napi rename [--options]
|
||||||
|
```
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Programatically
|
||||||
|
import { NapiCli } from '@napi-rs/cli'
|
||||||
|
|
||||||
|
new NapiCli().rename({
|
||||||
|
// options
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
| Options | CLI Options | type | required | default | description |
|
||||||
|
| --------------- | ------------------- | ------ | -------- | -------------- | ------------------------------------------------------------------------------------------------------------------ |
|
||||||
|
| | --help,-h | | | | get help |
|
||||||
|
| cwd | --cwd | string | false | process.cwd() | The working directory of where napi command will be executed in, all other paths options are relative to this path |
|
||||||
|
| packageJsonPath | --package-json-path | string | false | 'package.json' | Path to `package.json` |
|
||||||
|
| npmDir | --npm-dir | string | false | 'npm' | Path to the folder where the npm packages put |
|
||||||
|
| name | --name,-n | string | false | | The new name of the project |
|
||||||
|
| binaryName | --binary-name,-b | string | false | | The new binary name \*.node files |
|
||||||
|
| packageName | --package-name | string | false | | The new package name of the project |
|
||||||
|
| manifestPath | --manifest-path | string | false | 'Cargo.toml' | Path to `Cargo.toml` |
|
||||||
|
| repository | --repository | string | false | | The new repository of the project |
|
||||||
|
| description | --description | string | false | | The new description of the project |
|
30
cli/docs/universalize.md
Normal file
30
cli/docs/universalize.md
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
# Universalize
|
||||||
|
|
||||||
|
> This file is generated by cli/codegen. Do not edit this file manually.
|
||||||
|
|
||||||
|
Combile built binaries into one universal binary
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# CLI
|
||||||
|
napi universalize [--options]
|
||||||
|
```
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Programatically
|
||||||
|
import { NapiCli } from '@napi-rs/cli'
|
||||||
|
|
||||||
|
new NapiCli().universalize({
|
||||||
|
// options
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
| Options | CLI Options | type | required | default | description |
|
||||||
|
| --------------- | ------------------- | ------ | -------- | -------------- | ------------------------------------------------------------------------------------------------------------------ |
|
||||||
|
| | --help,-h | | | | get help |
|
||||||
|
| cwd | --cwd | string | false | process.cwd() | The working directory of where napi command will be executed in, all other paths options are relative to this path |
|
||||||
|
| packageJsonPath | --package-json-path | string | false | 'package.json' | Path to `package.json` |
|
||||||
|
| outputDir | --output-dir,-o | string | false | './' | Path to the folder where all built `.node` files put, same as `--output-dir` of build command |
|
30
cli/docs/version.md
Normal file
30
cli/docs/version.md
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
# Version
|
||||||
|
|
||||||
|
> This file is generated by cli/codegen. Do not edit this file manually.
|
||||||
|
|
||||||
|
Update version in created npm packages
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# CLI
|
||||||
|
napi version [--options]
|
||||||
|
```
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Programatically
|
||||||
|
import { NapiCli } from '@napi-rs/cli'
|
||||||
|
|
||||||
|
new NapiCli().version({
|
||||||
|
// options
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
| Options | CLI Options | type | required | default | description |
|
||||||
|
| --------------- | ------------------- | ------ | -------- | -------------- | ------------------------------------------------------------------------------------------------------------------ |
|
||||||
|
| | --help,-h | | | | get help |
|
||||||
|
| cwd | --cwd | string | false | process.cwd() | The working directory of where napi command will be executed in, all other paths options are relative to this path |
|
||||||
|
| packageJsonPath | --package-json-path | string | false | 'package.json' | Path to `package.json` |
|
||||||
|
| npmDir | --npm-dir | string | false | 'npm' | Path to the folder where the npm packages put |
|
11
cli/esbuild.mjs
Normal file
11
cli/esbuild.mjs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import * as esbuild from 'esbuild'
|
||||||
|
|
||||||
|
await esbuild.build({
|
||||||
|
entryPoints: ['./dist/index.js'],
|
||||||
|
outfile: './dist/index.cjs',
|
||||||
|
bundle: true,
|
||||||
|
platform: 'node',
|
||||||
|
define: {
|
||||||
|
'import.meta.url': '__filename',
|
||||||
|
},
|
||||||
|
})
|
|
@ -2,30 +2,57 @@
|
||||||
"name": "@napi-rs/cli",
|
"name": "@napi-rs/cli",
|
||||||
"version": "2.15.2",
|
"version": "2.15.2",
|
||||||
"description": "Cli tools for napi-rs",
|
"description": "Cli tools for napi-rs",
|
||||||
|
"author": "LongYinan <lynweklm@gmail.com>",
|
||||||
|
"homepage": "https://github.com/napi-rs/napi-rs",
|
||||||
|
"license": "MIT",
|
||||||
|
"type": "module",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 16"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"napi": "./dist/cli.js",
|
||||||
|
"napi-raw": "./cli.mjs"
|
||||||
|
},
|
||||||
|
"main": "./dist/index.cjs",
|
||||||
|
"module": "./dist/index.js",
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"import": {
|
||||||
|
"default": "./dist/index.js",
|
||||||
|
"types": "./dist/index.d.ts"
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"default": "./dist/index.cjs",
|
||||||
|
"types": "./dist/index.d.ts"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"./package.json": {
|
||||||
|
"import": "./package.json",
|
||||||
|
"require": "./package.json"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"dist",
|
||||||
|
"src"
|
||||||
|
],
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"cli",
|
"cli",
|
||||||
"rust",
|
"rust",
|
||||||
"napi",
|
"napi",
|
||||||
"n-api",
|
"n-api",
|
||||||
|
"node-api",
|
||||||
|
"node-addon",
|
||||||
"neon"
|
"neon"
|
||||||
],
|
],
|
||||||
"author": "LongYinan <lynweklm@gmail.com>",
|
|
||||||
"homepage": "https://github.com/napi-rs/napi-rs",
|
|
||||||
"license": "MIT",
|
|
||||||
"bin": {
|
|
||||||
"napi": "./scripts/index.js"
|
|
||||||
},
|
|
||||||
"files": [
|
|
||||||
"scripts"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 10"
|
|
||||||
},
|
|
||||||
"maintainers": [
|
"maintainers": [
|
||||||
{
|
{
|
||||||
"name": "LongYinan",
|
"name": "LongYinan",
|
||||||
"email": "lynweklm@gmail.com",
|
"email": "lynweklm@gmail.com",
|
||||||
"homepage": "https://github.com/Brooooooklyn"
|
"homepage": "https://github.com/Brooooooklyn"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "forehalo",
|
||||||
|
"homepage": "https://github.com/forehalo"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -39,26 +66,34 @@
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/napi-rs/napi-rs/issues"
|
"url": "https://github.com/napi-rs/napi-rs/issues"
|
||||||
},
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@octokit/rest": "^19.0.7",
|
||||||
|
"clipanion": "^3.2.0",
|
||||||
|
"colorette": "^2.0.19",
|
||||||
|
"debug": "^4.3.4",
|
||||||
|
"inquirer": "^9.1.5",
|
||||||
|
"js-yaml": "^4.1.0",
|
||||||
|
"lodash-es": "^4.17.21",
|
||||||
|
"typanion": "^3.12.1"
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@octokit/rest": "^19.0.5",
|
|
||||||
"@types/inquirer": "^9.0.3",
|
"@types/inquirer": "^9.0.3",
|
||||||
"@types/js-yaml": "^4.0.5",
|
"@types/js-yaml": "^4.0.5",
|
||||||
"@types/lodash-es": "^4.17.6",
|
"@types/lodash-es": "^4.17.6",
|
||||||
"clipanion": "^3.1.0",
|
"ava": "^5.2.0",
|
||||||
"colorette": "^2.0.19",
|
"esbuild": "^0.17.14",
|
||||||
"core-js": "^3.27.1",
|
"prettier": "^2.8.7",
|
||||||
"debug": "^4.3.4",
|
"ts-node": "^10.9.1",
|
||||||
"env-paths": "^3.0.0",
|
"typescript": "^4.9.4"
|
||||||
"fdir": "^5.3.0",
|
|
||||||
"inquirer": "^9.1.4",
|
|
||||||
"js-yaml": "^4.1.0",
|
|
||||||
"lodash-es": "4.17.21",
|
|
||||||
"toml": "^3.0.0",
|
|
||||||
"tslib": "^2.4.1",
|
|
||||||
"typanion": "^3.12.1"
|
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
"url": "https://github.com/sponsors/Brooooooklyn"
|
"url": "https://github.com/sponsors/Brooooooklyn"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"codegen": "node --loader ts-node/esm/transpile-only ./codegen/index.ts",
|
||||||
|
"build": "tsc && yarn build:cjs",
|
||||||
|
"build:cjs": "node ./esbuild.mjs",
|
||||||
|
"test": "ava"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,132 +0,0 @@
|
||||||
import test from 'ava'
|
|
||||||
|
|
||||||
import { parseTriple } from '../parse-triple'
|
|
||||||
|
|
||||||
const triples = [
|
|
||||||
{
|
|
||||||
name: 'x86_64-unknown-linux-musl',
|
|
||||||
expected: {
|
|
||||||
abi: 'musl',
|
|
||||||
arch: 'x64',
|
|
||||||
platform: 'linux',
|
|
||||||
platformArchABI: 'linux-x64-musl',
|
|
||||||
raw: 'x86_64-unknown-linux-musl',
|
|
||||||
} as const,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'x86_64-unknown-linux-gnu',
|
|
||||||
expected: {
|
|
||||||
abi: 'gnu',
|
|
||||||
arch: 'x64',
|
|
||||||
platform: 'linux',
|
|
||||||
platformArchABI: 'linux-x64-gnu',
|
|
||||||
raw: 'x86_64-unknown-linux-gnu',
|
|
||||||
} as const,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'x86_64-pc-windows-msvc',
|
|
||||||
expected: {
|
|
||||||
abi: 'msvc',
|
|
||||||
arch: 'x64',
|
|
||||||
platform: 'win32',
|
|
||||||
platformArchABI: 'win32-x64-msvc',
|
|
||||||
raw: 'x86_64-pc-windows-msvc',
|
|
||||||
} as const,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'x86_64-apple-darwin',
|
|
||||||
expected: {
|
|
||||||
abi: null,
|
|
||||||
arch: 'x64',
|
|
||||||
platform: 'darwin',
|
|
||||||
platformArchABI: 'darwin-x64',
|
|
||||||
raw: 'x86_64-apple-darwin',
|
|
||||||
} as const,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'i686-pc-windows-msvc',
|
|
||||||
expected: {
|
|
||||||
abi: 'msvc',
|
|
||||||
arch: 'ia32',
|
|
||||||
platform: 'win32',
|
|
||||||
platformArchABI: 'win32-ia32-msvc',
|
|
||||||
raw: 'i686-pc-windows-msvc',
|
|
||||||
} as const,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'x86_64-unknown-freebsd',
|
|
||||||
expected: {
|
|
||||||
abi: null,
|
|
||||||
arch: 'x64',
|
|
||||||
platform: 'freebsd',
|
|
||||||
platformArchABI: 'freebsd-x64',
|
|
||||||
raw: 'x86_64-unknown-freebsd',
|
|
||||||
} as const,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'aarch64-unknown-linux-gnu',
|
|
||||||
expected: {
|
|
||||||
abi: 'gnu',
|
|
||||||
arch: 'arm64',
|
|
||||||
platform: 'linux',
|
|
||||||
platformArchABI: 'linux-arm64-gnu',
|
|
||||||
raw: 'aarch64-unknown-linux-gnu',
|
|
||||||
} as const,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'aarch64-pc-windows-msvc',
|
|
||||||
expected: {
|
|
||||||
abi: 'msvc',
|
|
||||||
arch: 'arm64',
|
|
||||||
platform: 'win32',
|
|
||||||
platformArchABI: 'win32-arm64-msvc',
|
|
||||||
raw: 'aarch64-pc-windows-msvc',
|
|
||||||
} as const,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'armv7-unknown-linux-gnueabihf',
|
|
||||||
expected: {
|
|
||||||
abi: 'gnueabihf',
|
|
||||||
arch: 'arm',
|
|
||||||
platform: 'linux',
|
|
||||||
platformArchABI: 'linux-arm-gnueabihf',
|
|
||||||
raw: 'armv7-unknown-linux-gnueabihf',
|
|
||||||
} as const,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'aarch64-linux-android',
|
|
||||||
expected: {
|
|
||||||
abi: null,
|
|
||||||
arch: 'arm64',
|
|
||||||
platform: 'android',
|
|
||||||
platformArchABI: 'android-arm64',
|
|
||||||
raw: 'aarch64-linux-android',
|
|
||||||
},
|
|
||||||
} as const,
|
|
||||||
{
|
|
||||||
name: 'armv7-linux-androideabi',
|
|
||||||
expected: {
|
|
||||||
abi: 'eabi',
|
|
||||||
arch: 'arm',
|
|
||||||
platform: 'android',
|
|
||||||
platformArchABI: 'android-arm-eabi',
|
|
||||||
raw: 'armv7-linux-androideabi',
|
|
||||||
},
|
|
||||||
} as const,
|
|
||||||
{
|
|
||||||
name: 'universal-apple-darwin',
|
|
||||||
expected: {
|
|
||||||
abi: null,
|
|
||||||
arch: 'universal',
|
|
||||||
platform: 'darwin',
|
|
||||||
platformArchABI: 'darwin-universal',
|
|
||||||
raw: 'universal-apple-darwin',
|
|
||||||
},
|
|
||||||
} as const,
|
|
||||||
]
|
|
||||||
|
|
||||||
for (const triple of triples) {
|
|
||||||
test(`should parse ${triple.name}`, (t) => {
|
|
||||||
t.deepEqual(parseTriple(triple.name), triple.expected)
|
|
||||||
})
|
|
||||||
}
|
|
93
cli/src/api/artifacts.ts
Normal file
93
cli/src/api/artifacts.ts
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
import { join, parse, resolve } from 'path'
|
||||||
|
|
||||||
|
import * as colors from 'colorette'
|
||||||
|
|
||||||
|
import {
|
||||||
|
applyDefaultArtifactsOptions,
|
||||||
|
ArtifactsOptions,
|
||||||
|
} from '../def/artifacts.js'
|
||||||
|
import {
|
||||||
|
readNapiConfig,
|
||||||
|
debugFactory,
|
||||||
|
readFileAsync,
|
||||||
|
writeFileAsync,
|
||||||
|
UniArchsByPlatform,
|
||||||
|
readdirAsync,
|
||||||
|
} from '../utils/index.js'
|
||||||
|
|
||||||
|
const debug = debugFactory('artifacts')
|
||||||
|
|
||||||
|
export async function collectArtifacts(userOptions: ArtifactsOptions) {
|
||||||
|
const options = applyDefaultArtifactsOptions(userOptions)
|
||||||
|
|
||||||
|
const packageJsonPath = resolve(options.cwd, options.packageJsonPath)
|
||||||
|
const { targets, binaryName } = await readNapiConfig(packageJsonPath)
|
||||||
|
|
||||||
|
const distDirs = targets.map((platform) =>
|
||||||
|
resolve(options.cwd, options.npmDir, platform.platformArchABI),
|
||||||
|
)
|
||||||
|
|
||||||
|
const universalSourceBins = new Set(
|
||||||
|
targets
|
||||||
|
.filter((platform) => platform.arch === 'universal')
|
||||||
|
.flatMap((p) =>
|
||||||
|
UniArchsByPlatform[p.platform]?.map((a) => `${p.platform}-${a}`),
|
||||||
|
)
|
||||||
|
.filter(Boolean) as string[],
|
||||||
|
)
|
||||||
|
|
||||||
|
await collectNodeBinaries(resolve(options.cwd, options.outputDir)).then(
|
||||||
|
(output) =>
|
||||||
|
Promise.all(
|
||||||
|
output.map(async (filePath) => {
|
||||||
|
debug.info(`Read [${colors.yellowBright(filePath)}]`)
|
||||||
|
const sourceContent = await readFileAsync(filePath)
|
||||||
|
const parsedName = parse(filePath)
|
||||||
|
const [_binaryName, platformArchABI] = parsedName.name.split('.')
|
||||||
|
if (_binaryName !== binaryName) {
|
||||||
|
debug.warn(
|
||||||
|
`[${_binaryName}] is not matched with [${binaryName}], skip`,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const dir = distDirs.find((dir) => dir.includes(platformArchABI))
|
||||||
|
if (!dir && universalSourceBins.has(platformArchABI)) {
|
||||||
|
debug.warn(
|
||||||
|
`[${platformArchABI}] has no dist dir but it is source bin for universal arch, skip`,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!dir) {
|
||||||
|
throw new Error(`No dist dir found for ${filePath}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const distFilePath = join(dir, parsedName.base)
|
||||||
|
debug.info(
|
||||||
|
`Write file content to [${colors.yellowBright(distFilePath)}]`,
|
||||||
|
)
|
||||||
|
await writeFileAsync(distFilePath, sourceContent)
|
||||||
|
const distFilePathLocal = join(
|
||||||
|
parse(packageJsonPath).dir,
|
||||||
|
parsedName.base,
|
||||||
|
)
|
||||||
|
debug.info(
|
||||||
|
`Write file content to [${colors.yellowBright(distFilePathLocal)}]`,
|
||||||
|
)
|
||||||
|
await writeFileAsync(distFilePathLocal, sourceContent)
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function collectNodeBinaries(root: string) {
|
||||||
|
const files = await readdirAsync(root, { withFileTypes: true })
|
||||||
|
const nodeBinaries = files
|
||||||
|
.filter((file) => file.isFile() && file.name.endsWith('.node'))
|
||||||
|
.map((file) => join(root, file.name))
|
||||||
|
|
||||||
|
const dirs = files.filter((file) => file.isDirectory())
|
||||||
|
for (const dir of dirs) {
|
||||||
|
nodeBinaries.push(...(await collectNodeBinaries(join(root, dir.name))))
|
||||||
|
}
|
||||||
|
return nodeBinaries
|
||||||
|
}
|
549
cli/src/api/build.ts
Normal file
549
cli/src/api/build.ts
Normal file
|
@ -0,0 +1,549 @@
|
||||||
|
import { spawn } from 'child_process'
|
||||||
|
import { createHash } from 'crypto'
|
||||||
|
import { tmpdir } from 'os'
|
||||||
|
import { parse, join, resolve } from 'path'
|
||||||
|
|
||||||
|
import * as colors from 'colorette'
|
||||||
|
|
||||||
|
import { BuildOptions as RawBuildOptions } from '../def/build.js'
|
||||||
|
import {
|
||||||
|
CLI_VERSION,
|
||||||
|
copyFileAsync,
|
||||||
|
Crate,
|
||||||
|
debugFactory,
|
||||||
|
DEFAULT_TYPE_DEF_HEADER,
|
||||||
|
fileExists,
|
||||||
|
getSystemDefaultTarget,
|
||||||
|
getTargetLinker,
|
||||||
|
mkdirAsync,
|
||||||
|
NapiConfig,
|
||||||
|
parseMetadata,
|
||||||
|
parseTriple,
|
||||||
|
processTypeDef,
|
||||||
|
readNapiConfig,
|
||||||
|
Target,
|
||||||
|
targetToEnvVar,
|
||||||
|
tryInstallCargoBinary,
|
||||||
|
unlinkAsync,
|
||||||
|
writeFileAsync,
|
||||||
|
} from '../utils/index.js'
|
||||||
|
|
||||||
|
import { createJsBinding } from './templates/index.js'
|
||||||
|
|
||||||
|
const debug = debugFactory('build')
|
||||||
|
|
||||||
|
type OutputKind = 'js' | 'dts' | 'node' | 'exe'
|
||||||
|
type Output = {
|
||||||
|
kind: OutputKind
|
||||||
|
path: string
|
||||||
|
}
|
||||||
|
|
||||||
|
type BuildOptions = RawBuildOptions & {
|
||||||
|
cargoOptions?: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function buildProject(options: BuildOptions) {
|
||||||
|
debug('napi build command receive options: %O', options)
|
||||||
|
|
||||||
|
const cwd = options.cwd ?? process.cwd()
|
||||||
|
|
||||||
|
const resolvePath = (...paths: string[]) => resolve(cwd, ...paths)
|
||||||
|
|
||||||
|
const manifestPath = resolvePath(options.manifestPath ?? 'Cargo.toml')
|
||||||
|
const metadata = parseMetadata(manifestPath)
|
||||||
|
|
||||||
|
const pkg = metadata.packages.find((p) => {
|
||||||
|
// package with given name
|
||||||
|
if (options.package) {
|
||||||
|
return p.name === options.package
|
||||||
|
} else {
|
||||||
|
return p.manifest_path === manifestPath
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!pkg) {
|
||||||
|
throw new Error(
|
||||||
|
'Unable to find crate to build. It seems you are trying to build a crate in a workspace, try using `--package` option to specify the package to build.',
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const crateDir = parse(pkg.manifest_path).dir
|
||||||
|
|
||||||
|
const builder = new Builder(
|
||||||
|
options,
|
||||||
|
pkg,
|
||||||
|
cwd,
|
||||||
|
options.target
|
||||||
|
? parseTriple(options.target)
|
||||||
|
: process.env.CARGO_BUILD_TARGET
|
||||||
|
? parseTriple(process.env.CARGO_BUILD_TARGET)
|
||||||
|
: getSystemDefaultTarget(),
|
||||||
|
crateDir,
|
||||||
|
resolvePath(options.outputDir ?? crateDir),
|
||||||
|
options.targetDir ??
|
||||||
|
process.env.CARGO_BUILD_TARGET_DIR ??
|
||||||
|
metadata.target_directory,
|
||||||
|
await readNapiConfig(
|
||||||
|
resolvePath(options.packageJsonPath ?? 'package.json'),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
return builder.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
class Builder {
|
||||||
|
private readonly args: string[] = []
|
||||||
|
private readonly envs: Record<string, string> = {}
|
||||||
|
private readonly outputs: Output[] = []
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly options: BuildOptions,
|
||||||
|
private readonly crate: Crate,
|
||||||
|
private readonly cwd: string,
|
||||||
|
private readonly target: Target,
|
||||||
|
private readonly crateDir: string,
|
||||||
|
private readonly outputDir: string,
|
||||||
|
private readonly targetDir: string,
|
||||||
|
private readonly config: NapiConfig,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
get cdyLibName() {
|
||||||
|
return this.crate.targets.find((t) => t.crate_types.includes('cdylib'))
|
||||||
|
?.name
|
||||||
|
}
|
||||||
|
|
||||||
|
get binName() {
|
||||||
|
return (
|
||||||
|
this.options.bin ??
|
||||||
|
// only available if not cdylib or bin name specified
|
||||||
|
(this.cdyLibName
|
||||||
|
? null
|
||||||
|
: this.crate.targets.find((t) => t.crate_types.includes('bin'))?.name)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
build() {
|
||||||
|
if (!this.cdyLibName) {
|
||||||
|
const warning =
|
||||||
|
'Missing `crate-type = ["cdylib"]` in [lib] config. The build result will not be available as node addon.'
|
||||||
|
|
||||||
|
if (this.binName) {
|
||||||
|
debug.warn(warning)
|
||||||
|
} else {
|
||||||
|
throw new Error(warning)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.pickBinary()
|
||||||
|
.setPackage()
|
||||||
|
.setFeatures()
|
||||||
|
.setTarget()
|
||||||
|
.setEnvs()
|
||||||
|
.setBypassArgs()
|
||||||
|
.exec()
|
||||||
|
}
|
||||||
|
|
||||||
|
private exec() {
|
||||||
|
debug(`Start building crate: ${this.crate.name}`)
|
||||||
|
debug(' %i', `cargo ${this.args.join(' ')}`)
|
||||||
|
|
||||||
|
const controller = new AbortController()
|
||||||
|
|
||||||
|
const buildTask = new Promise<void>((resolve, reject) => {
|
||||||
|
const buildProcess = spawn('cargo', this.args, {
|
||||||
|
env: {
|
||||||
|
...process.env,
|
||||||
|
...this.envs,
|
||||||
|
},
|
||||||
|
stdio: 'inherit',
|
||||||
|
cwd: this.cwd,
|
||||||
|
signal: controller.signal,
|
||||||
|
})
|
||||||
|
|
||||||
|
buildProcess.once('exit', (code) => {
|
||||||
|
if (code === 0) {
|
||||||
|
debug('%i', `Build crate ${this.crate.name} successfully!`)
|
||||||
|
resolve()
|
||||||
|
} else {
|
||||||
|
reject(new Error(`Build failed with exit code ${code}`))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
buildProcess.once('error', (e) => {
|
||||||
|
reject(
|
||||||
|
new Error(`Build failed with error: ${e.message}`, {
|
||||||
|
cause: e,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
task: buildTask.then(() => this.postBuild()),
|
||||||
|
abort: () => controller.abort(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private pickBinary() {
|
||||||
|
let set = false
|
||||||
|
if (this.options.watch) {
|
||||||
|
if (process.env.CI) {
|
||||||
|
debug.warn('Watch mode is not supported in CI environment')
|
||||||
|
} else {
|
||||||
|
debug('Use %i', 'cargo-watch')
|
||||||
|
tryInstallCargoBinary('cargo-watch', 'watch')
|
||||||
|
// yarn napi watch --target x86_64-unknown-linux-gnu [--cross-compile]
|
||||||
|
// ===>
|
||||||
|
// cargo watch [...] -- build --target x86_64-unknown-linux-gnu
|
||||||
|
// cargo watch [...] -- zigbuild --target x86_64-unknown-linux-gnu
|
||||||
|
this.args.push(
|
||||||
|
'watch',
|
||||||
|
'--why',
|
||||||
|
'-i',
|
||||||
|
'*.{js,ts,node}',
|
||||||
|
'-w',
|
||||||
|
this.crateDir,
|
||||||
|
'--',
|
||||||
|
'cargo',
|
||||||
|
)
|
||||||
|
set = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.options.crossCompile) {
|
||||||
|
if (this.target.platform === 'win32') {
|
||||||
|
if (process.platform === 'win32') {
|
||||||
|
debug.warn(
|
||||||
|
'You are trying to cross compile to win32 platform on win32 platform which is unnecessary.',
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// use cargo-xwin to cross compile to win32 platform
|
||||||
|
debug('Use %i', 'cargo-xwin')
|
||||||
|
tryInstallCargoBinary('cargo-xwin', 'xwin')
|
||||||
|
this.args.push('xwin', 'build')
|
||||||
|
if (this.target.arch === 'ia32') {
|
||||||
|
this.envs.XWIN_ARCH = 'x86'
|
||||||
|
}
|
||||||
|
set = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (
|
||||||
|
this.target.platform === 'linux' &&
|
||||||
|
process.platform === 'linux' &&
|
||||||
|
this.target.arch === process.arch &&
|
||||||
|
(function (abi: string | null) {
|
||||||
|
const glibcVersionRuntime =
|
||||||
|
// @ts-expect-error
|
||||||
|
process.report?.getReport()?.header?.glibcVersionRuntime
|
||||||
|
const libc = glibcVersionRuntime ? 'gnu' : 'musl'
|
||||||
|
return abi === libc
|
||||||
|
})(this.target.abi)
|
||||||
|
) {
|
||||||
|
debug.warn(
|
||||||
|
'You are trying to cross compile to linux target on linux platform which is unnecessary.',
|
||||||
|
)
|
||||||
|
} else if (
|
||||||
|
this.target.platform === 'darwin' &&
|
||||||
|
process.platform === 'darwin'
|
||||||
|
) {
|
||||||
|
debug.warn(
|
||||||
|
'You are trying to cross compile to darwin target on darwin platform which is unnecessary.',
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// use cargo-zigbuild to cross compile to other platforms
|
||||||
|
debug('Use %i', 'cargo-zigbuild')
|
||||||
|
tryInstallCargoBinary('cargo-zigbuild', 'zigbuild')
|
||||||
|
this.args.push('zigbuild')
|
||||||
|
set = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!set) {
|
||||||
|
this.args.push('build')
|
||||||
|
}
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
private setPackage() {
|
||||||
|
const args = []
|
||||||
|
|
||||||
|
if (this.options.package) {
|
||||||
|
args.push('--package', this.options.package)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.binName) {
|
||||||
|
args.push('--bin', this.binName)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.length) {
|
||||||
|
debug('Set package flags: ')
|
||||||
|
debug(' %O', args)
|
||||||
|
this.args.push(...args)
|
||||||
|
}
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
private setTarget() {
|
||||||
|
debug('Set compiling target to: ')
|
||||||
|
debug(' %i', this.target.triple)
|
||||||
|
|
||||||
|
this.args.push('--target', this.target.triple)
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
private setEnvs() {
|
||||||
|
// type definition intermediate file
|
||||||
|
this.envs.TYPE_DEF_TMP_PATH = this.getIntermediateTypeFile()
|
||||||
|
this.envs.CARGO_CFG_NAPI_RS_CLI_VERSION = CLI_VERSION
|
||||||
|
|
||||||
|
// RUSTFLAGS
|
||||||
|
let rustflags =
|
||||||
|
process.env.RUSTFLAGS ?? process.env.CARGO_BUILD_RUSTFLAGS ?? ''
|
||||||
|
|
||||||
|
if (
|
||||||
|
this.target.abi?.includes('musl') &&
|
||||||
|
!rustflags.includes('target-feature=-crt-static')
|
||||||
|
) {
|
||||||
|
rustflags += ' -C target-feature=-crt-static'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.options.strip && !rustflags.includes('link-arg=-s')) {
|
||||||
|
rustflags += ' -C link-arg=-s'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rustflags.length) {
|
||||||
|
this.envs.RUSTFLAGS = rustflags
|
||||||
|
}
|
||||||
|
// END RUSTFLAGS
|
||||||
|
|
||||||
|
// LINKER
|
||||||
|
const linker = getTargetLinker(this.target.triple)
|
||||||
|
if (
|
||||||
|
linker &&
|
||||||
|
!process.env.RUSTC_LINKER &&
|
||||||
|
!process.env[`CARGET_TARGET_${targetToEnvVar(this.target.triple)}_LINKER`]
|
||||||
|
) {
|
||||||
|
this.envs.RUSTC_LINKER = linker
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.target.platform === 'android') {
|
||||||
|
const { ANDROID_NDK_LATEST_HOME } = process.env
|
||||||
|
if (!ANDROID_NDK_LATEST_HOME) {
|
||||||
|
debug.warn(
|
||||||
|
`${colors.red(
|
||||||
|
'ANDROID_NDK_LATEST_HOME',
|
||||||
|
)} environment variable is missing`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const targetArch = this.target.arch === 'arm' ? 'armv7a' : 'aarch64'
|
||||||
|
const targetPlatform =
|
||||||
|
this.target.arch === 'arm' ? 'androideabi24' : 'android24'
|
||||||
|
Object.assign(this.envs, {
|
||||||
|
CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER: `${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/${targetArch}-linux-android24-clang`,
|
||||||
|
CARGO_TARGET_ARMV7_LINUX_ANDROIDEABI_LINKER: `${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/${targetArch}-linux-androideabi24-clang`,
|
||||||
|
CC: `${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/${targetArch}-linux-${targetPlatform}-clang`,
|
||||||
|
CXX: `${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/${targetArch}-linux-${targetPlatform}-clang++`,
|
||||||
|
AR: `${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ar`,
|
||||||
|
ANDROID_NDK: ANDROID_NDK_LATEST_HOME,
|
||||||
|
PATH: `${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin:${process.env.PATH}`,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// END LINKER
|
||||||
|
|
||||||
|
debug('Set envs: ')
|
||||||
|
Object.entries(this.envs).forEach(([k, v]) => {
|
||||||
|
debug(' %i', `${k}=${v}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
private setFeatures() {
|
||||||
|
const args = []
|
||||||
|
if (this.options.allFeatures) {
|
||||||
|
args.push('--all-features')
|
||||||
|
} else if (this.options.noDefaultFeatures) {
|
||||||
|
args.push('--no-default-features')
|
||||||
|
} else if (this.options.features) {
|
||||||
|
args.push('--features', ...this.options.features)
|
||||||
|
}
|
||||||
|
|
||||||
|
debug('Set features flags: ')
|
||||||
|
debug(' %O', args)
|
||||||
|
this.args.push(...args)
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
private setBypassArgs() {
|
||||||
|
if (this.options.release) {
|
||||||
|
this.args.push('--release')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.options.verbose) {
|
||||||
|
this.args.push('--verbose')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.options.targetDir) {
|
||||||
|
this.args.push('--target-dir', this.options.targetDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.options.cargoOptions?.length) {
|
||||||
|
this.args.push(...this.options.cargoOptions)
|
||||||
|
}
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
private getIntermediateTypeFile() {
|
||||||
|
return join(
|
||||||
|
tmpdir(),
|
||||||
|
`${this.crate.name}-${createHash('sha256')
|
||||||
|
.update(this.crate.manifest_path)
|
||||||
|
.update(CLI_VERSION)
|
||||||
|
.digest('hex')
|
||||||
|
.substring(0, 8)}.napi_type_def.tmp`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private async postBuild() {
|
||||||
|
try {
|
||||||
|
debug(`Try to create output directory:`)
|
||||||
|
debug(' %i', this.outputDir)
|
||||||
|
await mkdirAsync(this.outputDir, { recursive: true })
|
||||||
|
debug(`Output directory created`)
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error(`Failed to create output directory ${this.outputDir}`, {
|
||||||
|
cause: e,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.copyArtifact()
|
||||||
|
|
||||||
|
// only for cdylib
|
||||||
|
if (this.cdyLibName) {
|
||||||
|
await this.generateTypeDef()
|
||||||
|
await this.writeJsBinding()
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.outputs
|
||||||
|
}
|
||||||
|
|
||||||
|
private async copyArtifact() {
|
||||||
|
const [srcName, destName] = this.getArtifactNames()
|
||||||
|
if (!srcName || !destName) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const src = join(
|
||||||
|
this.targetDir,
|
||||||
|
this.target.triple,
|
||||||
|
this.options.release ? 'release' : 'debug',
|
||||||
|
srcName,
|
||||||
|
)
|
||||||
|
const dest = join(this.outputDir, destName)
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (await fileExists(dest)) {
|
||||||
|
debug('Old artifact found, remove it first')
|
||||||
|
await unlinkAsync(dest)
|
||||||
|
}
|
||||||
|
debug('Copy artifact to:')
|
||||||
|
debug(' %i', dest)
|
||||||
|
await copyFileAsync(src, dest)
|
||||||
|
this.outputs.push({
|
||||||
|
kind: dest.endsWith('.node') ? 'node' : 'exe',
|
||||||
|
path: dest,
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error('Failed to copy artifact', {
|
||||||
|
cause: e,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private getArtifactNames() {
|
||||||
|
if (this.cdyLibName) {
|
||||||
|
const cdyLib = this.cdyLibName.replace(/-/g, '_')
|
||||||
|
|
||||||
|
const srcName =
|
||||||
|
this.target.platform === 'darwin'
|
||||||
|
? `lib${cdyLib}.dylib`
|
||||||
|
: this.target.platform === 'win32'
|
||||||
|
? `${cdyLib}.dll`
|
||||||
|
: `lib${cdyLib}.so`
|
||||||
|
|
||||||
|
let destName = this.config.binaryName
|
||||||
|
// add platform suffix to binary name
|
||||||
|
// index[.linux-x64-gnu].node
|
||||||
|
// ^^^^^^^^^^^^^^
|
||||||
|
if (this.options.platform) {
|
||||||
|
destName += `.${this.target.platformArchABI}`
|
||||||
|
}
|
||||||
|
destName += '.node'
|
||||||
|
|
||||||
|
return [srcName, destName]
|
||||||
|
} else if (this.binName) {
|
||||||
|
const srcName =
|
||||||
|
this.target.platform === 'win32' ? `${this.binName}.exe` : this.binName
|
||||||
|
|
||||||
|
return [srcName, srcName]
|
||||||
|
}
|
||||||
|
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
private async generateTypeDef() {
|
||||||
|
if (!(await fileExists(this.envs.TYPE_DEF_TMP_PATH))) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const dest = join(this.outputDir, this.options.dts ?? 'index.d.ts')
|
||||||
|
|
||||||
|
const dts = await processTypeDef(
|
||||||
|
this.envs.TYPE_DEF_TMP_PATH,
|
||||||
|
!this.options.noDtsHeader
|
||||||
|
? this.options.dtsHeader ?? DEFAULT_TYPE_DEF_HEADER
|
||||||
|
: '',
|
||||||
|
)
|
||||||
|
|
||||||
|
try {
|
||||||
|
debug('Writing type def to:')
|
||||||
|
debug(' %i', dest)
|
||||||
|
await writeFileAsync(dest, dts, 'utf-8')
|
||||||
|
this.outputs.push({
|
||||||
|
kind: 'dts',
|
||||||
|
path: dest,
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
debug.error('Failed to write type def file')
|
||||||
|
debug.error(e as Error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async writeJsBinding() {
|
||||||
|
if (!this.options.platform || this.options.noJsBinding) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const dest = join(this.outputDir, this.options.jsBinding ?? 'index.js')
|
||||||
|
|
||||||
|
const js = createJsBinding(this.config.binaryName, this.config.packageName)
|
||||||
|
|
||||||
|
try {
|
||||||
|
debug('Writing js binding to:')
|
||||||
|
debug(' %i', dest)
|
||||||
|
await writeFileAsync(dest, js, 'utf-8')
|
||||||
|
this.outputs.push({
|
||||||
|
kind: 'js',
|
||||||
|
path: dest,
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error('Failed to write js binding file', { cause: e })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
105
cli/src/api/create-npm-dirs.ts
Normal file
105
cli/src/api/create-npm-dirs.ts
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
import { join, resolve } from 'path'
|
||||||
|
|
||||||
|
import {
|
||||||
|
applyDefaultCreateNpmDirsOptions,
|
||||||
|
CreateNpmDirsOptions,
|
||||||
|
} from '../def/create-npm-dirs.js'
|
||||||
|
import {
|
||||||
|
debugFactory,
|
||||||
|
readNapiConfig,
|
||||||
|
mkdirAsync as rawMkdirAsync,
|
||||||
|
pick,
|
||||||
|
writeFileAsync as rawWriteFileAsync,
|
||||||
|
Target,
|
||||||
|
} from '../utils/index.js'
|
||||||
|
|
||||||
|
const debug = debugFactory('create-npm-dirs')
|
||||||
|
|
||||||
|
export async function createNpmDirs(userOptions: CreateNpmDirsOptions) {
|
||||||
|
const options = applyDefaultCreateNpmDirsOptions(userOptions)
|
||||||
|
|
||||||
|
async function mkdirAsync(dir: string) {
|
||||||
|
debug('Try to create dir: %i', dir)
|
||||||
|
if (options.dryRun) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
await rawMkdirAsync(dir, {
|
||||||
|
recursive: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function writeFileAsync(file: string, content: string) {
|
||||||
|
debug('Writing file %i', file)
|
||||||
|
|
||||||
|
if (options.dryRun) {
|
||||||
|
debug(content)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
await rawWriteFileAsync(file, content)
|
||||||
|
}
|
||||||
|
|
||||||
|
const packageJsonPath = resolve(options.cwd, options.packageJsonPath)
|
||||||
|
const npmPath = resolve(options.cwd, options.npmDir)
|
||||||
|
|
||||||
|
debug(`Read content from [${packageJsonPath}]`)
|
||||||
|
|
||||||
|
const { targets, binaryName, packageName, packageJson } =
|
||||||
|
await readNapiConfig(packageJsonPath)
|
||||||
|
|
||||||
|
for (const target of targets) {
|
||||||
|
const targetDir = join(npmPath, `${target.platformArchABI}`)
|
||||||
|
await mkdirAsync(targetDir)
|
||||||
|
|
||||||
|
const binaryFileName = `${binaryName}.${target.platformArchABI}.node`
|
||||||
|
const scopedPackageJson = {
|
||||||
|
name: `${packageName}-${target.platformArchABI}`,
|
||||||
|
version: packageJson.version,
|
||||||
|
os: [target.platform],
|
||||||
|
cpu: target.arch !== 'universal' ? [target.arch] : undefined,
|
||||||
|
main: binaryFileName,
|
||||||
|
files: [binaryFileName],
|
||||||
|
...pick(
|
||||||
|
packageJson,
|
||||||
|
'description',
|
||||||
|
'keywords',
|
||||||
|
'author',
|
||||||
|
'authors',
|
||||||
|
'homepage',
|
||||||
|
'license',
|
||||||
|
'engines',
|
||||||
|
'publishConfig',
|
||||||
|
'repository',
|
||||||
|
'bugs',
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only works with yarn 3.1+
|
||||||
|
// https://github.com/yarnpkg/berry/pull/3981
|
||||||
|
if (target.abi === 'gnu') {
|
||||||
|
// @ts-expect-error
|
||||||
|
scopedPackageJson.libc = ['glibc']
|
||||||
|
} else if (target.abi === 'musl') {
|
||||||
|
// @ts-expect-error
|
||||||
|
scopedPackageJson.libc = ['musl']
|
||||||
|
}
|
||||||
|
|
||||||
|
const targetPackageJson = join(targetDir, 'package.json')
|
||||||
|
await writeFileAsync(
|
||||||
|
targetPackageJson,
|
||||||
|
JSON.stringify(scopedPackageJson, null, 2),
|
||||||
|
)
|
||||||
|
const targetReadme = join(targetDir, 'README.md')
|
||||||
|
await writeFileAsync(targetReadme, readme(packageName, target))
|
||||||
|
|
||||||
|
debug.info(`${packageName}-${target.platformArchABI} created`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function readme(packageName: string, target: Target) {
|
||||||
|
return `# \`${packageName}-${target.platformArchABI}\`
|
||||||
|
|
||||||
|
This is the **${target.triple}** binary for \`${packageName}\`
|
||||||
|
`
|
||||||
|
}
|
206
cli/src/api/new.ts
Normal file
206
cli/src/api/new.ts
Normal file
|
@ -0,0 +1,206 @@
|
||||||
|
import path from 'path'
|
||||||
|
|
||||||
|
import {
|
||||||
|
applyDefaultNewOptions,
|
||||||
|
NewOptions as RawNewOptions,
|
||||||
|
} from '../def/new.js'
|
||||||
|
import {
|
||||||
|
AVAILABLE_TARGETS,
|
||||||
|
CLI_VERSION,
|
||||||
|
debugFactory,
|
||||||
|
DEFAULT_TARGETS,
|
||||||
|
mkdirAsync,
|
||||||
|
writeFileAsync,
|
||||||
|
} from '../utils/index.js'
|
||||||
|
import { napiEngineRequirement } from '../utils/version.js'
|
||||||
|
|
||||||
|
import {
|
||||||
|
createBuildRs,
|
||||||
|
createCargoToml,
|
||||||
|
createGithubActionsCIYml,
|
||||||
|
createLibRs,
|
||||||
|
createPackageJson,
|
||||||
|
gitIgnore,
|
||||||
|
npmIgnore,
|
||||||
|
} from './templates/index.js'
|
||||||
|
|
||||||
|
const debug = debugFactory('new')
|
||||||
|
|
||||||
|
interface Output {
|
||||||
|
target: string
|
||||||
|
content: string
|
||||||
|
}
|
||||||
|
|
||||||
|
type NewOptions = Required<RawNewOptions>
|
||||||
|
|
||||||
|
function processOptions(options: RawNewOptions) {
|
||||||
|
debug('Processing options...')
|
||||||
|
options.path = path.resolve(process.cwd(), options.path)
|
||||||
|
debug(`Resolved target path to: ${options.path}`)
|
||||||
|
|
||||||
|
if (!options.name) {
|
||||||
|
options.name = path.parse(options.path).base
|
||||||
|
debug(`No project name provided, fix it to dir name: ${options.name}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!options.targets?.length) {
|
||||||
|
if (options.enableAllTargets) {
|
||||||
|
options.targets = AVAILABLE_TARGETS.concat()
|
||||||
|
debug('Enable all targets')
|
||||||
|
} else if (options.enableDefaultTargets) {
|
||||||
|
options.targets = DEFAULT_TARGETS.concat()
|
||||||
|
debug('Enable default targets')
|
||||||
|
} else {
|
||||||
|
throw new Error('At least one target must be enabled')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return applyDefaultNewOptions(options) as NewOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function newProject(userOptions: RawNewOptions) {
|
||||||
|
debug('Will create napi-rs project with given options:')
|
||||||
|
debug(userOptions)
|
||||||
|
|
||||||
|
const options = processOptions(userOptions)
|
||||||
|
|
||||||
|
debug('Targets to be enabled:')
|
||||||
|
debug(options.targets)
|
||||||
|
|
||||||
|
const outputs = generateFiles(options)
|
||||||
|
|
||||||
|
try {
|
||||||
|
debug(`Try to create target directory: ${options.path}`)
|
||||||
|
if (!options.dryRun) {
|
||||||
|
await mkdirAsync(options.path, { recursive: true })
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error(`Failed to create target directory: ${options.path}`, {
|
||||||
|
cause: e,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
await dumpOutputs(outputs, options.dryRun)
|
||||||
|
debug(`Project created at: ${options.path}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateFiles(options: NewOptions): Output[] {
|
||||||
|
return [
|
||||||
|
generateCargoToml,
|
||||||
|
generateLibRs,
|
||||||
|
generateBuildRs,
|
||||||
|
generatePackageJson,
|
||||||
|
generateGithubWorkflow,
|
||||||
|
generateIgnoreFiles,
|
||||||
|
].flatMap((generator) => {
|
||||||
|
const output = generator(options)
|
||||||
|
|
||||||
|
if (!output) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(output)) {
|
||||||
|
return output.map((o) => ({
|
||||||
|
...o,
|
||||||
|
target: path.join(options.path, o.target),
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
return [{ ...output, target: path.join(options.path, output.target) }]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateCargoToml(options: NewOptions): Output {
|
||||||
|
return {
|
||||||
|
target: './Cargo.toml',
|
||||||
|
content: createCargoToml({
|
||||||
|
name: options.name,
|
||||||
|
license: options.license,
|
||||||
|
features: [`napi${options.minNodeApiVersion}`],
|
||||||
|
deriveFeatures: options.enableTypeDef ? ['type-def'] : [],
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateLibRs(_options: NewOptions): Output {
|
||||||
|
return {
|
||||||
|
target: './src/lib.rs',
|
||||||
|
content: createLibRs(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateBuildRs(_options: NewOptions): Output {
|
||||||
|
return {
|
||||||
|
target: './build.rs',
|
||||||
|
content: createBuildRs(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function generatePackageJson(options: NewOptions): Output {
|
||||||
|
return {
|
||||||
|
target: './package.json',
|
||||||
|
content: createPackageJson({
|
||||||
|
name: options.name,
|
||||||
|
binaryName: getBinaryName(options.name),
|
||||||
|
targets: options.targets,
|
||||||
|
license: options.license,
|
||||||
|
engineRequirement: napiEngineRequirement(options.minNodeApiVersion),
|
||||||
|
cliVersion: CLI_VERSION,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateGithubWorkflow(options: NewOptions): Output | null {
|
||||||
|
if (!options.enableGithubActions) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
target: './.github/workflows/ci.yml',
|
||||||
|
content: createGithubActionsCIYml(
|
||||||
|
getBinaryName(options.name),
|
||||||
|
options.targets,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateIgnoreFiles(_options: NewOptions): Output[] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
target: './.gitignore',
|
||||||
|
content: gitIgnore,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
target: './.npmignore',
|
||||||
|
content: npmIgnore,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
async function dumpOutputs(outputs: Output[], dryRun?: boolean) {
|
||||||
|
for (const output of outputs) {
|
||||||
|
if (!output) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
debug(`Writing project file: ${output.target}`)
|
||||||
|
// only output content to logger instead of writing to file system
|
||||||
|
if (dryRun) {
|
||||||
|
debug(output.content)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await mkdirAsync(path.dirname(output.target), { recursive: true })
|
||||||
|
await writeFileAsync(output.target, output.content, 'utf-8')
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error(`Failed to write file: ${output.target}`, { cause: e })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getBinaryName(name: string): string {
|
||||||
|
return name.split('/').pop()!
|
||||||
|
}
|
||||||
|
|
||||||
|
export { NewOptions }
|
216
cli/src/api/pre-publish.ts
Normal file
216
cli/src/api/pre-publish.ts
Normal file
|
@ -0,0 +1,216 @@
|
||||||
|
import { execSync } from 'child_process'
|
||||||
|
import { existsSync, statSync } from 'fs'
|
||||||
|
import { join, relative, resolve } from 'path'
|
||||||
|
|
||||||
|
import { Octokit } from '@octokit/rest'
|
||||||
|
|
||||||
|
import {
|
||||||
|
applyDefaultPrePublishOptions,
|
||||||
|
PrePublishOptions,
|
||||||
|
} from '../def/pre-publish.js'
|
||||||
|
import {
|
||||||
|
readFileAsync,
|
||||||
|
readNapiConfig,
|
||||||
|
debugFactory,
|
||||||
|
updatePackageJson,
|
||||||
|
} from '../utils/index.js'
|
||||||
|
|
||||||
|
import { version } from './version.js'
|
||||||
|
|
||||||
|
const debug = debugFactory('pre-publish')
|
||||||
|
|
||||||
|
interface PackageInfo {
|
||||||
|
name: string
|
||||||
|
version: string
|
||||||
|
tag: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function prePublish(userOptions: PrePublishOptions) {
|
||||||
|
debug('Receive pre-publish options:')
|
||||||
|
debug(' %O', userOptions)
|
||||||
|
|
||||||
|
const options = applyDefaultPrePublishOptions(userOptions)
|
||||||
|
|
||||||
|
const packageJsonPath = relative(options.cwd, options.packageJsonPath)
|
||||||
|
|
||||||
|
const { packageJson, targets, packageName, binaryName, npmClient } =
|
||||||
|
await readNapiConfig(packageJsonPath)
|
||||||
|
|
||||||
|
async function createGhRelease(packageName: string, version: string) {
|
||||||
|
if (!options.ghRelease) {
|
||||||
|
return {
|
||||||
|
owner: null,
|
||||||
|
repo: null,
|
||||||
|
pkgInfo: { name: null, version: null, tag: null },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const { repo, owner, pkgInfo, octokit } = getRepoInfo(packageName, version)
|
||||||
|
|
||||||
|
if (!repo || !owner) {
|
||||||
|
return {
|
||||||
|
owner: null,
|
||||||
|
repo: null,
|
||||||
|
pkgInfo: { name: null, version: null, tag: null },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!options.dryRun) {
|
||||||
|
try {
|
||||||
|
await octokit.repos.createRelease({
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
tag_name: pkgInfo.tag,
|
||||||
|
name: options.ghReleaseName,
|
||||||
|
prerelease:
|
||||||
|
version.includes('alpha') ||
|
||||||
|
version.includes('beta') ||
|
||||||
|
version.includes('rc'),
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
debug(
|
||||||
|
`Params: ${JSON.stringify(
|
||||||
|
{ owner, repo, tag_name: pkgInfo.tag },
|
||||||
|
null,
|
||||||
|
2,
|
||||||
|
)}`,
|
||||||
|
)
|
||||||
|
console.error(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { owner, repo, pkgInfo, octokit }
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRepoInfo(packageName: string, version: string) {
|
||||||
|
const headCommit = execSync('git log -1 --pretty=%B', {
|
||||||
|
encoding: 'utf-8',
|
||||||
|
}).trim()
|
||||||
|
|
||||||
|
const { GITHUB_REPOSITORY } = process.env
|
||||||
|
if (!GITHUB_REPOSITORY) {
|
||||||
|
return {
|
||||||
|
owner: null,
|
||||||
|
repo: null,
|
||||||
|
pkgInfo: { name: null, version: null, tag: null },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
debug(`Github repository: ${GITHUB_REPOSITORY}`)
|
||||||
|
const [owner, repo] = GITHUB_REPOSITORY.split('/')
|
||||||
|
const octokit = new Octokit({
|
||||||
|
auth: process.env.GITHUB_TOKEN,
|
||||||
|
})
|
||||||
|
let pkgInfo: PackageInfo | undefined
|
||||||
|
if (options.tagStyle === 'lerna') {
|
||||||
|
const packagesToPublish = headCommit
|
||||||
|
.split('\n')
|
||||||
|
.map((line) => line.trim())
|
||||||
|
.filter((line, index) => line.length && index)
|
||||||
|
.map((line) => line.substring(2))
|
||||||
|
.map(parseTag)
|
||||||
|
|
||||||
|
pkgInfo = packagesToPublish.find(
|
||||||
|
(pkgInfo) => pkgInfo.name === packageName,
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!pkgInfo) {
|
||||||
|
throw new TypeError(
|
||||||
|
`No release commit found with ${packageName}, original commit info: ${headCommit}`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pkgInfo = {
|
||||||
|
tag: `v${version}`,
|
||||||
|
version,
|
||||||
|
name: packageName,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { owner, repo, pkgInfo, octokit }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!options.dryRun) {
|
||||||
|
await version(userOptions)
|
||||||
|
await updatePackageJson(packageJsonPath, {
|
||||||
|
optionalDependencies: targets.reduce((deps, target) => {
|
||||||
|
deps[`${packageName}-${target.platformArchABI}`] = packageJson.version
|
||||||
|
|
||||||
|
return deps
|
||||||
|
}, {} as Record<string, string>),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const { owner, repo, pkgInfo, octokit } = options.ghReleaseId
|
||||||
|
? getRepoInfo(packageName, packageJson.version)
|
||||||
|
: await createGhRelease(packageName, packageJson.version)
|
||||||
|
|
||||||
|
for (const target of targets) {
|
||||||
|
const pkgDir = resolve(
|
||||||
|
options.cwd,
|
||||||
|
options.npmDir,
|
||||||
|
`${target.platformArchABI}`,
|
||||||
|
)
|
||||||
|
const filename = `${binaryName}.${target.platformArchABI}.node`
|
||||||
|
const dstPath = join(pkgDir, filename)
|
||||||
|
|
||||||
|
if (!options.dryRun) {
|
||||||
|
if (!existsSync(dstPath)) {
|
||||||
|
debug.warn(`%s doesn't exist`, dstPath)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
execSync(`${npmClient} publish`, {
|
||||||
|
cwd: pkgDir,
|
||||||
|
env: process.env,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (options.ghRelease && repo && owner) {
|
||||||
|
debug.info(`Creating GitHub release ${pkgInfo.tag}`)
|
||||||
|
try {
|
||||||
|
const releaseId = options.ghReleaseId
|
||||||
|
? Number(options.ghReleaseId)
|
||||||
|
: (
|
||||||
|
await octokit!.repos.getReleaseByTag({
|
||||||
|
repo: repo,
|
||||||
|
owner: owner,
|
||||||
|
tag: pkgInfo.tag,
|
||||||
|
})
|
||||||
|
).data.id
|
||||||
|
const dstFileStats = statSync(dstPath)
|
||||||
|
const assetInfo = await octokit!.repos.uploadReleaseAsset({
|
||||||
|
owner: owner,
|
||||||
|
repo: repo,
|
||||||
|
name: filename,
|
||||||
|
release_id: releaseId,
|
||||||
|
mediaType: { format: 'raw' },
|
||||||
|
headers: {
|
||||||
|
'content-length': dstFileStats.size,
|
||||||
|
'content-type': 'application/octet-stream',
|
||||||
|
},
|
||||||
|
data: await readFileAsync(dstPath, { encoding: 'utf-8' }),
|
||||||
|
})
|
||||||
|
debug.info(`GitHub release created`)
|
||||||
|
debug.info(`Download URL: %s`, assetInfo.data.browser_download_url)
|
||||||
|
} catch (e) {
|
||||||
|
debug.error(
|
||||||
|
`Param: ${JSON.stringify(
|
||||||
|
{ owner, repo, tag: pkgInfo.tag, filename: dstPath },
|
||||||
|
null,
|
||||||
|
2,
|
||||||
|
)}`,
|
||||||
|
)
|
||||||
|
debug.error(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseTag(tag: string) {
|
||||||
|
const segments = tag.split('@')
|
||||||
|
const version = segments.pop()!
|
||||||
|
const name = segments.join('@')
|
||||||
|
|
||||||
|
return {
|
||||||
|
name,
|
||||||
|
version,
|
||||||
|
tag,
|
||||||
|
}
|
||||||
|
}
|
51
cli/src/api/rename.ts
Normal file
51
cli/src/api/rename.ts
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
import { resolve } from 'path'
|
||||||
|
|
||||||
|
import { isNil, merge, omitBy, pick } from 'lodash-es'
|
||||||
|
|
||||||
|
import { applyDefaultRenameOptions, RenameOptions } from '../def/rename.js'
|
||||||
|
import { readFileAsync, writeFileAsync } from '../utils/index.js'
|
||||||
|
|
||||||
|
import { createNpmDirs } from './create-npm-dirs.js'
|
||||||
|
|
||||||
|
export async function renameProject(userOptions: RenameOptions) {
|
||||||
|
const options = applyDefaultRenameOptions(userOptions)
|
||||||
|
|
||||||
|
const packageJsonPath = resolve(options.cwd, options.packageJsonPath)
|
||||||
|
const cargoTomlPath = resolve(options.cwd, options.manifestPath)
|
||||||
|
|
||||||
|
const packageJsonContent = await readFileAsync(packageJsonPath, 'utf8')
|
||||||
|
const packageJsonData = JSON.parse(packageJsonContent)
|
||||||
|
|
||||||
|
merge(
|
||||||
|
packageJsonData,
|
||||||
|
omitBy(pick(options, ['name', 'description', 'author', 'license']), isNil),
|
||||||
|
{
|
||||||
|
napi: omitBy(
|
||||||
|
{
|
||||||
|
binaryName: options.binaryName,
|
||||||
|
packageName: options.packageName,
|
||||||
|
},
|
||||||
|
isNil,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
await writeFileAsync(
|
||||||
|
packageJsonPath,
|
||||||
|
JSON.stringify(packageJsonData, null, 2),
|
||||||
|
)
|
||||||
|
|
||||||
|
let tomlContent = await readFileAsync(cargoTomlPath, 'utf8')
|
||||||
|
tomlContent = tomlContent.replace(
|
||||||
|
/name\s?=\s?"([\w+])"/,
|
||||||
|
`name = "${options.binaryName}"`,
|
||||||
|
)
|
||||||
|
await writeFileAsync(cargoTomlPath, tomlContent)
|
||||||
|
|
||||||
|
await createNpmDirs({
|
||||||
|
cwd: options.cwd,
|
||||||
|
packageJsonPath: options.packageJsonPath,
|
||||||
|
npmDir: options.npmDir,
|
||||||
|
dryRun: false,
|
||||||
|
})
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
export const GitIgnore = `# Created by https://www.toptal.com/developers/gitignore/api/node
|
export const gitIgnore = `# Created by https://www.toptal.com/developers/gitignore/api/node
|
||||||
# Edit at https://www.toptal.com/developers/gitignore?templates=node
|
# Edit at https://www.toptal.com/developers/gitignore?templates=node
|
||||||
|
|
||||||
### Node ###
|
### Node ###
|
|
@ -1,4 +1,4 @@
|
||||||
export const NPMIgnoreFiles = `target
|
export const npmIgnore = `target
|
||||||
Cargo.lock
|
Cargo.lock
|
||||||
.cargo
|
.cargo
|
||||||
.github
|
.github
|
4
cli/src/api/templates/build.rs.ts
Normal file
4
cli/src/api/templates/build.rs.ts
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
export const createBuildRs = () => `fn main() {
|
||||||
|
napi_build::setup();
|
||||||
|
}
|
||||||
|
`
|
35
cli/src/api/templates/cargo.toml.ts
Normal file
35
cli/src/api/templates/cargo.toml.ts
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
export const createCargoToml = ({
|
||||||
|
name,
|
||||||
|
license,
|
||||||
|
features,
|
||||||
|
deriveFeatures,
|
||||||
|
}: {
|
||||||
|
name: string
|
||||||
|
license: string
|
||||||
|
features: string[]
|
||||||
|
deriveFeatures: string[]
|
||||||
|
}) => `[package]
|
||||||
|
name = "${name.replace('@', '').replace('/', '_').toLowerCase()}"
|
||||||
|
version = "1.0.0"
|
||||||
|
edition = "2021"
|
||||||
|
license = "${license}"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ["cdylib"]
|
||||||
|
|
||||||
|
[dependencies.napi]
|
||||||
|
version = "2"
|
||||||
|
default-features = false
|
||||||
|
# see https://nodejs.org/api/n-api.html#node-api-version-matrix
|
||||||
|
features = ${JSON.stringify(features)}
|
||||||
|
|
||||||
|
[dependencies.napi-derive]
|
||||||
|
version = "2"
|
||||||
|
features = ${JSON.stringify(deriveFeatures)}
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
napi-build = "2"
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
lto = true
|
||||||
|
`
|
|
@ -1,9 +1,8 @@
|
||||||
export const YAML = (app: string) => `
|
export const YAML = () => `
|
||||||
name: CI
|
name: CI
|
||||||
|
|
||||||
env:
|
env:
|
||||||
DEBUG: 'napi:*'
|
DEBUG: 'napi:*'
|
||||||
APP_NAME: '${app}'
|
|
||||||
MACOSX_DEPLOYMENT_TARGET: '10.13'
|
MACOSX_DEPLOYMENT_TARGET: '10.13'
|
||||||
|
|
||||||
on:
|
on:
|
||||||
|
@ -30,14 +29,14 @@ jobs:
|
||||||
- host: macos-latest
|
- host: macos-latest
|
||||||
target: 'x86_64-apple-darwin'
|
target: 'x86_64-apple-darwin'
|
||||||
build: |
|
build: |
|
||||||
yarn build
|
yarn build --platform
|
||||||
strip -x *.node
|
strip -x *.node
|
||||||
- host: windows-latest
|
- host: windows-latest
|
||||||
build: yarn build
|
build: yarn build --platform
|
||||||
target: 'x86_64-pc-windows-msvc'
|
target: 'x86_64-pc-windows-msvc'
|
||||||
- host: windows-latest
|
- host: windows-latest
|
||||||
build: |
|
build: |
|
||||||
yarn build --target i686-pc-windows-msvc
|
yarn build --platform --target i686-pc-windows-msvc
|
||||||
yarn test
|
yarn test
|
||||||
target: 'i686-pc-windows-msvc'
|
target: 'i686-pc-windows-msvc'
|
||||||
- host: ubuntu-latest
|
- host: ubuntu-latest
|
||||||
|
@ -45,26 +44,26 @@ jobs:
|
||||||
docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian
|
docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian
|
||||||
build: >-
|
build: >-
|
||||||
set -e &&\n
|
set -e &&\n
|
||||||
yarn build --target x86_64-unknown-linux-gnu &&\n
|
yarn build --platform --target x86_64-unknown-linux-gnu &&\n
|
||||||
strip *.node
|
strip *.node
|
||||||
- host: ubuntu-latest
|
- host: ubuntu-latest
|
||||||
target: 'x86_64-unknown-linux-musl'
|
target: 'x86_64-unknown-linux-musl'
|
||||||
docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-alpine
|
docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-alpine
|
||||||
build: >-
|
build: >-
|
||||||
set -e &&
|
set -e &&
|
||||||
yarn build &&
|
yarn build --platform &&
|
||||||
strip *.node
|
strip *.node
|
||||||
- host: macos-latest
|
- host: macos-latest
|
||||||
target: 'aarch64-apple-darwin'
|
target: 'aarch64-apple-darwin'
|
||||||
build: |
|
build: |
|
||||||
yarn build --target aarch64-apple-darwin
|
yarn build --platform --target aarch64-apple-darwin
|
||||||
strip -x *.node
|
strip -x *.node
|
||||||
- host: ubuntu-latest
|
- host: ubuntu-latest
|
||||||
target: 'aarch64-unknown-linux-gnu'
|
target: 'aarch64-unknown-linux-gnu'
|
||||||
docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian-aarch64
|
docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian-aarch64
|
||||||
build: >-
|
build: >-
|
||||||
set -e &&\n
|
set -e &&\n
|
||||||
yarn build --target aarch64-unknown-linux-gnu &&\n
|
yarn build --platform --target aarch64-unknown-linux-gnu &&\n
|
||||||
aarch64-unknown-linux-gnu-strip *.node
|
aarch64-unknown-linux-gnu-strip *.node
|
||||||
- host: ubuntu-latest
|
- host: ubuntu-latest
|
||||||
target: 'armv7-unknown-linux-gnueabihf'
|
target: 'armv7-unknown-linux-gnueabihf'
|
||||||
|
@ -72,17 +71,17 @@ jobs:
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install gcc-arm-linux-gnueabihf -y
|
sudo apt-get install gcc-arm-linux-gnueabihf -y
|
||||||
build: |
|
build: |
|
||||||
yarn build --target armv7-unknown-linux-gnueabihf
|
yarn build --platform --target armv7-unknown-linux-gnueabihf
|
||||||
arm-linux-gnueabihf-strip *.node
|
arm-linux-gnueabihf-strip *.node
|
||||||
- host: ubuntu-latest
|
- host: ubuntu-latest
|
||||||
target: 'aarch64-linux-android'
|
target: 'aarch64-linux-android'
|
||||||
build: |
|
build: |
|
||||||
yarn build --target aarch64-linux-android
|
yarn build --platform --target aarch64-linux-android
|
||||||
\${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip *.node
|
\${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip *.node
|
||||||
- host: ubuntu-latest
|
- host: ubuntu-latest
|
||||||
target: 'armv7-linux-androideabi'
|
target: 'armv7-linux-androideabi'
|
||||||
build: |
|
build: |
|
||||||
yarn build --target armv7-linux-androideabi
|
yarn build --platform --target armv7-linux-androideabi
|
||||||
\${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip *.node
|
\${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip *.node
|
||||||
- host: ubuntu-latest
|
- host: ubuntu-latest
|
||||||
target: 'aarch64-unknown-linux-musl'
|
target: 'aarch64-unknown-linux-musl'
|
||||||
|
@ -90,11 +89,11 @@ jobs:
|
||||||
build: >-
|
build: >-
|
||||||
set -e &&\n
|
set -e &&\n
|
||||||
rustup target add aarch64-unknown-linux-musl &&\n
|
rustup target add aarch64-unknown-linux-musl &&\n
|
||||||
yarn build --target aarch64-unknown-linux-musl &&\n
|
yarn build --platform --target aarch64-unknown-linux-musl &&\n
|
||||||
/aarch64-linux-musl-cross/bin/aarch64-linux-musl-strip *.node
|
/aarch64-linux-musl-cross/bin/aarch64-linux-musl-strip *.node
|
||||||
- host: windows-latest
|
- host: windows-latest
|
||||||
target: 'aarch64-pc-windows-msvc'
|
target: 'aarch64-pc-windows-msvc'
|
||||||
build: yarn build --target aarch64-pc-windows-msvc
|
build: yarn build --platform --target aarch64-pc-windows-msvc
|
||||||
|
|
||||||
name: stable - \${{ matrix.settings.target }} - node@18
|
name: stable - \${{ matrix.settings.target }} - node@18
|
||||||
runs-on: \${{ matrix.settings.host }}
|
runs-on: \${{ matrix.settings.host }}
|
||||||
|
@ -172,7 +171,7 @@ jobs:
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: bindings-\${{ matrix.settings.target }}
|
name: bindings-\${{ matrix.settings.target }}
|
||||||
path: \${{ env.APP_NAME }}.*.node
|
path: "*.node"
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
build-freebsd:
|
build-freebsd:
|
||||||
|
@ -223,7 +222,7 @@ jobs:
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: bindings-freebsd
|
name: bindings-freebsd
|
||||||
path: \${{ env.APP_NAME }}.*.node
|
path: "*.node"
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
test-macOS-windows-binding:
|
test-macOS-windows-binding:
|
||||||
|
@ -508,7 +507,7 @@ jobs:
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: bindings-universal-apple-darwin
|
name: bindings-universal-apple-darwin
|
||||||
path: \${{ env.APP_NAME }}.*.node
|
path: "*.node"
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
publish:
|
publish:
|
|
@ -1,8 +1,12 @@
|
||||||
import { load, dump } from 'js-yaml'
|
import { load, dump } from 'js-yaml'
|
||||||
|
|
||||||
import { NodeArchToCpu, UniArchsByPlatform, parseTriple } from '../parse-triple'
|
import {
|
||||||
|
NodeArchToCpu,
|
||||||
|
UniArchsByPlatform,
|
||||||
|
parseTriple,
|
||||||
|
} from '../../utils/index.js'
|
||||||
|
|
||||||
import { YAML } from './ci-template'
|
import { YAML } from './ci-template.js'
|
||||||
|
|
||||||
const BUILD_FREEBSD = 'build-freebsd'
|
const BUILD_FREEBSD = 'build-freebsd'
|
||||||
const TEST_MACOS_WINDOWS = 'test-macOS-windows-binding'
|
const TEST_MACOS_WINDOWS = 'test-macOS-windows-binding'
|
||||||
|
@ -29,7 +33,9 @@ export const createGithubActionsCIYml = (
|
||||||
return [t]
|
return [t]
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
const fullTemplate = load(YAML(binaryName)) as any
|
|
||||||
|
const fullTemplate = load(YAML()) as any
|
||||||
|
|
||||||
const requiredSteps = []
|
const requiredSteps = []
|
||||||
const enableWindowsX86 = allTargets.has('x86_64-pc-windows-msvc')
|
const enableWindowsX86 = allTargets.has('x86_64-pc-windows-msvc')
|
||||||
const enableMacOSX86 = allTargets.has('x86_64-apple-darwin')
|
const enableMacOSX86 = allTargets.has('x86_64-apple-darwin')
|
||||||
|
@ -40,7 +46,6 @@ export const createGithubActionsCIYml = (
|
||||||
const enableLinuxArm7 = allTargets.has('armv7-unknown-linux-gnueabihf')
|
const enableLinuxArm7 = allTargets.has('armv7-unknown-linux-gnueabihf')
|
||||||
const enableFreeBSD = allTargets.has('x86_64-unknown-freebsd')
|
const enableFreeBSD = allTargets.has('x86_64-unknown-freebsd')
|
||||||
const enableMacOSUni = allTargets.has('universal-apple-darwin')
|
const enableMacOSUni = allTargets.has('universal-apple-darwin')
|
||||||
fullTemplate.env.APP_NAME = binaryName
|
|
||||||
fullTemplate.jobs.build.strategy.matrix.settings =
|
fullTemplate.jobs.build.strategy.matrix.settings =
|
||||||
fullTemplate.jobs.build.strategy.matrix.settings.filter(
|
fullTemplate.jobs.build.strategy.matrix.settings.filter(
|
||||||
({ target }: { target: string }) => allTargets.has(target),
|
({ target }: { target: string }) => allTargets.has(target),
|
8
cli/src/api/templates/index.ts
Normal file
8
cli/src/api/templates/index.ts
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
export * from './.gitignore.js'
|
||||||
|
export * from './.npmignore.js'
|
||||||
|
export * from './build.rs.js'
|
||||||
|
export * from './cargo.toml.js'
|
||||||
|
export * from './ci.yml.js'
|
||||||
|
export * from './lib.rs.js'
|
||||||
|
export * from './package.json.js'
|
||||||
|
export * from './js-binding.js'
|
130
cli/src/api/templates/js-binding.ts
Normal file
130
cli/src/api/templates/js-binding.ts
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
/* eslint-disable @typescript-eslint/switch-exhaustiveness-check */
|
||||||
|
|
||||||
|
function loadNapiModule(binaryName: string, packageName: string) {
|
||||||
|
const { existsSync, readFileSync } = require('fs')
|
||||||
|
const { join } = require('path')
|
||||||
|
const { platform, arch } = process
|
||||||
|
|
||||||
|
const candidates: string[] = []
|
||||||
|
|
||||||
|
function isMusl() {
|
||||||
|
// For Node 10
|
||||||
|
if (!process.report || typeof process.report.getReport !== 'function') {
|
||||||
|
try {
|
||||||
|
const lddPath = require('child_process')
|
||||||
|
.execSync('which ldd')
|
||||||
|
.toString()
|
||||||
|
.trim()
|
||||||
|
return readFileSync(lddPath, 'utf8').includes('musl')
|
||||||
|
} catch (e) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// @ts-expect-error
|
||||||
|
const { glibcVersionRuntime } = process.report.getReport().header
|
||||||
|
return !glibcVersionRuntime
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (platform) {
|
||||||
|
case 'android':
|
||||||
|
switch (arch) {
|
||||||
|
case 'arm64':
|
||||||
|
candidates.push('android-arm64')
|
||||||
|
break
|
||||||
|
case 'arm':
|
||||||
|
candidates.push('android-arm-eabi')
|
||||||
|
break
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 'win32':
|
||||||
|
switch (arch) {
|
||||||
|
case 'x64':
|
||||||
|
candidates.push('win32-x64-msvc')
|
||||||
|
break
|
||||||
|
case 'ia32':
|
||||||
|
candidates.push('win32-ia32-msvc')
|
||||||
|
break
|
||||||
|
case 'arm64':
|
||||||
|
candidates.push('win32-arm64-msvc')
|
||||||
|
break
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 'darwin':
|
||||||
|
candidates.push('darwin-universal')
|
||||||
|
switch (arch) {
|
||||||
|
case 'x64':
|
||||||
|
candidates.push('darwin-x64')
|
||||||
|
break
|
||||||
|
case 'arm64':
|
||||||
|
candidates.push('darwin-arm64')
|
||||||
|
break
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 'freebsd':
|
||||||
|
if (arch === 'x64') {
|
||||||
|
candidates.push('freebsd-x64')
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 'linux':
|
||||||
|
switch (arch) {
|
||||||
|
case 'x64':
|
||||||
|
if (isMusl()) {
|
||||||
|
candidates.push('linux-x64-musl')
|
||||||
|
} else {
|
||||||
|
candidates.push('linux-x64-gnu')
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 'arm64':
|
||||||
|
if (isMusl()) {
|
||||||
|
candidates.push('linux-arm64-musl')
|
||||||
|
} else {
|
||||||
|
candidates.push('linux-arm64-gnu')
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 'arm':
|
||||||
|
candidates.push('linux-arm-gnueabihf')
|
||||||
|
break
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
let nativeBinding: any
|
||||||
|
let loadError: any
|
||||||
|
|
||||||
|
for (const suffix of candidates) {
|
||||||
|
const localPath = join(__dirname, `${binaryName}.${suffix}.node`)
|
||||||
|
const pkgPath = `${packageName}-${suffix}`
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (existsSync(localPath)) {
|
||||||
|
nativeBinding = require(localPath)
|
||||||
|
} else {
|
||||||
|
nativeBinding = require(pkgPath)
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
loadError = e
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
loadError = null
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!nativeBinding) {
|
||||||
|
if (loadError) {
|
||||||
|
throw loadError
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`Unsupported OS: ${platform}, architecture: ${arch}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nativeBinding
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createJsBinding(localName: string, pkgName: string): string {
|
||||||
|
return `${loadNapiModule.toString()}
|
||||||
|
|
||||||
|
module.exports = loadNapiModule('${localName}', '${pkgName}')
|
||||||
|
`
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
export const LibRs = `#![deny(clippy::all)]
|
export const createLibRs = () => `#![deny(clippy::all)]
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate napi_derive;
|
extern crate napi_derive;
|
44
cli/src/api/templates/package.json.ts
Normal file
44
cli/src/api/templates/package.json.ts
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
export const createPackageJson = ({
|
||||||
|
name,
|
||||||
|
binaryName,
|
||||||
|
targets,
|
||||||
|
license,
|
||||||
|
engineRequirement,
|
||||||
|
cliVersion,
|
||||||
|
}: {
|
||||||
|
name: string
|
||||||
|
binaryName: string
|
||||||
|
targets: string[]
|
||||||
|
license: string
|
||||||
|
engineRequirement: string
|
||||||
|
cliVersion: string
|
||||||
|
}) => {
|
||||||
|
return `{
|
||||||
|
"name": "${name}",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "index.js",
|
||||||
|
"types": "index.d.ts",
|
||||||
|
"license": "${license}",
|
||||||
|
"engines": {
|
||||||
|
"node": "${engineRequirement}"
|
||||||
|
},
|
||||||
|
"napi": {
|
||||||
|
"name": "${binaryName}",
|
||||||
|
"targets": [
|
||||||
|
${targets.map((t) => `"${t}"`).join(',\n ')}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"test": "yarn build:debug --platform && node -e \\"assert(require('.').sum(1, 2) === 3)\\"",
|
||||||
|
"build": "napi build --release --platform --strip",
|
||||||
|
"build:debug": "napi build",
|
||||||
|
"prepublishOnly": "napi prepublish -t npm",
|
||||||
|
"artifacts": "napi artifacts",
|
||||||
|
"universal": "napi universal",
|
||||||
|
"version": "napi version"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@napi-rs/cli": "^${cliVersion}"
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
}
|
76
cli/src/api/universalize.ts
Normal file
76
cli/src/api/universalize.ts
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
import { spawnSync } from 'child_process'
|
||||||
|
import { join, resolve } from 'path'
|
||||||
|
|
||||||
|
import {
|
||||||
|
applyDefaultUniversalizeOptions,
|
||||||
|
UniversalizeOptions,
|
||||||
|
} from '../def/universalize.js'
|
||||||
|
import { readNapiConfig } from '../utils/config.js'
|
||||||
|
import { debugFactory } from '../utils/log.js'
|
||||||
|
import { fileExists } from '../utils/misc.js'
|
||||||
|
import { UniArchsByPlatform } from '../utils/target.js'
|
||||||
|
|
||||||
|
const debug = debugFactory('universalize')
|
||||||
|
|
||||||
|
const universalizers: Partial<
|
||||||
|
Record<NodeJS.Platform, (inputs: string[], output: string) => void>
|
||||||
|
> = {
|
||||||
|
darwin: (inputs, output) => {
|
||||||
|
spawnSync('lipo', ['-create', '-output', output, ...inputs], {
|
||||||
|
stdio: 'inherit',
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function universalizeBinaries(userOptions: UniversalizeOptions) {
|
||||||
|
const options = applyDefaultUniversalizeOptions(userOptions)
|
||||||
|
|
||||||
|
const packageJsonPath = join(options.cwd, options.packageJsonPath)
|
||||||
|
|
||||||
|
const config = await readNapiConfig(packageJsonPath)
|
||||||
|
|
||||||
|
const target = config.targets.find(
|
||||||
|
(t) => t.platform === process.platform && t.arch === 'universal',
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!target) {
|
||||||
|
throw new Error(
|
||||||
|
`'universal' arch for platform '${process.platform}' not found in config!`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const srcFiles = UniArchsByPlatform[process.platform]?.map(
|
||||||
|
(arch) => `${config.binaryName}.${process.platform}-${arch}.node`,
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!srcFiles || !universalizers[process.platform]) {
|
||||||
|
throw new Error(
|
||||||
|
`'universal' arch for platform '${process.platform}' not supported.`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
debug(`Looking up source binaries to combine: `)
|
||||||
|
debug(' %O', srcFiles)
|
||||||
|
|
||||||
|
const srcFileLookup = await Promise.all(
|
||||||
|
srcFiles.map((f) => fileExists(resolve(options.cwd, options.outputDir, f))),
|
||||||
|
)
|
||||||
|
|
||||||
|
const notFoundFiles = srcFiles.filter((_, i) => !srcFileLookup[i])
|
||||||
|
|
||||||
|
if (notFoundFiles.length) {
|
||||||
|
throw new Error(
|
||||||
|
`Some binary files were not found: ${JSON.stringify(notFoundFiles)}`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const output = resolve(
|
||||||
|
options.cwd,
|
||||||
|
options.outputDir,
|
||||||
|
`${config.binaryName}.${process.platform}-universal.node`,
|
||||||
|
)
|
||||||
|
|
||||||
|
universalizers[process.platform]?.(srcFiles, output)
|
||||||
|
|
||||||
|
debug(`Produced universal binary: ${output}`)
|
||||||
|
}
|
26
cli/src/api/version.ts
Normal file
26
cli/src/api/version.ts
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import { join, resolve } from 'path'
|
||||||
|
|
||||||
|
import { applyDefaultVersionOptions, VersionOptions } from '../def/version.js'
|
||||||
|
import {
|
||||||
|
readNapiConfig,
|
||||||
|
debugFactory,
|
||||||
|
updatePackageJson,
|
||||||
|
} from '../utils/index.js'
|
||||||
|
|
||||||
|
const debug = debugFactory('version')
|
||||||
|
|
||||||
|
export async function version(userOptions: VersionOptions) {
|
||||||
|
const options = applyDefaultVersionOptions(userOptions)
|
||||||
|
const packageJsonPath = resolve(options.cwd, options.packageJsonPath)
|
||||||
|
|
||||||
|
const config = await readNapiConfig(packageJsonPath)
|
||||||
|
|
||||||
|
for (const target of config.targets) {
|
||||||
|
const pkgDir = resolve(options.cwd, options.npmDir, target.platformArchABI)
|
||||||
|
|
||||||
|
debug(`Update version to %i in [%i]`, config.packageJson.version, pkgDir)
|
||||||
|
await updatePackageJson(join(pkgDir, 'package.json'), {
|
||||||
|
version: config.packageJson.version,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,56 +0,0 @@
|
||||||
export const ARM_FEATURES_H = `/* Macros to test for CPU features on ARM. Generic ARM version.
|
|
||||||
Copyright (C) 2012-2022 Free Software Foundation, Inc.
|
|
||||||
This file is part of the GNU C Library.
|
|
||||||
The GNU C Library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
The GNU C Library is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with the GNU C Library. If not, see
|
|
||||||
<https://www.gnu.org/licenses/>. */
|
|
||||||
|
|
||||||
#ifndef _ARM_ARM_FEATURES_H
|
|
||||||
#define _ARM_ARM_FEATURES_H 1
|
|
||||||
|
|
||||||
/* An OS-specific arm-features.h file should define ARM_HAVE_VFP to
|
|
||||||
an appropriate expression for testing at runtime whether the VFP
|
|
||||||
hardware is present. We'll then redefine it to a constant if we
|
|
||||||
know at compile time that we can assume VFP. */
|
|
||||||
|
|
||||||
#ifndef __SOFTFP__
|
|
||||||
/* The compiler is generating VFP instructions, so we're already
|
|
||||||
assuming the hardware exists. */
|
|
||||||
# undef ARM_HAVE_VFP
|
|
||||||
# define ARM_HAVE_VFP 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* An OS-specific arm-features.h file may define ARM_ASSUME_NO_IWMMXT
|
|
||||||
to indicate at compile time that iWMMXt hardware is never present
|
|
||||||
at runtime (or that we never care about its state) and so need not
|
|
||||||
be checked for. */
|
|
||||||
|
|
||||||
/* A more-specific arm-features.h file may define ARM_ALWAYS_BX to indicate
|
|
||||||
that instructions using pc as a destination register must never be used,
|
|
||||||
so a "bx" (or "blx") instruction is always required. */
|
|
||||||
|
|
||||||
/* The log2 of the minimum alignment required for an address that
|
|
||||||
is the target of a computed branch (i.e. a "bx" instruction).
|
|
||||||
A more-specific arm-features.h file may define this to set a more
|
|
||||||
stringent requirement.
|
|
||||||
Using this only makes sense for code in ARM mode (where instructions
|
|
||||||
always have a fixed size of four bytes), or for Thumb-mode code that is
|
|
||||||
specifically aligning all the related branch targets to match (since
|
|
||||||
Thumb instructions might be either two or four bytes). */
|
|
||||||
#ifndef ARM_BX_ALIGN_LOG2
|
|
||||||
# define ARM_BX_ALIGN_LOG2 2
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* An OS-specific arm-features.h file may define ARM_NO_INDEX_REGISTER to
|
|
||||||
indicate that the two-register addressing modes must never be used. */
|
|
||||||
|
|
||||||
#endif /* arm-features.h */
|
|
||||||
`
|
|
|
@ -1,89 +0,0 @@
|
||||||
import { join, parse } from 'path'
|
|
||||||
|
|
||||||
import { Command, Option } from 'clipanion'
|
|
||||||
import * as chalk from 'colorette'
|
|
||||||
import { fdir } from 'fdir'
|
|
||||||
|
|
||||||
import { getNapiConfig } from './consts'
|
|
||||||
import { debugFactory } from './debug'
|
|
||||||
import { UniArchsByPlatform } from './parse-triple'
|
|
||||||
import { readFileAsync, writeFileAsync } from './utils'
|
|
||||||
|
|
||||||
const debug = debugFactory('artifacts')
|
|
||||||
|
|
||||||
export class ArtifactsCommand extends Command {
|
|
||||||
static usage = Command.Usage({
|
|
||||||
description: 'Copy artifacts from Github Actions into specified dir',
|
|
||||||
})
|
|
||||||
|
|
||||||
static paths = [['artifacts']]
|
|
||||||
|
|
||||||
sourceDir = Option.String('-d,--dir', 'artifacts')
|
|
||||||
|
|
||||||
distDir = Option.String('--dist', 'npm')
|
|
||||||
|
|
||||||
configFileName?: string = Option.String('-c,--config')
|
|
||||||
|
|
||||||
async execute() {
|
|
||||||
const { platforms, binaryName, packageJsonPath } = getNapiConfig(
|
|
||||||
this.configFileName,
|
|
||||||
)
|
|
||||||
|
|
||||||
const packageJsonDir = parse(packageJsonPath).dir
|
|
||||||
|
|
||||||
const sourceApi = new fdir()
|
|
||||||
.withFullPaths()
|
|
||||||
.crawl(join(process.cwd(), this.sourceDir))
|
|
||||||
|
|
||||||
const distDirs = platforms.map((platform) =>
|
|
||||||
join(process.cwd(), this.distDir, platform.platformArchABI),
|
|
||||||
)
|
|
||||||
|
|
||||||
const universalSourceBins = new Set(
|
|
||||||
platforms
|
|
||||||
.filter((platform) => platform.arch === 'universal')
|
|
||||||
.flatMap((p) =>
|
|
||||||
UniArchsByPlatform[p.platform].map((a) => `${p.platform}-${a}`),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
await sourceApi.withPromise().then((output) =>
|
|
||||||
Promise.all(
|
|
||||||
(output as string[]).map(async (filePath) => {
|
|
||||||
debug(`Read [${chalk.yellowBright(filePath)}]`)
|
|
||||||
const sourceContent = await readFileAsync(filePath)
|
|
||||||
const parsedName = parse(filePath)
|
|
||||||
const [_binaryName, platformArchABI] = parsedName.name.split('.')
|
|
||||||
if (_binaryName !== binaryName) {
|
|
||||||
debug(
|
|
||||||
`[${chalk.yellowBright(
|
|
||||||
_binaryName,
|
|
||||||
)}] is not matched with [${chalk.greenBright(binaryName)}], skip`,
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const dir = distDirs.find((dir) => dir.includes(platformArchABI))
|
|
||||||
if (!dir && universalSourceBins.has(platformArchABI)) {
|
|
||||||
debug(
|
|
||||||
`[${chalk.yellowBright(
|
|
||||||
platformArchABI,
|
|
||||||
)}] has no dist dir but it is source bin for universal arch, skip`,
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (!dir) {
|
|
||||||
throw new TypeError(`No dist dir found for ${filePath}`)
|
|
||||||
}
|
|
||||||
const distFilePath = join(dir, parsedName.base)
|
|
||||||
debug(`Write file content to [${chalk.yellowBright(distFilePath)}]`)
|
|
||||||
await writeFileAsync(distFilePath, sourceContent)
|
|
||||||
const distFilePathLocal = join(packageJsonDir, parsedName.base)
|
|
||||||
debug(
|
|
||||||
`Write file content to [${chalk.yellowBright(distFilePathLocal)}]`,
|
|
||||||
)
|
|
||||||
await writeFileAsync(distFilePathLocal, sourceContent)
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
921
cli/src/build.ts
921
cli/src/build.ts
|
@ -1,921 +0,0 @@
|
||||||
import { execSync } from 'child_process'
|
|
||||||
import { createHash } from 'crypto'
|
|
||||||
import { existsSync, mkdirSync } from 'fs'
|
|
||||||
import { tmpdir } from 'os'
|
|
||||||
import { join, parse, sep } from 'path'
|
|
||||||
|
|
||||||
import { Command, Option } from 'clipanion'
|
|
||||||
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'
|
|
||||||
import { createJsBinding } from './js-binding-template'
|
|
||||||
import { getHostTargetTriple, parseTriple } from './parse-triple'
|
|
||||||
import {
|
|
||||||
copyFileAsync,
|
|
||||||
mkdirAsync,
|
|
||||||
readFileAsync,
|
|
||||||
unlinkAsync,
|
|
||||||
writeFileAsync,
|
|
||||||
} from './utils'
|
|
||||||
|
|
||||||
const debug = debugFactory('build')
|
|
||||||
|
|
||||||
const ZIG_PLATFORM_TARGET_MAP = {
|
|
||||||
'x86_64-unknown-linux-musl': 'x86_64-linux-musl',
|
|
||||||
'x86_64-unknown-linux-gnu': 'x86_64-linux-gnu',
|
|
||||||
// Doesn't support Windows MSVC for now
|
|
||||||
// 'x86_64-pc-windows-gnu': 'x86_64-windows-gnu',
|
|
||||||
// https://github.com/ziglang/zig/issues/1759
|
|
||||||
// 'x86_64-unknown-freebsd': 'x86_64-freebsd',
|
|
||||||
'x86_64-apple-darwin': 'x86_64-macos',
|
|
||||||
'aarch64-apple-darwin': 'aarch64-macos',
|
|
||||||
'aarch64-unknown-linux-gnu': 'aarch64-linux-gnu',
|
|
||||||
'aarch64-unknown-linux-musl': 'aarch64-linux-musl',
|
|
||||||
'armv7-unknown-linux-gnueabihf': 'arm-linux-gnueabihf',
|
|
||||||
}
|
|
||||||
|
|
||||||
const DEFAULT_GLIBC_TARGET = process.env.GLIBC_ABI_TARGET ?? '2.17'
|
|
||||||
|
|
||||||
const SHEBANG_NODE = process.platform === 'win32' ? '' : '#!/usr/bin/env node\n'
|
|
||||||
const SHEBANG_SH = process.platform === 'win32' ? '' : '#!/usr/bin/env sh\n'
|
|
||||||
|
|
||||||
function processZigLinkerArgs(platform: string, args: string[]) {
|
|
||||||
if (platform.includes('apple')) {
|
|
||||||
const newArgs = args.filter(
|
|
||||||
(arg, index) =>
|
|
||||||
!arg.startsWith('-Wl,-exported_symbols_list') &&
|
|
||||||
arg !== '-Wl,-dylib' &&
|
|
||||||
arg !== '-liconv' &&
|
|
||||||
arg !== '-Wl,-dead_strip' &&
|
|
||||||
!(arg === '-framework' && args[index + 1] === 'CoreFoundation') &&
|
|
||||||
!(arg === 'CoreFoundation' && args[index - 1] === '-framework'),
|
|
||||||
)
|
|
||||||
newArgs.push('-Wl,"-undefined=dynamic_lookup"', '-dead_strip', '-lunwind')
|
|
||||||
return newArgs
|
|
||||||
}
|
|
||||||
if (platform.includes('linux')) {
|
|
||||||
return args
|
|
||||||
.map((arg) => {
|
|
||||||
if (arg === '-lgcc_s') {
|
|
||||||
return '-lunwind'
|
|
||||||
}
|
|
||||||
return arg
|
|
||||||
})
|
|
||||||
.filter((arg) => arg !== '-march=armv7-a')
|
|
||||||
}
|
|
||||||
return args
|
|
||||||
}
|
|
||||||
|
|
||||||
export class BuildCommand extends Command {
|
|
||||||
static usage = Command.Usage({
|
|
||||||
description: 'Build and copy native module into specified dir',
|
|
||||||
})
|
|
||||||
|
|
||||||
static paths = [['build']]
|
|
||||||
|
|
||||||
appendPlatformToFilename = Option.Boolean(`--platform`, false, {
|
|
||||||
description: `Add platform triple to the .node file. ${chalk.green(
|
|
||||||
'[name].linux-x64-gnu.node',
|
|
||||||
)} for example`,
|
|
||||||
})
|
|
||||||
|
|
||||||
isRelease = Option.Boolean(`--release`, false, {
|
|
||||||
description: `Bypass to ${chalk.green('cargo build --release')}`,
|
|
||||||
})
|
|
||||||
|
|
||||||
configFileName?: string = Option.String('--config,-c', {
|
|
||||||
description: `napi config path, only JSON format accepted. Default to ${chalk.underline(
|
|
||||||
chalk.green('package.json'),
|
|
||||||
)}`,
|
|
||||||
})
|
|
||||||
|
|
||||||
cargoName?: string = Option.String('--cargo-name', {
|
|
||||||
description: `Override the ${chalk.green(
|
|
||||||
'name',
|
|
||||||
)} field in ${chalk.underline(chalk.yellowBright('Cargo.toml'))}`,
|
|
||||||
})
|
|
||||||
|
|
||||||
targetTripleDir = Option.String(
|
|
||||||
'--target',
|
|
||||||
process.env.RUST_TARGET ?? process.env.CARGO_BUILD_TARGET ?? '',
|
|
||||||
{
|
|
||||||
description: `Bypass to ${chalk.green('cargo build --target')}`,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
features?: string = Option.String('--features', {
|
|
||||||
description: `Bypass to ${chalk.green('cargo build --features')}`,
|
|
||||||
})
|
|
||||||
|
|
||||||
bin?: string = Option.String('--bin', {
|
|
||||||
description: `Bypass to ${chalk.green('cargo build --bin')}`,
|
|
||||||
})
|
|
||||||
|
|
||||||
dts?: string = Option.String('--dts', 'index.d.ts', {
|
|
||||||
description: `The filename and path of ${chalk.green(
|
|
||||||
'.d.ts',
|
|
||||||
)} file, relative to cwd`,
|
|
||||||
})
|
|
||||||
|
|
||||||
constEnum?: boolean = Option.Boolean('--const-enum', true, {
|
|
||||||
description: `Generate ${chalk.green(
|
|
||||||
'const enum',
|
|
||||||
)} in .d.ts file or not, default is ${chalk.green('true')}`,
|
|
||||||
})
|
|
||||||
|
|
||||||
noDtsHeader = Option.Boolean('--no-dts-header', false, {
|
|
||||||
description: `Don't generate ${chalk.green('.d.ts')} header`,
|
|
||||||
})
|
|
||||||
|
|
||||||
project = Option.String('-p', {
|
|
||||||
description: `Bypass to ${chalk.green('cargo -p')}`,
|
|
||||||
})
|
|
||||||
|
|
||||||
cargoFlags = Option.String('--cargo-flags', '', {
|
|
||||||
description: `All the others flag passed to ${chalk.yellow('cargo build')}`,
|
|
||||||
})
|
|
||||||
|
|
||||||
jsBinding = Option.String('--js', 'index.js', {
|
|
||||||
description: `Path to the JS binding file, pass ${chalk.underline(
|
|
||||||
chalk.yellow('false'),
|
|
||||||
)} to disable it. Only affect if ${chalk.green('--target')} is specified.`,
|
|
||||||
})
|
|
||||||
|
|
||||||
jsPackageName = Option.String('--js-package-name', {
|
|
||||||
description: `Package name in generated js binding file, Only affect if ${chalk.green(
|
|
||||||
'--target',
|
|
||||||
)} specified and ${chalk.green('--js')} is not false.`,
|
|
||||||
required: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
cargoCwd?: string = Option.String('--cargo-cwd', {
|
|
||||||
description: `The cwd of ${chalk.underline(
|
|
||||||
chalk.yellow('Cargo.toml'),
|
|
||||||
)} file`,
|
|
||||||
})
|
|
||||||
|
|
||||||
pipe?: string = Option.String('--pipe', {
|
|
||||||
description: `Pipe [${chalk.green(
|
|
||||||
'.js/.ts',
|
|
||||||
)}] files to this command, eg ${chalk.green('prettier -w')}`,
|
|
||||||
})
|
|
||||||
|
|
||||||
// https://github.com/napi-rs/napi-rs/issues/297
|
|
||||||
disableWindowsX32Optimize?: boolean = Option.Boolean(
|
|
||||||
'--disable-windows-x32-optimize',
|
|
||||||
false,
|
|
||||||
{
|
|
||||||
description: `Disable windows x32 ${chalk.green(
|
|
||||||
'lto',
|
|
||||||
)} and increase ${chalk.green(
|
|
||||||
'codegen-units',
|
|
||||||
)}. Disabled by default. See ${chalk.underline(
|
|
||||||
chalk.blue('https://github.com/napi-rs/napi-rs/issues/297'),
|
|
||||||
)}`,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
destDir = Option.String({
|
|
||||||
required: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
useZig = Option.Boolean(`--zig`, false, {
|
|
||||||
description: `Use ${chalk.green('zig')} as linker ${chalk.yellowBright(
|
|
||||||
'(Experimental)',
|
|
||||||
)}`,
|
|
||||||
})
|
|
||||||
|
|
||||||
zigABIVersion = Option.String(`--zig-abi-suffix`, {
|
|
||||||
description: `The suffix of the ${chalk.green(
|
|
||||||
'zig --target',
|
|
||||||
)} ABI version. Eg. ${chalk.cyan(
|
|
||||||
'--target x86_64-unknown-linux-gnu',
|
|
||||||
)} ${chalk.green('--zig-abi-suffix=2.17')}`,
|
|
||||||
})
|
|
||||||
|
|
||||||
zigLinkOnly = Option.Boolean(`--zig-link-only`, false, {
|
|
||||||
description: `Only link the library with ${chalk.green('zig')}`,
|
|
||||||
})
|
|
||||||
|
|
||||||
isStrip = Option.Boolean(`--strip`, false, {
|
|
||||||
description: `${chalk.green('Strip')} the library for minimum file size`,
|
|
||||||
})
|
|
||||||
|
|
||||||
async execute() {
|
|
||||||
const cwd = this.cargoCwd
|
|
||||||
? join(process.cwd(), this.cargoCwd)
|
|
||||||
: process.cwd()
|
|
||||||
const cargoTomlPath = join(cwd, 'Cargo.toml')
|
|
||||||
|
|
||||||
let cargoMetadata: any
|
|
||||||
|
|
||||||
try {
|
|
||||||
debug('Start parse toml')
|
|
||||||
cargoMetadata = JSON.parse(
|
|
||||||
execSync(
|
|
||||||
`cargo metadata --format-version 1 --manifest-path "${cargoTomlPath}"`,
|
|
||||||
{
|
|
||||||
stdio: 'pipe',
|
|
||||||
maxBuffer: 1024 * 1024 * 10,
|
|
||||||
},
|
|
||||||
).toString('utf8'),
|
|
||||||
)
|
|
||||||
} catch (e) {
|
|
||||||
throw new TypeError('Could not parse the Cargo.toml: ' + e)
|
|
||||||
}
|
|
||||||
const packages = cargoMetadata.packages
|
|
||||||
|
|
||||||
let cargoPackageName: string
|
|
||||||
if (this.cargoName) {
|
|
||||||
cargoPackageName = this.cargoName
|
|
||||||
} else {
|
|
||||||
const root = cargoMetadata.resolve.root
|
|
||||||
if (root) {
|
|
||||||
const rootPackage = packages.find((p: { id: string }) => p.id === root)
|
|
||||||
cargoPackageName = rootPackage.name
|
|
||||||
} else {
|
|
||||||
throw new TypeError('No package.name field in Cargo.toml')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const cargoPackage = packages.find(
|
|
||||||
(p: { name: string }) => p.name === cargoPackageName,
|
|
||||||
)
|
|
||||||
if (
|
|
||||||
!this.bin &&
|
|
||||||
cargoPackage?.targets?.length === 1 &&
|
|
||||||
cargoPackage?.targets[0].kind.length === 1 &&
|
|
||||||
cargoPackage?.targets[0].kind[0] === 'bin'
|
|
||||||
) {
|
|
||||||
this.bin = cargoPackageName
|
|
||||||
}
|
|
||||||
const releaseFlag = this.isRelease ? `--release` : ''
|
|
||||||
|
|
||||||
const targetFlag = this.targetTripleDir
|
|
||||||
? `--target ${this.targetTripleDir}`
|
|
||||||
: ''
|
|
||||||
const featuresFlag = this.features ? `--features ${this.features}` : ''
|
|
||||||
const binFlag = this.bin ? `--bin ${this.bin}` : ''
|
|
||||||
const triple = this.targetTripleDir
|
|
||||||
? parseTriple(this.targetTripleDir)
|
|
||||||
: getHostTargetTriple()
|
|
||||||
debug(`Current triple is: ${chalk.green(triple.raw)}`)
|
|
||||||
const pFlag = this.project ? `-p ${this.project}` : ''
|
|
||||||
const externalFlags = [
|
|
||||||
releaseFlag,
|
|
||||||
targetFlag,
|
|
||||||
featuresFlag,
|
|
||||||
binFlag,
|
|
||||||
pFlag,
|
|
||||||
this.cargoFlags,
|
|
||||||
]
|
|
||||||
.filter((flag) => Boolean(flag))
|
|
||||||
.join(' ')
|
|
||||||
const additionalEnv = {}
|
|
||||||
const isCrossForWin =
|
|
||||||
triple.platform === 'win32' && process.platform !== 'win32'
|
|
||||||
const isCrossForLinux =
|
|
||||||
triple.platform === 'linux' &&
|
|
||||||
(process.platform !== 'linux' ||
|
|
||||||
triple.arch !== process.arch ||
|
|
||||||
(function () {
|
|
||||||
const glibcVersionRuntime =
|
|
||||||
// @ts-expect-error
|
|
||||||
process.report?.getReport()?.header?.glibcVersionRuntime
|
|
||||||
const libc = glibcVersionRuntime ? 'gnu' : 'musl'
|
|
||||||
return triple.abi !== libc
|
|
||||||
})())
|
|
||||||
const isCrossForMacOS =
|
|
||||||
triple.platform === 'darwin' && process.platform !== 'darwin'
|
|
||||||
const cargo = process.env.CARGO ?? (isCrossForWin ? 'cargo-xwin' : 'cargo')
|
|
||||||
if (isCrossForWin && triple.arch === 'ia32') {
|
|
||||||
additionalEnv['XWIN_ARCH'] = 'x86'
|
|
||||||
}
|
|
||||||
const cargoCommand = `${cargo} build ${externalFlags}`
|
|
||||||
debug(`Run ${chalk.green(cargoCommand)}`)
|
|
||||||
|
|
||||||
const rustflags = process.env.RUSTFLAGS
|
|
||||||
? process.env.RUSTFLAGS.split(' ')
|
|
||||||
: []
|
|
||||||
if (triple.raw.includes('musl') && !this.bin) {
|
|
||||||
if (!rustflags.includes('target-feature=-crt-static')) {
|
|
||||||
rustflags.push('-C target-feature=-crt-static')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.isStrip && !rustflags.includes('-C link-arg=-s')) {
|
|
||||||
rustflags.push('-C link-arg=-s')
|
|
||||||
}
|
|
||||||
|
|
||||||
let useZig = false
|
|
||||||
if (this.useZig || isCrossForLinux || isCrossForMacOS) {
|
|
||||||
try {
|
|
||||||
execSync('zig version')
|
|
||||||
useZig = true
|
|
||||||
} catch (e) {
|
|
||||||
if (this.useZig) {
|
|
||||||
throw new TypeError(
|
|
||||||
`Could not find ${chalk.green('zig')} on the PATH`,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
debug(
|
|
||||||
`Could not find ${chalk.green(
|
|
||||||
'zig',
|
|
||||||
)} on the PATH, fallback to normal linker`,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (useZig) {
|
|
||||||
const zigABIVersion =
|
|
||||||
this.zigABIVersion ??
|
|
||||||
(isCrossForLinux && triple.abi === 'gnu' ? DEFAULT_GLIBC_TARGET : null)
|
|
||||||
const mappedZigTarget = ZIG_PLATFORM_TARGET_MAP[triple.raw]
|
|
||||||
const zigTarget = `${mappedZigTarget}${
|
|
||||||
zigABIVersion ? `.${zigABIVersion}` : ''
|
|
||||||
}`
|
|
||||||
debug(`Using Zig with target ${chalk.green(zigTarget)}`)
|
|
||||||
if (!mappedZigTarget) {
|
|
||||||
throw new Error(`${triple.raw} can not be cross compiled by zig`)
|
|
||||||
}
|
|
||||||
const paths = envPaths('napi-rs')
|
|
||||||
const shellFileExt = process.platform === 'win32' ? 'cmd' : 'sh'
|
|
||||||
const linkerWrapperShell = join(
|
|
||||||
paths.cache,
|
|
||||||
`zig-linker-${triple.raw}.${shellFileExt}`,
|
|
||||||
)
|
|
||||||
const CCWrapperShell = join(
|
|
||||||
paths.cache,
|
|
||||||
`zig-cc-${triple.raw}.${shellFileExt}`,
|
|
||||||
)
|
|
||||||
const CXXWrapperShell = join(
|
|
||||||
paths.cache,
|
|
||||||
`zig-cxx-${triple.raw}.${shellFileExt}`,
|
|
||||||
)
|
|
||||||
const linkerWrapper = join(paths.cache, `zig-cc-${triple.raw}.js`)
|
|
||||||
mkdirSync(paths.cache, { recursive: true })
|
|
||||||
const forwardArgs = process.platform === 'win32' ? '"%*"' : '$@'
|
|
||||||
if (triple.arch === 'arm') {
|
|
||||||
await patchArmFeaturesHForArmTargets()
|
|
||||||
}
|
|
||||||
await writeFileAsync(
|
|
||||||
linkerWrapperShell,
|
|
||||||
process.platform === 'win32'
|
|
||||||
? `@IF EXIST "%~dp0\\node.exe" (
|
|
||||||
"%~dp0\\node.exe" "${linkerWrapper}" %*
|
|
||||||
) ELSE (
|
|
||||||
@SETLOCAL
|
|
||||||
@SET PATHEXT=%PATHEXT:;.JS;=;%
|
|
||||||
node "${linkerWrapper}" %*
|
|
||||||
)`
|
|
||||||
: `${SHEBANG_SH}node ${linkerWrapper} ${forwardArgs}`,
|
|
||||||
{
|
|
||||||
mode: '777',
|
|
||||||
},
|
|
||||||
)
|
|
||||||
await writeFileAsync(
|
|
||||||
CCWrapperShell,
|
|
||||||
`${SHEBANG_SH}node ${linkerWrapper} cc ${forwardArgs}`,
|
|
||||||
{
|
|
||||||
mode: '777',
|
|
||||||
},
|
|
||||||
)
|
|
||||||
await writeFileAsync(
|
|
||||||
CXXWrapperShell,
|
|
||||||
`${SHEBANG_SH}node ${linkerWrapper} c++ ${forwardArgs}`,
|
|
||||||
{
|
|
||||||
mode: '777',
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
await writeFileAsync(
|
|
||||||
linkerWrapper,
|
|
||||||
`${SHEBANG_NODE}const{writeFileSync} = require('fs')\n${processZigLinkerArgs.toString()}\nconst {status} = require('child_process').spawnSync('zig', [process.argv[2] === "c++" || process.argv[2] === "cc" ? "" : "cc", ...processZigLinkerArgs('${
|
|
||||||
triple.raw
|
|
||||||
}', process.argv.slice(2)), '-target', '${zigTarget}'], { stdio: 'inherit', shell: true })\nwriteFileSync('${linkerWrapper.replaceAll(
|
|
||||||
'\\',
|
|
||||||
'/',
|
|
||||||
)}.args.log', processZigLinkerArgs('${
|
|
||||||
triple.raw
|
|
||||||
}', process.argv.slice(2)).join(' '))\n\nprocess.exit(status || 0)\n`,
|
|
||||||
{
|
|
||||||
mode: '777',
|
|
||||||
},
|
|
||||||
)
|
|
||||||
const envTarget = triple.raw.replaceAll('-', '_').toUpperCase()
|
|
||||||
if (!this.zigLinkOnly) {
|
|
||||||
Object.assign(additionalEnv, {
|
|
||||||
CC: CCWrapperShell,
|
|
||||||
CXX: CXXWrapperShell,
|
|
||||||
TARGET_CC: CCWrapperShell,
|
|
||||||
TARGET_CXX: CXXWrapperShell,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
additionalEnv[`CARGO_TARGET_${envTarget}_LINKER`] = linkerWrapperShell
|
|
||||||
}
|
|
||||||
debug(`Platform: ${JSON.stringify(triple, null, 2)}`)
|
|
||||||
if (triple.platform === 'android') {
|
|
||||||
const { ANDROID_NDK_LATEST_HOME } = process.env
|
|
||||||
if (!ANDROID_NDK_LATEST_HOME) {
|
|
||||||
console.info(
|
|
||||||
`${chalk.yellow(
|
|
||||||
'ANDROID_NDK_LATEST_HOME',
|
|
||||||
)} environment variable is missing`,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
const targetArch = triple.arch === 'arm' ? 'armv7a' : 'aarch64'
|
|
||||||
const targetPlatform =
|
|
||||||
triple.arch === 'arm' ? 'androideabi24' : 'android24'
|
|
||||||
Object.assign(additionalEnv, {
|
|
||||||
CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER: `${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/${targetArch}-linux-android24-clang`,
|
|
||||||
CARGO_TARGET_ARMV7_LINUX_ANDROIDEABI_LINKER: `${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/${targetArch}-linux-androideabi24-clang`,
|
|
||||||
CC: `${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/${targetArch}-linux-${targetPlatform}-clang`,
|
|
||||||
CXX: `${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/${targetArch}-linux-${targetPlatform}-clang++`,
|
|
||||||
AR: `${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ar`,
|
|
||||||
ANDROID_NDK: ANDROID_NDK_LATEST_HOME,
|
|
||||||
PATH: `${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin:${process.env.PATH}`,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const {
|
|
||||||
binaryName,
|
|
||||||
packageName,
|
|
||||||
tsConstEnum: tsConstEnumFromConfig,
|
|
||||||
} = getNapiConfig(this.configFileName)
|
|
||||||
const tsConstEnum = this.constEnum ?? tsConstEnumFromConfig
|
|
||||||
if (triple.platform === 'wasi') {
|
|
||||||
try {
|
|
||||||
const emnapiDir = require.resolve('emnapi')
|
|
||||||
const linkDir = join(emnapiDir, '..', 'lib', 'wasm32-wasi')
|
|
||||||
additionalEnv['EMNAPI_LINK_DIR'] = linkDir
|
|
||||||
rustflags.push('-Z wasi-exec-model=reactor')
|
|
||||||
} catch (e) {
|
|
||||||
const err = new Error(`Could not find emnapi, please install emnapi`)
|
|
||||||
err.cause = e
|
|
||||||
throw err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (rustflags.length > 0) {
|
|
||||||
additionalEnv['RUSTFLAGS'] = rustflags.join(' ')
|
|
||||||
}
|
|
||||||
|
|
||||||
let cargoArtifactName = this.cargoName
|
|
||||||
if (!cargoArtifactName) {
|
|
||||||
if (this.bin) {
|
|
||||||
cargoArtifactName = cargoPackageName
|
|
||||||
} else {
|
|
||||||
cargoArtifactName = cargoPackageName.replace(/-/g, '_')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
!this.bin &&
|
|
||||||
!cargoPackage.targets.some((target: { crate_types: string[] }) =>
|
|
||||||
target.crate_types.includes('cdylib'),
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
throw new TypeError(
|
|
||||||
`Missing ${chalk.green('crate-type = ["cdylib"]')} in ${chalk.green(
|
|
||||||
'[lib]',
|
|
||||||
)}`,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.bin) {
|
|
||||||
debug(`Binary name: ${chalk.greenBright(cargoArtifactName)}`)
|
|
||||||
} else {
|
|
||||||
debug(`Dylib name: ${chalk.greenBright(cargoArtifactName)}`)
|
|
||||||
}
|
|
||||||
const cwdSha = createHash('sha256')
|
|
||||||
.update(process.cwd())
|
|
||||||
.update(version)
|
|
||||||
.digest('hex')
|
|
||||||
.substring(0, 8)
|
|
||||||
const intermediateTypeFile = join(
|
|
||||||
tmpdir(),
|
|
||||||
`${cargoArtifactName}-${cwdSha}.napi_type_def.tmp`,
|
|
||||||
)
|
|
||||||
const intermediateWasiRegisterFile = join(
|
|
||||||
tmpdir(),
|
|
||||||
`${cargoArtifactName}-${cwdSha}.napi_wasi_register.tmp`,
|
|
||||||
)
|
|
||||||
debug(`intermediate type def file: ${intermediateTypeFile}`)
|
|
||||||
|
|
||||||
const commandEnv = {
|
|
||||||
...process.env,
|
|
||||||
...additionalEnv,
|
|
||||||
TYPE_DEF_TMP_PATH: intermediateTypeFile,
|
|
||||||
WASI_REGISTER_TMP_PATH: intermediateWasiRegisterFile,
|
|
||||||
CARGO_CFG_NAPI_RS_CLI_VERSION: version,
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
execSync(cargoCommand, {
|
|
||||||
env: commandEnv,
|
|
||||||
stdio: 'inherit',
|
|
||||||
cwd,
|
|
||||||
})
|
|
||||||
} catch (e) {
|
|
||||||
if (cargo === 'cargo-xwin') {
|
|
||||||
console.warn(
|
|
||||||
`You are cross compiling ${chalk.underline(
|
|
||||||
triple.raw,
|
|
||||||
)} target on ${chalk.green(process.platform)} host`,
|
|
||||||
)
|
|
||||||
} else if (isCrossForLinux || isCrossForMacOS) {
|
|
||||||
console.warn(
|
|
||||||
`You are cross compiling ${chalk.underline(
|
|
||||||
triple.raw,
|
|
||||||
)} on ${chalk.green(process.platform)} host`,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
|
|
||||||
const platform = triple.platform
|
|
||||||
let libExt = ''
|
|
||||||
|
|
||||||
debug(`Platform: ${chalk.greenBright(platform)}`)
|
|
||||||
|
|
||||||
// Platform based massaging for build commands
|
|
||||||
if (!this.bin) {
|
|
||||||
switch (platform) {
|
|
||||||
case 'darwin':
|
|
||||||
libExt = '.dylib'
|
|
||||||
cargoArtifactName = `lib${cargoArtifactName}`
|
|
||||||
break
|
|
||||||
case 'win32':
|
|
||||||
libExt = '.dll'
|
|
||||||
break
|
|
||||||
case 'linux':
|
|
||||||
case 'freebsd':
|
|
||||||
case 'openbsd':
|
|
||||||
case 'android':
|
|
||||||
case 'sunos':
|
|
||||||
cargoArtifactName = `lib${cargoArtifactName}`
|
|
||||||
libExt = '.so'
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
throw new TypeError(
|
|
||||||
'Operating system not currently supported or recognized by the build script',
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const targetRootDir =
|
|
||||||
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
|
||||||
process.env.CARGO_TARGET_DIR ||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
|
||||||
process.env.CARGO_BUILD_TARGET_DIR ||
|
|
||||||
(await findUp(cwd))
|
|
||||||
|
|
||||||
if (!targetRootDir) {
|
|
||||||
throw new TypeError('No target dir found')
|
|
||||||
}
|
|
||||||
|
|
||||||
const targetDir = join(
|
|
||||||
this.targetTripleDir,
|
|
||||||
this.isRelease ? 'release' : 'debug',
|
|
||||||
)
|
|
||||||
|
|
||||||
const platformName = this.appendPlatformToFilename
|
|
||||||
? `.${triple.platformArchABI}`
|
|
||||||
: ''
|
|
||||||
|
|
||||||
debug(`Platform name: ${platformName || chalk.green('[Empty]')}`)
|
|
||||||
const distFileName = this.bin
|
|
||||||
? cargoArtifactName!
|
|
||||||
: `${binaryName}${platformName}.node`
|
|
||||||
|
|
||||||
const distModulePath = join(this.destDir ?? '.', distFileName)
|
|
||||||
|
|
||||||
const parsedDist = parse(distModulePath)
|
|
||||||
|
|
||||||
if (parsedDist.dir && !existsSync(parsedDist.dir)) {
|
|
||||||
await mkdirAsync(parsedDist.dir, { recursive: true }).catch((e) => {
|
|
||||||
console.warn(
|
|
||||||
chalk.bgYellowBright(
|
|
||||||
`Create dir [${parsedDist.dir}] failed, reason: ${e.message}`,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const sourcePath = join(
|
|
||||||
targetRootDir,
|
|
||||||
targetDir,
|
|
||||||
`${cargoArtifactName}${libExt}`,
|
|
||||||
)
|
|
||||||
|
|
||||||
if (existsSync(distModulePath)) {
|
|
||||||
debug(`remove old binary [${chalk.yellowBright(distModulePath)}]`)
|
|
||||||
await unlinkAsync(distModulePath)
|
|
||||||
}
|
|
||||||
|
|
||||||
debug(`Write binary content to [${chalk.yellowBright(distModulePath)}]`)
|
|
||||||
await copyFileAsync(sourcePath, distModulePath)
|
|
||||||
|
|
||||||
if (!this.bin) {
|
|
||||||
const dtsFilePath = join(
|
|
||||||
process.cwd(),
|
|
||||||
this.destDir ?? '.',
|
|
||||||
this.dts ?? 'index.d.ts',
|
|
||||||
)
|
|
||||||
|
|
||||||
const jsBindingFilePath =
|
|
||||||
this.jsBinding &&
|
|
||||||
this.jsBinding !== 'false' &&
|
|
||||||
this.appendPlatformToFilename
|
|
||||||
? join(process.cwd(), this.destDir ?? '.', this.jsBinding)
|
|
||||||
: null
|
|
||||||
const idents = await processIntermediateTypeFile(
|
|
||||||
intermediateTypeFile,
|
|
||||||
dtsFilePath,
|
|
||||||
this.noDtsHeader,
|
|
||||||
tsConstEnum,
|
|
||||||
)
|
|
||||||
await writeJsBinding(
|
|
||||||
binaryName,
|
|
||||||
this.jsPackageName ?? packageName,
|
|
||||||
jsBindingFilePath,
|
|
||||||
idents,
|
|
||||||
)
|
|
||||||
if (this.pipe) {
|
|
||||||
if (jsBindingFilePath) {
|
|
||||||
const pipeCommand = `${this.pipe} ${jsBindingFilePath}`
|
|
||||||
console.info(`Run ${chalk.green(pipeCommand)}`)
|
|
||||||
try {
|
|
||||||
execSync(pipeCommand, { stdio: 'inherit', env: commandEnv })
|
|
||||||
} catch (e) {
|
|
||||||
console.warn(
|
|
||||||
chalk.bgYellowBright(
|
|
||||||
'Pipe the js binding file to command failed',
|
|
||||||
),
|
|
||||||
e,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const pipeCommand = `${this.pipe} ${dtsFilePath}`
|
|
||||||
console.info(`Run ${chalk.green(pipeCommand)}`)
|
|
||||||
try {
|
|
||||||
execSync(pipeCommand, { stdio: 'inherit', env: commandEnv })
|
|
||||||
} catch (e) {
|
|
||||||
console.warn(
|
|
||||||
chalk.bgYellowBright('Pipe the dts file to command failed'),
|
|
||||||
e,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function findUp(dir = process.cwd()): Promise<string | null> {
|
|
||||||
const dist = join(dir, 'target')
|
|
||||||
if (existsSync(dist)) {
|
|
||||||
return dist
|
|
||||||
}
|
|
||||||
const dirs = dir.split(sep)
|
|
||||||
if (dirs.length < 2) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
dirs.pop()
|
|
||||||
return findUp(dirs.join(sep))
|
|
||||||
}
|
|
||||||
|
|
||||||
interface TypeDef {
|
|
||||||
kind: 'fn' | 'struct' | 'impl' | 'enum' | 'interface'
|
|
||||||
name: string
|
|
||||||
original_name?: string
|
|
||||||
def: string
|
|
||||||
js_mod?: string
|
|
||||||
js_doc: string
|
|
||||||
}
|
|
||||||
|
|
||||||
async function processIntermediateTypeFile(
|
|
||||||
source: string,
|
|
||||||
target: string,
|
|
||||||
noDtsHeader: boolean,
|
|
||||||
tsConstEnum: boolean,
|
|
||||||
): Promise<string[]> {
|
|
||||||
const idents: string[] = []
|
|
||||||
if (!existsSync(source)) {
|
|
||||||
debug(`do not find tmp type file. skip type generation`)
|
|
||||||
return idents
|
|
||||||
}
|
|
||||||
|
|
||||||
const tmpFile = await readFileAsync(source, 'utf8')
|
|
||||||
const lines = tmpFile
|
|
||||||
.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
|
|
||||||
}
|
|
||||||
|
|
||||||
const allDefs = lines.map((line) => JSON.parse(line) as TypeDef)
|
|
||||||
|
|
||||||
function convertDefs(defs: TypeDef[], nested = false): string {
|
|
||||||
const classes = new Map<
|
|
||||||
string,
|
|
||||||
{ def: string; js_doc: string; original_name?: string }
|
|
||||||
>()
|
|
||||||
const impls = new Map<string, string>()
|
|
||||||
let dts = ''
|
|
||||||
const nest = nested ? 2 : 0
|
|
||||||
|
|
||||||
defs.forEach((def) => {
|
|
||||||
switch (def.kind) {
|
|
||||||
case 'struct':
|
|
||||||
if (!nested) {
|
|
||||||
idents.push(def.name)
|
|
||||||
}
|
|
||||||
classes.set(def.name, {
|
|
||||||
original_name: def.original_name,
|
|
||||||
def: def.def,
|
|
||||||
js_doc: def.js_doc,
|
|
||||||
})
|
|
||||||
break
|
|
||||||
case 'impl':
|
|
||||||
const existed = impls.get(def.name)
|
|
||||||
impls.set(
|
|
||||||
def.name,
|
|
||||||
`${existed ? existed + '\n' : ''}${def.js_doc}${def.def}`,
|
|
||||||
)
|
|
||||||
break
|
|
||||||
case 'interface':
|
|
||||||
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':
|
|
||||||
if (!nested) {
|
|
||||||
idents.push(def.name)
|
|
||||||
}
|
|
||||||
const enumPrefix = tsConstEnum ? ' const' : ''
|
|
||||||
dts +=
|
|
||||||
indentLines(
|
|
||||||
`${def.js_doc}export${enumPrefix} 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 += indentLines(`${def.js_doc}${def.def}`, nest) + '\n'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
for (const [name, { js_doc, def, original_name }] of classes.entries()) {
|
|
||||||
const implDef = impls.get(name)
|
|
||||||
|
|
||||||
if (original_name && name !== original_name) {
|
|
||||||
dts += indentLines(`export type ${original_name} = ${name}\n`, nest)
|
|
||||||
}
|
|
||||||
|
|
||||||
dts += indentLines(`${js_doc}export class ${name} {`, nest)
|
|
||||||
|
|
||||||
if (def) {
|
|
||||||
dts += '\n' + indentLines(def, nest + 2)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (implDef) {
|
|
||||||
dts += '\n' + indentLines(implDef, nest + 2)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (def || implDef) {
|
|
||||||
dts += '\n'
|
|
||||||
} else {
|
|
||||||
dts += ` `
|
|
||||||
}
|
|
||||||
|
|
||||||
dts += indentLines(`}`, nest) + '\n'
|
|
||||||
}
|
|
||||||
|
|
||||||
return dts
|
|
||||||
}
|
|
||||||
|
|
||||||
const topLevelDef = convertDefs(allDefs.filter((def) => !def.js_mod))
|
|
||||||
|
|
||||||
const namespaceDefs = Object.entries(
|
|
||||||
groupBy(
|
|
||||||
allDefs.filter((def) => def.js_mod),
|
|
||||||
'js_mod',
|
|
||||||
),
|
|
||||||
).reduce((acc, [mod, defs]) => {
|
|
||||||
idents.push(mod)
|
|
||||||
return acc + `export namespace ${mod} {\n${convertDefs(defs, true)}}\n`
|
|
||||||
}, '')
|
|
||||||
|
|
||||||
const dtsHeader = noDtsHeader
|
|
||||||
? ''
|
|
||||||
: `/* tslint:disable */
|
|
||||||
/* eslint-disable */
|
|
||||||
|
|
||||||
/* auto-generated by NAPI-RS */\n
|
|
||||||
`
|
|
||||||
|
|
||||||
const externalDef =
|
|
||||||
topLevelDef.indexOf('ExternalObject<') > -1 ||
|
|
||||||
namespaceDefs.indexOf('ExternalObject<') > -1
|
|
||||||
? `export class ExternalObject<T> {
|
|
||||||
readonly '': {
|
|
||||||
readonly '': unique symbol
|
|
||||||
[K: symbol]: T
|
|
||||||
}
|
|
||||||
}\n`
|
|
||||||
: ''
|
|
||||||
|
|
||||||
await writeFileAsync(
|
|
||||||
target,
|
|
||||||
dtsHeader + externalDef + topLevelDef + namespaceDefs,
|
|
||||||
'utf8',
|
|
||||||
)
|
|
||||||
return idents
|
|
||||||
}
|
|
||||||
|
|
||||||
function indentLines(input: string, spaces: number) {
|
|
||||||
return input
|
|
||||||
.split('\n')
|
|
||||||
.map(
|
|
||||||
(line) =>
|
|
||||||
''.padEnd(spaces, ' ') +
|
|
||||||
(line.startsWith(' *') ? line.trimEnd() : line.trim()),
|
|
||||||
)
|
|
||||||
.join('\n')
|
|
||||||
}
|
|
||||||
|
|
||||||
async function writeJsBinding(
|
|
||||||
localName: string,
|
|
||||||
packageName: string,
|
|
||||||
distFileName: string | null,
|
|
||||||
idents: string[],
|
|
||||||
) {
|
|
||||||
if (distFileName && idents.length) {
|
|
||||||
const template = createJsBinding(localName, packageName)
|
|
||||||
const declareCodes = `const { ${idents.join(', ')} } = nativeBinding\n`
|
|
||||||
const exportsCode = idents.reduce(
|
|
||||||
(acc, cur) => `${acc}\nmodule.exports.${cur} = ${cur}`,
|
|
||||||
'',
|
|
||||||
)
|
|
||||||
await writeFileAsync(
|
|
||||||
distFileName,
|
|
||||||
template + declareCodes + exportsCode + '\n',
|
|
||||||
'utf8',
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function patchArmFeaturesHForArmTargets() {
|
|
||||||
let zigExePath: string
|
|
||||||
let zigLibDir: string | undefined
|
|
||||||
try {
|
|
||||||
const zigEnv = JSON.parse(execSync(`zig env`, { encoding: 'utf8' }).trim())
|
|
||||||
zigExePath = zigEnv['zig_exe']
|
|
||||||
zigLibDir = zigEnv['lib_dir']
|
|
||||||
} catch (e) {
|
|
||||||
throw new Error(
|
|
||||||
'Cannot get zig env correctly, please ensure the zig is installed correctly on your system',
|
|
||||||
)
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const p = zigLibDir
|
|
||||||
? join(zigLibDir, 'libc/glibc/sysdeps/arm/arm-features.h')
|
|
||||||
: join(zigExePath, '../lib/libc/glibc/sysdeps/arm/arm-features.h')
|
|
||||||
if (!existsSync(p)) {
|
|
||||||
await writeFileAsync(p, ARM_FEATURES_H, {
|
|
||||||
mode: 0o644,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error(
|
|
||||||
Error(
|
|
||||||
`Cannot patch arm-features.h, error: ${
|
|
||||||
(e as Error).message || e
|
|
||||||
}. See: https://github.com/ziglang/zig/issues/3287`,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
29
cli/src/cli.ts
Normal file
29
cli/src/cli.ts
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import { Cli } from 'clipanion'
|
||||||
|
|
||||||
|
import { ArtifactsCommand } from './commands/artifacts.js'
|
||||||
|
import { BuildCommand } from './commands/build.js'
|
||||||
|
import { CreateNpmDirsCommand } from './commands/create-npm-dirs.js'
|
||||||
|
import { HelpCommand } from './commands/help.js'
|
||||||
|
import { NewCommand } from './commands/new.js'
|
||||||
|
import { PrePublishCommand } from './commands/pre-publish.js'
|
||||||
|
import { RenameCommand } from './commands/rename.js'
|
||||||
|
import { UniversalizeCommand } from './commands/universalize.js'
|
||||||
|
import { VersionCommand } from './commands/version.js'
|
||||||
|
import { CLI_VERSION } from './utils/misc.js'
|
||||||
|
|
||||||
|
const cli = new Cli({
|
||||||
|
binaryName: 'napi',
|
||||||
|
binaryVersion: CLI_VERSION,
|
||||||
|
})
|
||||||
|
|
||||||
|
cli.register(NewCommand)
|
||||||
|
cli.register(BuildCommand)
|
||||||
|
cli.register(CreateNpmDirsCommand)
|
||||||
|
cli.register(ArtifactsCommand)
|
||||||
|
cli.register(UniversalizeCommand)
|
||||||
|
cli.register(RenameCommand)
|
||||||
|
cli.register(PrePublishCommand)
|
||||||
|
cli.register(VersionCommand)
|
||||||
|
cli.register(HelpCommand)
|
||||||
|
|
||||||
|
void cli.runExit(process.argv.slice(2))
|
23
cli/src/commands/artifacts.ts
Normal file
23
cli/src/commands/artifacts.ts
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import { Command } from 'clipanion'
|
||||||
|
|
||||||
|
import { collectArtifacts } from '../api/artifacts.js'
|
||||||
|
import { BaseArtifactsCommand } from '../def/artifacts.js'
|
||||||
|
|
||||||
|
export class ArtifactsCommand extends BaseArtifactsCommand {
|
||||||
|
static usage = Command.Usage({
|
||||||
|
description: 'Copy artifacts from Github Actions into specified dir',
|
||||||
|
examples: [
|
||||||
|
[
|
||||||
|
'$0 artifacts --dir . --dist ./npm',
|
||||||
|
`Copy [binaryName].[platform].node under current dir(.) into packages under npm dir.
|
||||||
|
e.g: index.linux-x64-gnu.node --> ./npm/linux-x64-gnu/index.node`,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
static paths = [['artifacts']]
|
||||||
|
|
||||||
|
async execute() {
|
||||||
|
await collectArtifacts(this.getOptions())
|
||||||
|
}
|
||||||
|
}
|
42
cli/src/commands/build.ts
Normal file
42
cli/src/commands/build.ts
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
import { execSync } from 'child_process'
|
||||||
|
|
||||||
|
import { Option } from 'clipanion'
|
||||||
|
|
||||||
|
import { buildProject } from '../api/build.js'
|
||||||
|
import { BaseBuildCommand } from '../def/build.js'
|
||||||
|
import { debugFactory } from '../utils/index.js'
|
||||||
|
|
||||||
|
const debug = debugFactory('build')
|
||||||
|
|
||||||
|
export class BuildCommand extends BaseBuildCommand {
|
||||||
|
pipe = Option.String('--pipe', {
|
||||||
|
description:
|
||||||
|
'Pipe all outputs file to given command. e.g. `napi build --pipe "npx prettier --write"`',
|
||||||
|
})
|
||||||
|
|
||||||
|
cargoOptions = Option.Rest()
|
||||||
|
|
||||||
|
async execute() {
|
||||||
|
const { task } = await buildProject({
|
||||||
|
...this.getOptions(),
|
||||||
|
cargoOptions: this.cargoOptions,
|
||||||
|
})
|
||||||
|
|
||||||
|
const outputs = await task
|
||||||
|
|
||||||
|
if (this.pipe) {
|
||||||
|
for (const output of outputs) {
|
||||||
|
debug('Piping output file to command: %s', this.pipe)
|
||||||
|
try {
|
||||||
|
execSync(`${this.pipe} ${output.path}`, {
|
||||||
|
stdio: 'inherit',
|
||||||
|
cwd: this.cwd,
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
debug.error(`Failed to pipe output file ${output.path} to command`)
|
||||||
|
debug.error(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
8
cli/src/commands/create-npm-dirs.ts
Normal file
8
cli/src/commands/create-npm-dirs.ts
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
import { createNpmDirs } from '../api/create-npm-dirs.js'
|
||||||
|
import { BaseCreateNpmDirsCommand } from '../def/create-npm-dirs.js'
|
||||||
|
|
||||||
|
export class CreateNpmDirsCommand extends BaseCreateNpmDirsCommand {
|
||||||
|
async execute() {
|
||||||
|
await createNpmDirs(this.getOptions())
|
||||||
|
}
|
||||||
|
}
|
138
cli/src/commands/new.ts
Normal file
138
cli/src/commands/new.ts
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
import path from 'path'
|
||||||
|
|
||||||
|
import { Option } from 'clipanion'
|
||||||
|
import inquirer from 'inquirer'
|
||||||
|
|
||||||
|
import { newProject } from '../api/new.js'
|
||||||
|
import { BaseNewCommand } from '../def/new.js'
|
||||||
|
import {
|
||||||
|
AVAILABLE_TARGETS,
|
||||||
|
debugFactory,
|
||||||
|
DEFAULT_TARGETS,
|
||||||
|
TargetTriple,
|
||||||
|
} from '../utils/index.js'
|
||||||
|
import { napiEngineRequirement } from '../utils/version.js'
|
||||||
|
|
||||||
|
const debug = debugFactory('new')
|
||||||
|
|
||||||
|
export class NewCommand extends BaseNewCommand {
|
||||||
|
interactive = Option.Boolean('--interactive,-i', false, {
|
||||||
|
description:
|
||||||
|
'Ask project basic information interactively without just using the default.',
|
||||||
|
})
|
||||||
|
|
||||||
|
async execute() {
|
||||||
|
try {
|
||||||
|
const options = await this.fetchOptions()
|
||||||
|
await newProject(options)
|
||||||
|
return 0
|
||||||
|
} catch (e) {
|
||||||
|
debug('Failed to create new project')
|
||||||
|
debug.error(e)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async fetchOptions() {
|
||||||
|
const cmdOptions = super.getOptions()
|
||||||
|
|
||||||
|
if (this.interactive) {
|
||||||
|
return {
|
||||||
|
...cmdOptions,
|
||||||
|
name: await this.fetchName(path.parse(cmdOptions.path).base),
|
||||||
|
minNodeApiVersion: await this.fetchNapiVersion(),
|
||||||
|
targets: await this.fetchTargets(),
|
||||||
|
license: await this.fetchLicense(),
|
||||||
|
enableTypeDef: await this.fetchTypeDef(),
|
||||||
|
enableGithubActions: await this.fetchGithubActions(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cmdOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
private async fetchName(defaultName: string): Promise<string> {
|
||||||
|
return (
|
||||||
|
this.$$name ??
|
||||||
|
(await inquirer
|
||||||
|
.prompt({
|
||||||
|
type: 'input',
|
||||||
|
name: 'name',
|
||||||
|
message: 'Package name (the name field in your package.json file)',
|
||||||
|
default: defaultName,
|
||||||
|
})
|
||||||
|
.then(({ name }) => name))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private async fetchLicense(): Promise<string> {
|
||||||
|
return inquirer
|
||||||
|
.prompt({
|
||||||
|
type: 'input',
|
||||||
|
name: 'license',
|
||||||
|
message: 'License for open-sourced project',
|
||||||
|
default: this.license,
|
||||||
|
})
|
||||||
|
.then(({ license }) => license)
|
||||||
|
}
|
||||||
|
|
||||||
|
private async fetchNapiVersion(): Promise<number> {
|
||||||
|
return inquirer
|
||||||
|
.prompt({
|
||||||
|
type: 'list',
|
||||||
|
name: 'minNodeApiVersion',
|
||||||
|
message: 'Minimum node-api version (with node version requirement)',
|
||||||
|
loop: false,
|
||||||
|
choices: new Array(8).fill(0).map((_, i) => ({
|
||||||
|
name: `napi${i + 1} (${napiEngineRequirement(i + 1)})`,
|
||||||
|
value: i + 1,
|
||||||
|
})),
|
||||||
|
// choice index
|
||||||
|
default: this.minNodeApiVersion - 1,
|
||||||
|
})
|
||||||
|
.then(({ minNodeApiVersion }) => minNodeApiVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
private async fetchTargets(): Promise<TargetTriple[]> {
|
||||||
|
if (this.enableDefaultTargets) {
|
||||||
|
return DEFAULT_TARGETS.concat()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.enableAllTargets) {
|
||||||
|
return AVAILABLE_TARGETS.concat()
|
||||||
|
}
|
||||||
|
|
||||||
|
const { targets } = await inquirer.prompt({
|
||||||
|
name: 'targets',
|
||||||
|
type: 'checkbox',
|
||||||
|
loop: false,
|
||||||
|
message: 'Choose target(s) your crate will be compiled to',
|
||||||
|
default: DEFAULT_TARGETS,
|
||||||
|
choices: AVAILABLE_TARGETS,
|
||||||
|
})
|
||||||
|
|
||||||
|
return targets
|
||||||
|
}
|
||||||
|
|
||||||
|
private async fetchTypeDef(): Promise<boolean> {
|
||||||
|
const { enableTypeDef } = await inquirer.prompt({
|
||||||
|
name: 'enableTypeDef',
|
||||||
|
type: 'confirm',
|
||||||
|
message: 'Enable type definition auto-generation',
|
||||||
|
default: this.enableTypeDef,
|
||||||
|
})
|
||||||
|
|
||||||
|
return enableTypeDef
|
||||||
|
}
|
||||||
|
|
||||||
|
private async fetchGithubActions(): Promise<boolean> {
|
||||||
|
const { enableGithubActions } = await inquirer.prompt({
|
||||||
|
name: 'enableGithubActions',
|
||||||
|
type: 'confirm',
|
||||||
|
message: 'Enable Github Actions CI',
|
||||||
|
default: this.enableGithubActions,
|
||||||
|
})
|
||||||
|
|
||||||
|
return enableGithubActions
|
||||||
|
}
|
||||||
|
}
|
9
cli/src/commands/pre-publish.ts
Normal file
9
cli/src/commands/pre-publish.ts
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import { prePublish } from '../api/pre-publish.js'
|
||||||
|
import { BasePrePublishCommand } from '../def/pre-publish.js'
|
||||||
|
|
||||||
|
export class PrePublishCommand extends BasePrePublishCommand {
|
||||||
|
async execute() {
|
||||||
|
// @ts-expect-error const 'npm' | 'lerna' to string
|
||||||
|
await prePublish(this.getOptions())
|
||||||
|
}
|
||||||
|
}
|
8
cli/src/commands/rename.ts
Normal file
8
cli/src/commands/rename.ts
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
import { renameProject } from '../api/rename.js'
|
||||||
|
import { BaseRenameCommand } from '../def/rename.js'
|
||||||
|
|
||||||
|
export class RenameCommand extends BaseRenameCommand {
|
||||||
|
async execute() {
|
||||||
|
await renameProject(this.getOptions())
|
||||||
|
}
|
||||||
|
}
|
8
cli/src/commands/universalize.ts
Normal file
8
cli/src/commands/universalize.ts
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
import { universalizeBinaries } from '../api/universalize.js'
|
||||||
|
import { BaseUniversalizeCommand } from '../def/universalize.js'
|
||||||
|
|
||||||
|
export class UniversalizeCommand extends BaseUniversalizeCommand {
|
||||||
|
async execute() {
|
||||||
|
await universalizeBinaries(this.getOptions())
|
||||||
|
}
|
||||||
|
}
|
8
cli/src/commands/version.ts
Normal file
8
cli/src/commands/version.ts
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
import { version } from '../api/version.js'
|
||||||
|
import { BaseVersionCommand } from '../def/version.js'
|
||||||
|
|
||||||
|
export class VersionCommand extends BaseVersionCommand {
|
||||||
|
async execute() {
|
||||||
|
await version(this.getOptions())
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,40 +0,0 @@
|
||||||
import { join } from 'path'
|
|
||||||
|
|
||||||
import { DefaultPlatforms, PlatformDetail, parseTriple } from './parse-triple'
|
|
||||||
|
|
||||||
export function getNapiConfig(
|
|
||||||
packageJson = 'package.json',
|
|
||||||
cwd = process.cwd(),
|
|
||||||
) {
|
|
||||||
const packageJsonPath = join(cwd, packageJson)
|
|
||||||
|
|
||||||
const pkgJson = require(packageJsonPath)
|
|
||||||
const { version: packageVersion, napi, name } = pkgJson
|
|
||||||
const additionPlatforms: PlatformDetail[] = (
|
|
||||||
napi?.triples?.additional ?? []
|
|
||||||
).map(parseTriple)
|
|
||||||
const defaultPlatforms =
|
|
||||||
napi?.triples?.defaults === false ? [] : [...DefaultPlatforms]
|
|
||||||
const tsConstEnum: boolean = napi?.ts?.constEnum ?? true
|
|
||||||
const platforms = [...defaultPlatforms, ...additionPlatforms]
|
|
||||||
const releaseVersion = process.env.RELEASE_VERSION
|
|
||||||
const releaseVersionWithoutPrefix = releaseVersion?.startsWith('v')
|
|
||||||
? releaseVersion.substring(1)
|
|
||||||
: releaseVersion
|
|
||||||
const version = releaseVersionWithoutPrefix ?? packageVersion
|
|
||||||
const packageName = napi?.package?.name ?? name
|
|
||||||
const npmClient: string = napi?.npmClient ?? 'npm'
|
|
||||||
|
|
||||||
const binaryName: string = napi?.name ?? 'index'
|
|
||||||
|
|
||||||
return {
|
|
||||||
platforms,
|
|
||||||
version,
|
|
||||||
packageName,
|
|
||||||
binaryName,
|
|
||||||
packageJsonPath,
|
|
||||||
content: pkgJson,
|
|
||||||
npmClient,
|
|
||||||
tsConstEnum,
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,105 +0,0 @@
|
||||||
import { mkdirSync } from 'fs'
|
|
||||||
import { join } from 'path'
|
|
||||||
|
|
||||||
import { Command, Option } from 'clipanion'
|
|
||||||
import * as chalk from 'colorette'
|
|
||||||
|
|
||||||
import { getNapiConfig } from './consts'
|
|
||||||
import { debugFactory } from './debug'
|
|
||||||
import { PlatformDetail } from './parse-triple'
|
|
||||||
import { writeFileAsync, pick } from './utils'
|
|
||||||
|
|
||||||
const debug = debugFactory('create-npm-dir')
|
|
||||||
|
|
||||||
export class CreateNpmDirCommand extends Command {
|
|
||||||
static usage = Command.Usage({
|
|
||||||
description: 'Create npm packages dir for platforms',
|
|
||||||
})
|
|
||||||
|
|
||||||
static paths = [['create-npm-dir']]
|
|
||||||
|
|
||||||
static create = async (
|
|
||||||
config: string,
|
|
||||||
targetDirPath: string,
|
|
||||||
cwd: string,
|
|
||||||
) => {
|
|
||||||
const pkgJsonDir = config
|
|
||||||
debug(`Read content from [${chalk.yellowBright(pkgJsonDir)}]`)
|
|
||||||
const { platforms, packageName, version, binaryName, content } =
|
|
||||||
getNapiConfig(pkgJsonDir, cwd)
|
|
||||||
|
|
||||||
for (const platformDetail of platforms) {
|
|
||||||
const targetDir = join(
|
|
||||||
targetDirPath,
|
|
||||||
'npm',
|
|
||||||
`${platformDetail.platformArchABI}`,
|
|
||||||
)
|
|
||||||
mkdirSync(targetDir, {
|
|
||||||
recursive: true,
|
|
||||||
})
|
|
||||||
const binaryFileName = `${binaryName}.${platformDetail.platformArchABI}.node`
|
|
||||||
const targetPackageJson = join(targetDir, 'package.json')
|
|
||||||
debug(`Write file [${chalk.yellowBright(targetPackageJson)}]`)
|
|
||||||
const packageJson: {
|
|
||||||
name: string
|
|
||||||
libc?: string[]
|
|
||||||
} = {
|
|
||||||
name: `${packageName}-${platformDetail.platformArchABI}`,
|
|
||||||
version,
|
|
||||||
os: [platformDetail.platform],
|
|
||||||
cpu:
|
|
||||||
platformDetail.arch !== 'universal'
|
|
||||||
? [platformDetail.arch]
|
|
||||||
: undefined,
|
|
||||||
main: binaryFileName,
|
|
||||||
files: [binaryFileName],
|
|
||||||
...pick(
|
|
||||||
content,
|
|
||||||
'description',
|
|
||||||
'keywords',
|
|
||||||
'author',
|
|
||||||
'authors',
|
|
||||||
'homepage',
|
|
||||||
'license',
|
|
||||||
'engines',
|
|
||||||
'publishConfig',
|
|
||||||
'repository',
|
|
||||||
'bugs',
|
|
||||||
),
|
|
||||||
}
|
|
||||||
// Only works with yarn 3.1+
|
|
||||||
// https://github.com/yarnpkg/berry/pull/3981
|
|
||||||
if (platformDetail.abi === 'gnu') {
|
|
||||||
packageJson.libc = ['glibc']
|
|
||||||
} else if (platformDetail.abi === 'musl') {
|
|
||||||
packageJson.libc = ['musl']
|
|
||||||
}
|
|
||||||
await writeFileAsync(
|
|
||||||
targetPackageJson,
|
|
||||||
JSON.stringify(packageJson, null, 2),
|
|
||||||
)
|
|
||||||
const targetReadme = join(targetDir, 'README.md')
|
|
||||||
debug(`Write target README.md [${chalk.yellowBright(targetReadme)}]`)
|
|
||||||
await writeFileAsync(targetReadme, readme(packageName, platformDetail))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
targetDir: string = Option.String('-t,--target')!
|
|
||||||
|
|
||||||
config = Option.String('-c,--config', 'package.json')
|
|
||||||
|
|
||||||
async execute() {
|
|
||||||
await CreateNpmDirCommand.create(
|
|
||||||
this.config,
|
|
||||||
join(process.cwd(), this.targetDir),
|
|
||||||
process.cwd(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function readme(packageName: string, platformDetail: PlatformDetail) {
|
|
||||||
return `# \`${packageName}-${platformDetail.platformArchABI}\`
|
|
||||||
|
|
||||||
This is the **${platformDetail.raw}** binary for \`${packageName}\`
|
|
||||||
`
|
|
||||||
}
|
|
|
@ -1,3 +0,0 @@
|
||||||
import debug from 'debug'
|
|
||||||
|
|
||||||
export const debugFactory = (namespace: string) => debug(`napi:${namespace}`)
|
|
79
cli/src/def/artifacts.ts
Normal file
79
cli/src/def/artifacts.ts
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
// This file is generated by codegen/index.ts
|
||||||
|
// Do not edit this file manually
|
||||||
|
import { Command, Option } from 'clipanion'
|
||||||
|
|
||||||
|
export abstract class BaseArtifactsCommand extends Command {
|
||||||
|
static paths = [['artifacts']]
|
||||||
|
|
||||||
|
static usage = Command.Usage({
|
||||||
|
description:
|
||||||
|
'Copy artifacts from Github Actions into npm packages and ready to publish',
|
||||||
|
})
|
||||||
|
|
||||||
|
cwd = Option.String('--cwd', process.cwd(), {
|
||||||
|
description:
|
||||||
|
'The working directory of where napi command will be executed in, all other paths options are relative to this path',
|
||||||
|
})
|
||||||
|
|
||||||
|
packageJsonPath = Option.String('--package-json-path', 'package.json', {
|
||||||
|
description: 'Path to `package.json`',
|
||||||
|
})
|
||||||
|
|
||||||
|
outputDir = Option.String('--output-dir,-o', './', {
|
||||||
|
description:
|
||||||
|
'Path to the folder where all built `.node` files put, same as `--output-dir` of build command',
|
||||||
|
})
|
||||||
|
|
||||||
|
npmDir = Option.String('--npm-dir', 'npm', {
|
||||||
|
description: 'Path to the folder where the npm packages put',
|
||||||
|
})
|
||||||
|
|
||||||
|
getOptions() {
|
||||||
|
return {
|
||||||
|
cwd: this.cwd,
|
||||||
|
packageJsonPath: this.packageJsonPath,
|
||||||
|
outputDir: this.outputDir,
|
||||||
|
npmDir: this.npmDir,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy artifacts from Github Actions into npm packages and ready to publish
|
||||||
|
*/
|
||||||
|
export interface ArtifactsOptions {
|
||||||
|
/**
|
||||||
|
* The working directory of where napi command will be executed in, all other paths options are relative to this path
|
||||||
|
*
|
||||||
|
* @default process.cwd()
|
||||||
|
*/
|
||||||
|
cwd?: string
|
||||||
|
/**
|
||||||
|
* Path to `package.json`
|
||||||
|
*
|
||||||
|
* @default 'package.json'
|
||||||
|
*/
|
||||||
|
packageJsonPath?: string
|
||||||
|
/**
|
||||||
|
* Path to the folder where all built `.node` files put, same as `--output-dir` of build command
|
||||||
|
*
|
||||||
|
* @default './'
|
||||||
|
*/
|
||||||
|
outputDir?: string
|
||||||
|
/**
|
||||||
|
* Path to the folder where the npm packages put
|
||||||
|
*
|
||||||
|
* @default 'npm'
|
||||||
|
*/
|
||||||
|
npmDir?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyDefaultArtifactsOptions(options: ArtifactsOptions) {
|
||||||
|
return {
|
||||||
|
cwd: process.cwd(),
|
||||||
|
packageJsonPath: 'package.json',
|
||||||
|
outputDir: './',
|
||||||
|
npmDir: 'npm',
|
||||||
|
...options,
|
||||||
|
}
|
||||||
|
}
|
242
cli/src/def/build.ts
Normal file
242
cli/src/def/build.ts
Normal file
|
@ -0,0 +1,242 @@
|
||||||
|
// This file is generated by codegen/index.ts
|
||||||
|
// Do not edit this file manually
|
||||||
|
import { Command, Option } from 'clipanion'
|
||||||
|
|
||||||
|
export abstract class BaseBuildCommand extends Command {
|
||||||
|
static paths = [['build']]
|
||||||
|
|
||||||
|
static usage = Command.Usage({
|
||||||
|
description: 'Build the napi-rs project',
|
||||||
|
})
|
||||||
|
|
||||||
|
target?: string = Option.String('--target,-t', {
|
||||||
|
description:
|
||||||
|
'Build for the target triple, bypassed to `cargo build --target`',
|
||||||
|
})
|
||||||
|
|
||||||
|
cwd?: string = Option.String('--cwd', {
|
||||||
|
description:
|
||||||
|
'The working directory of where napi command will be executed in, all other paths options are relative to this path',
|
||||||
|
})
|
||||||
|
|
||||||
|
manifestPath?: string = Option.String('--manifest-path', {
|
||||||
|
description: 'Path to `Cargo.toml`',
|
||||||
|
})
|
||||||
|
|
||||||
|
packageJsonPath?: string = Option.String('--package-json-path', {
|
||||||
|
description: 'Path to `package.json`',
|
||||||
|
})
|
||||||
|
|
||||||
|
targetDir?: string = Option.String('--target-dir', {
|
||||||
|
description:
|
||||||
|
'Directory for all crate generated artifacts, see `cargo build --target-dir`',
|
||||||
|
})
|
||||||
|
|
||||||
|
outputDir?: string = Option.String('--output-dir,-o', {
|
||||||
|
description:
|
||||||
|
'Path to where all the built files would be put. Default to the crate folder',
|
||||||
|
})
|
||||||
|
|
||||||
|
platform?: boolean = Option.Boolean('--platform', {
|
||||||
|
description:
|
||||||
|
'Add platform triple to the generated nodejs binding file, eg: `[name].linux-x64-gnu.node`',
|
||||||
|
})
|
||||||
|
|
||||||
|
jsPackageName?: string = Option.String('--js-package-name', {
|
||||||
|
description:
|
||||||
|
'Package name in generated js binding file. Only works with `--platform` flag',
|
||||||
|
})
|
||||||
|
|
||||||
|
jsBinding?: string = Option.String('--js', {
|
||||||
|
description:
|
||||||
|
'Path and filename of generated JS binding file. Only works with `--platform` flag. Relative to `--output_dir`.',
|
||||||
|
})
|
||||||
|
|
||||||
|
noJsBinding?: boolean = Option.Boolean('--no-js', {
|
||||||
|
description:
|
||||||
|
'Whether to disable the generation JS binding file. Only works with `--platform` flag.',
|
||||||
|
})
|
||||||
|
|
||||||
|
dts?: string = Option.String('--dts', {
|
||||||
|
description:
|
||||||
|
'Path and filename of generated type def file. Relative to `--output_dir`',
|
||||||
|
})
|
||||||
|
|
||||||
|
dtsHeader?: string = Option.String('--dts-header', {
|
||||||
|
description:
|
||||||
|
'Custom file header for generated type def file. Only works when `typedef` feature enabled.',
|
||||||
|
})
|
||||||
|
|
||||||
|
noDtsHeader?: boolean = Option.Boolean('--no-dts-header', {
|
||||||
|
description:
|
||||||
|
'Whether to disable the default file header for generated type def file. Only works when `typedef` feature enabled.',
|
||||||
|
})
|
||||||
|
|
||||||
|
strip?: boolean = Option.Boolean('--strip,-s', {
|
||||||
|
description: 'Whether strip the library to achieve the minimum file size',
|
||||||
|
})
|
||||||
|
|
||||||
|
release?: boolean = Option.Boolean('--release,-r', {
|
||||||
|
description: 'Build in release mode',
|
||||||
|
})
|
||||||
|
|
||||||
|
verbose?: boolean = Option.Boolean('--verbose,-v', {
|
||||||
|
description: 'Verbosely log build command trace',
|
||||||
|
})
|
||||||
|
|
||||||
|
bin?: string = Option.String('--bin', {
|
||||||
|
description: 'Build only the specified binary',
|
||||||
|
})
|
||||||
|
|
||||||
|
package?: string = Option.String('--package,-p', {
|
||||||
|
description: 'Build the specified library or the one at cwd',
|
||||||
|
})
|
||||||
|
|
||||||
|
crossCompile?: boolean = Option.Boolean('--cross-compile,-x', {
|
||||||
|
description:
|
||||||
|
'[experimental] cross-compile for the specified target with `cargo-xwin` on windows and `cargo-zigbuild` on other platform',
|
||||||
|
})
|
||||||
|
|
||||||
|
watch?: boolean = Option.Boolean('--watch,-w', {
|
||||||
|
description:
|
||||||
|
'watch the crate changes and build continiously with `cargo-watch` crates',
|
||||||
|
})
|
||||||
|
|
||||||
|
features?: string[] = Option.Array('--features,-F', {
|
||||||
|
description: 'Space-separated list of features to activate',
|
||||||
|
})
|
||||||
|
|
||||||
|
allFeatures?: boolean = Option.Boolean('--all-features', {
|
||||||
|
description: 'Activate all available features',
|
||||||
|
})
|
||||||
|
|
||||||
|
noDefaultFeatures?: boolean = Option.Boolean('--no-default-features', {
|
||||||
|
description: 'Do not activate the `default` feature',
|
||||||
|
})
|
||||||
|
|
||||||
|
getOptions() {
|
||||||
|
return {
|
||||||
|
target: this.target,
|
||||||
|
cwd: this.cwd,
|
||||||
|
manifestPath: this.manifestPath,
|
||||||
|
packageJsonPath: this.packageJsonPath,
|
||||||
|
targetDir: this.targetDir,
|
||||||
|
outputDir: this.outputDir,
|
||||||
|
platform: this.platform,
|
||||||
|
jsPackageName: this.jsPackageName,
|
||||||
|
jsBinding: this.jsBinding,
|
||||||
|
noJsBinding: this.noJsBinding,
|
||||||
|
dts: this.dts,
|
||||||
|
dtsHeader: this.dtsHeader,
|
||||||
|
noDtsHeader: this.noDtsHeader,
|
||||||
|
strip: this.strip,
|
||||||
|
release: this.release,
|
||||||
|
verbose: this.verbose,
|
||||||
|
bin: this.bin,
|
||||||
|
package: this.package,
|
||||||
|
crossCompile: this.crossCompile,
|
||||||
|
watch: this.watch,
|
||||||
|
features: this.features,
|
||||||
|
allFeatures: this.allFeatures,
|
||||||
|
noDefaultFeatures: this.noDefaultFeatures,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the napi-rs project
|
||||||
|
*/
|
||||||
|
export interface BuildOptions {
|
||||||
|
/**
|
||||||
|
* Build for the target triple, bypassed to `cargo build --target`
|
||||||
|
*/
|
||||||
|
target?: string
|
||||||
|
/**
|
||||||
|
* The working directory of where napi command will be executed in, all other paths options are relative to this path
|
||||||
|
*/
|
||||||
|
cwd?: string
|
||||||
|
/**
|
||||||
|
* Path to `Cargo.toml`
|
||||||
|
*/
|
||||||
|
manifestPath?: string
|
||||||
|
/**
|
||||||
|
* Path to `package.json`
|
||||||
|
*/
|
||||||
|
packageJsonPath?: string
|
||||||
|
/**
|
||||||
|
* Directory for all crate generated artifacts, see `cargo build --target-dir`
|
||||||
|
*/
|
||||||
|
targetDir?: string
|
||||||
|
/**
|
||||||
|
* Path to where all the built files would be put. Default to the crate folder
|
||||||
|
*/
|
||||||
|
outputDir?: string
|
||||||
|
/**
|
||||||
|
* Add platform triple to the generated nodejs binding file, eg: `[name].linux-x64-gnu.node`
|
||||||
|
*/
|
||||||
|
platform?: boolean
|
||||||
|
/**
|
||||||
|
* Package name in generated js binding file. Only works with `--platform` flag
|
||||||
|
*/
|
||||||
|
jsPackageName?: string
|
||||||
|
/**
|
||||||
|
* Path and filename of generated JS binding file. Only works with `--platform` flag. Relative to `--output_dir`.
|
||||||
|
*/
|
||||||
|
jsBinding?: string
|
||||||
|
/**
|
||||||
|
* Whether to disable the generation JS binding file. Only works with `--platform` flag.
|
||||||
|
*/
|
||||||
|
noJsBinding?: boolean
|
||||||
|
/**
|
||||||
|
* Path and filename of generated type def file. Relative to `--output_dir`
|
||||||
|
*/
|
||||||
|
dts?: string
|
||||||
|
/**
|
||||||
|
* Custom file header for generated type def file. Only works when `typedef` feature enabled.
|
||||||
|
*/
|
||||||
|
dtsHeader?: string
|
||||||
|
/**
|
||||||
|
* Whether to disable the default file header for generated type def file. Only works when `typedef` feature enabled.
|
||||||
|
*/
|
||||||
|
noDtsHeader?: boolean
|
||||||
|
/**
|
||||||
|
* Whether strip the library to achieve the minimum file size
|
||||||
|
*/
|
||||||
|
strip?: boolean
|
||||||
|
/**
|
||||||
|
* Build in release mode
|
||||||
|
*/
|
||||||
|
release?: boolean
|
||||||
|
/**
|
||||||
|
* Verbosely log build command trace
|
||||||
|
*/
|
||||||
|
verbose?: boolean
|
||||||
|
/**
|
||||||
|
* Build only the specified binary
|
||||||
|
*/
|
||||||
|
bin?: string
|
||||||
|
/**
|
||||||
|
* Build the specified library or the one at cwd
|
||||||
|
*/
|
||||||
|
package?: string
|
||||||
|
/**
|
||||||
|
* [experimental] cross-compile for the specified target with `cargo-xwin` on windows and `cargo-zigbuild` on other platform
|
||||||
|
*/
|
||||||
|
crossCompile?: boolean
|
||||||
|
/**
|
||||||
|
* watch the crate changes and build continiously with `cargo-watch` crates
|
||||||
|
*/
|
||||||
|
watch?: boolean
|
||||||
|
/**
|
||||||
|
* Space-separated list of features to activate
|
||||||
|
*/
|
||||||
|
features?: string[]
|
||||||
|
/**
|
||||||
|
* Activate all available features
|
||||||
|
*/
|
||||||
|
allFeatures?: boolean
|
||||||
|
/**
|
||||||
|
* Do not activate the `default` feature
|
||||||
|
*/
|
||||||
|
noDefaultFeatures?: boolean
|
||||||
|
}
|
79
cli/src/def/create-npm-dirs.ts
Normal file
79
cli/src/def/create-npm-dirs.ts
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
// This file is generated by codegen/index.ts
|
||||||
|
// Do not edit this file manually
|
||||||
|
import { Command, Option } from 'clipanion'
|
||||||
|
|
||||||
|
export abstract class BaseCreateNpmDirsCommand extends Command {
|
||||||
|
static paths = [['create-npm-dirs']]
|
||||||
|
|
||||||
|
static usage = Command.Usage({
|
||||||
|
description: 'Create npm package dirs for different platforms',
|
||||||
|
})
|
||||||
|
|
||||||
|
cwd = Option.String('--cwd', process.cwd(), {
|
||||||
|
description:
|
||||||
|
'The working directory of where napi command will be executed in, all other paths options are relative to this path',
|
||||||
|
})
|
||||||
|
|
||||||
|
packageJsonPath = Option.String('--package-json-path', 'package.json', {
|
||||||
|
description: 'Path to `package.json`',
|
||||||
|
})
|
||||||
|
|
||||||
|
npmDir = Option.String('--npm-dir', 'npm', {
|
||||||
|
description: 'Path to the folder where the npm packages put',
|
||||||
|
})
|
||||||
|
|
||||||
|
dryRun = Option.Boolean('--dry-run', false, {
|
||||||
|
description: 'Dry run without touching file system',
|
||||||
|
})
|
||||||
|
|
||||||
|
getOptions() {
|
||||||
|
return {
|
||||||
|
cwd: this.cwd,
|
||||||
|
packageJsonPath: this.packageJsonPath,
|
||||||
|
npmDir: this.npmDir,
|
||||||
|
dryRun: this.dryRun,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create npm package dirs for different platforms
|
||||||
|
*/
|
||||||
|
export interface CreateNpmDirsOptions {
|
||||||
|
/**
|
||||||
|
* The working directory of where napi command will be executed in, all other paths options are relative to this path
|
||||||
|
*
|
||||||
|
* @default process.cwd()
|
||||||
|
*/
|
||||||
|
cwd?: string
|
||||||
|
/**
|
||||||
|
* Path to `package.json`
|
||||||
|
*
|
||||||
|
* @default 'package.json'
|
||||||
|
*/
|
||||||
|
packageJsonPath?: string
|
||||||
|
/**
|
||||||
|
* Path to the folder where the npm packages put
|
||||||
|
*
|
||||||
|
* @default 'npm'
|
||||||
|
*/
|
||||||
|
npmDir?: string
|
||||||
|
/**
|
||||||
|
* Dry run without touching file system
|
||||||
|
*
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
dryRun?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyDefaultCreateNpmDirsOptions(
|
||||||
|
options: CreateNpmDirsOptions,
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
cwd: process.cwd(),
|
||||||
|
packageJsonPath: 'package.json',
|
||||||
|
npmDir: 'npm',
|
||||||
|
dryRun: false,
|
||||||
|
...options,
|
||||||
|
}
|
||||||
|
}
|
144
cli/src/def/new.ts
Normal file
144
cli/src/def/new.ts
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
// This file is generated by codegen/index.ts
|
||||||
|
// Do not edit this file manually
|
||||||
|
import { Command, Option } from 'clipanion'
|
||||||
|
import * as typanion from 'typanion'
|
||||||
|
|
||||||
|
export abstract class BaseNewCommand extends Command {
|
||||||
|
static paths = [['new']]
|
||||||
|
|
||||||
|
static usage = Command.Usage({
|
||||||
|
description: 'Create a new project with pre-configured boilerplate',
|
||||||
|
})
|
||||||
|
|
||||||
|
$$path = Option.String({ required: true })
|
||||||
|
|
||||||
|
$$name?: string = Option.String('--name,-n', {
|
||||||
|
description:
|
||||||
|
'The name of the project, default to the name of the directory if not provided',
|
||||||
|
})
|
||||||
|
|
||||||
|
minNodeApiVersion = Option.String('--min-node-api,-v', '4', {
|
||||||
|
validator: typanion.isNumber(),
|
||||||
|
description: 'The minimum Node-API version to support',
|
||||||
|
})
|
||||||
|
|
||||||
|
license = Option.String('--license,-l', 'MIT', {
|
||||||
|
description: 'License for open-sourced project',
|
||||||
|
})
|
||||||
|
|
||||||
|
targets = Option.Array('--targets,-t', [], {
|
||||||
|
description: 'All targets the crate will be compiled for.',
|
||||||
|
})
|
||||||
|
|
||||||
|
enableDefaultTargets = Option.Boolean('--enable-default-targets', true, {
|
||||||
|
description: 'Whether enable default targets',
|
||||||
|
})
|
||||||
|
|
||||||
|
enableAllTargets = Option.Boolean('--enable-all-targets', false, {
|
||||||
|
description: 'Whether enable all targets',
|
||||||
|
})
|
||||||
|
|
||||||
|
enableTypeDef = Option.Boolean('--enable-type-def', true, {
|
||||||
|
description:
|
||||||
|
'Whether enable the `type-def` feature for typescript definitions auto-generation',
|
||||||
|
})
|
||||||
|
|
||||||
|
enableGithubActions = Option.Boolean('--enable-github-actions', true, {
|
||||||
|
description: 'Whether generate preconfigured GitHub Actions workflow',
|
||||||
|
})
|
||||||
|
|
||||||
|
dryRun = Option.Boolean('--dry-run', false, {
|
||||||
|
description: 'Whether to run the command in dry-run mode',
|
||||||
|
})
|
||||||
|
|
||||||
|
getOptions() {
|
||||||
|
return {
|
||||||
|
path: this.$$path,
|
||||||
|
name: this.$$name,
|
||||||
|
minNodeApiVersion: this.minNodeApiVersion,
|
||||||
|
license: this.license,
|
||||||
|
targets: this.targets,
|
||||||
|
enableDefaultTargets: this.enableDefaultTargets,
|
||||||
|
enableAllTargets: this.enableAllTargets,
|
||||||
|
enableTypeDef: this.enableTypeDef,
|
||||||
|
enableGithubActions: this.enableGithubActions,
|
||||||
|
dryRun: this.dryRun,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new project with pre-configured boilerplate
|
||||||
|
*/
|
||||||
|
export interface NewOptions {
|
||||||
|
/**
|
||||||
|
* The path where the napi-rs project will be created.
|
||||||
|
*/
|
||||||
|
path: string
|
||||||
|
/**
|
||||||
|
* The name of the project, default to the name of the directory if not provided
|
||||||
|
*/
|
||||||
|
name?: string
|
||||||
|
/**
|
||||||
|
* The minimum Node-API version to support
|
||||||
|
*
|
||||||
|
* @default 4
|
||||||
|
*/
|
||||||
|
minNodeApiVersion?: number
|
||||||
|
/**
|
||||||
|
* License for open-sourced project
|
||||||
|
*
|
||||||
|
* @default 'MIT'
|
||||||
|
*/
|
||||||
|
license?: string
|
||||||
|
/**
|
||||||
|
* All targets the crate will be compiled for.
|
||||||
|
*
|
||||||
|
* @default []
|
||||||
|
*/
|
||||||
|
targets?: string[]
|
||||||
|
/**
|
||||||
|
* Whether enable default targets
|
||||||
|
*
|
||||||
|
* @default true
|
||||||
|
*/
|
||||||
|
enableDefaultTargets?: boolean
|
||||||
|
/**
|
||||||
|
* Whether enable all targets
|
||||||
|
*
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
enableAllTargets?: boolean
|
||||||
|
/**
|
||||||
|
* Whether enable the `type-def` feature for typescript definitions auto-generation
|
||||||
|
*
|
||||||
|
* @default true
|
||||||
|
*/
|
||||||
|
enableTypeDef?: boolean
|
||||||
|
/**
|
||||||
|
* Whether generate preconfigured GitHub Actions workflow
|
||||||
|
*
|
||||||
|
* @default true
|
||||||
|
*/
|
||||||
|
enableGithubActions?: boolean
|
||||||
|
/**
|
||||||
|
* Whether to run the command in dry-run mode
|
||||||
|
*
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
dryRun?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyDefaultNewOptions(options: NewOptions) {
|
||||||
|
return {
|
||||||
|
minNodeApiVersion: 4,
|
||||||
|
license: 'MIT',
|
||||||
|
targets: [],
|
||||||
|
enableDefaultTargets: true,
|
||||||
|
enableAllTargets: false,
|
||||||
|
enableTypeDef: true,
|
||||||
|
enableGithubActions: true,
|
||||||
|
dryRun: false,
|
||||||
|
...options,
|
||||||
|
}
|
||||||
|
}
|
120
cli/src/def/pre-publish.ts
Normal file
120
cli/src/def/pre-publish.ts
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
// This file is generated by codegen/index.ts
|
||||||
|
// Do not edit this file manually
|
||||||
|
import { Command, Option } from 'clipanion'
|
||||||
|
|
||||||
|
export abstract class BasePrePublishCommand extends Command {
|
||||||
|
static paths = [['pre-publish']]
|
||||||
|
|
||||||
|
static usage = Command.Usage({
|
||||||
|
description:
|
||||||
|
'Update package.json and copy addons into per platform packages',
|
||||||
|
})
|
||||||
|
|
||||||
|
cwd = Option.String('--cwd', process.cwd(), {
|
||||||
|
description:
|
||||||
|
'The working directory of where napi command will be executed in, all other paths options are relative to this path',
|
||||||
|
})
|
||||||
|
|
||||||
|
packageJsonPath = Option.String('--package-json-path', 'package.json', {
|
||||||
|
description: 'Path to `package.json`',
|
||||||
|
})
|
||||||
|
|
||||||
|
npmDir = Option.String('--npm-dir', 'npm', {
|
||||||
|
description: 'Path to the folder where the npm packages put',
|
||||||
|
})
|
||||||
|
|
||||||
|
tagStyle = Option.String('--tag-style', 'lerna', {
|
||||||
|
description: 'git tag style, `npm` or `lerna`',
|
||||||
|
})
|
||||||
|
|
||||||
|
ghRelease = Option.Boolean('--gh-release', true, {
|
||||||
|
description: 'Whether create GitHub release',
|
||||||
|
})
|
||||||
|
|
||||||
|
ghReleaseName?: string = Option.String('--gh-release-name', {
|
||||||
|
description: 'GitHub release name',
|
||||||
|
})
|
||||||
|
|
||||||
|
ghReleaseId?: string = Option.String('--gh-release-id', {
|
||||||
|
description: 'Existing GitHub release id',
|
||||||
|
})
|
||||||
|
|
||||||
|
dryRun = Option.Boolean('--dry-run', false, {
|
||||||
|
description: 'Dry run without touching file system',
|
||||||
|
})
|
||||||
|
|
||||||
|
getOptions() {
|
||||||
|
return {
|
||||||
|
cwd: this.cwd,
|
||||||
|
packageJsonPath: this.packageJsonPath,
|
||||||
|
npmDir: this.npmDir,
|
||||||
|
tagStyle: this.tagStyle,
|
||||||
|
ghRelease: this.ghRelease,
|
||||||
|
ghReleaseName: this.ghReleaseName,
|
||||||
|
ghReleaseId: this.ghReleaseId,
|
||||||
|
dryRun: this.dryRun,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update package.json and copy addons into per platform packages
|
||||||
|
*/
|
||||||
|
export interface PrePublishOptions {
|
||||||
|
/**
|
||||||
|
* The working directory of where napi command will be executed in, all other paths options are relative to this path
|
||||||
|
*
|
||||||
|
* @default process.cwd()
|
||||||
|
*/
|
||||||
|
cwd?: string
|
||||||
|
/**
|
||||||
|
* Path to `package.json`
|
||||||
|
*
|
||||||
|
* @default 'package.json'
|
||||||
|
*/
|
||||||
|
packageJsonPath?: string
|
||||||
|
/**
|
||||||
|
* Path to the folder where the npm packages put
|
||||||
|
*
|
||||||
|
* @default 'npm'
|
||||||
|
*/
|
||||||
|
npmDir?: string
|
||||||
|
/**
|
||||||
|
* git tag style, `npm` or `lerna`
|
||||||
|
*
|
||||||
|
* @default 'lerna'
|
||||||
|
*/
|
||||||
|
tagStyle?: 'npm' | 'lerna'
|
||||||
|
/**
|
||||||
|
* Whether create GitHub release
|
||||||
|
*
|
||||||
|
* @default true
|
||||||
|
*/
|
||||||
|
ghRelease?: boolean
|
||||||
|
/**
|
||||||
|
* GitHub release name
|
||||||
|
*/
|
||||||
|
ghReleaseName?: string
|
||||||
|
/**
|
||||||
|
* Existing GitHub release id
|
||||||
|
*/
|
||||||
|
ghReleaseId?: string
|
||||||
|
/**
|
||||||
|
* Dry run without touching file system
|
||||||
|
*
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
dryRun?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyDefaultPrePublishOptions(options: PrePublishOptions) {
|
||||||
|
return {
|
||||||
|
cwd: process.cwd(),
|
||||||
|
packageJsonPath: 'package.json',
|
||||||
|
npmDir: 'npm',
|
||||||
|
tagStyle: 'lerna',
|
||||||
|
ghRelease: true,
|
||||||
|
dryRun: false,
|
||||||
|
...options,
|
||||||
|
}
|
||||||
|
}
|
122
cli/src/def/rename.ts
Normal file
122
cli/src/def/rename.ts
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
// This file is generated by codegen/index.ts
|
||||||
|
// Do not edit this file manually
|
||||||
|
import { Command, Option } from 'clipanion'
|
||||||
|
|
||||||
|
export abstract class BaseRenameCommand extends Command {
|
||||||
|
static paths = [['rename']]
|
||||||
|
|
||||||
|
static usage = Command.Usage({
|
||||||
|
description: 'Rename the napi-rs project',
|
||||||
|
})
|
||||||
|
|
||||||
|
cwd = Option.String('--cwd', process.cwd(), {
|
||||||
|
description:
|
||||||
|
'The working directory of where napi command will be executed in, all other paths options are relative to this path',
|
||||||
|
})
|
||||||
|
|
||||||
|
packageJsonPath = Option.String('--package-json-path', 'package.json', {
|
||||||
|
description: 'Path to `package.json`',
|
||||||
|
})
|
||||||
|
|
||||||
|
npmDir = Option.String('--npm-dir', 'npm', {
|
||||||
|
description: 'Path to the folder where the npm packages put',
|
||||||
|
})
|
||||||
|
|
||||||
|
$$name?: string = Option.String('--name,-n', {
|
||||||
|
description: 'The new name of the project',
|
||||||
|
})
|
||||||
|
|
||||||
|
binaryName?: string = Option.String('--binary-name,-b', {
|
||||||
|
description: 'The new binary name *.node files',
|
||||||
|
})
|
||||||
|
|
||||||
|
packageName?: string = Option.String('--package-name', {
|
||||||
|
description: 'The new package name of the project',
|
||||||
|
})
|
||||||
|
|
||||||
|
manifestPath = Option.String('--manifest-path', 'Cargo.toml', {
|
||||||
|
description: 'Path to `Cargo.toml`',
|
||||||
|
})
|
||||||
|
|
||||||
|
repository?: string = Option.String('--repository', {
|
||||||
|
description: 'The new repository of the project',
|
||||||
|
})
|
||||||
|
|
||||||
|
description?: string = Option.String('--description', {
|
||||||
|
description: 'The new description of the project',
|
||||||
|
})
|
||||||
|
|
||||||
|
getOptions() {
|
||||||
|
return {
|
||||||
|
cwd: this.cwd,
|
||||||
|
packageJsonPath: this.packageJsonPath,
|
||||||
|
npmDir: this.npmDir,
|
||||||
|
name: this.$$name,
|
||||||
|
binaryName: this.binaryName,
|
||||||
|
packageName: this.packageName,
|
||||||
|
manifestPath: this.manifestPath,
|
||||||
|
repository: this.repository,
|
||||||
|
description: this.description,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rename the napi-rs project
|
||||||
|
*/
|
||||||
|
export interface RenameOptions {
|
||||||
|
/**
|
||||||
|
* The working directory of where napi command will be executed in, all other paths options are relative to this path
|
||||||
|
*
|
||||||
|
* @default process.cwd()
|
||||||
|
*/
|
||||||
|
cwd?: string
|
||||||
|
/**
|
||||||
|
* Path to `package.json`
|
||||||
|
*
|
||||||
|
* @default 'package.json'
|
||||||
|
*/
|
||||||
|
packageJsonPath?: string
|
||||||
|
/**
|
||||||
|
* Path to the folder where the npm packages put
|
||||||
|
*
|
||||||
|
* @default 'npm'
|
||||||
|
*/
|
||||||
|
npmDir?: string
|
||||||
|
/**
|
||||||
|
* The new name of the project
|
||||||
|
*/
|
||||||
|
name?: string
|
||||||
|
/**
|
||||||
|
* The new binary name *.node files
|
||||||
|
*/
|
||||||
|
binaryName?: string
|
||||||
|
/**
|
||||||
|
* The new package name of the project
|
||||||
|
*/
|
||||||
|
packageName?: string
|
||||||
|
/**
|
||||||
|
* Path to `Cargo.toml`
|
||||||
|
*
|
||||||
|
* @default 'Cargo.toml'
|
||||||
|
*/
|
||||||
|
manifestPath?: string
|
||||||
|
/**
|
||||||
|
* The new repository of the project
|
||||||
|
*/
|
||||||
|
repository?: string
|
||||||
|
/**
|
||||||
|
* The new description of the project
|
||||||
|
*/
|
||||||
|
description?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyDefaultRenameOptions(options: RenameOptions) {
|
||||||
|
return {
|
||||||
|
cwd: process.cwd(),
|
||||||
|
packageJsonPath: 'package.json',
|
||||||
|
npmDir: 'npm',
|
||||||
|
manifestPath: 'Cargo.toml',
|
||||||
|
...options,
|
||||||
|
}
|
||||||
|
}
|
66
cli/src/def/universalize.ts
Normal file
66
cli/src/def/universalize.ts
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
// This file is generated by codegen/index.ts
|
||||||
|
// Do not edit this file manually
|
||||||
|
import { Command, Option } from 'clipanion'
|
||||||
|
|
||||||
|
export abstract class BaseUniversalizeCommand extends Command {
|
||||||
|
static paths = [['universalize']]
|
||||||
|
|
||||||
|
static usage = Command.Usage({
|
||||||
|
description: 'Combile built binaries into one universal binary',
|
||||||
|
})
|
||||||
|
|
||||||
|
cwd = Option.String('--cwd', process.cwd(), {
|
||||||
|
description:
|
||||||
|
'The working directory of where napi command will be executed in, all other paths options are relative to this path',
|
||||||
|
})
|
||||||
|
|
||||||
|
packageJsonPath = Option.String('--package-json-path', 'package.json', {
|
||||||
|
description: 'Path to `package.json`',
|
||||||
|
})
|
||||||
|
|
||||||
|
outputDir = Option.String('--output-dir,-o', './', {
|
||||||
|
description:
|
||||||
|
'Path to the folder where all built `.node` files put, same as `--output-dir` of build command',
|
||||||
|
})
|
||||||
|
|
||||||
|
getOptions() {
|
||||||
|
return {
|
||||||
|
cwd: this.cwd,
|
||||||
|
packageJsonPath: this.packageJsonPath,
|
||||||
|
outputDir: this.outputDir,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Combile built binaries into one universal binary
|
||||||
|
*/
|
||||||
|
export interface UniversalizeOptions {
|
||||||
|
/**
|
||||||
|
* The working directory of where napi command will be executed in, all other paths options are relative to this path
|
||||||
|
*
|
||||||
|
* @default process.cwd()
|
||||||
|
*/
|
||||||
|
cwd?: string
|
||||||
|
/**
|
||||||
|
* Path to `package.json`
|
||||||
|
*
|
||||||
|
* @default 'package.json'
|
||||||
|
*/
|
||||||
|
packageJsonPath?: string
|
||||||
|
/**
|
||||||
|
* Path to the folder where all built `.node` files put, same as `--output-dir` of build command
|
||||||
|
*
|
||||||
|
* @default './'
|
||||||
|
*/
|
||||||
|
outputDir?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyDefaultUniversalizeOptions(options: UniversalizeOptions) {
|
||||||
|
return {
|
||||||
|
cwd: process.cwd(),
|
||||||
|
packageJsonPath: 'package.json',
|
||||||
|
outputDir: './',
|
||||||
|
...options,
|
||||||
|
}
|
||||||
|
}
|
65
cli/src/def/version.ts
Normal file
65
cli/src/def/version.ts
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
// This file is generated by codegen/index.ts
|
||||||
|
// Do not edit this file manually
|
||||||
|
import { Command, Option } from 'clipanion'
|
||||||
|
|
||||||
|
export abstract class BaseVersionCommand extends Command {
|
||||||
|
static paths = [['version']]
|
||||||
|
|
||||||
|
static usage = Command.Usage({
|
||||||
|
description: 'Update version in created npm packages',
|
||||||
|
})
|
||||||
|
|
||||||
|
cwd = Option.String('--cwd', process.cwd(), {
|
||||||
|
description:
|
||||||
|
'The working directory of where napi command will be executed in, all other paths options are relative to this path',
|
||||||
|
})
|
||||||
|
|
||||||
|
packageJsonPath = Option.String('--package-json-path', 'package.json', {
|
||||||
|
description: 'Path to `package.json`',
|
||||||
|
})
|
||||||
|
|
||||||
|
npmDir = Option.String('--npm-dir', 'npm', {
|
||||||
|
description: 'Path to the folder where the npm packages put',
|
||||||
|
})
|
||||||
|
|
||||||
|
getOptions() {
|
||||||
|
return {
|
||||||
|
cwd: this.cwd,
|
||||||
|
packageJsonPath: this.packageJsonPath,
|
||||||
|
npmDir: this.npmDir,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update version in created npm packages
|
||||||
|
*/
|
||||||
|
export interface VersionOptions {
|
||||||
|
/**
|
||||||
|
* The working directory of where napi command will be executed in, all other paths options are relative to this path
|
||||||
|
*
|
||||||
|
* @default process.cwd()
|
||||||
|
*/
|
||||||
|
cwd?: string
|
||||||
|
/**
|
||||||
|
* Path to `package.json`
|
||||||
|
*
|
||||||
|
* @default 'package.json'
|
||||||
|
*/
|
||||||
|
packageJsonPath?: string
|
||||||
|
/**
|
||||||
|
* Path to the folder where the npm packages put
|
||||||
|
*
|
||||||
|
* @default 'npm'
|
||||||
|
*/
|
||||||
|
npmDir?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyDefaultVersionOptions(options: VersionOptions) {
|
||||||
|
return {
|
||||||
|
cwd: process.cwd(),
|
||||||
|
packageJsonPath: 'package.json',
|
||||||
|
npmDir: 'npm',
|
||||||
|
...options,
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,42 +1,31 @@
|
||||||
import 'core-js/es/string/replace-all'
|
import { collectArtifacts } from './api/artifacts.js'
|
||||||
|
import { buildProject } from './api/build.js'
|
||||||
|
import { createNpmDirs } from './api/create-npm-dirs.js'
|
||||||
|
import { newProject } from './api/new.js'
|
||||||
|
import { prePublish } from './api/pre-publish.js'
|
||||||
|
import { renameProject } from './api/rename.js'
|
||||||
|
import { universalizeBinaries } from './api/universalize.js'
|
||||||
|
import { version } from './api/version.js'
|
||||||
|
|
||||||
import { Cli } from 'clipanion'
|
/**
|
||||||
|
*
|
||||||
import { version } from '../package.json'
|
* @usage
|
||||||
|
*
|
||||||
import { ArtifactsCommand } from './artifacts'
|
* ```ts
|
||||||
import { BuildCommand } from './build'
|
* const cli = new NapiCli()
|
||||||
import { CreateNpmDirCommand } from './create-npm-dir'
|
*
|
||||||
import { HelpCommand } from './help'
|
* cli.build({
|
||||||
import { NewProjectCommand } from './new'
|
* cwd: '/path/to/your/project',
|
||||||
import { PrePublishCommand } from './pre-publish'
|
* })
|
||||||
import { RenameCommand } from './rename'
|
* ```
|
||||||
import { UniversalCommand } from './universal'
|
*/
|
||||||
import { VersionCommand } from './version'
|
export class NapiCli {
|
||||||
|
artifacts = collectArtifacts
|
||||||
const cli = new Cli({
|
new = newProject
|
||||||
binaryName: 'napi',
|
build = buildProject
|
||||||
binaryVersion: version,
|
createNpmDirs = createNpmDirs
|
||||||
})
|
prePublish = prePublish
|
||||||
|
rename = renameProject
|
||||||
cli.register(ArtifactsCommand)
|
universalize = universalizeBinaries
|
||||||
cli.register(BuildCommand)
|
version = version
|
||||||
cli.register(CreateNpmDirCommand)
|
}
|
||||||
cli.register(PrePublishCommand)
|
|
||||||
cli.register(VersionCommand)
|
|
||||||
cli.register(UniversalCommand)
|
|
||||||
cli.register(NewProjectCommand)
|
|
||||||
cli.register(RenameCommand)
|
|
||||||
cli.register(HelpCommand)
|
|
||||||
|
|
||||||
cli
|
|
||||||
.run(process.argv.slice(2), {
|
|
||||||
...Cli.defaultContext,
|
|
||||||
})
|
|
||||||
.then((status) => {
|
|
||||||
process.exit(status)
|
|
||||||
})
|
|
||||||
.catch((e) => {
|
|
||||||
console.error(e)
|
|
||||||
process.exit(1)
|
|
||||||
})
|
|
||||||
|
|
|
@ -1,258 +0,0 @@
|
||||||
export const createJsBinding = (
|
|
||||||
localName: string,
|
|
||||||
pkgName: string,
|
|
||||||
) => `/* tslint:disable */
|
|
||||||
/* eslint-disable */
|
|
||||||
/* prettier-ignore */
|
|
||||||
|
|
||||||
/* auto-generated by NAPI-RS */
|
|
||||||
|
|
||||||
const { existsSync, readFileSync } = require('fs')
|
|
||||||
const { join } = require('path')
|
|
||||||
|
|
||||||
const { platform, arch } = process
|
|
||||||
|
|
||||||
let nativeBinding = null
|
|
||||||
let localFileExisted = false
|
|
||||||
let loadError = null
|
|
||||||
|
|
||||||
function isMusl() {
|
|
||||||
// For Node 10
|
|
||||||
if (!process.report || typeof process.report.getReport !== 'function') {
|
|
||||||
try {
|
|
||||||
const lddPath = require('child_process').execSync('which ldd').toString().trim()
|
|
||||||
return readFileSync(lddPath, 'utf8').includes('musl')
|
|
||||||
} catch (e) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const { glibcVersionRuntime } = process.report.getReport().header
|
|
||||||
return !glibcVersionRuntime
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (platform) {
|
|
||||||
case 'android':
|
|
||||||
switch (arch) {
|
|
||||||
case 'arm64':
|
|
||||||
localFileExisted = existsSync(join(__dirname, '${localName}.android-arm64.node'))
|
|
||||||
try {
|
|
||||||
if (localFileExisted) {
|
|
||||||
nativeBinding = require('./${localName}.android-arm64.node')
|
|
||||||
} else {
|
|
||||||
nativeBinding = require('${pkgName}-android-arm64')
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
loadError = e
|
|
||||||
}
|
|
||||||
break
|
|
||||||
case 'arm':
|
|
||||||
localFileExisted = existsSync(join(__dirname, '${localName}.android-arm-eabi.node'))
|
|
||||||
try {
|
|
||||||
if (localFileExisted) {
|
|
||||||
nativeBinding = require('./${localName}.android-arm-eabi.node')
|
|
||||||
} else {
|
|
||||||
nativeBinding = require('${pkgName}-android-arm-eabi')
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
loadError = e
|
|
||||||
}
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
throw new Error(\`Unsupported architecture on Android \${arch}\`)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
case 'win32':
|
|
||||||
switch (arch) {
|
|
||||||
case 'x64':
|
|
||||||
localFileExisted = existsSync(
|
|
||||||
join(__dirname, '${localName}.win32-x64-msvc.node')
|
|
||||||
)
|
|
||||||
try {
|
|
||||||
if (localFileExisted) {
|
|
||||||
nativeBinding = require('./${localName}.win32-x64-msvc.node')
|
|
||||||
} else {
|
|
||||||
nativeBinding = require('${pkgName}-win32-x64-msvc')
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
loadError = e
|
|
||||||
}
|
|
||||||
break
|
|
||||||
case 'ia32':
|
|
||||||
localFileExisted = existsSync(
|
|
||||||
join(__dirname, '${localName}.win32-ia32-msvc.node')
|
|
||||||
)
|
|
||||||
try {
|
|
||||||
if (localFileExisted) {
|
|
||||||
nativeBinding = require('./${localName}.win32-ia32-msvc.node')
|
|
||||||
} else {
|
|
||||||
nativeBinding = require('${pkgName}-win32-ia32-msvc')
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
loadError = e
|
|
||||||
}
|
|
||||||
break
|
|
||||||
case 'arm64':
|
|
||||||
localFileExisted = existsSync(
|
|
||||||
join(__dirname, '${localName}.win32-arm64-msvc.node')
|
|
||||||
)
|
|
||||||
try {
|
|
||||||
if (localFileExisted) {
|
|
||||||
nativeBinding = require('./${localName}.win32-arm64-msvc.node')
|
|
||||||
} else {
|
|
||||||
nativeBinding = require('${pkgName}-win32-arm64-msvc')
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
loadError = e
|
|
||||||
}
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
throw new Error(\`Unsupported architecture on Windows: \${arch}\`)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
case 'darwin':
|
|
||||||
localFileExisted = existsSync(join(__dirname, '${localName}.darwin-universal.node'))
|
|
||||||
try {
|
|
||||||
if (localFileExisted) {
|
|
||||||
nativeBinding = require('./${localName}.darwin-universal.node')
|
|
||||||
} else {
|
|
||||||
nativeBinding = require('${pkgName}-darwin-universal')
|
|
||||||
}
|
|
||||||
break
|
|
||||||
} catch {}
|
|
||||||
switch (arch) {
|
|
||||||
case 'x64':
|
|
||||||
localFileExisted = existsSync(join(__dirname, '${localName}.darwin-x64.node'))
|
|
||||||
try {
|
|
||||||
if (localFileExisted) {
|
|
||||||
nativeBinding = require('./${localName}.darwin-x64.node')
|
|
||||||
} else {
|
|
||||||
nativeBinding = require('${pkgName}-darwin-x64')
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
loadError = e
|
|
||||||
}
|
|
||||||
break
|
|
||||||
case 'arm64':
|
|
||||||
localFileExisted = existsSync(
|
|
||||||
join(__dirname, '${localName}.darwin-arm64.node')
|
|
||||||
)
|
|
||||||
try {
|
|
||||||
if (localFileExisted) {
|
|
||||||
nativeBinding = require('./${localName}.darwin-arm64.node')
|
|
||||||
} else {
|
|
||||||
nativeBinding = require('${pkgName}-darwin-arm64')
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
loadError = e
|
|
||||||
}
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
throw new Error(\`Unsupported architecture on macOS: \${arch}\`)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
case 'freebsd':
|
|
||||||
if (arch !== 'x64') {
|
|
||||||
throw new Error(\`Unsupported architecture on FreeBSD: \${arch}\`)
|
|
||||||
}
|
|
||||||
localFileExisted = existsSync(join(__dirname, '${localName}.freebsd-x64.node'))
|
|
||||||
try {
|
|
||||||
if (localFileExisted) {
|
|
||||||
nativeBinding = require('./${localName}.freebsd-x64.node')
|
|
||||||
} else {
|
|
||||||
nativeBinding = require('${pkgName}-freebsd-x64')
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
loadError = e
|
|
||||||
}
|
|
||||||
break
|
|
||||||
case 'linux':
|
|
||||||
switch (arch) {
|
|
||||||
case 'x64':
|
|
||||||
if (isMusl()) {
|
|
||||||
localFileExisted = existsSync(
|
|
||||||
join(__dirname, '${localName}.linux-x64-musl.node')
|
|
||||||
)
|
|
||||||
try {
|
|
||||||
if (localFileExisted) {
|
|
||||||
nativeBinding = require('./${localName}.linux-x64-musl.node')
|
|
||||||
} else {
|
|
||||||
nativeBinding = require('${pkgName}-linux-x64-musl')
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
loadError = e
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
localFileExisted = existsSync(
|
|
||||||
join(__dirname, '${localName}.linux-x64-gnu.node')
|
|
||||||
)
|
|
||||||
try {
|
|
||||||
if (localFileExisted) {
|
|
||||||
nativeBinding = require('./${localName}.linux-x64-gnu.node')
|
|
||||||
} else {
|
|
||||||
nativeBinding = require('${pkgName}-linux-x64-gnu')
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
loadError = e
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break
|
|
||||||
case 'arm64':
|
|
||||||
if (isMusl()) {
|
|
||||||
localFileExisted = existsSync(
|
|
||||||
join(__dirname, '${localName}.linux-arm64-musl.node')
|
|
||||||
)
|
|
||||||
try {
|
|
||||||
if (localFileExisted) {
|
|
||||||
nativeBinding = require('./${localName}.linux-arm64-musl.node')
|
|
||||||
} else {
|
|
||||||
nativeBinding = require('${pkgName}-linux-arm64-musl')
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
loadError = e
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
localFileExisted = existsSync(
|
|
||||||
join(__dirname, '${localName}.linux-arm64-gnu.node')
|
|
||||||
)
|
|
||||||
try {
|
|
||||||
if (localFileExisted) {
|
|
||||||
nativeBinding = require('./${localName}.linux-arm64-gnu.node')
|
|
||||||
} else {
|
|
||||||
nativeBinding = require('${pkgName}-linux-arm64-gnu')
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
loadError = e
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break
|
|
||||||
case 'arm':
|
|
||||||
localFileExisted = existsSync(
|
|
||||||
join(__dirname, '${localName}.linux-arm-gnueabihf.node')
|
|
||||||
)
|
|
||||||
try {
|
|
||||||
if (localFileExisted) {
|
|
||||||
nativeBinding = require('./${localName}.linux-arm-gnueabihf.node')
|
|
||||||
} else {
|
|
||||||
nativeBinding = require('${pkgName}-linux-arm-gnueabihf')
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
loadError = e
|
|
||||||
}
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
throw new Error(\`Unsupported architecture on Linux: \${arch}\`)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
throw new Error(\`Unsupported OS: \${platform}, architecture: \${arch}\`)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!nativeBinding) {
|
|
||||||
if (loadError) {
|
|
||||||
throw loadError
|
|
||||||
}
|
|
||||||
throw new Error(\`Failed to load native binding\`)
|
|
||||||
}
|
|
||||||
|
|
||||||
`
|
|
|
@ -1,9 +0,0 @@
|
||||||
export const createCargoConfig = (enableLinuxArm8Musl: boolean) => {
|
|
||||||
const result: string[] = []
|
|
||||||
if (enableLinuxArm8Musl) {
|
|
||||||
result.push(`[target.aarch64-unknown-linux-musl]
|
|
||||||
linker = "aarch64-linux-musl-gcc"
|
|
||||||
rustflags = ["-C", "target-feature=-crt-static"]`)
|
|
||||||
}
|
|
||||||
return result.join('\n')
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
export const createCargoContent = (name: string) => `[package]
|
|
||||||
edition = "2021"
|
|
||||||
name = "${name.replace('@', '').replace('/', '_').toLowerCase()}"
|
|
||||||
version = "0.0.0"
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
crate-type = ["cdylib"]
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
# Default enable napi4 feature, see https://nodejs.org/api/n-api.html#node-api-version-matrix
|
|
||||||
napi = { version = "NAPI_VERSION", default-features = false, features = ["napi4"] }
|
|
||||||
napi-derive = "NAPI_DERIVE_VERSION"
|
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
napi-build = "NAPI_BUILD_VERSION"
|
|
||||||
|
|
||||||
[profile.release]
|
|
||||||
lto = true
|
|
||||||
`
|
|
|
@ -1,230 +0,0 @@
|
||||||
import { writeFileSync, mkdirSync } from 'fs'
|
|
||||||
import { join } from 'path'
|
|
||||||
|
|
||||||
import { Command, Option } from 'clipanion'
|
|
||||||
import * as chalk from 'colorette'
|
|
||||||
import inquirer from 'inquirer'
|
|
||||||
|
|
||||||
import { CreateNpmDirCommand } from '../create-npm-dir'
|
|
||||||
import { debugFactory } from '../debug'
|
|
||||||
import { DefaultPlatforms } from '../parse-triple'
|
|
||||||
import { spawn } from '../spawn'
|
|
||||||
|
|
||||||
import { createCargoContent } from './cargo'
|
|
||||||
import { createCargoConfig } from './cargo-config'
|
|
||||||
import { createGithubActionsCIYml } from './ci-yml'
|
|
||||||
import { GitIgnore } from './gitignore-template'
|
|
||||||
import { LibRs } from './lib-rs'
|
|
||||||
import { NPMIgnoreFiles } from './npmignore'
|
|
||||||
import { createPackageJson } from './package'
|
|
||||||
|
|
||||||
const NAME_PROMOTE_NAME = 'Package name'
|
|
||||||
const DIR_PROMOTE_NAME = 'Dir name'
|
|
||||||
const ENABLE_GITHUB_ACTIONS_PROMOTE_NAME = 'Enable github actions'
|
|
||||||
|
|
||||||
const debug = debugFactory('create')
|
|
||||||
|
|
||||||
const BUILD_RS = `extern crate napi_build;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
napi_build::setup();
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const SupportedPlatforms: string[] = [
|
|
||||||
'aarch64-apple-darwin',
|
|
||||||
'aarch64-linux-android',
|
|
||||||
'aarch64-unknown-linux-gnu',
|
|
||||||
'aarch64-unknown-linux-musl',
|
|
||||||
'aarch64-pc-windows-msvc',
|
|
||||||
'armv7-unknown-linux-gnueabihf',
|
|
||||||
'x86_64-apple-darwin',
|
|
||||||
'x86_64-pc-windows-msvc',
|
|
||||||
'x86_64-unknown-linux-gnu',
|
|
||||||
'x86_64-unknown-linux-musl',
|
|
||||||
'x86_64-unknown-freebsd',
|
|
||||||
'i686-pc-windows-msvc',
|
|
||||||
'armv7-linux-androideabi',
|
|
||||||
'universal-apple-darwin',
|
|
||||||
]
|
|
||||||
|
|
||||||
export class NewProjectCommand extends Command {
|
|
||||||
static usage = Command.Usage({
|
|
||||||
description: 'Create a new project from scratch',
|
|
||||||
})
|
|
||||||
|
|
||||||
static paths = [['new']]
|
|
||||||
|
|
||||||
name?: string = Option.String({
|
|
||||||
name: '-n,--name',
|
|
||||||
required: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
dirname?: string = Option.String({
|
|
||||||
name: '-d,--dirname',
|
|
||||||
required: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
targets?: string[] = Option.Array('--targets,-t')
|
|
||||||
|
|
||||||
dryRun = Option.Boolean(`--dry-run`, false)
|
|
||||||
|
|
||||||
enableGithubActions?: boolean = Option.Boolean(`--enable-github-actions`)
|
|
||||||
|
|
||||||
async execute() {
|
|
||||||
await this.getName()
|
|
||||||
if (!this.dirname) {
|
|
||||||
const [scope, name] = this.name?.split('/') ?? []
|
|
||||||
const defaultProjectDir = name ?? scope
|
|
||||||
const dirAnswer = await inquirer.prompt({
|
|
||||||
type: 'input',
|
|
||||||
name: DIR_PROMOTE_NAME,
|
|
||||||
default: defaultProjectDir,
|
|
||||||
})
|
|
||||||
|
|
||||||
this.dirname = dirAnswer[DIR_PROMOTE_NAME]
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.targets) {
|
|
||||||
const { targets } = await inquirer.prompt([
|
|
||||||
{
|
|
||||||
type: 'checkbox',
|
|
||||||
name: 'targets',
|
|
||||||
message: 'Choose targets you want to support',
|
|
||||||
default: DefaultPlatforms.map((p) => p.raw),
|
|
||||||
choices: SupportedPlatforms,
|
|
||||||
},
|
|
||||||
])
|
|
||||||
|
|
||||||
if (!targets.length) {
|
|
||||||
throw new TypeError('At least choose one target')
|
|
||||||
}
|
|
||||||
|
|
||||||
this.targets = targets
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.enableGithubActions === undefined) {
|
|
||||||
const answer = await inquirer.prompt([
|
|
||||||
{
|
|
||||||
type: 'confirm',
|
|
||||||
name: ENABLE_GITHUB_ACTIONS_PROMOTE_NAME,
|
|
||||||
message: 'Enable github actions?',
|
|
||||||
default: true,
|
|
||||||
choices: SupportedPlatforms,
|
|
||||||
},
|
|
||||||
])
|
|
||||||
this.enableGithubActions = answer[ENABLE_GITHUB_ACTIONS_PROMOTE_NAME]
|
|
||||||
}
|
|
||||||
|
|
||||||
debug(`Running command: ${chalk.green('[${command}]')}`)
|
|
||||||
if (!this.dryRun) {
|
|
||||||
mkdirSync(join(process.cwd(), this.dirname!), {
|
|
||||||
recursive: true,
|
|
||||||
})
|
|
||||||
mkdirSync(join(process.cwd(), this.dirname!, 'src'), {
|
|
||||||
recursive: true,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const [s, pkgName] = this.name!.split('/')
|
|
||||||
const binaryName = pkgName ?? s
|
|
||||||
|
|
||||||
this.writeFile('Cargo.toml', createCargoContent(this.name!))
|
|
||||||
this.writeFile('.npmignore', NPMIgnoreFiles)
|
|
||||||
this.writeFile('build.rs', BUILD_RS)
|
|
||||||
this.writeFile(
|
|
||||||
'package.json',
|
|
||||||
JSON.stringify(
|
|
||||||
createPackageJson(this.name!, binaryName, this.targets!),
|
|
||||||
null,
|
|
||||||
2,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
this.writeFile('src/lib.rs', LibRs)
|
|
||||||
|
|
||||||
mkdirSync(join(process.cwd(), this.dirname!, '__test__'), {
|
|
||||||
recursive: true,
|
|
||||||
})
|
|
||||||
this.writeFile(
|
|
||||||
'__test__/index.spec.mjs',
|
|
||||||
`import test from 'ava'
|
|
||||||
|
|
||||||
import { sum } from '../index.js'
|
|
||||||
|
|
||||||
test('sum from native', (t) => {
|
|
||||||
t.is(sum(1, 2), 3)
|
|
||||||
})
|
|
||||||
`,
|
|
||||||
)
|
|
||||||
|
|
||||||
if (this.enableGithubActions) {
|
|
||||||
const githubDir = join(process.cwd(), this.dirname!, '.github')
|
|
||||||
const workflowsDir = join(githubDir, 'workflows')
|
|
||||||
if (!this.dryRun) {
|
|
||||||
mkdirSync(githubDir, { recursive: true })
|
|
||||||
mkdirSync(workflowsDir, { recursive: true })
|
|
||||||
}
|
|
||||||
this.writeFile(
|
|
||||||
join('.github', 'workflows', 'CI.yml'),
|
|
||||||
createGithubActionsCIYml(binaryName, this.targets!),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
await CreateNpmDirCommand.create(
|
|
||||||
'package.json',
|
|
||||||
join(process.cwd(), this.dirname!),
|
|
||||||
join(process.cwd(), this.dirname!),
|
|
||||||
)
|
|
||||||
|
|
||||||
const enableLinuxArm8Musl = this.targets!.includes(
|
|
||||||
'aarch64-unknown-linux-musl',
|
|
||||||
)
|
|
||||||
const cargoConfig = createCargoConfig(enableLinuxArm8Musl)
|
|
||||||
if (cargoConfig.length) {
|
|
||||||
const configDir = join(process.cwd(), this.dirname!, '.cargo')
|
|
||||||
if (!this.dryRun) {
|
|
||||||
mkdirSync(configDir, { recursive: true })
|
|
||||||
this.writeFile(join('.cargo', 'config.toml'), cargoConfig)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.writeFile(
|
|
||||||
'rustfmt.toml',
|
|
||||||
`tab_spaces = 2
|
|
||||||
edition = "2021"
|
|
||||||
`,
|
|
||||||
)
|
|
||||||
this.writeFile('.gitignore', GitIgnore)
|
|
||||||
this.writeFile('.yarnrc.yml', 'nodeLinker: node-modules')
|
|
||||||
await spawn(`yarn set version stable`, {
|
|
||||||
cwd: join(process.cwd(), this.dirname!),
|
|
||||||
})
|
|
||||||
await spawn(`yarn install`, {
|
|
||||||
cwd: join(process.cwd(), this.dirname!),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
private writeFile(path: string, content: string) {
|
|
||||||
const distDir = join(process.cwd(), this.dirname!)
|
|
||||||
this.context.stdout.write(chalk.green(`Writing ${chalk.blue(path)}\n`))
|
|
||||||
if (!this.dryRun) {
|
|
||||||
writeFileSync(join(distDir, path), content)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async getName() {
|
|
||||||
if (!this.name) {
|
|
||||||
const nameAnswer = await inquirer.prompt({
|
|
||||||
type: 'input',
|
|
||||||
name: NAME_PROMOTE_NAME,
|
|
||||||
suffix: ' (The name filed in your package.json)',
|
|
||||||
})
|
|
||||||
|
|
||||||
const name = nameAnswer[NAME_PROMOTE_NAME]
|
|
||||||
if (!name) {
|
|
||||||
await this.getName()
|
|
||||||
} else {
|
|
||||||
this.name = name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,64 +0,0 @@
|
||||||
import { version } from '../../package.json'
|
|
||||||
import { DefaultPlatforms } from '../parse-triple'
|
|
||||||
|
|
||||||
export const createPackageJson = (
|
|
||||||
name: string,
|
|
||||||
binaryName: string,
|
|
||||||
targets: string[],
|
|
||||||
) => {
|
|
||||||
const pkgContent = {
|
|
||||||
name,
|
|
||||||
version: '0.0.0',
|
|
||||||
main: 'index.js',
|
|
||||||
types: 'index.d.ts',
|
|
||||||
napi: {
|
|
||||||
name: binaryName,
|
|
||||||
},
|
|
||||||
license: 'MIT',
|
|
||||||
devDependencies: {
|
|
||||||
'@napi-rs/cli': `^${version}`,
|
|
||||||
ava: '^5.1.1',
|
|
||||||
},
|
|
||||||
ava: {
|
|
||||||
timeout: '3m',
|
|
||||||
},
|
|
||||||
engines: {
|
|
||||||
node: '>= 10',
|
|
||||||
},
|
|
||||||
scripts: {
|
|
||||||
artifacts: 'napi artifacts',
|
|
||||||
build: 'napi build --platform --release',
|
|
||||||
'build:debug': 'napi build --platform',
|
|
||||||
prepublishOnly: 'napi prepublish -t npm',
|
|
||||||
test: 'ava',
|
|
||||||
universal: 'napi universal',
|
|
||||||
version: 'napi version',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
const triples: any = {}
|
|
||||||
|
|
||||||
const defaultTargetsSupported = DefaultPlatforms.every((p) =>
|
|
||||||
targets!.includes(p.raw),
|
|
||||||
)
|
|
||||||
|
|
||||||
const isOnlyDefaultTargets =
|
|
||||||
targets.length === 3 &&
|
|
||||||
DefaultPlatforms.every((p) => targets.includes(p.raw))
|
|
||||||
|
|
||||||
if (!isOnlyDefaultTargets) {
|
|
||||||
if (!defaultTargetsSupported) {
|
|
||||||
triples.defaults = false
|
|
||||||
triples.additional = targets
|
|
||||||
} else {
|
|
||||||
triples.additional = targets.filter(
|
|
||||||
(t) => !DefaultPlatforms.map((p) => p.raw).includes(t),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// @ts-expect-error
|
|
||||||
pkgContent.napi.triples = triples
|
|
||||||
|
|
||||||
return pkgContent
|
|
||||||
}
|
|
|
@ -1,242 +0,0 @@
|
||||||
import { existsSync, statSync } from 'fs'
|
|
||||||
import { join } from 'path'
|
|
||||||
|
|
||||||
import { Octokit } from '@octokit/rest'
|
|
||||||
import { Command, Option } from 'clipanion'
|
|
||||||
import * as chalk from 'colorette'
|
|
||||||
|
|
||||||
import { getNapiConfig } from './consts'
|
|
||||||
import { debugFactory } from './debug'
|
|
||||||
import { spawn } from './spawn'
|
|
||||||
import { updatePackageJson } from './update-package'
|
|
||||||
import { readFileAsync } from './utils'
|
|
||||||
import { VersionCommand } from './version'
|
|
||||||
|
|
||||||
const debug = debugFactory('prepublish')
|
|
||||||
|
|
||||||
interface PackageInfo {
|
|
||||||
name: string
|
|
||||||
version: string
|
|
||||||
tag: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export class PrePublishCommand extends Command {
|
|
||||||
static usage = Command.Usage({
|
|
||||||
description:
|
|
||||||
'Update package.json and copy addons into per platform packages',
|
|
||||||
})
|
|
||||||
|
|
||||||
static paths = [['prepublish']]
|
|
||||||
|
|
||||||
prefix = Option.String(`-p,--prefix`, 'npm')
|
|
||||||
|
|
||||||
tagStyle: 'npm' | 'lerna' = Option.String('--tagstyle,-t', 'lerna')
|
|
||||||
|
|
||||||
configFileName?: string = Option.String('-c,--config')
|
|
||||||
|
|
||||||
isDryRun = Option.Boolean('--dry-run', false)
|
|
||||||
|
|
||||||
skipGHRelease = Option.Boolean('--skip-gh-release', false)
|
|
||||||
|
|
||||||
ghReleaseName?: string = Option.String('--gh-release-name')
|
|
||||||
|
|
||||||
existingReleaseId?: string = Option.String('--gh-release-id')
|
|
||||||
|
|
||||||
async execute() {
|
|
||||||
const {
|
|
||||||
packageJsonPath,
|
|
||||||
platforms,
|
|
||||||
version,
|
|
||||||
packageName,
|
|
||||||
binaryName,
|
|
||||||
npmClient,
|
|
||||||
} = getNapiConfig(this.configFileName)
|
|
||||||
debug(`Update optionalDependencies in [${packageJsonPath}]`)
|
|
||||||
if (!this.isDryRun) {
|
|
||||||
await VersionCommand.updatePackageJson(this.prefix, this.configFileName)
|
|
||||||
await updatePackageJson(packageJsonPath, {
|
|
||||||
optionalDependencies: platforms.reduce(
|
|
||||||
(acc: Record<string, string>, cur) => {
|
|
||||||
acc[`${packageName}-${cur.platformArchABI}`] = `${version}`
|
|
||||||
return acc
|
|
||||||
},
|
|
||||||
{},
|
|
||||||
),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const { owner, repo, pkgInfo, octokit } = this.existingReleaseId
|
|
||||||
? await this.getRepoInfo(packageName, version)
|
|
||||||
: await this.createGhRelease(packageName, version)
|
|
||||||
|
|
||||||
for (const platformDetail of platforms) {
|
|
||||||
const pkgDir = join(
|
|
||||||
process.cwd(),
|
|
||||||
this.prefix,
|
|
||||||
`${platformDetail.platformArchABI}`,
|
|
||||||
)
|
|
||||||
const filename = `${binaryName}.${platformDetail.platformArchABI}.node`
|
|
||||||
const dstPath = join(pkgDir, filename)
|
|
||||||
|
|
||||||
if (!this.isDryRun) {
|
|
||||||
if (!existsSync(dstPath)) {
|
|
||||||
console.warn(`[${chalk.yellowBright(dstPath)}] doesn't exist`)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
await spawn(`${npmClient} publish`, {
|
|
||||||
cwd: pkgDir,
|
|
||||||
env: process.env,
|
|
||||||
})
|
|
||||||
if (!this.skipGHRelease && repo && owner) {
|
|
||||||
debug(
|
|
||||||
`Start upload [${chalk.greenBright(
|
|
||||||
dstPath,
|
|
||||||
)}] to Github release, [${chalk.greenBright(pkgInfo.tag)}]`,
|
|
||||||
)
|
|
||||||
try {
|
|
||||||
const releaseId = this.existingReleaseId
|
|
||||||
? Number(this.existingReleaseId)
|
|
||||||
: (
|
|
||||||
await octokit!.repos.getReleaseByTag({
|
|
||||||
repo: repo,
|
|
||||||
owner: owner,
|
|
||||||
tag: pkgInfo.tag,
|
|
||||||
})
|
|
||||||
).data.id
|
|
||||||
const dstFileStats = statSync(dstPath)
|
|
||||||
const assetInfo = await octokit!.repos.uploadReleaseAsset({
|
|
||||||
owner: owner,
|
|
||||||
repo: repo,
|
|
||||||
name: filename,
|
|
||||||
release_id: releaseId,
|
|
||||||
mediaType: { format: 'raw' },
|
|
||||||
headers: {
|
|
||||||
'content-length': dstFileStats.size,
|
|
||||||
'content-type': 'application/octet-stream',
|
|
||||||
},
|
|
||||||
// @ts-expect-error
|
|
||||||
data: await readFileAsync(dstPath),
|
|
||||||
})
|
|
||||||
console.info(`${chalk.green(dstPath)} upload success`)
|
|
||||||
console.info(
|
|
||||||
`Download url: ${chalk.blueBright(
|
|
||||||
assetInfo.data.browser_download_url,
|
|
||||||
)}`,
|
|
||||||
)
|
|
||||||
} catch (e) {
|
|
||||||
debug(
|
|
||||||
`Param: ${JSON.stringify(
|
|
||||||
{ owner, repo, tag: pkgInfo.tag, filename: dstPath },
|
|
||||||
null,
|
|
||||||
2,
|
|
||||||
)}`,
|
|
||||||
)
|
|
||||||
console.error(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async createGhRelease(packageName: string, version: string) {
|
|
||||||
if (this.skipGHRelease) {
|
|
||||||
return {
|
|
||||||
owner: null,
|
|
||||||
repo: null,
|
|
||||||
pkgInfo: { name: null, version: null, tag: null },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const { repo, owner, pkgInfo, octokit } = await this.getRepoInfo(
|
|
||||||
packageName,
|
|
||||||
version,
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!repo || !owner) {
|
|
||||||
return {
|
|
||||||
owner: null,
|
|
||||||
repo: null,
|
|
||||||
pkgInfo: { name: null, version: null, tag: null },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.isDryRun) {
|
|
||||||
try {
|
|
||||||
await octokit.repos.createRelease({
|
|
||||||
owner,
|
|
||||||
repo,
|
|
||||||
tag_name: pkgInfo.tag,
|
|
||||||
name: this.ghReleaseName,
|
|
||||||
prerelease:
|
|
||||||
version.includes('alpha') ||
|
|
||||||
version.includes('beta') ||
|
|
||||||
version.includes('rc'),
|
|
||||||
})
|
|
||||||
} catch (e) {
|
|
||||||
debug(
|
|
||||||
`Params: ${JSON.stringify(
|
|
||||||
{ owner, repo, tag_name: pkgInfo.tag },
|
|
||||||
null,
|
|
||||||
2,
|
|
||||||
)}`,
|
|
||||||
)
|
|
||||||
console.error(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return { owner, repo, pkgInfo, octokit }
|
|
||||||
}
|
|
||||||
|
|
||||||
private async getRepoInfo(packageName: string, version: string) {
|
|
||||||
const headCommit = (await spawn('git log -1 --pretty=%B'))
|
|
||||||
.toString('utf8')
|
|
||||||
.trim()
|
|
||||||
const { GITHUB_REPOSITORY } = process.env
|
|
||||||
if (!GITHUB_REPOSITORY) {
|
|
||||||
return {
|
|
||||||
owner: null,
|
|
||||||
repo: null,
|
|
||||||
pkgInfo: { name: null, version: null, tag: null },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
debug(`Github repository: ${GITHUB_REPOSITORY}`)
|
|
||||||
const [owner, repo] = GITHUB_REPOSITORY.split('/')
|
|
||||||
const octokit = new Octokit({
|
|
||||||
auth: process.env.GITHUB_TOKEN,
|
|
||||||
})
|
|
||||||
let pkgInfo: PackageInfo | undefined
|
|
||||||
if (this.tagStyle === 'lerna') {
|
|
||||||
const packagesToPublish = headCommit
|
|
||||||
.split('\n')
|
|
||||||
.map((line) => line.trim())
|
|
||||||
.filter((line, index) => line.length && index)
|
|
||||||
.map((line) => line.substring(2))
|
|
||||||
.map(this.parseTag)
|
|
||||||
pkgInfo = packagesToPublish.find(
|
|
||||||
(pkgInfo) => pkgInfo.name === packageName,
|
|
||||||
)
|
|
||||||
if (!pkgInfo) {
|
|
||||||
throw new TypeError(
|
|
||||||
`No release commit found with ${packageName}, original commit info: ${headCommit}`,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
pkgInfo = {
|
|
||||||
tag: `v${version}`,
|
|
||||||
version,
|
|
||||||
name: packageName,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return { owner, repo, pkgInfo, octokit }
|
|
||||||
}
|
|
||||||
|
|
||||||
private parseTag(tag: string) {
|
|
||||||
const segments = tag.split('@')
|
|
||||||
const version = segments.pop()!
|
|
||||||
const name = segments.join('@')
|
|
||||||
|
|
||||||
return {
|
|
||||||
name,
|
|
||||||
version,
|
|
||||||
tag,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,121 +0,0 @@
|
||||||
import { join } from 'path'
|
|
||||||
|
|
||||||
import { Command, Option } from 'clipanion'
|
|
||||||
import * as chalk from 'colorette'
|
|
||||||
import inquirer from 'inquirer'
|
|
||||||
import { load, dump } from 'js-yaml'
|
|
||||||
|
|
||||||
import { debugFactory } from './debug'
|
|
||||||
import { spawn } from './spawn'
|
|
||||||
import { readFileAsync, writeFileAsync } from './utils'
|
|
||||||
|
|
||||||
const debug = debugFactory('rename')
|
|
||||||
|
|
||||||
export class RenameCommand extends Command {
|
|
||||||
static paths = [['rename']]
|
|
||||||
|
|
||||||
name = Option.String('-n', {
|
|
||||||
required: false,
|
|
||||||
description: 'The new name of the project',
|
|
||||||
})
|
|
||||||
|
|
||||||
napiName = Option.String('--napi-name', {
|
|
||||||
required: false,
|
|
||||||
description: 'The new napi addon name',
|
|
||||||
})
|
|
||||||
|
|
||||||
repository = Option.String('--repository', {
|
|
||||||
required: false,
|
|
||||||
description: 'The repository of the package',
|
|
||||||
})
|
|
||||||
|
|
||||||
description = Option.String('-d,--description', {
|
|
||||||
required: false,
|
|
||||||
description: 'The description of the package',
|
|
||||||
})
|
|
||||||
|
|
||||||
cwd = Option.String({
|
|
||||||
required: false,
|
|
||||||
description: 'The working directory, default is [process.cwd()]',
|
|
||||||
})
|
|
||||||
|
|
||||||
async execute() {
|
|
||||||
const cwd = this.cwd ?? process.cwd()
|
|
||||||
const packageJson = await readFileAsync(join(cwd, 'package.json'), 'utf8')
|
|
||||||
const packageJsonData = JSON.parse(packageJson)
|
|
||||||
const name =
|
|
||||||
this.name ??
|
|
||||||
(
|
|
||||||
await inquirer.prompt({
|
|
||||||
name: 'name',
|
|
||||||
type: 'input',
|
|
||||||
suffix: chalk.dim(' name field in package.json'),
|
|
||||||
})
|
|
||||||
).name
|
|
||||||
const napiName =
|
|
||||||
this.napiName ??
|
|
||||||
(
|
|
||||||
await inquirer.prompt({
|
|
||||||
name: 'napi name',
|
|
||||||
type: 'input',
|
|
||||||
default: name.split('/')[1],
|
|
||||||
})
|
|
||||||
)['napi name']
|
|
||||||
debug('name: %s, napi name: %s', name, napiName)
|
|
||||||
packageJsonData.name = name
|
|
||||||
packageJsonData.napi.name = napiName
|
|
||||||
const repository =
|
|
||||||
this.repository ??
|
|
||||||
(
|
|
||||||
await inquirer.prompt({
|
|
||||||
name: 'repository',
|
|
||||||
type: 'input',
|
|
||||||
suffix: chalk.dim(' Leave empty to skip'),
|
|
||||||
})
|
|
||||||
).repository
|
|
||||||
if (repository) {
|
|
||||||
packageJsonData.repository = repository
|
|
||||||
}
|
|
||||||
const description =
|
|
||||||
this.description ??
|
|
||||||
(
|
|
||||||
await inquirer.prompt({
|
|
||||||
name: 'description',
|
|
||||||
type: 'input',
|
|
||||||
suffix: chalk.dim(' Leave empty to skip'),
|
|
||||||
})
|
|
||||||
).description
|
|
||||||
|
|
||||||
if (description) {
|
|
||||||
packageJsonData.description = description
|
|
||||||
}
|
|
||||||
|
|
||||||
await writeFileAsync(
|
|
||||||
join(cwd, 'package.json'),
|
|
||||||
JSON.stringify(packageJsonData, null, 2),
|
|
||||||
)
|
|
||||||
|
|
||||||
const CI = await readFileAsync(
|
|
||||||
join(cwd, '.github', 'workflows', 'CI.yml'),
|
|
||||||
'utf8',
|
|
||||||
)
|
|
||||||
const CIObject = load(CI) as any
|
|
||||||
CIObject.env.APP_NAME = napiName
|
|
||||||
|
|
||||||
await writeFileAsync(
|
|
||||||
join(cwd, '.github', 'workflows', 'CI.yml'),
|
|
||||||
dump(CIObject, {
|
|
||||||
lineWidth: 1000,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
let tomlContent = await readFileAsync(join(cwd, 'Cargo.toml'), 'utf8')
|
|
||||||
tomlContent = tomlContent.replace(
|
|
||||||
'name = "napi-package-template"',
|
|
||||||
`name = "${napiName}"`,
|
|
||||||
)
|
|
||||||
await writeFileAsync(join(cwd, 'Cargo.toml'), tomlContent)
|
|
||||||
|
|
||||||
await spawn('napi create-npm-dir -t .')
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
import { spawn as _spawn, SpawnOptionsWithoutStdio } from 'child_process'
|
|
||||||
|
|
||||||
import { debugFactory } from './debug'
|
|
||||||
|
|
||||||
const debug = debugFactory('spawn')
|
|
||||||
|
|
||||||
export function spawn(
|
|
||||||
command: string,
|
|
||||||
options: SpawnOptionsWithoutStdio = {},
|
|
||||||
): Promise<Buffer> {
|
|
||||||
const [cmd, ...args] = command.split(' ').map((s) => s.trim())
|
|
||||||
debug(`execute ${cmd} ${args.join(' ')}`)
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const spawnStream = _spawn(cmd, args, { ...options, shell: true })
|
|
||||||
const chunks: Buffer[] = []
|
|
||||||
process.stdin.pipe(spawnStream.stdin)
|
|
||||||
spawnStream.stdout?.on('data', (chunk) => {
|
|
||||||
chunks.push(chunk)
|
|
||||||
})
|
|
||||||
spawnStream.stdout.pipe(process.stdout)
|
|
||||||
spawnStream.stderr.pipe(process.stderr)
|
|
||||||
spawnStream.on('close', (code) => {
|
|
||||||
if (code !== 0) {
|
|
||||||
reject()
|
|
||||||
} else {
|
|
||||||
resolve(Buffer.concat(chunks))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,81 +0,0 @@
|
||||||
import { spawnSync } from 'child_process'
|
|
||||||
import { join } from 'path'
|
|
||||||
|
|
||||||
import { Command, Option } from 'clipanion'
|
|
||||||
import * as chalk from 'colorette'
|
|
||||||
|
|
||||||
import { getNapiConfig } from './consts'
|
|
||||||
import { debugFactory } from './debug'
|
|
||||||
import { UniArchsByPlatform } from './parse-triple'
|
|
||||||
import { fileExists } from './utils'
|
|
||||||
|
|
||||||
const debug = debugFactory('universal')
|
|
||||||
|
|
||||||
export class UniversalCommand extends Command {
|
|
||||||
static usage = Command.Usage({
|
|
||||||
description: 'Combine built binaries to universal binaries',
|
|
||||||
})
|
|
||||||
|
|
||||||
static paths = [['universal']]
|
|
||||||
|
|
||||||
sourceDir = Option.String('-d,--dir', 'artifacts')
|
|
||||||
|
|
||||||
distDir = Option.String('--dist', '.')
|
|
||||||
|
|
||||||
configFileName?: string = Option.String('-c,--config')
|
|
||||||
|
|
||||||
buildUniversal: Record<
|
|
||||||
keyof typeof UniArchsByPlatform,
|
|
||||||
(binName: string, srcFiles: string[]) => string
|
|
||||||
> = {
|
|
||||||
darwin: (binName, srcFiles) => {
|
|
||||||
const outPath = join(
|
|
||||||
this.distDir,
|
|
||||||
`${binName}.${process.platform}-universal.node`,
|
|
||||||
)
|
|
||||||
const srcPaths = srcFiles.map((f) => join(this.sourceDir, f))
|
|
||||||
spawnSync('lipo', ['-create', '-output', outPath, ...srcPaths])
|
|
||||||
return outPath
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
async execute() {
|
|
||||||
const { platforms, binaryName } = getNapiConfig(this.configFileName)
|
|
||||||
|
|
||||||
const targetPlatform = platforms.find(
|
|
||||||
(p) => p.platform === process.platform && p.arch === 'universal',
|
|
||||||
)
|
|
||||||
if (!targetPlatform) {
|
|
||||||
throw new TypeError(
|
|
||||||
`'universal' arch for platform '${process.platform}' not found in config!`,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const srcFiles = UniArchsByPlatform[process.platform]?.map(
|
|
||||||
(a) => `${binaryName}.${process.platform}-${a}.node`,
|
|
||||||
)
|
|
||||||
if (!srcFiles) {
|
|
||||||
throw new TypeError(
|
|
||||||
`'universal' arch for platform '${process.platform}' not supported.`,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
debug(
|
|
||||||
`Looking up source binaries to combine: ${chalk.yellowBright(
|
|
||||||
srcFiles.join(', '),
|
|
||||||
)}`,
|
|
||||||
)
|
|
||||||
const srcFileLookup = await Promise.all(
|
|
||||||
srcFiles.map((f) => fileExists(join(this.sourceDir, f))),
|
|
||||||
)
|
|
||||||
const notFoundFiles = srcFiles.filter((_f, i) => !srcFileLookup[i])
|
|
||||||
if (notFoundFiles.length > 0) {
|
|
||||||
throw new TypeError(
|
|
||||||
`Some binary files were not found: ${JSON.stringify(notFoundFiles)}`,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const outPath = this.buildUniversal[process.platform](binaryName, srcFiles)
|
|
||||||
debug(`Produced universal binary: ${outPath}`)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
import { debugFactory } from './debug'
|
|
||||||
import { writeFileAsync, fileExists } from './utils'
|
|
||||||
|
|
||||||
const debug = debugFactory('update-package')
|
|
||||||
|
|
||||||
export async function updatePackageJson(
|
|
||||||
path: string,
|
|
||||||
partial: Record<string, any>,
|
|
||||||
) {
|
|
||||||
const exists = await fileExists(path)
|
|
||||||
if (!exists) {
|
|
||||||
debug(`File not exists ${path}`)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const old = require(path)
|
|
||||||
await writeFileAsync(path, JSON.stringify({ ...old, ...partial }, null, 2))
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
import { readFile, writeFile, copyFile, mkdir, unlink, stat } from 'fs'
|
|
||||||
import { promisify } from 'util'
|
|
||||||
|
|
||||||
export const readFileAsync = promisify(readFile)
|
|
||||||
export const writeFileAsync = promisify(writeFile)
|
|
||||||
export const unlinkAsync = promisify(unlink)
|
|
||||||
export const copyFileAsync = promisify(copyFile)
|
|
||||||
export const mkdirAsync = promisify(mkdir)
|
|
||||||
export const statAsync = promisify(stat)
|
|
||||||
|
|
||||||
export async function fileExists(path: string) {
|
|
||||||
const exists = await statAsync(path)
|
|
||||||
.then(() => true)
|
|
||||||
.catch(() => false)
|
|
||||||
return exists
|
|
||||||
}
|
|
||||||
|
|
||||||
export function pick<O, K extends keyof O>(o: O, ...keys: K[]): Pick<O, K> {
|
|
||||||
return keys.reduce((acc, key) => {
|
|
||||||
acc[key] = o[key]
|
|
||||||
return acc
|
|
||||||
}, {} as O)
|
|
||||||
}
|
|
201
cli/src/utils/__tests__/__fixtures__/napi_type_def
Normal file
201
cli/src/utils/__tests__/__fixtures__/napi_type_def
Normal file
|
@ -0,0 +1,201 @@
|
||||||
|
{"kind": "const", "name": "DEFAULT_COST", "js_doc": "/** This is a const */\n", "def": "export const DEFAULT_COST: number", "original_name": "DEFAULT_COST"}
|
||||||
|
{"kind": "fn", "name": "getWords", "js_doc": "", "def": "export function getWords(): Array<string>"}
|
||||||
|
{"kind": "fn", "name": "getNums", "js_doc": "/** Gets some numbers */\n", "def": "export function getNums(): Array<number>"}
|
||||||
|
{"kind": "fn", "name": "sumNums", "js_doc": "", "def": "export function sumNums(nums: Array<number>): number"}
|
||||||
|
{"kind": "fn", "name": "toJsObj", "js_doc": "", "def": "export function toJsObj(): object"}
|
||||||
|
{"kind": "fn", "name": "getNumArr", "js_doc": "", "def": "export function getNumArr(): number[]"}
|
||||||
|
{"kind": "fn", "name": "getNestedNumArr", "js_doc": "", "def": "export function getNestedNumArr(): number[][][]"}
|
||||||
|
{"kind": "fn", "name": "readFileAsync", "js_doc": "", "def": "export function readFileAsync(path: string): Promise<Buffer>"}
|
||||||
|
{"kind": "fn", "name": "asyncMultiTwo", "js_doc": "", "def": "export function asyncMultiTwo(arg: number): Promise<number>"}
|
||||||
|
{"kind": "fn", "name": "bigintAdd", "js_doc": "", "def": "export function bigintAdd(a: bigint, b: bigint): bigint"}
|
||||||
|
{"kind": "fn", "name": "createBigInt", "js_doc": "", "def": "export function createBigInt(): bigint"}
|
||||||
|
{"kind": "fn", "name": "createBigIntI64", "js_doc": "", "def": "export function createBigIntI64(): bigint"}
|
||||||
|
{"kind": "fn", "name": "bigintGetU64AsString", "js_doc": "", "def": "export function bigintGetU64AsString(bi: bigint): string"}
|
||||||
|
{"kind": "fn", "name": "bigintFromI64", "js_doc": "", "def": "export function bigintFromI64(): bigint"}
|
||||||
|
{"kind": "fn", "name": "bigintFromI128", "js_doc": "", "def": "export function bigintFromI128(): bigint"}
|
||||||
|
{"kind": "fn", "name": "getCwd", "js_doc": "", "def": "export function getCwd(callback: (arg0: string) => void): void"}
|
||||||
|
{"kind": "fn", "name": "optionEnd", "js_doc": "", "def": "export function optionEnd(callback: (arg0: string, arg1?: string | undefined | null) => void): void"}
|
||||||
|
{"kind": "fn", "name": "optionStart", "js_doc": "", "def": "export function optionStart(callback: (arg0: string | undefined | null, arg1: string) => void): void"}
|
||||||
|
{"kind": "fn", "name": "optionStartEnd", "js_doc": "", "def": "export function optionStartEnd(callback: (arg0: string | undefined | null, arg1: string, arg2?: string | undefined | null) => void): void"}
|
||||||
|
{"kind": "fn", "name": "optionOnly", "js_doc": "", "def": "export function optionOnly(callback: (arg0?: string | undefined | null) => void): void"}
|
||||||
|
{"kind": "fn", "name": "readFile", "js_doc": "/** napi = { version = 2, features = [\"serde-json\"] } */\n", "def": "export function readFile(callback: (arg0: Error | undefined, arg1?: string | undefined | null) => void): void"}
|
||||||
|
{"kind": "fn", "name": "returnJsFunction", "js_doc": "", "def": "export function returnJsFunction(): (...args: any[]) => any"}
|
||||||
|
{"kind": "fn", "name": "callbackReturnPromise", "js_doc": "", "def": "export function callbackReturnPromise<T>(functionInput: () => T | Promise<T>, callback: (err: Error | null, result: T) => void): T | Promise<T>"}
|
||||||
|
{"kind": "fn", "name": "captureErrorInCallback", "js_doc": "", "def": "export function captureErrorInCallback(cb1: () => void, cb2: (arg0: Error) => void): void"}
|
||||||
|
{"kind": "struct", "name": "Animal", "js_doc": "/**\n * `constructor` option for `struct` requires all fields to be public,\n * otherwise tag impl fn as constructor\n * #[napi(constructor)]\n */\n", "def": "/** Kind of animal */\nreadonly kind: Kind", "original_name": "Animal"}
|
||||||
|
{"kind": "impl", "name": "Animal", "js_doc": "", "def": "/** This is the constructor */\n constructor(kind: Kind, name: string)\n/** This is a factory method */\nstatic withKind(kind: Kind): Animal\nget name(): string\nset name(name: string)\nget type(): Kind\nset type(kind: Kind)\n/**\n * This is a\n * multi-line comment\n * with an emoji \uD83D\uDE80\n */\n whoami(): string\n/** This is static... */\nstatic getDogKind(): Kind\n/**\n * Here are some characters and character sequences\n * that should be escaped correctly:\n * \\[]{}/\\:\"\"{\n * }\n */\n returnOtherClass(): Dog\n returnOtherClassWithCustomConstructor(): Bird\n overrideIndividualArgOnMethod(normalTy: string, overriddenTy: {n: string}): Bird"}
|
||||||
|
{"kind": "struct", "name": "Dog", "js_doc": "", "def": "name: string\nconstructor(name: string)", "original_name": "Dog"}
|
||||||
|
{"kind": "struct", "name": "Bird", "js_doc": "", "def": "name: string", "original_name": "Bird"}
|
||||||
|
{"kind": "impl", "name": "Bird", "js_doc": "", "def": " constructor(name: string)\n getCount(): number\n getNameAsync(): Promise<string>"}
|
||||||
|
{"kind": "struct", "name": "Blake2BHasher", "js_doc": "/** Smoking test for type generation */\n", "def": "", "original_name": "Blake2bHasher"}
|
||||||
|
{"kind": "impl", "name": "Blake2BHasher", "js_doc": "", "def": "static withKey(key: Blake2bKey): Blake2BHasher"}
|
||||||
|
{"kind": "impl", "name": "Blake2BHasher", "js_doc": "", "def": " update(data: Buffer): void"}
|
||||||
|
{"kind": "struct", "name": "Blake2BKey", "js_doc": "", "def": "", "original_name": "Blake2bKey"}
|
||||||
|
{"kind": "struct", "name": "Context", "js_doc": "", "def": "maybeNeed?: boolean\nbuffer: Uint8Array", "original_name": "Context"}
|
||||||
|
{"kind": "impl", "name": "Context", "js_doc": "", "def": " constructor()\nstatic withData(data: string): Context\nstatic withBuffer(buf: Uint8Array): Context\n method(): string"}
|
||||||
|
{"kind": "struct", "name": "AnimalWithDefaultConstructor", "js_doc": "", "def": "name: string\nkind: number\nconstructor(name: string, kind: number)", "original_name": "AnimalWithDefaultConstructor"}
|
||||||
|
{"kind": "struct", "name": "NinjaTurtle", "js_doc": "", "def": "name: string", "original_name": "NinjaTurtle"}
|
||||||
|
{"kind": "impl", "name": "NinjaTurtle", "js_doc": "", "def": "static isInstanceOf(value: unknown): boolean\n/** Create your ninja turtle! \uD83D\uDC22 */\nstatic newRaph(): NinjaTurtle\n getMaskColor(): string\n getName(): string\n returnThis(this: this): this"}
|
||||||
|
{"kind": "struct", "name": "Assets", "js_doc": "", "def": "", "original_name": "JsAssets"}
|
||||||
|
{"kind": "impl", "name": "Assets", "js_doc": "", "def": " constructor()\n get(id: number): JsAsset | null"}
|
||||||
|
{"kind": "struct", "name": "Asset", "js_doc": "", "def": "", "original_name": "JsAsset"}
|
||||||
|
{"kind": "impl", "name": "Asset", "js_doc": "", "def": " constructor()\nget filePath(): number"}
|
||||||
|
{"kind": "struct", "name": "Optional", "js_doc": "", "def": "", "original_name": "Optional"}
|
||||||
|
{"kind": "impl", "name": "Optional", "js_doc": "", "def": "static optionEnd(required: string, optional?: string | undefined | null): string\nstatic optionStart(optional: string | undefined | null, required: string): string\nstatic optionStartEnd(optional1: string | undefined | null, required: string, optional2?: string | undefined | null): string\nstatic optionOnly(optional?: string | undefined | null): string"}
|
||||||
|
{"kind": "interface", "name": "ObjectFieldClassInstance", "js_doc": "", "def": "bird: Bird", "original_name": "ObjectFieldClassInstance"}
|
||||||
|
{"kind": "fn", "name": "createObjectWithClassField", "js_doc": "", "def": "export function createObjectWithClassField(): ObjectFieldClassInstance"}
|
||||||
|
{"kind": "fn", "name": "receiveObjectWithClassField", "js_doc": "", "def": "export function receiveObjectWithClassField(object: ObjectFieldClassInstance): Bird"}
|
||||||
|
{"kind": "struct", "name": "NotWritableClass", "js_doc": "", "def": "name: string\nconstructor(name: string)", "original_name": "NotWritableClass"}
|
||||||
|
{"kind": "impl", "name": "NotWritableClass", "js_doc": "", "def": " setName(name: string): void"}
|
||||||
|
{"kind": "struct", "name": "CustomFinalize", "js_doc": "", "def": "", "original_name": "CustomFinalize"}
|
||||||
|
{"kind": "impl", "name": "CustomFinalize", "js_doc": "", "def": " constructor(width: number, height: number)"}
|
||||||
|
{"kind": "struct", "name": "Width", "js_doc": "", "def": "value: number\nconstructor(value: number)", "original_name": "Width"}
|
||||||
|
{"kind": "fn", "name": "plusOne", "js_doc": "", "def": "export function plusOne(this: Width): number"}
|
||||||
|
{"kind": "struct", "name": "ClassWithFactory", "js_doc": "", "def": "name: string", "original_name": "ClassWithFactory"}
|
||||||
|
{"kind": "impl", "name": "ClassWithFactory", "js_doc": "", "def": "static withName(name: string): ClassWithFactory\n setName(name: string): this"}
|
||||||
|
{"kind": "fn", "name": "dateToNumber", "js_doc": "", "def": "export function dateToNumber(input: Date): number"}
|
||||||
|
{"kind": "fn", "name": "chronoDateToMillis", "js_doc": "", "def": "export function chronoDateToMillis(input: Date): number"}
|
||||||
|
{"kind": "fn", "name": "chronoDateAdd1Minute", "js_doc": "", "def": "export function chronoDateAdd1Minute(input: Date): Date"}
|
||||||
|
{"kind": "interface", "name": "Dates", "js_doc": "", "def": "start: Date\nend?: Date", "original_name": "Dates"}
|
||||||
|
{"kind": "fn", "name": "eitherStringOrNumber", "js_doc": "", "def": "export function eitherStringOrNumber(input: string | number): number"}
|
||||||
|
{"kind": "fn", "name": "returnEither", "js_doc": "", "def": "export function returnEither(input: number): string | number"}
|
||||||
|
{"kind": "fn", "name": "either3", "js_doc": "", "def": "export function either3(input: string | number | boolean): number"}
|
||||||
|
{"kind": "interface", "name": "Obj", "js_doc": "", "def": "v: string | number", "original_name": "Obj"}
|
||||||
|
{"kind": "fn", "name": "either4", "js_doc": "", "def": "export function either4(input: string | number | boolean | Obj): number"}
|
||||||
|
{"kind": "struct", "name": "JsClassForEither", "js_doc": "", "def": "", "original_name": "JsClassForEither"}
|
||||||
|
{"kind": "impl", "name": "JsClassForEither", "js_doc": "", "def": " constructor()"}
|
||||||
|
{"kind": "struct", "name": "AnotherClassForEither", "js_doc": "", "def": "", "original_name": "AnotherClassForEither"}
|
||||||
|
{"kind": "impl", "name": "AnotherClassForEither", "js_doc": "", "def": " constructor()"}
|
||||||
|
{"kind": "fn", "name": "receiveClassOrNumber", "js_doc": "", "def": "export function receiveClassOrNumber(either: number | JsClassForEither): number"}
|
||||||
|
{"kind": "fn", "name": "receiveMutClassOrNumber", "js_doc": "", "def": "export function receiveMutClassOrNumber(either: number | JsClassForEither): number"}
|
||||||
|
{"kind": "fn", "name": "receiveDifferentClass", "js_doc": "", "def": "export function receiveDifferentClass(either: JsClassForEither | AnotherClassForEither): number"}
|
||||||
|
{"kind": "fn", "name": "returnEitherClass", "js_doc": "", "def": "export function returnEitherClass(input: number): number | JsClassForEither"}
|
||||||
|
{"kind": "fn", "name": "eitherFromOption", "js_doc": "", "def": "export function eitherFromOption(): JsClassForEither | undefined"}
|
||||||
|
{"kind": "interface", "name": "A", "js_doc": "", "def": "foo: number", "original_name": "A"}
|
||||||
|
{"kind": "interface", "name": "B", "js_doc": "", "def": "bar: number", "original_name": "B"}
|
||||||
|
{"kind": "interface", "name": "C", "js_doc": "", "def": "baz: number", "original_name": "C"}
|
||||||
|
{"kind": "fn", "name": "eitherFromObjects", "js_doc": "", "def": "export function eitherFromObjects(input: A | B | C): string"}
|
||||||
|
{"kind": "fn", "name": "eitherBoolOrFunction", "js_doc": "", "def": "export function eitherBoolOrFunction(input: boolean | ((...args: any[]) => any)): void"}
|
||||||
|
{"kind": "fn", "name": "promiseInEither", "js_doc": "", "def": "export function promiseInEither(input: number | Promise<number>): Promise<boolean>"}
|
||||||
|
{"kind": "enum", "name": "Kind", "js_doc": "/** default enum values are continuos i32s start from 0 */\n", "def": "/** Barks */\nDog = 0,\n /** Kills birds */\nCat = 1,\n /** Tasty */\nDuck = 2", "original_name": "Kind"}
|
||||||
|
{"kind": "enum", "name": "Empty", "js_doc": "", "def": "", "original_name": "Empty"}
|
||||||
|
{"kind": "enum", "name": "CustomNumEnum", "js_doc": "/** You could break the step and for an new continuous value. */\n", "def": "One = 1,\n Two = 2,\n Three = 3,\n Four = 4,\n Six = 6,\n Eight = 8,\n Nine = 9,\n Ten = 10", "original_name": "CustomNumEnum"}
|
||||||
|
{"kind": "fn", "name": "enumToI32", "js_doc": "", "def": "export function enumToI32(e: CustomNumEnum): number"}
|
||||||
|
{"kind": "fn", "name": "throwError", "js_doc": "", "def": "export function throwError(): void"}
|
||||||
|
{"kind": "fn", "name": "panic", "js_doc": "", "def": "export function panic(): void"}
|
||||||
|
{"kind": "fn", "name": "receiveString", "js_doc": "", "def": "export function receiveString(s: string): string"}
|
||||||
|
{"kind": "fn", "name": "customStatusCode", "js_doc": "", "def": "export function customStatusCode(): void"}
|
||||||
|
{"kind": "fn", "name": "createExternal", "js_doc": "", "def": "export function createExternal(size: number): ExternalObject<number>"}
|
||||||
|
{"kind": "fn", "name": "createExternalString", "js_doc": "", "def": "export function createExternalString(content: string): ExternalObject<string>"}
|
||||||
|
{"kind": "fn", "name": "getExternal", "js_doc": "", "def": "export function getExternal(external: ExternalObject<number>): number"}
|
||||||
|
{"kind": "fn", "name": "mutateExternal", "js_doc": "", "def": "export function mutateExternal(external: ExternalObject<number>, newVal: number): void"}
|
||||||
|
{"kind": "fn", "name": "validateArray", "js_doc": "", "def": "export function validateArray(arr: Array<number>): number"}
|
||||||
|
{"kind": "fn", "name": "validateBuffer", "js_doc": "", "def": "export function validateBuffer(b: Buffer): number"}
|
||||||
|
{"kind": "fn", "name": "validateTypedArray", "js_doc": "", "def": "export function validateTypedArray(input: Uint8Array): number"}
|
||||||
|
{"kind": "fn", "name": "validateBigint", "js_doc": "", "def": "export function validateBigint(input: bigint): bigint"}
|
||||||
|
{"kind": "fn", "name": "validateBoolean", "js_doc": "", "def": "export function validateBoolean(i: boolean): boolean"}
|
||||||
|
{"kind": "fn", "name": "validateDate", "js_doc": "", "def": "export function validateDate(d: Date): number"}
|
||||||
|
{"kind": "fn", "name": "validateDateTime", "js_doc": "", "def": "export function validateDateTime(d: Date): number"}
|
||||||
|
{"kind": "fn", "name": "validateExternal", "js_doc": "", "def": "export function validateExternal(e: ExternalObject<number>): number"}
|
||||||
|
{"kind": "fn", "name": "validateFunction", "js_doc": "", "def": "export function validateFunction(cb: () => number): number"}
|
||||||
|
{"kind": "fn", "name": "validateHashMap", "js_doc": "", "def": "export function validateHashMap(input: Record<string, number>): number"}
|
||||||
|
{"kind": "fn", "name": "validateNull", "js_doc": "", "def": "export function validateNull(i: null): boolean"}
|
||||||
|
{"kind": "fn", "name": "validateUndefined", "js_doc": "", "def": "export function validateUndefined(i: undefined): boolean"}
|
||||||
|
{"kind": "fn", "name": "validateNumber", "js_doc": "", "def": "export function validateNumber(i: number): number"}
|
||||||
|
{"kind": "fn", "name": "validatePromise", "js_doc": "", "def": "export function validatePromise(p: Promise<number>): Promise<number>"}
|
||||||
|
{"kind": "fn", "name": "validateString", "js_doc": "", "def": "export function validateString(s: string): string"}
|
||||||
|
{"kind": "fn", "name": "validateSymbol", "js_doc": "", "def": "export function validateSymbol(s: symbol): boolean"}
|
||||||
|
{"kind": "fn", "name": "validateOptional", "js_doc": "", "def": "export function validateOptional(input1?: string | undefined | null, input2?: boolean | undefined | null): boolean"}
|
||||||
|
{"kind": "fn", "name": "returnUndefinedIfInvalid", "js_doc": "", "def": "export function returnUndefinedIfInvalid(input: boolean): boolean"}
|
||||||
|
{"kind": "fn", "name": "returnUndefinedIfInvalidPromise", "js_doc": "", "def": "export function returnUndefinedIfInvalidPromise(input: Promise<boolean>): Promise<boolean>"}
|
||||||
|
{"kind": "fn", "name": "tsRename", "js_doc": "", "def": "export function tsRename(a: { foo: number }): string[]"}
|
||||||
|
{"kind": "fn", "name": "overrideIndividualArgOnFunction", "js_doc": "", "def": "export function overrideIndividualArgOnFunction(notOverridden: string, f: () => string, notOverridden2: number): string"}
|
||||||
|
{"kind": "fn", "name": "overrideIndividualArgOnFunctionWithCbArg", "js_doc": "", "def": "export function overrideIndividualArgOnFunctionWithCbArg(callback: (town: string, name?: string | undefined | null) => string, notOverridden: number): object"}
|
||||||
|
{"kind": "struct", "name": "Fib", "js_doc": "", "def": "", "original_name": "Fib"}
|
||||||
|
{"kind": "impl", "name": "Fib", "js_doc": "", "def": "[Symbol.iterator](): Iterator<number, void, number>"}
|
||||||
|
{"kind": "impl", "name": "Fib", "js_doc": "", "def": " constructor()"}
|
||||||
|
{"kind": "struct", "name": "Fib2", "js_doc": "", "def": "", "original_name": "Fib2"}
|
||||||
|
{"kind": "impl", "name": "Fib2", "js_doc": "", "def": "[Symbol.iterator](): Iterator<number, void, number>"}
|
||||||
|
{"kind": "impl", "name": "Fib2", "js_doc": "", "def": "static create(seed: number): Fib2"}
|
||||||
|
{"kind": "struct", "name": "Fib3", "js_doc": "", "def": "current: number\nnext: number\nconstructor(current: number, next: number)", "original_name": "Fib3"}
|
||||||
|
{"kind": "impl", "name": "Fib3", "js_doc": "", "def": "[Symbol.iterator](): Iterator<number, void, number>"}
|
||||||
|
{"kind": "const", "name": "ALIGNMENT", "js_doc": "", "def": "export const ALIGNMENT: number", "original_name": "ALIGNMENT", "js_mod": "xxh3"}
|
||||||
|
{"kind": "fn", "name": "xxh3_64", "js_doc": "", "def": "export function xxh3_64(input: Buffer): bigint", "js_mod": "xxh3"}
|
||||||
|
{"kind": "fn", "name": "xxh128", "js_doc": "/** xxh128 function */\n", "def": "export function xxh128(input: Buffer): bigint", "js_mod": "xxh3"}
|
||||||
|
{"kind": "struct", "name": "Xxh3", "js_doc": "/** Xxh3 class */\n", "def": "", "original_name": "Xxh3", "js_mod": "xxh3"}
|
||||||
|
{"kind": "impl", "name": "Xxh3", "js_doc": "", "def": " constructor()\n/** update */\n update(input: Buffer): void\n digest(): bigint", "js_mod": "xxh3"}
|
||||||
|
{"kind": "fn", "name": "xxh2Plus", "js_doc": "", "def": "export function xxh2Plus(a: number, b: number): number", "js_mod": "xxh2"}
|
||||||
|
{"kind": "fn", "name": "xxh3Xxh64Alias", "js_doc": "", "def": "export function xxh3Xxh64Alias(input: Buffer): bigint", "js_mod": "xxh2"}
|
||||||
|
{"kind": "fn", "name": "xxh64Alias", "js_doc": "", "def": "export function xxh64Alias(input: Buffer): bigint"}
|
||||||
|
{"kind": "fn", "name": "getMapping", "js_doc": "", "def": "export function getMapping(): Record<string, number>"}
|
||||||
|
{"kind": "fn", "name": "sumMapping", "js_doc": "", "def": "export function sumMapping(nums: Record<string, number>): number"}
|
||||||
|
{"kind": "fn", "name": "mapOption", "js_doc": "", "def": "export function mapOption(val?: number | undefined | null): number | null"}
|
||||||
|
{"kind": "fn", "name": "returnNull", "js_doc": "", "def": "export function returnNull(): null"}
|
||||||
|
{"kind": "fn", "name": "returnUndefined", "js_doc": "", "def": "export function returnUndefined(): void"}
|
||||||
|
{"kind": "fn", "name": "add", "js_doc": "", "def": "export function add(a: number, b: number): number"}
|
||||||
|
{"kind": "fn", "name": "fibonacci", "js_doc": "", "def": "export function fibonacci(n: number): number"}
|
||||||
|
{"kind": "fn", "name": "listObjKeys", "js_doc": "", "def": "export function listObjKeys(obj: object): Array<string>"}
|
||||||
|
{"kind": "fn", "name": "createObj", "js_doc": "", "def": "export function createObj(): object"}
|
||||||
|
{"kind": "fn", "name": "getGlobal", "js_doc": "", "def": "export function getGlobal(): typeof global"}
|
||||||
|
{"kind": "fn", "name": "getUndefined", "js_doc": "", "def": "export function getUndefined(): void"}
|
||||||
|
{"kind": "fn", "name": "getNull", "js_doc": "", "def": "export function getNull(): null"}
|
||||||
|
{"kind": "interface", "name": "AllOptionalObject", "js_doc": "", "def": "name?: string\nage?: number", "original_name": "AllOptionalObject"}
|
||||||
|
{"kind": "fn", "name": "receiveAllOptionalObject", "js_doc": "", "def": "export function receiveAllOptionalObject(obj?: AllOptionalObject | undefined | null): void"}
|
||||||
|
{"kind": "enum", "name": "ALIAS", "js_doc": "", "def": "A = 0,\n B = 1", "original_name": "AliasedEnum"}
|
||||||
|
{"kind": "interface", "name": "AliasedStruct", "js_doc": "", "def": "a: ALIAS\nb: number", "original_name": "StructContainsAliasedEnum"}
|
||||||
|
{"kind": "fn", "name": "fnReceivedAliased", "js_doc": "", "def": "export function fnReceivedAliased(s: AliasedStruct, e: ALIAS): void"}
|
||||||
|
{"kind": "interface", "name": "StrictObject", "js_doc": "", "def": "name: string", "original_name": "StrictObject"}
|
||||||
|
{"kind": "fn", "name": "receiveStrictObject", "js_doc": "", "def": "export function receiveStrictObject(strictObject: StrictObject): void"}
|
||||||
|
{"kind": "fn", "name": "getStrFromObject", "js_doc": "", "def": "export function getStrFromObject(): void"}
|
||||||
|
{"kind": "interface", "name": "TsTypeChanged", "js_doc": "", "def": "typeOverride: object\ntypeOverrideOptional?: object", "original_name": "TsTypeChanged"}
|
||||||
|
{"kind": "fn", "name": "createObjWithProperty", "js_doc": "", "def": "export function createObjWithProperty(): { value: ArrayBuffer, get getter(): number }"}
|
||||||
|
{"kind": "fn", "name": "getterFromObj", "js_doc": "", "def": "export function getterFromObj(): number"}
|
||||||
|
{"kind": "interface", "name": "ObjectOnlyFromJs", "js_doc": "", "def": "count: number\ncallback: (err: Error | null, value: number) => any", "original_name": "ObjectOnlyFromJs"}
|
||||||
|
{"kind": "fn", "name": "receiveObjectOnlyFromJs", "js_doc": "", "def": "export function receiveObjectOnlyFromJs(obj: { count: number, callback: (err: Error | null, count: number) => void }): void"}
|
||||||
|
{"kind": "fn", "name": "asyncPlus100", "js_doc": "", "def": "export function asyncPlus100(p: Promise<number>): Promise<number>"}
|
||||||
|
{"kind": "struct", "name": "JsRepo", "js_doc": "", "def": "", "original_name": "JsRepo"}
|
||||||
|
{"kind": "impl", "name": "JsRepo", "js_doc": "", "def": " constructor(dir: string)\n remote(): JsRemote"}
|
||||||
|
{"kind": "struct", "name": "JsRemote", "js_doc": "", "def": "", "original_name": "JsRemote"}
|
||||||
|
{"kind": "impl", "name": "JsRemote", "js_doc": "", "def": " name(): string"}
|
||||||
|
{"kind": "struct", "name": "CssRuleList", "js_doc": "", "def": "", "original_name": "CSSRuleList"}
|
||||||
|
{"kind": "impl", "name": "CssRuleList", "js_doc": "", "def": " getRules(): Array<string>\nget parentStyleSheet(): CSSStyleSheet\nget name(): string | null"}
|
||||||
|
{"kind": "struct", "name": "CssStyleSheet", "js_doc": "", "def": "", "original_name": "CSSStyleSheet"}
|
||||||
|
{"kind": "struct", "name": "AnotherCssStyleSheet", "js_doc": "", "def": "", "original_name": "AnotherCSSStyleSheet"}
|
||||||
|
{"kind": "impl", "name": "AnotherCssStyleSheet", "js_doc": "", "def": "get rules(): CssRuleList"}
|
||||||
|
{"kind": "impl", "name": "CssStyleSheet", "js_doc": "", "def": " constructor(name: string, rules: Array<string>)\nget rules(): CssRuleList\n anotherCssStyleSheet(): AnotherCssStyleSheet"}
|
||||||
|
{"kind": "interface", "name": "PackageJson", "js_doc": "/** This is an interface for package.json */\n", "def": "name: string\n/** The version of the package */\nversion: string\ndependencies?: Record<string, any>\ndevDependencies?: Record<string, any>", "original_name": "PackageJson"}
|
||||||
|
{"kind": "fn", "name": "readPackageJson", "js_doc": "", "def": "export function readPackageJson(): PackageJson"}
|
||||||
|
{"kind": "fn", "name": "getPackageJsonName", "js_doc": "", "def": "export function getPackageJsonName(packageJson: PackageJson): string"}
|
||||||
|
{"kind": "fn", "name": "testSerdeRoundtrip", "js_doc": "", "def": "export function testSerdeRoundtrip(data: any): any"}
|
||||||
|
{"kind": "fn", "name": "contains", "js_doc": "", "def": "export function contains(source: string, target: string): boolean"}
|
||||||
|
{"kind": "fn", "name": "concatStr", "js_doc": "", "def": "export function concatStr(s: string): string"}
|
||||||
|
{"kind": "fn", "name": "concatUtf16", "js_doc": "", "def": "export function concatUtf16(s: string): string"}
|
||||||
|
{"kind": "fn", "name": "concatLatin1", "js_doc": "", "def": "export function concatLatin1(s: string): string"}
|
||||||
|
{"kind": "fn", "name": "roundtripStr", "js_doc": "", "def": "export function roundtripStr(s: string): string"}
|
||||||
|
{"kind": "fn", "name": "setSymbolInObj", "js_doc": "", "def": "export function setSymbolInObj(symbol: symbol): object"}
|
||||||
|
{"kind": "fn", "name": "createSymbol", "js_doc": "", "def": "export function createSymbol(): symbol"}
|
||||||
|
{"kind": "impl", "name": "DelaySum", "js_doc": "", "def": ""}
|
||||||
|
{"kind": "fn", "name": "withoutAbortController", "js_doc": "", "def": "export function withoutAbortController(a: number, b: number): Promise<number>"}
|
||||||
|
{"kind": "fn", "name": "withAbortController", "js_doc": "", "def": "export function withAbortController(a: number, b: number, signal: AbortSignal): Promise<number>"}
|
||||||
|
{"kind": "fn", "name": "callThreadsafeFunction", "js_doc": "", "def": "export function callThreadsafeFunction(callback: (...args: any[]) => any): void"}
|
||||||
|
{"kind": "fn", "name": "threadsafeFunctionThrowError", "js_doc": "", "def": "export function threadsafeFunctionThrowError(cb: (...args: any[]) => any): void"}
|
||||||
|
{"kind": "fn", "name": "threadsafeFunctionFatalMode", "js_doc": "", "def": "export function threadsafeFunctionFatalMode(cb: (...args: any[]) => any): void"}
|
||||||
|
{"kind": "fn", "name": "threadsafeFunctionFatalModeError", "js_doc": "", "def": "export function threadsafeFunctionFatalModeError(cb: (...args: any[]) => any): void"}
|
||||||
|
{"kind": "fn", "name": "threadsafeFunctionClosureCapture", "js_doc": "", "def": "export function threadsafeFunctionClosureCapture(func: (...args: any[]) => any): void"}
|
||||||
|
{"kind": "fn", "name": "tsfnCallWithCallback", "js_doc": "", "def": "export function tsfnCallWithCallback(func: (...args: any[]) => any): void"}
|
||||||
|
{"kind": "fn", "name": "tsfnAsyncCall", "js_doc": "", "def": "export function tsfnAsyncCall(func: (...args: any[]) => any): Promise<void>"}
|
||||||
|
{"kind": "fn", "name": "acceptThreadsafeFunction", "js_doc": "", "def": "export function acceptThreadsafeFunction(func: (err: Error | null, value: number) => any): void"}
|
||||||
|
{"kind": "fn", "name": "acceptThreadsafeFunctionFatal", "js_doc": "", "def": "export function acceptThreadsafeFunctionFatal(func: (value: number) => any): void"}
|
||||||
|
{"kind": "fn", "name": "acceptThreadsafeFunctionTupleArgs", "js_doc": "", "def": "export function acceptThreadsafeFunctionTupleArgs(func: (err: Error | null, arg0: number, arg1: boolean, arg2: string) => any): void"}
|
||||||
|
{"kind": "fn", "name": "getBuffer", "js_doc": "", "def": "export function getBuffer(): Buffer"}
|
||||||
|
{"kind": "fn", "name": "appendBuffer", "js_doc": "", "def": "export function appendBuffer(buf: Buffer): Buffer"}
|
||||||
|
{"kind": "fn", "name": "getEmptyBuffer", "js_doc": "", "def": "export function getEmptyBuffer(): Buffer"}
|
||||||
|
{"kind": "fn", "name": "convertU32Array", "js_doc": "", "def": "export function convertU32Array(input: Uint32Array): Array<number>"}
|
||||||
|
{"kind": "fn", "name": "createExternalTypedArray", "js_doc": "", "def": "export function createExternalTypedArray(): Uint32Array"}
|
||||||
|
{"kind": "fn", "name": "mutateTypedArray", "js_doc": "", "def": "export function mutateTypedArray(input: Float32Array): void"}
|
||||||
|
{"kind": "fn", "name": "derefUint8Array", "js_doc": "", "def": "export function derefUint8Array(a: Uint8Array, b: Uint8ClampedArray): number"}
|
||||||
|
{"kind": "fn", "name": "bufferPassThrough", "js_doc": "", "def": "export function bufferPassThrough(buf: Buffer): Promise<Buffer>"}
|
||||||
|
{"kind": "fn", "name": "arrayBufferPassThrough", "js_doc": "", "def": "export function arrayBufferPassThrough(buf: Uint8Array): Promise<Uint8Array>"}
|
||||||
|
{"kind": "impl", "name": "AsyncBuffer", "js_doc": "", "def": ""}
|
||||||
|
{"kind": "fn", "name": "asyncReduceBuffer", "js_doc": "", "def": "export function asyncReduceBuffer(buf: Buffer): Promise<number>"}
|
||||||
|
{"kind": "fn", "name": "runScript", "js_doc": "", "def": "export function runScript(script: string): unknown"}
|
110
cli/src/utils/__tests__/__snapshots__/target.spec.ts.md
Normal file
110
cli/src/utils/__tests__/__snapshots__/target.spec.ts.md
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
# Snapshot report for `src/utils/__tests__/target.spec.ts`
|
||||||
|
|
||||||
|
The actual snapshot is saved in `target.spec.ts.snap`.
|
||||||
|
|
||||||
|
Generated by [AVA](https://avajs.dev).
|
||||||
|
|
||||||
|
## should parse triple correctly
|
||||||
|
|
||||||
|
> Snapshot 1
|
||||||
|
|
||||||
|
[
|
||||||
|
{
|
||||||
|
abi: null,
|
||||||
|
arch: 'arm64',
|
||||||
|
platform: 'darwin',
|
||||||
|
platformArchABI: 'darwin-arm64',
|
||||||
|
triple: 'aarch64-apple-darwin',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
abi: null,
|
||||||
|
arch: 'arm64',
|
||||||
|
platform: 'android',
|
||||||
|
platformArchABI: 'android-arm64',
|
||||||
|
triple: 'aarch64-linux-android',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
abi: 'gnu',
|
||||||
|
arch: 'arm64',
|
||||||
|
platform: 'linux',
|
||||||
|
platformArchABI: 'linux-arm64-gnu',
|
||||||
|
triple: 'aarch64-unknown-linux-gnu',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
abi: 'musl',
|
||||||
|
arch: 'arm64',
|
||||||
|
platform: 'linux',
|
||||||
|
platformArchABI: 'linux-arm64-musl',
|
||||||
|
triple: 'aarch64-unknown-linux-musl',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
abi: 'msvc',
|
||||||
|
arch: 'arm64',
|
||||||
|
platform: 'win32',
|
||||||
|
platformArchABI: 'win32-arm64-msvc',
|
||||||
|
triple: 'aarch64-pc-windows-msvc',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
abi: null,
|
||||||
|
arch: 'x64',
|
||||||
|
platform: 'darwin',
|
||||||
|
platformArchABI: 'darwin-x64',
|
||||||
|
triple: 'x86_64-apple-darwin',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
abi: 'msvc',
|
||||||
|
arch: 'x64',
|
||||||
|
platform: 'win32',
|
||||||
|
platformArchABI: 'win32-x64-msvc',
|
||||||
|
triple: 'x86_64-pc-windows-msvc',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
abi: 'gnu',
|
||||||
|
arch: 'x64',
|
||||||
|
platform: 'linux',
|
||||||
|
platformArchABI: 'linux-x64-gnu',
|
||||||
|
triple: 'x86_64-unknown-linux-gnu',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
abi: 'musl',
|
||||||
|
arch: 'x64',
|
||||||
|
platform: 'linux',
|
||||||
|
platformArchABI: 'linux-x64-musl',
|
||||||
|
triple: 'x86_64-unknown-linux-musl',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
abi: null,
|
||||||
|
arch: 'x64',
|
||||||
|
platform: 'freebsd',
|
||||||
|
platformArchABI: 'freebsd-x64',
|
||||||
|
triple: 'x86_64-unknown-freebsd',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
abi: 'msvc',
|
||||||
|
arch: 'ia32',
|
||||||
|
platform: 'win32',
|
||||||
|
platformArchABI: 'win32-ia32-msvc',
|
||||||
|
triple: 'i686-pc-windows-msvc',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
abi: 'gnueabihf',
|
||||||
|
arch: 'arm',
|
||||||
|
platform: 'linux',
|
||||||
|
platformArchABI: 'linux-arm-gnueabihf',
|
||||||
|
triple: 'armv7-unknown-linux-gnueabihf',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
abi: 'eabi',
|
||||||
|
arch: 'arm',
|
||||||
|
platform: 'android',
|
||||||
|
platformArchABI: 'android-arm-eabi',
|
||||||
|
triple: 'armv7-linux-androideabi',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
abi: null,
|
||||||
|
arch: 'universal',
|
||||||
|
platform: 'darwin',
|
||||||
|
platformArchABI: 'darwin-universal',
|
||||||
|
triple: 'universal-apple-darwin',
|
||||||
|
},
|
||||||
|
]
|
BIN
cli/src/utils/__tests__/__snapshots__/target.spec.ts.snap
Normal file
BIN
cli/src/utils/__tests__/__snapshots__/target.spec.ts.snap
Normal file
Binary file not shown.
618
cli/src/utils/__tests__/__snapshots__/typegen.spec.ts.md
Normal file
618
cli/src/utils/__tests__/__snapshots__/typegen.spec.ts.md
Normal file
|
@ -0,0 +1,618 @@
|
||||||
|
# Snapshot report for `src/utils/__tests__/typegen.spec.ts`
|
||||||
|
|
||||||
|
The actual snapshot is saved in `typegen.spec.ts.snap`.
|
||||||
|
|
||||||
|
Generated by [AVA](https://avajs.dev).
|
||||||
|
|
||||||
|
## should ident string correctly
|
||||||
|
|
||||||
|
> original ident is 0
|
||||||
|
|
||||||
|
`␊
|
||||||
|
/**␊
|
||||||
|
* should keep␊
|
||||||
|
* class A {␊
|
||||||
|
* foo = () => {}␊
|
||||||
|
* bar = () => {}␊
|
||||||
|
* }␊
|
||||||
|
*/␊
|
||||||
|
class A {␊
|
||||||
|
foo() {␊
|
||||||
|
a = b␊
|
||||||
|
}␊
|
||||||
|
␊
|
||||||
|
bar = () => {␊
|
||||||
|
␊
|
||||||
|
}␊
|
||||||
|
boz = 1␊
|
||||||
|
}␊
|
||||||
|
␊
|
||||||
|
namespace B {␊
|
||||||
|
namespace C {␊
|
||||||
|
type D = A␊
|
||||||
|
}␊
|
||||||
|
}␊
|
||||||
|
`
|
||||||
|
|
||||||
|
> original ident is 2
|
||||||
|
|
||||||
|
`␊
|
||||||
|
/**␊
|
||||||
|
* should keep␊
|
||||||
|
* class A {␊
|
||||||
|
* foo = () => {}␊
|
||||||
|
* bar = () => {}␊
|
||||||
|
* }␊
|
||||||
|
*/␊
|
||||||
|
class A {␊
|
||||||
|
foo() {␊
|
||||||
|
a = b␊
|
||||||
|
}␊
|
||||||
|
␊
|
||||||
|
bar = () => {␊
|
||||||
|
␊
|
||||||
|
}␊
|
||||||
|
boz = 1␊
|
||||||
|
}␊
|
||||||
|
␊
|
||||||
|
namespace B {␊
|
||||||
|
namespace C {␊
|
||||||
|
type D = A␊
|
||||||
|
}␊
|
||||||
|
}␊
|
||||||
|
`
|
||||||
|
|
||||||
|
## should process type def correctly
|
||||||
|
|
||||||
|
> Snapshot 1
|
||||||
|
|
||||||
|
`␊
|
||||||
|
export class ExternalObject<T> {␊
|
||||||
|
readonly '': {␊
|
||||||
|
readonly '': unique symbol␊
|
||||||
|
[K: symbol]: T␊
|
||||||
|
}␊
|
||||||
|
}␊
|
||||||
|
/**␊
|
||||||
|
* \`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)␊
|
||||||
|
get type(): Kind␊
|
||||||
|
set type(kind: Kind)␊
|
||||||
|
/**␊
|
||||||
|
* This is a␊
|
||||||
|
* multi-line comment␊
|
||||||
|
* with an emoji 🚀␊
|
||||||
|
*/␊
|
||||||
|
whoami(): string␊
|
||||||
|
/** This is static... */␊
|
||||||
|
static getDogKind(): Kind␊
|
||||||
|
/**␊
|
||||||
|
* Here are some characters and character sequences␊
|
||||||
|
* that should be escaped correctly:␊
|
||||||
|
* \\[]{}/\\:""{␊
|
||||||
|
* }␊
|
||||||
|
*/␊
|
||||||
|
returnOtherClass(): Dog␊
|
||||||
|
returnOtherClassWithCustomConstructor(): Bird␊
|
||||||
|
overrideIndividualArgOnMethod(normalTy: string, overriddenTy: {n: string}): Bird␊
|
||||||
|
}␊
|
||||||
|
␊
|
||||||
|
export class AnimalWithDefaultConstructor {␊
|
||||||
|
name: string␊
|
||||||
|
kind: number␊
|
||||||
|
constructor(name: string, kind: number)␊
|
||||||
|
}␊
|
||||||
|
␊
|
||||||
|
export class AnotherClassForEither {␊
|
||||||
|
constructor()␊
|
||||||
|
}␊
|
||||||
|
␊
|
||||||
|
export class AnotherCssStyleSheet {␊
|
||||||
|
get rules(): CssRuleList␊
|
||||||
|
}␊
|
||||||
|
export type AnotherCSSStyleSheet = AnotherCssStyleSheet␊
|
||||||
|
␊
|
||||||
|
export class Asset {␊
|
||||||
|
constructor()␊
|
||||||
|
get filePath(): number␊
|
||||||
|
}␊
|
||||||
|
export type JsAsset = Asset␊
|
||||||
|
␊
|
||||||
|
export class Assets {␊
|
||||||
|
constructor()␊
|
||||||
|
get(id: number): JsAsset | null␊
|
||||||
|
}␊
|
||||||
|
export type JsAssets = Assets␊
|
||||||
|
␊
|
||||||
|
export class Bird {␊
|
||||||
|
name: string␊
|
||||||
|
constructor(name: string)␊
|
||||||
|
getCount(): number␊
|
||||||
|
getNameAsync(): Promise<string>␊
|
||||||
|
}␊
|
||||||
|
␊
|
||||||
|
/** Smoking test for type generation */␊
|
||||||
|
export class Blake2BHasher {␊
|
||||||
|
static withKey(key: Blake2bKey): Blake2BHasher␊
|
||||||
|
update(data: Buffer): void␊
|
||||||
|
}␊
|
||||||
|
export type Blake2bHasher = Blake2BHasher␊
|
||||||
|
␊
|
||||||
|
export class Blake2BKey {␊
|
||||||
|
␊
|
||||||
|
}␊
|
||||||
|
export type Blake2bKey = Blake2BKey␊
|
||||||
|
␊
|
||||||
|
export class ClassWithFactory {␊
|
||||||
|
name: string␊
|
||||||
|
static withName(name: string): ClassWithFactory␊
|
||||||
|
setName(name: string): this␊
|
||||||
|
}␊
|
||||||
|
␊
|
||||||
|
export class Context {␊
|
||||||
|
maybeNeed?: boolean␊
|
||||||
|
buffer: Uint8Array␊
|
||||||
|
constructor()␊
|
||||||
|
static withData(data: string): Context␊
|
||||||
|
static withBuffer(buf: Uint8Array): Context␊
|
||||||
|
method(): string␊
|
||||||
|
}␊
|
||||||
|
␊
|
||||||
|
export class CssRuleList {␊
|
||||||
|
getRules(): Array<string>␊
|
||||||
|
get parentStyleSheet(): CSSStyleSheet␊
|
||||||
|
get name(): string | null␊
|
||||||
|
}␊
|
||||||
|
export type CSSRuleList = CssRuleList␊
|
||||||
|
␊
|
||||||
|
export class CssStyleSheet {␊
|
||||||
|
constructor(name: string, rules: Array<string>)␊
|
||||||
|
get rules(): CssRuleList␊
|
||||||
|
anotherCssStyleSheet(): AnotherCssStyleSheet␊
|
||||||
|
}␊
|
||||||
|
export type CSSStyleSheet = CssStyleSheet␊
|
||||||
|
␊
|
||||||
|
export class CustomFinalize {␊
|
||||||
|
constructor(width: number, height: number)␊
|
||||||
|
}␊
|
||||||
|
␊
|
||||||
|
export class Dog {␊
|
||||||
|
name: string␊
|
||||||
|
constructor(name: string)␊
|
||||||
|
}␊
|
||||||
|
␊
|
||||||
|
export class Fib {␊
|
||||||
|
[Symbol.iterator](): Iterator<number, void, number>␊
|
||||||
|
constructor()␊
|
||||||
|
}␊
|
||||||
|
␊
|
||||||
|
export class Fib2 {␊
|
||||||
|
[Symbol.iterator](): Iterator<number, void, number>␊
|
||||||
|
static create(seed: number): Fib2␊
|
||||||
|
}␊
|
||||||
|
␊
|
||||||
|
export class Fib3 {␊
|
||||||
|
current: number␊
|
||||||
|
next: number␊
|
||||||
|
constructor(current: number, next: number)␊
|
||||||
|
[Symbol.iterator](): Iterator<number, void, number>␊
|
||||||
|
}␊
|
||||||
|
␊
|
||||||
|
export class JsClassForEither {␊
|
||||||
|
constructor()␊
|
||||||
|
}␊
|
||||||
|
␊
|
||||||
|
export class JsRemote {␊
|
||||||
|
name(): string␊
|
||||||
|
}␊
|
||||||
|
␊
|
||||||
|
export class JsRepo {␊
|
||||||
|
constructor(dir: string)␊
|
||||||
|
remote(): JsRemote␊
|
||||||
|
}␊
|
||||||
|
␊
|
||||||
|
export class NinjaTurtle {␊
|
||||||
|
name: string␊
|
||||||
|
static isInstanceOf(value: unknown): boolean␊
|
||||||
|
/** Create your ninja turtle! 🐢 */␊
|
||||||
|
static newRaph(): NinjaTurtle␊
|
||||||
|
getMaskColor(): string␊
|
||||||
|
getName(): string␊
|
||||||
|
returnThis(this: this): this␊
|
||||||
|
}␊
|
||||||
|
␊
|
||||||
|
export class NotWritableClass {␊
|
||||||
|
name: string␊
|
||||||
|
constructor(name: string)␊
|
||||||
|
setName(name: string): void␊
|
||||||
|
}␊
|
||||||
|
␊
|
||||||
|
export class Optional {␊
|
||||||
|
static optionEnd(required: string, optional?: string | undefined | null): string␊
|
||||||
|
static optionStart(optional: string | undefined | null, required: string): string␊
|
||||||
|
static optionStartEnd(optional1: string | undefined | null, required: string, optional2?: string | undefined | null): string␊
|
||||||
|
static optionOnly(optional?: string | undefined | null): string␊
|
||||||
|
}␊
|
||||||
|
␊
|
||||||
|
export class Width {␊
|
||||||
|
value: number␊
|
||||||
|
constructor(value: number)␊
|
||||||
|
}␊
|
||||||
|
␊
|
||||||
|
export interface A {␊
|
||||||
|
foo: number␊
|
||||||
|
}␊
|
||||||
|
␊
|
||||||
|
export function acceptThreadsafeFunction(func: (err: Error | null, value: number) => any): void␊
|
||||||
|
␊
|
||||||
|
export function acceptThreadsafeFunctionFatal(func: (value: number) => any): void␊
|
||||||
|
␊
|
||||||
|
export function acceptThreadsafeFunctionTupleArgs(func: (err: Error | null, arg0: number, arg1: boolean, arg2: string) => any): void␊
|
||||||
|
␊
|
||||||
|
export function add(a: number, b: number): number␊
|
||||||
|
␊
|
||||||
|
export const enum ALIAS {␊
|
||||||
|
A = 0,␊
|
||||||
|
B = 1␊
|
||||||
|
}␊
|
||||||
|
␊
|
||||||
|
export interface AliasedStruct {␊
|
||||||
|
a: ALIAS␊
|
||||||
|
b: number␊
|
||||||
|
}␊
|
||||||
|
␊
|
||||||
|
export interface AllOptionalObject {␊
|
||||||
|
name?: string␊
|
||||||
|
age?: number␊
|
||||||
|
}␊
|
||||||
|
␊
|
||||||
|
export function appendBuffer(buf: Buffer): Buffer␊
|
||||||
|
␊
|
||||||
|
export function arrayBufferPassThrough(buf: Uint8Array): Promise<Uint8Array>␊
|
||||||
|
␊
|
||||||
|
export function asyncMultiTwo(arg: number): Promise<number>␊
|
||||||
|
␊
|
||||||
|
export function asyncPlus100(p: Promise<number>): Promise<number>␊
|
||||||
|
␊
|
||||||
|
export function asyncReduceBuffer(buf: Buffer): Promise<number>␊
|
||||||
|
␊
|
||||||
|
export interface B {␊
|
||||||
|
bar: number␊
|
||||||
|
}␊
|
||||||
|
␊
|
||||||
|
export function bigintAdd(a: bigint, b: bigint): bigint␊
|
||||||
|
␊
|
||||||
|
export function bigintFromI128(): bigint␊
|
||||||
|
␊
|
||||||
|
export function bigintFromI64(): bigint␊
|
||||||
|
␊
|
||||||
|
export function bigintGetU64AsString(bi: bigint): string␊
|
||||||
|
␊
|
||||||
|
export function bufferPassThrough(buf: Buffer): Promise<Buffer>␊
|
||||||
|
␊
|
||||||
|
export interface C {␊
|
||||||
|
baz: number␊
|
||||||
|
}␊
|
||||||
|
␊
|
||||||
|
export function callbackReturnPromise<T>(functionInput: () => T | Promise<T>, callback: (err: Error | null, result: T) => void): T | Promise<T>␊
|
||||||
|
␊
|
||||||
|
export function callThreadsafeFunction(callback: (...args: any[]) => any): void␊
|
||||||
|
␊
|
||||||
|
export function captureErrorInCallback(cb1: () => void, cb2: (arg0: Error) => void): void␊
|
||||||
|
␊
|
||||||
|
export function chronoDateAdd1Minute(input: Date): Date␊
|
||||||
|
␊
|
||||||
|
export function chronoDateToMillis(input: Date): number␊
|
||||||
|
␊
|
||||||
|
export function concatLatin1(s: string): string␊
|
||||||
|
␊
|
||||||
|
export function concatStr(s: string): string␊
|
||||||
|
␊
|
||||||
|
export function concatUtf16(s: string): string␊
|
||||||
|
␊
|
||||||
|
export function contains(source: string, target: string): boolean␊
|
||||||
|
␊
|
||||||
|
export function convertU32Array(input: Uint32Array): Array<number>␊
|
||||||
|
␊
|
||||||
|
export function createBigInt(): bigint␊
|
||||||
|
␊
|
||||||
|
export function createBigIntI64(): bigint␊
|
||||||
|
␊
|
||||||
|
export function createExternal(size: number): ExternalObject<number>␊
|
||||||
|
␊
|
||||||
|
export function createExternalString(content: string): ExternalObject<string>␊
|
||||||
|
␊
|
||||||
|
export function createExternalTypedArray(): Uint32Array␊
|
||||||
|
␊
|
||||||
|
export function createObj(): object␊
|
||||||
|
␊
|
||||||
|
export function createObjectWithClassField(): ObjectFieldClassInstance␊
|
||||||
|
␊
|
||||||
|
export function createObjWithProperty(): { value: ArrayBuffer, get getter(): number }␊
|
||||||
|
␊
|
||||||
|
export function createSymbol(): symbol␊
|
||||||
|
␊
|
||||||
|
/** You could break the step and for an new continuous value. */␊
|
||||||
|
export const enum CustomNumEnum {␊
|
||||||
|
One = 1,␊
|
||||||
|
Two = 2,␊
|
||||||
|
Three = 3,␊
|
||||||
|
Four = 4,␊
|
||||||
|
Six = 6,␊
|
||||||
|
Eight = 8,␊
|
||||||
|
Nine = 9,␊
|
||||||
|
Ten = 10␊
|
||||||
|
}␊
|
||||||
|
␊
|
||||||
|
export function customStatusCode(): void␊
|
||||||
|
␊
|
||||||
|
export interface Dates {␊
|
||||||
|
start: Date␊
|
||||||
|
end?: Date␊
|
||||||
|
}␊
|
||||||
|
␊
|
||||||
|
export function dateToNumber(input: Date): number␊
|
||||||
|
␊
|
||||||
|
/** This is a const */␊
|
||||||
|
export const DEFAULT_COST: number␊
|
||||||
|
␊
|
||||||
|
export function derefUint8Array(a: Uint8Array, b: Uint8ClampedArray): number␊
|
||||||
|
␊
|
||||||
|
export function either3(input: string | number | boolean): number␊
|
||||||
|
␊
|
||||||
|
export function either4(input: string | number | boolean | Obj): number␊
|
||||||
|
␊
|
||||||
|
export function eitherBoolOrFunction(input: boolean | ((...args: any[]) => any)): void␊
|
||||||
|
␊
|
||||||
|
export function eitherFromObjects(input: A | B | C): string␊
|
||||||
|
␊
|
||||||
|
export function eitherFromOption(): JsClassForEither | undefined␊
|
||||||
|
␊
|
||||||
|
export function eitherStringOrNumber(input: string | number): number␊
|
||||||
|
␊
|
||||||
|
export const enum Empty {␊
|
||||||
|
␊
|
||||||
|
}␊
|
||||||
|
␊
|
||||||
|
export function enumToI32(e: CustomNumEnum): number␊
|
||||||
|
␊
|
||||||
|
export function fibonacci(n: number): number␊
|
||||||
|
␊
|
||||||
|
export function fnReceivedAliased(s: AliasedStruct, e: ALIAS): void␊
|
||||||
|
␊
|
||||||
|
export function getBuffer(): Buffer␊
|
||||||
|
␊
|
||||||
|
export function getCwd(callback: (arg0: string) => void): void␊
|
||||||
|
␊
|
||||||
|
export function getEmptyBuffer(): Buffer␊
|
||||||
|
␊
|
||||||
|
export function getExternal(external: ExternalObject<number>): number␊
|
||||||
|
␊
|
||||||
|
export function getGlobal(): typeof global␊
|
||||||
|
␊
|
||||||
|
export function getMapping(): Record<string, number>␊
|
||||||
|
␊
|
||||||
|
export function getNestedNumArr(): number[][][]␊
|
||||||
|
␊
|
||||||
|
export function getNull(): null␊
|
||||||
|
␊
|
||||||
|
export function getNumArr(): number[]␊
|
||||||
|
␊
|
||||||
|
/** Gets some numbers */␊
|
||||||
|
export function getNums(): Array<number>␊
|
||||||
|
␊
|
||||||
|
export function getPackageJsonName(packageJson: PackageJson): string␊
|
||||||
|
␊
|
||||||
|
export function getStrFromObject(): void␊
|
||||||
|
␊
|
||||||
|
export function getterFromObj(): number␊
|
||||||
|
␊
|
||||||
|
export function getUndefined(): void␊
|
||||||
|
␊
|
||||||
|
export function getWords(): Array<string>␊
|
||||||
|
␊
|
||||||
|
/** default enum values are continuos i32s start from 0 */␊
|
||||||
|
export const enum Kind {␊
|
||||||
|
/** Barks */␊
|
||||||
|
Dog = 0,␊
|
||||||
|
/** Kills birds */␊
|
||||||
|
Cat = 1,␊
|
||||||
|
/** Tasty */␊
|
||||||
|
Duck = 2␊
|
||||||
|
}␊
|
||||||
|
␊
|
||||||
|
export function listObjKeys(obj: object): Array<string>␊
|
||||||
|
␊
|
||||||
|
export function mapOption(val?: number | undefined | null): number | null␊
|
||||||
|
␊
|
||||||
|
export function mutateExternal(external: ExternalObject<number>, newVal: number): void␊
|
||||||
|
␊
|
||||||
|
export function mutateTypedArray(input: Float32Array): void␊
|
||||||
|
␊
|
||||||
|
export interface Obj {␊
|
||||||
|
v: string | number␊
|
||||||
|
}␊
|
||||||
|
␊
|
||||||
|
export interface ObjectFieldClassInstance {␊
|
||||||
|
bird: Bird␊
|
||||||
|
}␊
|
||||||
|
␊
|
||||||
|
export interface ObjectOnlyFromJs {␊
|
||||||
|
count: number␊
|
||||||
|
callback: (err: Error | null, value: number) => any␊
|
||||||
|
}␊
|
||||||
|
␊
|
||||||
|
export function optionEnd(callback: (arg0: string, arg1?: string | undefined | null) => void): void␊
|
||||||
|
␊
|
||||||
|
export function optionOnly(callback: (arg0?: string | undefined | null) => void): void␊
|
||||||
|
␊
|
||||||
|
export function optionStart(callback: (arg0: string | undefined | null, arg1: string) => void): void␊
|
||||||
|
␊
|
||||||
|
export function optionStartEnd(callback: (arg0: string | undefined | null, arg1: string, arg2?: string | undefined | null) => void): void␊
|
||||||
|
␊
|
||||||
|
export function overrideIndividualArgOnFunction(notOverridden: string, f: () => string, notOverridden2: number): string␊
|
||||||
|
␊
|
||||||
|
export function overrideIndividualArgOnFunctionWithCbArg(callback: (town: string, name?: string | undefined | null) => string, notOverridden: number): object␊
|
||||||
|
␊
|
||||||
|
/** This is an interface for package.json */␊
|
||||||
|
export interface PackageJson {␊
|
||||||
|
name: string␊
|
||||||
|
/** The version of the package */␊
|
||||||
|
version: string␊
|
||||||
|
dependencies?: Record<string, any>␊
|
||||||
|
devDependencies?: Record<string, any>␊
|
||||||
|
}␊
|
||||||
|
␊
|
||||||
|
export function panic(): void␊
|
||||||
|
␊
|
||||||
|
export function plusOne(this: Width): number␊
|
||||||
|
␊
|
||||||
|
export function promiseInEither(input: number | Promise<number>): Promise<boolean>␊
|
||||||
|
␊
|
||||||
|
/** napi = { version = 2, features = ["serde-json"] } */␊
|
||||||
|
export function readFile(callback: (arg0: Error | undefined, arg1?: string | undefined | null) => void): void␊
|
||||||
|
␊
|
||||||
|
export function readFileAsync(path: string): Promise<Buffer>␊
|
||||||
|
␊
|
||||||
|
export function readPackageJson(): PackageJson␊
|
||||||
|
␊
|
||||||
|
export function receiveAllOptionalObject(obj?: AllOptionalObject | undefined | null): void␊
|
||||||
|
␊
|
||||||
|
export function receiveClassOrNumber(either: number | JsClassForEither): number␊
|
||||||
|
␊
|
||||||
|
export function receiveDifferentClass(either: JsClassForEither | AnotherClassForEither): number␊
|
||||||
|
␊
|
||||||
|
export function receiveMutClassOrNumber(either: number | JsClassForEither): number␊
|
||||||
|
␊
|
||||||
|
export function receiveObjectOnlyFromJs(obj: { count: number, callback: (err: Error | null, count: number) => void }): void␊
|
||||||
|
␊
|
||||||
|
export function receiveObjectWithClassField(object: ObjectFieldClassInstance): Bird␊
|
||||||
|
␊
|
||||||
|
export function receiveStrictObject(strictObject: StrictObject): void␊
|
||||||
|
␊
|
||||||
|
export function receiveString(s: string): string␊
|
||||||
|
␊
|
||||||
|
export function returnEither(input: number): string | number␊
|
||||||
|
␊
|
||||||
|
export function returnEitherClass(input: number): number | JsClassForEither␊
|
||||||
|
␊
|
||||||
|
export function returnJsFunction(): (...args: any[]) => any␊
|
||||||
|
␊
|
||||||
|
export function returnNull(): null␊
|
||||||
|
␊
|
||||||
|
export function returnUndefined(): void␊
|
||||||
|
␊
|
||||||
|
export function returnUndefinedIfInvalid(input: boolean): boolean␊
|
||||||
|
␊
|
||||||
|
export function returnUndefinedIfInvalidPromise(input: Promise<boolean>): Promise<boolean>␊
|
||||||
|
␊
|
||||||
|
export function roundtripStr(s: string): string␊
|
||||||
|
␊
|
||||||
|
export function runScript(script: string): unknown␊
|
||||||
|
␊
|
||||||
|
export function setSymbolInObj(symbol: symbol): object␊
|
||||||
|
␊
|
||||||
|
export interface StrictObject {␊
|
||||||
|
name: string␊
|
||||||
|
}␊
|
||||||
|
␊
|
||||||
|
export function sumMapping(nums: Record<string, number>): number␊
|
||||||
|
␊
|
||||||
|
export function sumNums(nums: Array<number>): number␊
|
||||||
|
␊
|
||||||
|
export function testSerdeRoundtrip(data: any): any␊
|
||||||
|
␊
|
||||||
|
export function threadsafeFunctionClosureCapture(func: (...args: any[]) => any): void␊
|
||||||
|
␊
|
||||||
|
export function threadsafeFunctionFatalMode(cb: (...args: any[]) => any): void␊
|
||||||
|
␊
|
||||||
|
export function threadsafeFunctionFatalModeError(cb: (...args: any[]) => any): void␊
|
||||||
|
␊
|
||||||
|
export function threadsafeFunctionThrowError(cb: (...args: any[]) => any): void␊
|
||||||
|
␊
|
||||||
|
export function throwError(): void␊
|
||||||
|
␊
|
||||||
|
export function toJsObj(): object␊
|
||||||
|
␊
|
||||||
|
export function tsfnAsyncCall(func: (...args: any[]) => any): Promise<void>␊
|
||||||
|
␊
|
||||||
|
export function tsfnCallWithCallback(func: (...args: any[]) => any): void␊
|
||||||
|
␊
|
||||||
|
export function tsRename(a: { foo: number }): string[]␊
|
||||||
|
␊
|
||||||
|
export interface TsTypeChanged {␊
|
||||||
|
typeOverride: object␊
|
||||||
|
typeOverrideOptional?: object␊
|
||||||
|
}␊
|
||||||
|
␊
|
||||||
|
export function validateArray(arr: Array<number>): number␊
|
||||||
|
␊
|
||||||
|
export function validateBigint(input: bigint): bigint␊
|
||||||
|
␊
|
||||||
|
export function validateBoolean(i: boolean): boolean␊
|
||||||
|
␊
|
||||||
|
export function validateBuffer(b: Buffer): number␊
|
||||||
|
␊
|
||||||
|
export function validateDate(d: Date): number␊
|
||||||
|
␊
|
||||||
|
export function validateDateTime(d: Date): number␊
|
||||||
|
␊
|
||||||
|
export function validateExternal(e: ExternalObject<number>): number␊
|
||||||
|
␊
|
||||||
|
export function validateFunction(cb: () => number): number␊
|
||||||
|
␊
|
||||||
|
export function validateHashMap(input: Record<string, number>): number␊
|
||||||
|
␊
|
||||||
|
export function validateNull(i: null): boolean␊
|
||||||
|
␊
|
||||||
|
export function validateNumber(i: number): number␊
|
||||||
|
␊
|
||||||
|
export function validateOptional(input1?: string | undefined | null, input2?: boolean | undefined | null): boolean␊
|
||||||
|
␊
|
||||||
|
export function validatePromise(p: Promise<number>): Promise<number>␊
|
||||||
|
␊
|
||||||
|
export function validateString(s: string): string␊
|
||||||
|
␊
|
||||||
|
export function validateSymbol(s: symbol): boolean␊
|
||||||
|
␊
|
||||||
|
export function validateTypedArray(input: Uint8Array): number␊
|
||||||
|
␊
|
||||||
|
export function validateUndefined(i: undefined): boolean␊
|
||||||
|
␊
|
||||||
|
export function withAbortController(a: number, b: number, signal: AbortSignal): Promise<number>␊
|
||||||
|
␊
|
||||||
|
export function withoutAbortController(a: number, b: number): Promise<number>␊
|
||||||
|
␊
|
||||||
|
export function xxh64Alias(input: Buffer): bigint␊
|
||||||
|
␊
|
||||||
|
export namespace xxh2 {␊
|
||||||
|
export function xxh2Plus(a: number, b: number): number␊
|
||||||
|
export function xxh3Xxh64Alias(input: Buffer): bigint␊
|
||||||
|
}␊
|
||||||
|
␊
|
||||||
|
export namespace xxh3 {␊
|
||||||
|
/** Xxh3 class */␊
|
||||||
|
export class Xxh3 {␊
|
||||||
|
constructor()␊
|
||||||
|
/** update */␊
|
||||||
|
update(input: Buffer): void␊
|
||||||
|
digest(): bigint␊
|
||||||
|
}␊
|
||||||
|
export const ALIGNMENT: number␊
|
||||||
|
/** xxh128 function */␊
|
||||||
|
export function xxh128(input: Buffer): bigint␊
|
||||||
|
export function xxh3_64(input: Buffer): bigint␊
|
||||||
|
}␊
|
||||||
|
␊
|
||||||
|
`
|
BIN
cli/src/utils/__tests__/__snapshots__/typegen.spec.ts.snap
Normal file
BIN
cli/src/utils/__tests__/__snapshots__/typegen.spec.ts.snap
Normal file
Binary file not shown.
20
cli/src/utils/__tests__/__snapshots__/version.spec.ts.md
Normal file
20
cli/src/utils/__tests__/__snapshots__/version.spec.ts.md
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
# Snapshot report for `src/utils/__tests__/version.spec.ts`
|
||||||
|
|
||||||
|
The actual snapshot is saved in `version.spec.ts.snap`.
|
||||||
|
|
||||||
|
Generated by [AVA](https://avajs.dev).
|
||||||
|
|
||||||
|
## should generate correct napi engine requirement
|
||||||
|
|
||||||
|
> Snapshot 1
|
||||||
|
|
||||||
|
[
|
||||||
|
'>= 8.6.0',
|
||||||
|
'>= 8.10.0 && < 9 || >= 9.3.0',
|
||||||
|
'>= 6.14.2 && < 7 || >= 8.11.2 && < 9 || >= 9.11.0',
|
||||||
|
'>= 10.16.0 && < 11 || >= 11.8.0',
|
||||||
|
'>= 10.17.0 && < 11 || >= 12.11.0',
|
||||||
|
'>= 10.20.0 && < 11 || >= 12.17.0 && < 13 || >= 14.0.0',
|
||||||
|
'>= 10.23.0 && < 11 || >= 12.19.0 && < 13 || >= 14.12.0',
|
||||||
|
'>= 12.22.0 && < 13 || >= 14.17.0 && < 15 || >= 15.12.0',
|
||||||
|
]
|
BIN
cli/src/utils/__tests__/__snapshots__/version.spec.ts.snap
Normal file
BIN
cli/src/utils/__tests__/__snapshots__/version.spec.ts.snap
Normal file
Binary file not shown.
19
cli/src/utils/__tests__/target.spec.ts
Normal file
19
cli/src/utils/__tests__/target.spec.ts
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import os from 'os'
|
||||||
|
|
||||||
|
import test from 'ava'
|
||||||
|
|
||||||
|
import {
|
||||||
|
parseTriple,
|
||||||
|
getSystemDefaultTarget,
|
||||||
|
AVAILABLE_TARGETS,
|
||||||
|
} from '../target.js'
|
||||||
|
|
||||||
|
test('should parse triple correctly', (t) => {
|
||||||
|
t.snapshot(AVAILABLE_TARGETS.map(parseTriple))
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should get system default target correctly', (t) => {
|
||||||
|
const target = getSystemDefaultTarget()
|
||||||
|
|
||||||
|
t.is(target.platform, os.platform())
|
||||||
|
})
|
49
cli/src/utils/__tests__/typegen.spec.ts
Normal file
49
cli/src/utils/__tests__/typegen.spec.ts
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
import { join } from 'path'
|
||||||
|
import { fileURLToPath } from 'url'
|
||||||
|
|
||||||
|
import test from 'ava'
|
||||||
|
|
||||||
|
import { correctStringIdent, processTypeDef } from '../typegen.js'
|
||||||
|
|
||||||
|
test('should ident string correctly', (t) => {
|
||||||
|
const input = `
|
||||||
|
/**
|
||||||
|
* should keep
|
||||||
|
* class A {
|
||||||
|
* foo = () => {}
|
||||||
|
* bar = () => {}
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
class A {
|
||||||
|
foo() {
|
||||||
|
a = b
|
||||||
|
}
|
||||||
|
|
||||||
|
bar = () => {
|
||||||
|
|
||||||
|
}
|
||||||
|
boz = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace B {
|
||||||
|
namespace C {
|
||||||
|
type D = A
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
t.snapshot(correctStringIdent(input, 0), 'original ident is 0')
|
||||||
|
t.snapshot(correctStringIdent(input, 2), 'original ident is 2')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should process type def correctly', async (t) => {
|
||||||
|
const dts = await processTypeDef(
|
||||||
|
join(
|
||||||
|
fileURLToPath(import.meta.url),
|
||||||
|
'../',
|
||||||
|
'__fixtures__',
|
||||||
|
'napi_type_def',
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
t.snapshot(dts)
|
||||||
|
})
|
13
cli/src/utils/__tests__/version.spec.ts
Normal file
13
cli/src/utils/__tests__/version.spec.ts
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import test from 'ava'
|
||||||
|
|
||||||
|
import { napiEngineRequirement, NapiVersion } from '../version.js'
|
||||||
|
|
||||||
|
test('should generate correct napi engine requirement', (t) => {
|
||||||
|
t.snapshot(
|
||||||
|
(
|
||||||
|
Object.values(NapiVersion).filter(
|
||||||
|
(v) => typeof v === 'number',
|
||||||
|
) as NapiVersion[]
|
||||||
|
).map(napiEngineRequirement),
|
||||||
|
)
|
||||||
|
})
|
35
cli/src/utils/cargo.ts
Normal file
35
cli/src/utils/cargo.ts
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
import { execSync } from 'child_process'
|
||||||
|
|
||||||
|
import { debug } from './log.js'
|
||||||
|
|
||||||
|
export function tryInstallCargoBinary(name: string, bin: string) {
|
||||||
|
if (detectCargoBinary(bin)) {
|
||||||
|
debug('Cargo binary already installed: %s', name)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
debug('Installing cargo binary: %s', name)
|
||||||
|
execSync(`cargo install ${name}`, {
|
||||||
|
stdio: 'inherit',
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error(`Failed to install cargo binary: ${name}`, {
|
||||||
|
cause: e,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function detectCargoBinary(bin: string) {
|
||||||
|
debug('Detecting cargo binary: %s', bin)
|
||||||
|
try {
|
||||||
|
execSync(`cargo help ${bin}`, {
|
||||||
|
stdio: 'ignore',
|
||||||
|
})
|
||||||
|
debug('Cargo binary detected: %s', bin)
|
||||||
|
return true
|
||||||
|
} catch (e) {
|
||||||
|
debug('Cargo binary not detected: %s', bin)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue