From 41924211f22a9ede913ba73933d4cfb96d0f6088 Mon Sep 17 00:00:00 2001 From: dave caruso Date: Wed, 18 Dec 2024 19:27:59 -0800 Subject: [PATCH] add `throw: true` in Bun.build, to be made default in 1.2 (#15861) --- docs/bundler/index.md | 202 ++++++++++++------ docs/bundler/plugins.md | 2 +- docs/bundler/vs-esbuild.md | 2 +- docs/runtime/modules.md | 1 + docs/runtime/plugins.md | 6 +- .../bun-inspector-frontend/scripts/build.ts | 1 + packages/bun-plugin-yaml/README.md | 2 +- packages/bun-types/bun.d.ts | 11 +- src/bake/DevServer.zig | 2 +- src/bun.js/api/JSBundler.zig | 5 + src/bundler/bundle_v2.zig | 22 +- src/cli/build_command.zig | 8 + src/codegen/bundle-functions.ts | 2 + src/logger.zig | 28 +-- src/options.zig | 34 +-- test/bundler/bun-build-api.test.ts | 21 ++ test/bundler/bundler_bun.test.ts | 47 ++++ test/bundler/bundler_defer.test.ts | 16 +- test/bundler/expectBundled.ts | 6 +- test/bundler/native-plugin.test.ts | 7 +- test/js/bun/css/css-fuzz.test.ts | 5 +- test/js/bun/css/doesnt_crash.test.ts | 2 + test/napi/napi.test.ts | 1 + test/regression/issue/14976/14976.test.ts | 1 + 24 files changed, 304 insertions(+), 130 deletions(-) diff --git a/docs/bundler/index.md b/docs/bundler/index.md index a13cd4871ccb57..dbb7a95038a5da 100644 --- a/docs/bundler/index.md +++ b/docs/bundler/index.md @@ -1259,7 +1259,7 @@ $ bun build ./index.tsx --outdir ./out --drop=console --drop=debugger --drop=any ### `experimentalCss` -Whether to enable _experimental_ support for bundling CSS files. Defaults to `false`. +Whether to enable _experimental_ support for bundling CSS files. Defaults to `false`. In 1.2, this property will be deleted, and CSS bundling will always be enabled. This supports bundling CSS files imported from JS, as well as CSS entrypoints. @@ -1275,6 +1275,12 @@ const result = await Bun.build({ {% /codetabs %} +### `throw` + +If set to `true`, `Bun.build` will throw on build failure. See the section ["Logs and Errors"](#logs-and-errors) for more details on the error message structure. + +In 1.2, this will default to `true`, with the previous behavior as `throw: false` + ## Outputs The `Bun.build` function returns a `Promise`, defined as: @@ -1414,7 +1420,70 @@ Refer to [Bundler > Executables](https://bun.sh/docs/bundler/executables) for co ## Logs and errors -`Bun.build` only throws if invalid options are provided. Read the `success` property to determine if the build was successful; the `logs` property will contain additional details. + + + +By default, `Bun.build` only throws if invalid options are provided. Read the `success` property to determine if the build was successful; the `logs` property will contain additional details. ```ts const result = await Bun.build({ @@ -1457,6 +1526,27 @@ if (!result.success) { } ``` +In Bun 1.2, throwing an aggregate error like this will become the default beahavior. You can opt-into it early using the `throw: true` option. + +```ts +try { + const result = await Bun.build({ + entrypoints: ["./index.tsx"], + outdir: "./out", + }); +} catch (e) { + // TypeScript does not allow annotations on the catch clause + const error = e as AggregateError; + console.error("Build Failed"); + + // Example: Using the built-in formatter + console.error(error); + + // Example: Serializing the failure as a JSON string. + console.error(JSON.stringify(error, null, 2)); +} +``` + ## Reference ```ts @@ -1478,39 +1568,23 @@ interface BuildConfig { * * @default "esm" */ - format?: /** - - * ECMAScript Module format - */ - | "esm" - /** - * CommonJS format - * **Experimental** - */ - | "cjs" - /** - * IIFE format - * **Experimental** - */ - | "iife"; + format?: "esm" | "cjs" | "iife"; naming?: | string | { chunk?: string; entry?: string; asset?: string; - }; // | string; + }; root?: string; // project root splitting?: boolean; // default true, enable code splitting plugins?: BunPlugin[]; - // manifest?: boolean; // whether to return manifest external?: string[]; packages?: "bundle" | "external"; publicPath?: string; define?: Record; - // origin?: string; // e.g. http://mydomain.com loader?: { [k in string]: Loader }; - sourcemap?: "none" | "linked" | "inline" | "external" | "linked"; // default: "none", true -> "inline" + sourcemap?: "none" | "linked" | "inline" | "external" | "linked" | boolean; // default: "none", true -> "inline" /** * package.json `exports` conditions used when resolving imports * @@ -1519,6 +1593,18 @@ interface BuildConfig { * https://nodejs.org/api/packages.html#exports */ conditions?: Array | string; + + /** + * Controls how environment variables are handled during bundling. + * + * Can be one of: + * - `"inline"`: Injects environment variables into the bundled output by converting `process.env.FOO` + * references to string literals containing the actual environment variable values + * - `"disable"`: Disables environment variable injection entirely + * - A string ending in `*`: Inlines environment variables that match the given prefix. + * For example, `"MY_PUBLIC_*"` will only include env vars starting with "MY_PUBLIC_" + */ + env?: "inline" | "disable" | `${string}*`; minify?: | boolean | { @@ -1536,20 +1622,6 @@ interface BuildConfig { * Force emitting @__PURE__ annotations even if minify.whitespace is true. */ emitDCEAnnotations?: boolean; - // treeshaking?: boolean; - - // jsx?: - // | "automatic" - // | "classic" - // | /* later: "preserve" */ { - // runtime?: "automatic" | "classic"; // later: "preserve" - // /** Only works when runtime=classic */ - // factory?: string; // default: "React.createElement" - // /** Only works when runtime=classic */ - // fragment?: string; // default: "React.Fragment" - // /** Only works when runtime=automatic */ - // importSource?: string; // default: "react" - // }; /** * Generate bytecode for the output. This can dramatically improve cold @@ -1562,6 +1634,37 @@ interface BuildConfig { * @default false */ bytecode?: boolean; + /** + * Add a banner to the bundled code such as "use client"; + */ + banner?: string; + /** + * Add a footer to the bundled code such as a comment block like + * + * `// made with bun!` + */ + footer?: string; + + /** + * **Experimental** + * + * Enable CSS support. + */ + experimentalCss?: boolean; + + /** + * Drop function calls to matching property accesses. + */ + drop?: string[]; + + /** + * When set to `true`, the returned promise rejects with an AggregateError when a build failure happens. + * When set to `false`, the `success` property of the returned object will be `false` when a build failure happens. + * + * This defaults to `false` in Bun 1.1 and will change to `true` in Bun 1.2 + * as most usage of `Bun.build` forgets to check for errors. + */ + throw?: boolean; } interface BuildOutput { @@ -1619,32 +1722,3 @@ declare class ResolveMessage { toString(): string; } ``` - - diff --git a/docs/bundler/plugins.md b/docs/bundler/plugins.md index 0cfd29ade63beb..8e6b79c0e7df9f 100644 --- a/docs/bundler/plugins.md +++ b/docs/bundler/plugins.md @@ -58,7 +58,7 @@ const myPlugin: BunPlugin = { This plugin can be passed into the `plugins` array when calling `Bun.build`. ```ts -Bun.build({ +await Bun.build({ entrypoints: ["./app.ts"], outdir: "./out", plugins: [myPlugin], diff --git a/docs/bundler/vs-esbuild.md b/docs/bundler/vs-esbuild.md index 1266914c057233..35bb62f6f70271 100644 --- a/docs/bundler/vs-esbuild.md +++ b/docs/bundler/vs-esbuild.md @@ -695,7 +695,7 @@ In Bun's CLI, simple boolean flags like `--minify` do not accept an argument. Ot - In Bun, `minify` can be a boolean or an object. ```ts - Bun.build({ + await Bun.build({ entrypoints: ['./index.tsx'], // enable all minification minify: true diff --git a/docs/runtime/modules.md b/docs/runtime/modules.md index 526446751ea295..cde5890a8600ce 100644 --- a/docs/runtime/modules.md +++ b/docs/runtime/modules.md @@ -259,6 +259,7 @@ await Bun.build({ conditions: ["react-server"], target: "bun", entryPoints: ["./app/foo/route.js"], + throw: true, }); ``` diff --git a/docs/runtime/plugins.md b/docs/runtime/plugins.md index e555e232e30f1c..714fb491913952 100644 --- a/docs/runtime/plugins.md +++ b/docs/runtime/plugins.md @@ -307,7 +307,7 @@ await import("my-object-virtual-module"); // { baz: "quix" } Plugins can read and write to the [build config](https://bun.sh/docs/bundler#api) with `build.config`. ```ts -Bun.build({ +await Bun.build({ entrypoints: ["./app.ts"], outdir: "./dist", sourcemap: "external", @@ -324,6 +324,7 @@ Bun.build({ }, }, ], + throw: true, }); ``` @@ -332,7 +333,7 @@ Bun.build({ **NOTE**: Plugin lifcycle callbacks (`onStart()`, `onResolve()`, etc.) do not have the ability to modify the `build.config` object in the `setup()` function. If you want to mutate `build.config`, you must do so directly in the `setup()` function: ```ts -Bun.build({ +await Bun.build({ entrypoints: ["./app.ts"], outdir: "./dist", sourcemap: "external", @@ -350,6 +351,7 @@ Bun.build({ }, }, ], + throw: true, }); ``` diff --git a/packages/bun-inspector-frontend/scripts/build.ts b/packages/bun-inspector-frontend/scripts/build.ts index 08f6e68e158be0..cbe1737d3aeac4 100644 --- a/packages/bun-inspector-frontend/scripts/build.ts +++ b/packages/bun-inspector-frontend/scripts/build.ts @@ -116,6 +116,7 @@ try { entrypoints: [join(import.meta.dir, "out/manifest.js")], outdir: "out", minify: true, + throw: true, }); const jsFilename = "manifest-" + jsBundle.outputs[0].hash + ".js"; // const cssBundle = await build({ diff --git a/packages/bun-plugin-yaml/README.md b/packages/bun-plugin-yaml/README.md index 556a67bbaff8a1..58450954fae49a 100644 --- a/packages/bun-plugin-yaml/README.md +++ b/packages/bun-plugin-yaml/README.md @@ -15,7 +15,7 @@ This plugin can be used to support `.yaml` loaders in Bun's bundler by passing i ```ts import yamlPlugin from "bun-plugin-yaml"; -Bun.build({ +await Bun.build({ entrypoints: ["./index.tsx"], // other config diff --git a/packages/bun-types/bun.d.ts b/packages/bun-types/bun.d.ts index c077e3b6f096fc..1d14d6f15a6af0 100644 --- a/packages/bun-types/bun.d.ts +++ b/packages/bun-types/bun.d.ts @@ -1544,7 +1544,7 @@ declare module "bun" { define?: Record; // origin?: string; // e.g. http://mydomain.com loader?: { [k in string]: Loader }; - sourcemap?: "none" | "linked" | "inline" | "external" | "linked"; // default: "none", true -> "inline" + sourcemap?: "none" | "linked" | "inline" | "external" | "linked" | boolean; // default: "none", true -> "inline" /** * package.json `exports` conditions used when resolving imports * @@ -1638,6 +1638,15 @@ declare module "bun" { * Drop function calls to matching property accesses. */ drop?: string[]; + + /** + * When set to `true`, the returned promise rejects with an AggregateError when a build failure happens. + * When set to `false`, the `success` property of the returned object will be `false` when a build failure happens. + * + * This defaults to `false` in Bun 1.1 and will change to `true` in Bun 1.2 + * as most usage of `Bun.build` forgets to check for errors. + */ + throw?: boolean; } namespace Password { diff --git a/src/bake/DevServer.zig b/src/bake/DevServer.zig index fc00d222c54079..47ba7fac5420db 100644 --- a/src/bake/DevServer.zig +++ b/src/bake/DevServer.zig @@ -314,7 +314,7 @@ pub fn init(options: Options) bun.JSOOM!*DevServer { dev.framework = dev.framework.resolve(&dev.server_bundler.resolver, &dev.client_bundler.resolver, options.arena) catch { if (dev.framework.is_built_in_react) try bake.Framework.addReactInstallCommandNote(&dev.log); - return global.throwValue(dev.log.toJSAggregateError(global, "Framework is missing required files!")); + return global.throwValue(dev.log.toJSAggregateError(global, bun.String.static("Framework is missing required files!"))); }; errdefer dev.route_lookup.clearAndFree(allocator); diff --git a/src/bun.js/api/JSBundler.zig b/src/bun.js/api/JSBundler.zig index 444e68f3d2bb96..d122808bde8356 100644 --- a/src/bun.js/api/JSBundler.zig +++ b/src/bun.js/api/JSBundler.zig @@ -79,6 +79,7 @@ pub const JSBundler = struct { css_chunking: bool = false, drop: bun.StringSet = bun.StringSet.init(bun.default_allocator), has_any_on_before_parse: bool = false, + throw_on_error: bool = if (bun.FeatureFlags.breaking_changes_1_2) true else false, env_behavior: Api.DotEnvBehavior = if (!bun.FeatureFlags.breaking_changes_1_2) .load_all else .disable, env_prefix: OwnedString = OwnedString.initEmpty(bun.default_allocator), @@ -506,6 +507,10 @@ pub const JSBundler = struct { }; } + if (try config.getBooleanLoose(globalThis, "throw")) |flag| { + this.throw_on_error = flag; + } + return this; } diff --git a/src/bundler/bundle_v2.zig b/src/bundler/bundle_v2.zig index b708cc681080ec..260b82c24244d5 100644 --- a/src/bundler/bundle_v2.zig +++ b/src/bundler/bundle_v2.zig @@ -1690,6 +1690,12 @@ pub const BundleV2 = struct { bundler.configureLinker(); try bundler.configureDefines(); + if (bun.FeatureFlags.breaking_changes_1_2) { + if (!bundler.options.production) { + try bundler.options.conditions.appendSlice(&.{"development"}); + } + } + bundler.resolver.opts = bundler.options; } @@ -1717,11 +1723,16 @@ pub const BundleV2 = struct { this.poll_ref.unref(globalThis.bunVM()); const promise = this.promise.swap(); - const root_obj = JSC.JSValue.createEmptyObject(globalThis, 2); switch (this.result) { .pending => unreachable, - .err => { + .err => brk: { + if (this.config.throw_on_error) { + promise.reject(globalThis, this.log.toJSAggregateError(globalThis, bun.String.static("Bundle failed"))); + break :brk; + } + + const root_obj = JSC.JSValue.createEmptyObject(globalThis, 3); root_obj.put(globalThis, JSC.ZigString.static("outputs"), JSC.JSValue.createEmptyArray(globalThis, 0)); root_obj.put( globalThis, @@ -1733,8 +1744,10 @@ pub const BundleV2 = struct { JSC.ZigString.static("logs"), this.log.toJSArray(globalThis, bun.default_allocator), ); + promise.resolve(globalThis, root_obj); }, .value => |*build| { + const root_obj = JSC.JSValue.createEmptyObject(globalThis, 3); const output_files: []options.OutputFile = build.output_files.items; const output_files_js = JSC.JSValue.createEmptyArray(globalThis, output_files.len); if (output_files_js == .zero) { @@ -1799,10 +1812,13 @@ pub const BundleV2 = struct { JSC.ZigString.static("logs"), this.log.toJSArray(globalThis, bun.default_allocator), ); + promise.resolve(globalThis, root_obj); }, } - promise.resolve(globalThis, root_obj); + if (Environment.isDebug) { + bun.assert(promise.status(globalThis.vm()) != .pending); + } } }; diff --git a/src/cli/build_command.zig b/src/cli/build_command.zig index 3a975221f6e6c4..f9600fd33567c0 100644 --- a/src/cli/build_command.zig +++ b/src/cli/build_command.zig @@ -217,6 +217,13 @@ pub const BuildCommand = struct { try this_bundler.configureDefines(); this_bundler.configureLinker(); + if (bun.FeatureFlags.breaking_changes_1_2) { + // This is currently done in DevServer by default, but not in Bun.build + if (!this_bundler.options.production) { + try this_bundler.options.conditions.appendSlice(&.{"development"}); + } + } + this_bundler.resolver.opts = this_bundler.options; this_bundler.options.jsx.development = !this_bundler.options.production; this_bundler.resolver.opts.jsx.development = this_bundler.options.jsx.development; @@ -237,6 +244,7 @@ pub const BuildCommand = struct { client_bundler.options = this_bundler.options; client_bundler.options.target = .browser; client_bundler.options.server_components = true; + client_bundler.options.conditions = try this_bundler.options.conditions.clone(); try this_bundler.options.conditions.appendSlice(&.{"react-server"}); this_bundler.options.react_fast_refresh = false; this_bundler.options.minify_syntax = true; diff --git a/src/codegen/bundle-functions.ts b/src/codegen/bundle-functions.ts index 7756e44afdced7..0cebe25825e818 100644 --- a/src/codegen/bundle-functions.ts +++ b/src/codegen/bundle-functions.ts @@ -223,7 +223,9 @@ $$capture_start$$(${fn.async ? "async " : ""}${ define, target: "bun", minify: { syntax: true, whitespace: false }, + throw: true, }); + // TODO: Wait a few versions before removing this if (!build.success) { throw new AggregateError(build.logs, "Failed bundling builtin function " + fn.name + " from " + basename + ".ts"); } diff --git a/src/logger.zig b/src/logger.zig index 516dd70ed84150..d0af8cb71e319f 100644 --- a/src/logger.zig +++ b/src/logger.zig @@ -742,35 +742,15 @@ pub const Log = struct { } /// unlike toJS, this always produces an AggregateError object - pub fn toJSAggregateError(this: Log, global: *JSC.JSGlobalObject, message: []const u8) JSC.JSValue { - const msgs: []const Msg = this.msgs.items; - - // TODO: remove arbitrary upper limit. cannot use the heap because - // values could be GC'd. to do this correctly, expose a binding that - // allows creating an AggregateError using an array - var errors_stack: [256]JSC.JSValue = undefined; - - const count = @min(msgs.len, errors_stack.len); - - for (msgs[0..count], 0..) |msg, i| { - errors_stack[i] = switch (msg.metadata) { - .build => JSC.BuildMessage.create(global, bun.default_allocator, msg), - .resolve => JSC.ResolveMessage.create(global, bun.default_allocator, msg, ""), - }; - } - - const out = JSC.ZigString.init(message); - return global.createAggregateError(errors_stack[0..count], &out); + pub fn toJSAggregateError(this: Log, global: *JSC.JSGlobalObject, message: bun.String) JSC.JSValue { + return global.createAggregateErrorWithArray(message, this.toJSArray(global, bun.default_allocator)); } pub fn toJSArray(this: Log, global: *JSC.JSGlobalObject, allocator: std.mem.Allocator) JSC.JSValue { const msgs: []const Msg = this.msgs.items; - const errors_stack: [256]*anyopaque = undefined; - - const count = @as(u16, @intCast(@min(msgs.len, errors_stack.len))); - var arr = JSC.JSValue.createEmptyArray(global, count); - for (msgs[0..count], 0..) |msg, i| { + const arr = JSC.JSValue.createEmptyArray(global, msgs.len); + for (msgs, 0..) |msg, i| { arr.putIndex(global, @as(u32, @intCast(i)), msg.toJS(global, allocator)); } diff --git a/src/options.zig b/src/options.zig index 4bccea93e6c805..1b1a6746860a02 100644 --- a/src/options.zig +++ b/src/options.zig @@ -909,9 +909,9 @@ pub const defaultLoaders = bun.ComptimeStringMap(Loader, default_loaders); // https://webpack.js.org/guides/package-exports/#reference-syntax pub const ESMConditions = struct { - default: ConditionsMap = undefined, - import: ConditionsMap = undefined, - require: ConditionsMap = undefined, + default: ConditionsMap, + import: ConditionsMap, + require: ConditionsMap, pub fn init(allocator: std.mem.Allocator, defaults: []const string) !ESMConditions { var default_condition_amp = ConditionsMap.init(allocator); @@ -936,22 +936,37 @@ pub const ESMConditions = struct { import_condition_map.putAssumeCapacity("default", {}); require_condition_map.putAssumeCapacity("default", {}); - return ESMConditions{ + return .{ .default = default_condition_amp, .import = import_condition_map, .require = require_condition_map, }; } + pub fn clone(self: *const ESMConditions) !ESMConditions { + var default = try self.default.clone(); + errdefer default.deinit(); + var import = try self.import.clone(); + errdefer import.deinit(); + var require = try self.require.clone(); + errdefer require.deinit(); + + return .{ + .default = default, + .import = import, + .require = require, + }; + } + pub fn appendSlice(self: *ESMConditions, conditions: []const string) !void { try self.default.ensureUnusedCapacity(conditions.len); try self.import.ensureUnusedCapacity(conditions.len); try self.require.ensureUnusedCapacity(conditions.len); for (conditions) |condition| { - self.default.putAssumeCapacityNoClobber(condition, {}); - self.import.putAssumeCapacityNoClobber(condition, {}); - self.require.putAssumeCapacityNoClobber(condition, {}); + self.default.putAssumeCapacity(condition, {}); + self.import.putAssumeCapacity(condition, {}); + self.require.putAssumeCapacity(condition, {}); } } }; @@ -1714,11 +1729,6 @@ pub const BundleOptions = struct { opts.conditions = try ESMConditions.init(allocator, opts.target.defaultConditions()); - if (bun.FeatureFlags.breaking_changes_1_2) { - // This is currently done in DevServer by default, but not in Bun.build - @compileError("if (!production) { add \"development\" condition }"); - } - if (transform.conditions.len > 0) { opts.conditions.appendSlice(transform.conditions) catch bun.outOfMemory(); } diff --git a/test/bundler/bun-build-api.test.ts b/test/bundler/bun-build-api.test.ts index df1624622e196c..3ea2c0416ac2f1 100644 --- a/test/bundler/bun-build-api.test.ts +++ b/test/bundler/bun-build-api.test.ts @@ -2,6 +2,7 @@ import { describe, expect, test } from "bun:test"; import { readFileSync, writeFileSync } from "fs"; import { bunEnv, bunExe, tempDirWithFiles } from "harness"; import path, { join } from "path"; +import assert from "assert"; describe("Bun.build", () => { test("experimentalCss = true works", async () => { @@ -175,6 +176,26 @@ describe("Bun.build", () => { Bun.gc(true); }); + test("`throw: true` works", async () => { + Bun.gc(true); + try { + await Bun.build({ + entrypoints: [join(import.meta.dir, "does-not-exist.ts")], + throw: true, + }); + expect.unreachable(); + } catch (e) { + assert(e instanceof AggregateError); + expect(e.errors).toHaveLength(1); + expect(e.errors[0]).toBeInstanceOf(BuildMessage); + expect(e.errors[0].message).toMatch(/ModuleNotFound/); + expect(e.errors[0].name).toBe("BuildMessage"); + expect(e.errors[0].position).toEqual(null); + expect(e.errors[0].level).toEqual("error"); + Bun.gc(true); + } + }); + test("returns output files", async () => { Bun.gc(true); const build = await Bun.build({ diff --git a/test/bundler/bundler_bun.test.ts b/test/bundler/bundler_bun.test.ts index 43b29c517e9725..9638fe5254390c 100644 --- a/test/bundler/bundler_bun.test.ts +++ b/test/bundler/bundler_bun.test.ts @@ -102,4 +102,51 @@ error: Hello World`, }, run: { stdout: "" }, }); + if (Bun.version.startsWith("1.2")) { + throw new Error("TODO: enable these tests please"); + for (const backend of ["api", "cli"] as const) { + itBundled("bun/ExportsConditionsDevelopment" + backend.toUpperCase(), { + files: { + "src/entry.js": `import 'pkg1'`, + "node_modules/pkg1/package.json": /* json */ ` + { + "exports": { + "development": "./custom1.js", + "default": "./default.js" + } + } + `, + "node_modules/pkg1/custom1.js": `console.log('SUCCESS')`, + "node_modules/pkg1/default.js": `console.log('FAIL')`, + }, + backend, + outfile: "out.js", + define: { "process.env.NODE_ENV": '"development"' }, + run: { + stdout: "SUCCESS", + }, + }); + itBundled("bun/ExportsConditionsDevelopmentInProduction" + backend.toUpperCase(), { + files: { + "src/entry.js": `import 'pkg1'`, + "node_modules/pkg1/package.json": /* json */ ` + { + "exports": { + "development": "./custom1.js", + "default": "./default.js" + } + } + `, + "node_modules/pkg1/custom1.js": `console.log('FAIL')`, + "node_modules/pkg1/default.js": `console.log('SUCCESS')`, + }, + backend, + outfile: "/Users/user/project/out.js", + define: { "process.env.NODE_ENV": '"production"' }, + run: { + stdout: "SUCCESS", + }, + }); + } + } }); diff --git a/test/bundler/bundler_defer.test.ts b/test/bundler/bundler_defer.test.ts index c3becd5ac483bb..7f9bca5f13e4f1 100644 --- a/test/bundler/bundler_defer.test.ts +++ b/test/bundler/bundler_defer.test.ts @@ -1,7 +1,7 @@ import { describe, expect, test } from "bun:test"; -import { itBundled } from './expectBundled'; -import { bunExe, bunEnv, tempDirWithFiles } from 'harness'; -import * as path from 'node:path'; +import { itBundled } from "./expectBundled"; +import { bunExe, bunEnv, tempDirWithFiles } from "harness"; +import * as path from "node:path"; describe("defer", () => { { @@ -179,10 +179,12 @@ describe("defer", () => { }, }, ], + throw: true, }); console.log(result); - } catch (err) { + } catch (err: any) { expect(err).toBeDefined(); + expect(err.message).toBe("WOOPS"); return; } throw new Error("DIDNT GET ERROR!"); @@ -213,15 +215,15 @@ describe("defer", () => { console.log("Foo", foo, lmao); `, - "/lmao.ts": ` + "/lmao.ts": ` import { foo } from "./foo.ts"; export const lmao = "lolss"; console.log(foo); `, - "/foo.ts": ` + "/foo.ts": ` export const foo = 'lkdfjlsdf'; console.log('hi')`, - "/a.css": ` + "/a.css": ` h1 { color: blue; } diff --git a/test/bundler/expectBundled.ts b/test/bundler/expectBundled.ts index 555e2a9e8808fe..28ad2eb78c279f 100644 --- a/test/bundler/expectBundled.ts +++ b/test/bundler/expectBundled.ts @@ -1034,6 +1034,7 @@ function expectBundled( ignoreDCEAnnotations, experimentalCss, drop, + define: define ?? {}, } as BuildConfig; if (dotenv) { @@ -1050,12 +1051,9 @@ function expectBundled( const debugFile = `import path from 'path'; import assert from 'assert'; const {plugins} = (${x})({ root: ${JSON.stringify(root)} }); -const options = ${JSON.stringify({ ...buildConfig, plugins: undefined }, null, 2)}; +const options = ${JSON.stringify({ ...buildConfig, throw: true, plugins: undefined }, null, 2)}; options.plugins = typeof plugins === "function" ? [{ name: "plugin", setup: plugins }] : plugins; const build = await Bun.build(options); -if (build.logs) { - throw build.logs; -} for (const [key, blob] of build.outputs) { await Bun.write(path.join(options.outdir, blob.path), blob.result); } diff --git a/test/bundler/native-plugin.test.ts b/test/bundler/native-plugin.test.ts index 10c67d8ce56a3e..8a09905eaf7601 100644 --- a/test/bundler/native-plugin.test.ts +++ b/test/bundler/native-plugin.test.ts @@ -264,7 +264,7 @@ const many_foo = ["foo","foo","foo","foo","foo","foo","foo"] await Bun.$`echo ${files.map(([fp]) => fp).join("\n")} >> index.ts`; await Bun.$`echo ${files.map(([, varname]) => `console.log(JSON.stringify(${varname}))`).join("\n")} >> index.ts`; - const resultPromise = Bun.build({ + const result = await Bun.build({ outdir, entrypoints: [path.join(tempdir, "index.ts")], plugins: [ @@ -290,12 +290,9 @@ const many_foo = ["foo","foo","foo","foo","foo","foo","foo"] }, }, ], + throw: true, }); - const result = await resultPromise; - - if (!result.success) console.log(result); - expect(result.success).toBeTrue(); const output = await Bun.$`${bunExe()} run dist/index.js`.cwd(tempdir).text(); const outputJsons = output .trim() diff --git a/test/js/bun/css/css-fuzz.test.ts b/test/js/bun/css/css-fuzz.test.ts index 668847af94c754..6a144bc60708e3 100644 --- a/test/js/bun/css/css-fuzz.test.ts +++ b/test/js/bun/css/css-fuzz.test.ts @@ -183,12 +183,9 @@ if (!isCI) { const result = await Bun.build({ entrypoints: ["invalid.css"], experimentalCss: true, + throw: true, }); - if (result.logs.length > 0) { - throw new AggregateError("CSS parser returned logs", result.logs); - } - // We expect the parser to either throw an error or return a valid result // If it returns undefined/null, that's a potential issue if (result === undefined || result === null) { diff --git a/test/js/bun/css/doesnt_crash.test.ts b/test/js/bun/css/doesnt_crash.test.ts index c7cdad59479792..64eaa7172ddf45 100644 --- a/test/js/bun/css/doesnt_crash.test.ts +++ b/test/js/bun/css/doesnt_crash.test.ts @@ -35,6 +35,7 @@ describe("doesnt_crash", async () => { experimentalCss: true, minify: minify, target, + throw: true, }); console.timeEnd(timeLog); @@ -56,6 +57,7 @@ describe("doesnt_crash", async () => { experimentalCss: true, target, minify: minify, + throw: true, }); if (logs?.length) { diff --git a/test/napi/napi.test.ts b/test/napi/napi.test.ts index 7c79e53de0cf07..b00513cb861a86 100644 --- a/test/napi/napi.test.ts +++ b/test/napi/napi.test.ts @@ -115,6 +115,7 @@ describe("napi", () => { outdir: dir, target, format, + throw: true, }); expect(build.logs).toBeEmpty(); diff --git a/test/regression/issue/14976/14976.test.ts b/test/regression/issue/14976/14976.test.ts index 37e7c72df0672e..804eb7cc20b97e 100644 --- a/test/regression/issue/14976/14976.test.ts +++ b/test/regression/issue/14976/14976.test.ts @@ -39,6 +39,7 @@ test("bun build --target=bun outputs only ascii", async () => { const build_result = await Bun.build({ entrypoints: [import.meta.dirname + "/import_target.ts"], target: "bun", + throw: true, }); expect(build_result.success).toBe(true); expect(build_result.outputs.length).toBe(1);