Skip to content

Commit

Permalink
add throw: true in Bun.build, to be made default in 1.2 (#15861)
Browse files Browse the repository at this point in the history
  • Loading branch information
paperdave authored Dec 19, 2024
1 parent 5d2b72a commit 4192421
Show file tree
Hide file tree
Showing 24 changed files with 304 additions and 130 deletions.
202 changes: 138 additions & 64 deletions docs/bundler/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -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<BuildOutput>`, defined as:
Expand Down Expand Up @@ -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.
<!-- 1.2 documentation -->
<!-- On failure, `Bun.build` returns a rejected promise with an `AggregateError`. This can be logged to the console for pretty printing of the error list, or programmatically read with a `try`/`catch` block.
```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));
}
```
{% callout %}
Most of the time, an explicit `try`/`catch` is not needed, as Bun will neatly print uncaught exceptions. It is enough to just use a top-level `await` on the `Bun.build` call.
{% /callout %}
Each item in `error.errors` is an instance of `BuildMessage` or `ResolveMessage` (subclasses of Error), containing detailed information for each error.
```ts
class BuildMessage {
name: string;
position?: Position;
message: string;
level: "error" | "warning" | "info" | "debug" | "verbose";
}
class ResolveMessage extends BuildMessage {
code: string;
referrer: string;
specifier: string;
importKind: ImportKind;
}
```
On build success, the returned object contains a `logs` property, which contains bundler warnings and info messages.
```ts
const result = await Bun.build({
entrypoints: ["./index.tsx"],
outdir: "./out",
});
if (result.logs.length > 0) {
console.warn("Build succeeded with warnings:");
for (const message of result.logs) {
// Bun will pretty print the message object
console.warn(message);
}
}
``` -->

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({
Expand Down Expand Up @@ -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
Expand All @@ -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<string, string>;
// 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
*
Expand All @@ -1519,6 +1593,18 @@ interface BuildConfig {
* https://nodejs.org/api/packages.html#exports
*/
conditions?: Array<string> | 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
| {
Expand All @@ -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
Expand All @@ -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 {
Expand Down Expand Up @@ -1619,32 +1722,3 @@ declare class ResolveMessage {
toString(): string;
}
```

<!--
interface BuildManifest {
inputs: {
[path: string]: {
output: {
path: string;
};
imports: {
path: string;
kind: ImportKind;
external?: boolean;
asset?: boolean; // whether the import defaulted to "file" loader
}[];
};
};
outputs: {
[path: string]: {
type: "chunk" | "entrypoint" | "asset";
inputs: { path: string }[];
imports: {
path: string;
kind: ImportKind;
external?: boolean;
}[];
exports: string[];
};
};
} -->
2 changes: 1 addition & 1 deletion docs/bundler/plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -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],
Expand Down
2 changes: 1 addition & 1 deletion docs/bundler/vs-esbuild.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions docs/runtime/modules.md
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,7 @@ await Bun.build({
conditions: ["react-server"],
target: "bun",
entryPoints: ["./app/foo/route.js"],
throw: true,
});
```

Expand Down
6 changes: 4 additions & 2 deletions docs/runtime/plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -324,6 +324,7 @@ Bun.build({
},
},
],
throw: true,
});
```

Expand All @@ -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",
Expand All @@ -350,6 +351,7 @@ Bun.build({
},
},
],
throw: true,
});
```

Expand Down
1 change: 1 addition & 0 deletions packages/bun-inspector-frontend/scripts/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand Down
2 changes: 1 addition & 1 deletion packages/bun-plugin-yaml/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
11 changes: 10 additions & 1 deletion packages/bun-types/bun.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1544,7 +1544,7 @@ declare module "bun" {
define?: Record<string, string>;
// 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
*
Expand Down Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion src/bake/DevServer.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Loading

0 comments on commit 4192421

Please sign in to comment.