Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 23 additions & 16 deletions .github/workflows/lint-format.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,21 +25,22 @@ jobs:
with:
node-version: 22

- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10.24.0

- name: Install workspace dependencies
run: pnpm install --frozen-lockfile

- name: Validate prek config
run: npm exec --yes --package @j178/prek -- prek validate-config prek.toml
run: pnpm exec prek validate-config prek.toml

- name: Check Zig syntax
run: |
set -euo pipefail
while IFS= read -r -d '' file; do
zig ast-check "$file"
done < <(git ls-files -z '*.zig')
while IFS= read -r -d '' file; do
zig ast-check --zon "$file"
done < <(git ls-files -z '*.zon')
run: pnpm run check:zig

- name: Build root package
run: zig build --summary all
run: pnpm run build

format:
runs-on: ubuntu-latest
Expand All @@ -59,10 +60,16 @@ jobs:
with:
node-version: 22

- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10.24.0

- name: Install workspace dependencies
run: pnpm install --frozen-lockfile

- name: Format
run: pnpm run format

- name: Check formatting
run: |
set -euo pipefail
git ls-files -z '*.zig' '*.zon' | xargs -0 zig fmt
git ls-files -z '*.js' '*.jsx' '*.ts' '*.tsx' '*.ets' \
| xargs -0 npm exec --yes --package @ohos-rs/oxk -- oxk format
git diff --exit-code
run: git diff --exit-code
46 changes: 31 additions & 15 deletions .github/workflows/node-addon.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,26 +39,42 @@ jobs:
- name: Verify Zig
run: zig version

- name: Setup Node.js
- name: Setup Node.js for install
uses: actions/setup-node@v4
with:
node-version: '20'

- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10.24.0

- name: Install node-test dependencies
run: pnpm install --filter zig-napi-node-test --frozen-lockfile

- name: Setup Node.js for test
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}

- name: Build and test Node addon matrix
- name: Build Node addon matrix
if: runner.os != 'Windows'
run: |
cd node-test
npm install --no-package-lock
zig build --summary all
find . -maxdepth 2 -name '*.node' -print
npm test
working-directory: node-test
run: npm run build:test

- name: Test Node addon matrix
if: runner.os != 'Windows'
working-directory: node-test
run: npm run test:run

- name: Build Windows Node addon matrix
if: runner.os == 'Windows'
shell: pwsh
working-directory: node-test
run: npm run build:test:windows

- name: Build and test Node addon matrix
- name: Test Windows Node addon matrix
if: runner.os == 'Windows'
shell: pwsh
run: |
cd node-test
npm install --no-package-lock
zig build -Dtarget=x86_64-windows-msvc --summary all
Get-ChildItem -Recurse -Filter *.node | ForEach-Object { $_.FullName }
npm test
working-directory: node-test
run: npm run test:run
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ zig-out
node_modules
website/.void
website/dist
examples/node/*.node
examples/node/npm/*/*.node
node-test/*.node
node-test/*.wasm
.tmp_arkvm_runner
.tmp_arkvm_memory_runner
10 changes: 10 additions & 0 deletions .oxlintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"$schema": "./node_modules/oxlint/configuration_schema.json",
"ignorePatterns": [
"node_modules/**",
"website/dist/**",
"website/.void/**",
"**/*.d.ts",
"examples/node/npm/**"
]
}
45 changes: 40 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,12 +142,47 @@ The Node.js example is in `examples/node`:

```bash
cd examples/node
zig build
node test.js
pnpm build
pnpm test
```

It installs the addon as `zig-out/node/hello.<platform-arch-abi>.node`, for example `hello.darwin-arm64.node`, `hello.linux-x64-gnu.node`, or `hello.win32-x64-msvc.node`.

The package also provides a `zig-napi` CLI for Node.js addons. Zig-specific commands such as `new` and `build` are implemented by this project. Packaging commands reuse the community `@napi-rs/cli` API for npm package directory creation, artifact collection, and pre-publish processing.

The CLI requires Node.js 20.17 or newer.

Create a new Node addon project:

```bash
pnpm install
pnpm --filter zig-napi cli new ../../my-addon
cd my-addon
pnpm install
pnpm build
pnpm test
```

`zig-napi new` asks for the package name, native addon binary name, and target platforms interactively by default, matching napi-rs' `new` workflow. For scripted usage, pass `--no-interactive` with explicit options:

```bash
pnpm --filter zig-napi cli new ../../my-addon --no-interactive --name my-addon --addon my_addon --targets x86_64-unknown-linux-gnu
```

Pass `--targets <triple>` repeatedly or as a comma-separated list to choose the generated package targets manually, or pass `--enable-all-targets` to enable every napi-rs target known to the CLI.

Run the bundled Node example:

```bash
pnpm install
pnpm run node-example:package
pnpm --filter zig-napi-node-example run test
```

`zig-napi create-npm-dirs` calls `@napi-rs/cli`'s `createNpmDirs` API and creates `npm/<platform-arch-abi>` packages from the `napi` field in `package.json`. `zig-napi artifacts --output-dir zig-out/node` calls the community `artifacts` API and copies Zig's `<binary>.<platform-arch-abi>.node` outputs into those packages and into the root package. `zig-napi pre-publish` calls the community `prePublish` API to update optional dependencies and handle publish preparation.

Upstream `napi build` and `napi new` are not used directly for Zig addons because they currently expect Cargo projects and napi-rs' Rust templates.

Node.js matrix tests live in `node-test`. It mirrors the NAPI-RS example split with two independent demos:

- `node-test/napi-compat-mode` covers compat-mode style APIs and runtime-gated N-API v4/v5/v6/v7/v8 scenarios.
Expand All @@ -161,9 +196,9 @@ The documentation website lives in `website` and builds as a standalone Vite sit

```bash
cd website
npm install
npm run dev
npm run build
pnpm install
pnpm dev
pnpm build
```

## Credits
Expand Down
2 changes: 1 addition & 1 deletion docs/napi-conversion-refactor-plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ Goal: make failed conversion impossible to accidentally use by changing conversi
- Run:
- `zig build --summary failures`
- `zig build --summary failures` in `node-test`
- `just test-node-matrix`
- `pnpm run test:node-matrix`
- `git diff --check`

### Estimate
Expand Down
11 changes: 11 additions & 0 deletions examples/node/build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,15 @@ pub fn build(b: *std.Build) !void {
},
});
_ = addon;

const dts = try napi_build.generateTypeDefinition(b, .{
.root_source_file = b.path("src/hello.zig"),
.output = b.path("index.d.ts"),
.napi_module = napi,
.node_api = .{
.version = .v8,
.experimental = false,
},
});
b.getInstallStep().dependOn(&dts.step);
}
2 changes: 1 addition & 1 deletion examples/node/build.zig.zon
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@
.minimum_zig_version = "0.16.0",
.fingerprint = 0xc8e4deed2741664f,
.dependencies = .{ .@"zig-napi" = .{ .path = "../.." } },
.paths = .{ "build.zig", "build.zig.zon", "src", "test.js" },
.paths = .{ "build.zig", "build.zig.zon", "src", "index.js", "test.js", "package.json" },
}
5 changes: 5 additions & 0 deletions examples/node/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/* auto-generated by zig-addon */
/* eslint-disable */
export declare function add(left: number, right: number): number;
export declare function hello(): string;
export declare function requestedNapiVersion(): number;
52 changes: 52 additions & 0 deletions examples/node/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
const fs = require("fs");
const path = require("path");

const packageName = "zig-napi-node-example";
const binaryName = "hello";

function loadAddon() {
const platformArchABI = detectPlatformArchABI();
const optionalPackage = `${packageName}-${platformArchABI}`;
const candidates = [
() => require(optionalPackage),
() => require(path.join(__dirname, `${binaryName}.${platformArchABI}.node`)),
() => require(path.join(__dirname, "zig-out", "node", `${binaryName}.${platformArchABI}.node`)),
];

const errors = [];
for (const candidate of candidates) {
try {
return candidate();
} catch (error) {
errors.push(error);
}
}

throw new Error(
`Unable to load ${binaryName}.${platformArchABI}.node\n` +
errors.map((error) => `- ${error.message}`).join("\n"),
);
}

function detectPlatformArchABI() {
if (process.platform === "linux") {
return `${process.platform}-${process.arch}-${isMusl() ? "musl" : "gnu"}`;
}
if (process.platform === "win32") {
return `${process.platform}-${process.arch}-msvc`;
}
return `${process.platform}-${process.arch}`;
}

function isMusl() {
if (process.report && typeof process.report.getReport === "function") {
return !process.report.getReport().header.glibcVersionRuntime;
}
try {
return fs.readFileSync("/usr/bin/ldd", "utf8").includes("musl");
} catch {
return false;
}
}

module.exports = loadAddon();
3 changes: 3 additions & 0 deletions examples/node/npm/darwin-arm64/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# `zig-napi-node-example-darwin-arm64`

This is the **aarch64-apple-darwin** binary for `zig-napi-node-example`
16 changes: 16 additions & 0 deletions examples/node/npm/darwin-arm64/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "zig-napi-node-example-darwin-arm64",
"version": "0.1.0",
"cpu": [
"arm64"
],
"main": "hello.darwin-arm64.node",
"files": [
"hello.darwin-arm64.node"
],
"description": "Node.js addon example built with zig-napi.",
"license": "MIT",
"os": [
"darwin"
]
}
3 changes: 3 additions & 0 deletions examples/node/npm/darwin-x64/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# `zig-napi-node-example-darwin-x64`

This is the **x86_64-apple-darwin** binary for `zig-napi-node-example`
16 changes: 16 additions & 0 deletions examples/node/npm/darwin-x64/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "zig-napi-node-example-darwin-x64",
"version": "0.1.0",
"cpu": [
"x64"
],
"main": "hello.darwin-x64.node",
"files": [
"hello.darwin-x64.node"
],
"description": "Node.js addon example built with zig-napi.",
"license": "MIT",
"os": [
"darwin"
]
}
3 changes: 3 additions & 0 deletions examples/node/npm/linux-arm64-gnu/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# `zig-napi-node-example-linux-arm64-gnu`

This is the **aarch64-unknown-linux-gnu** binary for `zig-napi-node-example`
19 changes: 19 additions & 0 deletions examples/node/npm/linux-arm64-gnu/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"name": "zig-napi-node-example-linux-arm64-gnu",
"version": "0.1.0",
"cpu": [
"arm64"
],
"main": "hello.linux-arm64-gnu.node",
"files": [
"hello.linux-arm64-gnu.node"
],
"description": "Node.js addon example built with zig-napi.",
"license": "MIT",
"os": [
"linux"
],
"libc": [
"glibc"
]
}
3 changes: 3 additions & 0 deletions examples/node/npm/linux-arm64-musl/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# `zig-napi-node-example-linux-arm64-musl`

This is the **aarch64-unknown-linux-musl** binary for `zig-napi-node-example`
19 changes: 19 additions & 0 deletions examples/node/npm/linux-arm64-musl/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"name": "zig-napi-node-example-linux-arm64-musl",
"version": "0.1.0",
"cpu": [
"arm64"
],
"main": "hello.linux-arm64-musl.node",
"files": [
"hello.linux-arm64-musl.node"
],
"description": "Node.js addon example built with zig-napi.",
"license": "MIT",
"os": [
"linux"
],
"libc": [
"musl"
]
}
3 changes: 3 additions & 0 deletions examples/node/npm/linux-x64-gnu/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# `zig-napi-node-example-linux-x64-gnu`

This is the **x86_64-unknown-linux-gnu** binary for `zig-napi-node-example`
Loading
Loading