Files
bun.sh/src/node-fallbacks/build-fallbacks.ts
taylor.fish f14f3b03bb Add new bindings generator; port SSLConfig (#23169)
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>
2025-10-03 17:10:28 -07:00

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);