Bun's fast native bundler is now in beta. It can be used via the bun build
CLI command or the Bun.build()
JavaScript API.
await Bun.build({
entrypoints: ['./index.tsx'],
outdir: './build',
});
bun build ./index.tsx --outdir ./build
It's fast. The numbers below represent performance on esbuild's three.js benchmark.
Why bundle?
The bundler is a key piece of infrastructure in the JavaScript ecosystem. As a brief overview of why bundling is so important:
- Reducing HTTP requests. A single package in
node_modules
may consist of hundreds of files, and large applications may have dozens of such dependencies. Loading each of these files with a separate HTTP request becomes untenable very quickly, so bundlers are used to convert our application source code into a smaller number of self-contained "bundles" that can be loaded with a single request. - Code transforms. Modern apps are commonly built with languages or tools like TypeScript, JSX, and CSS modules, all of which must be converted into plain JavaScript and CSS before they can be consumed by a browser. The bundler is the natural place to configure these transformations.
- Framework features. Frameworks rely on bundler plugins & code transformations to implement common patterns like file-system routing, client-server code co-location (think
getServerSideProps
or Remix loaders), and server components.
Let's jump into the bundler API.
Note that the Bun bundler is not intended to replace tsc
for typechecking or generating type declarations.
Basic example
Let's build our first bundle. You have the following two files, which implement a simple client-side rendered React app.
import * as ReactDOM from 'react-dom/client';
import {Component} from "./Component"
const root = ReactDOM.createRoot(document.getElementById('root')!);
root.render(<Component message="Sup!" />)
export function Component(props: {message: string}) {
return <p>{props.message}</p>
}
Here, index.tsx
is the "entrypoint" to our application. Commonly, this will be a script that performs some side effect, like starting a server or—in this case—initializing a React root. Because we're using TypeScript & JSX, we need to bundle our code before it can be sent to the browser.
To create our bundle:
await Bun.build({
entrypoints: ['./index.tsx'],
outdir: './out',
})
bun build ./index.tsx --outdir ./out
For each file specified in entrypoints
, Bun will generate a new bundle. This bundle will be written to disk in the ./out
directory (as resolved from the current working directory). After running the build, the file system looks like this:
.
├── index.tsx
├── Component.tsx
└── out
└── index.js
The contents of out/index.js
will look something like this:
// ...
// ~20k lines of code
// including the contents of `react-dom/client` and all its dependencies
// this is where the $jsxDEV and $createRoot functions are defined
// Component.tsx
function Component(props) {
return $jsxDEV("p", {
children: props.message
}, undefined, false, undefined, this);
}
// index.tsx
var rootNode = document.getElementById("root");
var root = $createRoot(rootNode);
root.render($jsxDEV(Component, {
message: "Sup!"
}, undefined, false, undefined, this));
Tutorial: Run this file in your browser