mirror of
https://github.com/oven-sh/bun
synced 2026-02-09 18:38:55 +00:00
Add a new generator for JS → Zig bindings. The bulk of the conversion is done in C++, after which the data is transformed into an FFI-safe representation, passed to Zig, and then finally transformed into idiomatic Zig types. In its current form, the new bindings generator supports: * Signed and unsigned integers * Floats (plus a “finite” variant that disallows NaN and infinities) * Strings * ArrayBuffer (accepts ArrayBuffer, TypedArray, or DataView) * Blob * Optional types * Nullable types (allows null, whereas Optional only allows undefined) * Arrays * User-defined string enumerations * User-defined unions (fields can optionally be named to provide a better experience in Zig) * Null and undefined, for use in unions (can more efficiently represent optional/nullable unions than wrapping a union in an optional) * User-defined dictionaries (arbitrary key-value pairs; expects a JS object and parses it into a struct) * Default values for dictionary members * Alternative names for dictionary members (e.g., to support both `serverName` and `servername` without taking up twice the space) * Descriptive error messages * Automatic `fromJS` functions in Zig for dictionaries * Automatic `deinit` functions for the generated Zig types Although this bindings generator has many features not present in `bindgen.ts`, it does not yet implement all of `bindgen.ts`'s functionality, so for the time being, it has been named `bindgenv2`, and its configuration is specified in `.bindv2.ts` files. Once all `bindgen.ts`'s functionality has been incorporated, it will be renamed. This PR ports `SSLConfig` to use the new bindings generator; see `SSLConfig.bindv2.ts`. (For internal tracking: fixes STAB-1319, STAB-1322, STAB-1323, STAB-1324) --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Alistair Smith <hi@alistair.sh>
82 lines
2.8 KiB
TypeScript
82 lines
2.8 KiB
TypeScript
import * as fs from "fs";
|
|
import * as Module from "module";
|
|
import { basename, extname } from "path";
|
|
|
|
const allFiles = fs.readdirSync(".").filter(f => f.endsWith(".js"));
|
|
const outdir = process.argv[2];
|
|
const builtins = Module.builtinModules;
|
|
let commands: Promise<void>[] = [];
|
|
|
|
let moduleFiles: string[] = [];
|
|
for (const name of allFiles) {
|
|
const mod = basename(name, extname(name)).replaceAll(".", "/");
|
|
const file = allFiles.find(f => f.startsWith(mod));
|
|
moduleFiles.push(file as string);
|
|
}
|
|
|
|
for (let fileIndex = 0; fileIndex < allFiles.length; fileIndex++) {
|
|
const name = allFiles[fileIndex];
|
|
const mod = basename(name, extname(name)).replaceAll(".", "/");
|
|
const file = allFiles.find(f => f.startsWith(mod));
|
|
const externals = [...builtins];
|
|
const i = externals.indexOf(name);
|
|
if (i !== -1) {
|
|
externals.splice(i, 1);
|
|
}
|
|
|
|
// Build all files at once with specific options
|
|
const externalModules = builtins
|
|
.concat(moduleFiles.filter(f => f !== name))
|
|
.flatMap(b => [`--external:node:${b}`, `--external:${b}`])
|
|
.join(" ");
|
|
|
|
// Create the build command with all the specified options
|
|
const buildCommand =
|
|
Bun.$`bun build --define=process.env.NODE_DEBUG:"false" --define=process.env.READABLE_STREAM="'enable'" --define=global:globalThis --outdir=${outdir} ${name} --minify-syntax --minify-whitespace --format=${name.includes("stream") ? "cjs" : "esm"} --target=node ${{ raw: externalModules }}`.text();
|
|
|
|
commands.push(
|
|
buildCommand.then(async text => {
|
|
// This is very brittle. But that should be okay for our usecase
|
|
let outfile = (await Bun.file(`${outdir}/${name}`).text())
|
|
.replaceAll("__require(", "require(")
|
|
.replaceAll("import.meta.url", "''")
|
|
.replaceAll("createRequire", "")
|
|
.replaceAll("global.process", "require('process')")
|
|
.trim();
|
|
|
|
while (outfile.startsWith("import{")) {
|
|
outfile = outfile.slice(outfile.indexOf(";") + 1);
|
|
}
|
|
|
|
if (outfile.includes('"node:module"')) {
|
|
console.log(outfile);
|
|
throw new Error("Unexpected import in " + name);
|
|
}
|
|
|
|
if (outfile.includes("import.meta")) {
|
|
throw new Error("Unexpected import.meta in " + name);
|
|
}
|
|
|
|
if (outfile.includes(".$apply")) {
|
|
throw new Error("$apply is not supported in browsers (while building " + name + ")");
|
|
}
|
|
|
|
if (outfile.includes(".$call")) {
|
|
throw new Error("$call is not supported in browsers (while building " + name + ")");
|
|
}
|
|
|
|
if (
|
|
outfile.includes("$isObject(") ||
|
|
outfile.includes("$isPromise(") ||
|
|
outfile.includes("$isUndefinedOrNull(")
|
|
) {
|
|
throw new Error("Unsupported function in " + name);
|
|
}
|
|
|
|
await Bun.write(`${outdir}/${name}`, outfile);
|
|
}),
|
|
);
|
|
}
|
|
|
|
await Promise.all(commands);
|