import path from "node:path"; import NodeErrors from "../bun.js/bindings/ErrorCode.ts"; import { writeIfNotChanged } from "./helpers.ts"; const outputDir = process.argv[2]; if (!outputDir) { throw new Error("Missing output directory"); } const extra_count = NodeErrors.map(x => x.slice(3)) .filter(x => x.length > 0) .reduce((ac, cv) => ac + cv.length, 0); const count = NodeErrors.length + extra_count; if (count > 1 << 16) { // increase size of the enums below to have more tags throw new Error(`NodeError can't fit ${count} codes in a u16`); } let enumHeader = ``; let listHeader = ``; let zig = ``; enumHeader = ` // clang-format off // Generated by: src/codegen/generate-node-errors.ts // Input: src/bun.js/bindings/ErrorCode.ts #pragma once #include namespace Bun { static constexpr size_t NODE_ERROR_COUNT = ${count}; enum class ErrorCode : uint16_t { `; listHeader = ` // clang-format off // Generated by: src/codegen/generate-node-errors.ts #pragma once #include struct ErrorCodeData { JSC::ErrorType type; WTF::ASCIILiteral name; WTF::ASCIILiteral code; }; static constexpr ErrorCodeData errors[${count}] = { `; zig = ` // Generated by: src/codegen/generate-node-errors.ts const std = @import("std"); const bun = @import("bun"); const jsc = bun.jsc; pub fn ErrorBuilder(comptime code: Error, comptime fmt: [:0]const u8, Args: type) type { return struct { global: *jsc.JSGlobalObject, args: Args, // Throw this error as a JS exception pub inline fn throw(this: @This()) bun.JSError { return code.throw(this.global, fmt, this.args); } /// Turn this into a JSValue pub inline fn toJS(this: @This()) jsc.JSValue { return code.fmt(this.global, fmt, this.args); } /// Turn this into a JSPromise that is already rejected. pub inline fn reject(this: @This()) jsc.JSValue { if (comptime bun.FeatureFlags.breaking_changes_1_3) { return jsc.JSPromise.rejectedPromise(this.globalThis, code.fmt(this.global, fmt, this.args)).toJS(); } else { return jsc.JSPromise.dangerouslyCreateRejectedPromiseValueWithoutNotifyingVM(this.global, code.fmt(this.global, fmt, this.args)); } } }; } pub const Error = enum(u16) { `; let i = 0; for (let [code, constructor, name, ...other_constructors] of NodeErrors) { if (name == null) name = constructor.name; // it's useful to avoid the prefix, but module not found has a prefixed and unprefixed version const codeWithoutPrefix = code === "ERR_MODULE_NOT_FOUND" ? code : code.replace(/^ERR_/, ""); enumHeader += ` ${code} = ${i},\n`; listHeader += ` { JSC::ErrorType::${constructor.name}, "${name}"_s, "${code}"_s },\n`; zig += ` /// ${name}: ${code} (instanceof ${constructor.name})\n`; zig += ` ${codeWithoutPrefix} = ${i},\n`; i++; for (const con of other_constructors) { if (con == null) continue; if (name == null) name = con.name; enumHeader += ` ${code}_${con.name} = ${i},\n`; listHeader += ` { JSC::ErrorType::${con.name}, "${con.name}"_s, "${code}"_s },\n`; zig += ` /// ${name}: ${code} (instanceof ${con.name})\n`; zig += ` ${codeWithoutPrefix}_${con.name} = ${i},\n`; i++; } } enumHeader += ` }; } // namespace Bun `; listHeader += ` }; `; zig += ` extern fn Bun__createErrorWithCode(globalThis: *jsc.JSGlobalObject, code: Error, message: *bun.String) jsc.JSValue; /// Creates an Error object with the given error code. /// If an error is thrown while creating the Error object, returns that error instead. /// Derefs the message string. pub fn toJS(this: Error, globalThis: *jsc.JSGlobalObject, message: *bun.String) jsc.JSValue { defer message.deref(); return Bun__createErrorWithCode(globalThis, this, message); } pub fn fmt(this: Error, globalThis: *jsc.JSGlobalObject, comptime fmt_str: [:0]const u8, args: anytype) jsc.JSValue { if (comptime std.meta.fieldNames(@TypeOf(args)).len == 0) { var message = bun.String.static(fmt_str); return toJS(this, globalThis, &message); } var message = bun.handleOom(bun.String.createFormat(fmt_str, args)); return toJS(this, globalThis, &message); } pub fn throw(this: Error, globalThis: *jsc.JSGlobalObject, comptime fmt_str: [:0]const u8, args: anytype) bun.JSError { return globalThis.throwValue(fmt(this, globalThis, fmt_str, args)); } }; `; let builtindtsPath = path.join(import.meta.dir, "..", "..", "src", "js", "builtins.d.ts"); let builtindts = await Bun.file(builtindtsPath).text(); let dts = ` // Generated by: src/codegen/generate-node-errors.ts // Input: src/bun.js/bindings/ErrorCode.ts // Global error code functions for TypeScript `; for (const [code, constructor, name, ...other_constructors] of NodeErrors) { const hasExistingOverride = builtindts.includes(`declare function $${code}`); if (hasExistingOverride) { continue; } const namedError = name && name !== constructor.name ? `${constructor.name} & { name: "${name}", code: "${code}" }` : `${constructor.name} & { code: "${code}" }`; dts += ` /** * Construct an {@link ${constructor.name} ${constructor.name}} with the \`"${code}"\` error code. * * To override this, update ErrorCode.cpp. To remove this generated type, mention \`"${code}"\` in builtins.d.ts. */ declare function $${code}(message: string): ${namedError};\n`; } writeIfNotChanged(path.join(outputDir, "ErrorCode+List.h"), enumHeader); writeIfNotChanged(path.join(outputDir, "ErrorCode+Data.h"), listHeader); writeIfNotChanged(path.join(outputDir, "ErrorCode.zig"), zig); writeIfNotChanged(path.join(outputDir, "ErrorCode.d.ts"), dts);