From ef1a3481734feb733c072b385401d678fe99dafa Mon Sep 17 00:00:00 2001 From: bing Date: Fri, 22 May 2026 15:55:27 +0800 Subject: [PATCH] feat: warn when dsl incompatible functions are skipped Zig functions are skipped if they: 1) are not `pub fn`, 2) do not use DSL-compatible types from the `js` module We can't error since we want flexibility to define and use other functions in the same file, so we just add a simple warning to warn the user about this at comptime. This is to avoid future situations encountered here: https://github.com/ChainSafe/lodestar-z/pull/371 --- build.zig.zon | 15 +++++++++++++ examples/non_dsl_warning/mod.test.ts | 32 ++++++++++++++++++++++++++++ examples/non_dsl_warning/mod.zig | 29 +++++++++++++++++++++++++ src/js/export_module.zig | 5 ++++- 4 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 examples/non_dsl_warning/mod.test.ts create mode 100644 examples/non_dsl_warning/mod.zig diff --git a/build.zig.zon b/build.zig.zon index b29943f..cb03bea 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -49,6 +49,11 @@ .imports = .{.zapi}, .link_libc = true, }, + .example_non_dsl_warning = .{ + .root_source_file = "examples/non_dsl_warning/mod.zig", + .imports = .{.zapi}, + .link_libc = true, + }, }, .libraries = .{ .example_hello_world = .{ @@ -69,6 +74,12 @@ .linker_allow_shlib_undefined = true, .dest_sub_path = "example_js_dsl.node", }, + .example_non_dsl_warning = .{ + .root_module = .example_non_dsl_warning, + .linkage = .dynamic, + .linker_allow_shlib_undefined = true, + .dest_sub_path = "example_non_dsl_warning.node", + }, }, .tests = .{ .napi = .{ .root_module = .napi }, @@ -88,5 +99,9 @@ .root_module = .example_js_dsl, .linker_allow_shlib_undefined = true, }, + .example_non_dsl_warning = .{ + .root_module = .example_non_dsl_warning, + .linker_allow_shlib_undefined = true, + }, }, } diff --git a/examples/non_dsl_warning/mod.test.ts b/examples/non_dsl_warning/mod.test.ts new file mode 100644 index 0000000..c36ce15 --- /dev/null +++ b/examples/non_dsl_warning/mod.test.ts @@ -0,0 +1,32 @@ +import { describe, expect, it } from "vitest"; +import { spawnSync } from "node:child_process"; +const non_dsl_mod = require("../../zig-out/lib/example_non_dsl_warning.node"); + +const cwd = new URL("../..", import.meta.url); + +function runNode(source: string) { + return spawnSync(process.execPath, ["-e", source], { + cwd, + encoding: "utf8", + }); +} + +describe("non-DSL fn should warn user", () => { + it("exports DSL functions and skips non-DSL functions", () => { + const result = runNode(` + const mod = require("./zig-out/lib/example_non_dsl_warning.node"); + process.stdout.write(JSON.stringify({ + exported: mod.exported(41), + skipped: typeof mod.skipped, + })); + `); + + expect(result.status).toEqual(0); + expect(JSON.parse(result.stdout)).toEqual({ + exported: 42, + skipped: "undefined", + }); + + expect(result.stderr).toContain("zapi: skipping non-DSL function mod.skipped, this will not be exported"); + }); +}); diff --git a/examples/non_dsl_warning/mod.zig b/examples/non_dsl_warning/mod.zig new file mode 100644 index 0000000..966341e --- /dev/null +++ b/examples/non_dsl_warning/mod.zig @@ -0,0 +1,29 @@ +//! This example module demonstrates a public function with non-DSL parameters +//! being skipped by `zapi`'s `exportModule` functionality. +//! +//! `zapi` will only export zig functions as bindings if they: +//! +//! 1) are public functions (`pub fn`), +//! 2) use DSL-compatible parameters (eg. `js.Number`). +//! +//! In this scenario, even though `skipped` is public, it does not use +//! a DSL-compatible type for its `value` parameter and is therefore +//! not exported. +//! +//! If the user should want to export any other functions manually, +//! they would need to pass a custom `.register` to `js.exportModule`. +const js = @import("zapi").js; + +const Number = js.Number; + +pub fn exported(value: Number) Number { + return Number.from(value.assertI32() + 1); +} + +pub fn skipped(value: u32) u32 { + return value + 1; +} + +comptime { + js.exportModule(@This(), .{}); +} diff --git a/src/js/export_module.zig b/src/js/export_module.zig index aa70ba1..c28ad68 100644 --- a/src/js/export_module.zig +++ b/src/js/export_module.zig @@ -150,7 +150,10 @@ fn registerDecls(comptime Module: type, env: napi.Env, module: napi.Value, compt } break :blk true; }; - if (!is_dsl_fn) continue; + if (!is_dsl_fn) { + std.log.warn("zapi: skipping non-DSL function {s}.{s}, this will not be exported", .{ @typeName(Module), decl.name }); + continue; + } // DSL function — wrap and register const cb = wrap_function.wrapFunction(field);