Skip to content

docs: bun bake - #45751

Draft
alii wants to merge 4 commits into
mainfrom
ali/bake-docs
Draft

alii wants to merge 4 commits into
mainfrom
ali/bake-docs

Conversation

@alii

@alii alii commented Oct 17, 2026 •

Copy link
Copy Markdown
Member

This PR adds the docs for bun bake (#45682). Merge it when Bake is on in a release build.

  • docs/bundler/bake.mdx (new), in the "Development Server" group: pages in app/pages/, app/server.ts, the flags, and what Bun.main, import.meta.main and process.argv are under bun bake.
  • docs/runtime/utils.mdx: Bun.main is an empty string when no file is the entry point, as under bun bake.
  • packages/bun-types/bun.d.ts: the same note in the JSDoc of Bun.main.
  • docs/bundler/bake.mdx: bake.config.ts and its plugins, and the options of Bun.serve() that the app/server.ts export can have (from the PR after add bun bake: serve app/pages and app/server.ts #45682).
  • packages/bun-types/bake.d.ts (new): the types of bun:bake (defineConfig, Config, ConfigEnv), with a fixture in test/integration/bun-types/fixture/bake.ts.

Before merging, update the warning at the top of bake.mdx. It says that release builds need BUN_FEATURE_FLAG_EXPERIMENTAL_BAKE=1.

@robobun

robobun commented Oct 17, 2026 •

Copy link
Copy Markdown
Collaborator
Updated 9:05 AM PT - Oct 17th, 2026

❌ @alii, your commit 6d1dce4 has some failures in Build #127585 (All Failures)


🧪   To try this PR locally:

bunx bun-pr 45751

That installs a local version of the PR into your bun-45751 executable, so you can run:

bun-45751 --bun

@robobun

robobun commented Oct 17, 2026

Copy link
Copy Markdown
Collaborator
Updated 9:05 AM PT - Oct 17th, 2026

@alii, your commit 1e973fb is building: #127584

@robobun

robobun commented Oct 17, 2026

Copy link
Copy Markdown
Collaborator
Updated 9:05 AM PT - Oct 17th, 2026

@alii, your commit 6d1dce4 is building: #127585

@robobun

robobun commented Oct 17, 2026

Copy link
Copy Markdown
Collaborator
Updated 9:05 AM PT - Oct 17th, 2026

❌ @alii, your commit 1e973fb has some failures in Build #127584 (All Failures)


🧪   To try this PR locally:

bunx bun-pr 45751

That installs a local version of the PR into your bun-45751 executable, so you can run:

bun-45751 --bun

@robobun

robobun commented Oct 17, 2026 •

Copy link
Copy Markdown
Collaborator
Updated 8:07 AM PT - Oct 18th, 2026

✅ @alii, your commit 64753e1ee68df9d1d826c445c59334516bb493eb passed in Build #128261! 🎉


🧪   To try this PR locally:

bunx bun-pr 45751

That installs a local version of the PR into your bun-45751 executable, so you can run:

bun-45751 --bun

Base automatically changed from ali/bake-cli to main October 18, 2026 01:54
alii added a commit that referenced this pull request Oct 18, 2026
This PR adds build plugins to `bun bake`, from a `bake.config.ts` file.
It is the second step of the new Bake design
(https://github.com/alii/bake-rfc), after #45682.

```ts
// bake.config.ts
import { defineConfig } from "bun:bake";
import tailwind from "bun-plugin-tailwind";

export default defineConfig({
  plugins: [tailwind],
});
```

- The plugins are Bun plugins (`{ name, setup(build) }`). The dev server
runs them on the pages and the files that the pages import, the same as
the plugins in `[serve.static] plugins` of `bunfig.toml`.
- The default export can also be a function. It gets `{ mode:
"development" }`.
- `bun:bake` is a new built-in module. It has one function,
`defineConfig`, which returns its argument. It resolves only where `bun
bake` is (the Bake feature flag).
- The plugins of `bunfig.toml` still apply, before the plugins of
`bake.config.ts`.

How it works

- `internal/bake.ts` loads `bake.config.ts` before it starts the server,
and checks it. Then it gives the plugin objects and their names to the
VM (`$newRustFunction("bake/config/plugins.rs", "setConfigPlugins")`).
The VM keeps them in `bake_config_plugins` (a JS array) and
`bake_config_plugin_names`.
- The server already keeps the names of the `bunfig.toml` plugins on the
VM, and loads them when a route needs plugins. Now it also starts that
load when only `bake.config.ts` has plugins, and
`loadAndResolvePluginsForServe` takes plugin objects as well as module
names.
- The dev server's Tailwind check (which bundles the Tailwind CSS again
after a change) also looks at the names of the `bake.config.ts` plugins.

What `bun bake` rejects, with an error

- A default export that is not an object or a function.
- An option other than `plugins`.
- A plugin that is not an object or a function, has no `name`, or has no
`setup()`. As in `Bun.build()`, a function with `name` and `setup` is a
plugin.

`bun bake` now always reloads `app/server.ts` in place on a change, as
`bun --hot` does, so it has no `--watch` and `--hot`:

- The CLI sets `HotReload::Hot` for `bun bake`. `bun bake --watch` is an
unknown argument, and `bun --watch bake` is an error, also with
`--check`. The CLI records `--watch` in `ctx.debug.watch_flag`, because
`HotReload::Watch` alone does not tell it from `--hot` with `--check`.
`bun bake --check` still runs as `--watch` (it starts again after a
change, to type check again), as `bun --check` does with `--hot`.
- An error that a change to `app/server.ts` or `bake.config.ts` can fix
no longer exits: the process shows it and reloads after the fix, as
under `bun --hot`. An error in the arguments, in the names of the pages,
or "nothing to serve" still exits with code 1, because no change to a
loaded file can fix it.
- A bad `--port` or `--host` must not leave the process waiting for a
change: the shared flag parser (`internal/serve_cli.ts`) takes a port
only as a whole number from 0 to 65535, and a `Bun.serve()` failure at
the start exits 1 when `--port` or `--host` is set. `bun ./index.html`
gets the same port check: `--port=abc` is an error there too.
- A mistake that `bun bake` finds in the arguments or the app's files (a
`BakeError`) prints as one line, `error: <message>`, with no stack trace
into `internal/bake.ts`. Then the process exits 1, or waits for a change
when one can fix it. "Nothing to serve" now says what to add. An error
that the app's own code throws keeps its stack trace.
- `Bun__loadBuiltinEntryPoint` marks the promise of the built-in entry
point as handled. The VM reports its rejection; before, the rejection
tracker reported it a second time. `bun ./index.html` gets this too.
- The plugins load once. When `bake.config.ts` changes, `bun bake`
prints `"bake.config.ts" changed. Restart "bun bake" to use the new
plugins.`

Also in this PR: `bun bake` reads the `host` option of the
`app/server.ts` export when it has no `hostname`, as `Bun.serve()` does
(a review comment on #45682).

Not in this PR

- Vite-style plugins (`transform`, `load` and other hooks). `bun bake`
takes Bun plugins.
- A restart of the process when `bake.config.ts` changes, so the new
plugins apply on their own. Today `bun bake` asks for a restart. A
follow-up PR.
- The types of `bun:bake` and the docs are in the docs PR (#45751), to
merge when Bake is on in a release. Until then, `bun --check` on a
project with `bake.config.ts` reports that `bun:bake` has no types.

Tests

- `test/cli/bake/bake.test.ts`: a `bake.config.ts` plugin (in a
`defineConfig` function, with `mode`) and a `bunfig.toml` plugin both
change the CSS of a page; a plugin function sees the project as
`build.config.root` when there is no bunfig.toml; each error above; the
restart note; a reload of `app/server.ts` on the same server, with no
flag; `--watch` is refused.
- `test/bake/serve-plugins-dev-server.test.ts`,
`test/bake/dev/css.test.ts`, `test/bake/hmr-socket-protocol.test.ts` and
the `Bun.serve` HTML tests pass, for the `bunfig.toml` plugins.
alii added a commit that referenced this pull request Oct 18, 2026
This PR adds `.tsx` and `.jsx` pages to `bun bake`, and frameworks in
`bake.config.ts`. It is the next step of the new Bake design
(https://github.com/alii/bake-rfc), after #45902.

```tsx
// app/pages/blog/[slug].tsx
export default function Post({ params }) {
  return <h1>{params.slug}</h1>;
}
```

- A framework decides how files become routes and how a route becomes
HTML. `bun bake` has a default framework: the `.tsx` and `.jsx` pages of
`app/pages/`, in Next.js pages style (`index.tsx`, `[slug].tsx`,
`_layout.tsx` for layouts; files and folders that start with `_` are not
pages).
- The default framework uses the project's own `react` and `react-dom`,
not a copy in Bun. It streams the HTML from the server, then hydrates it
in the browser. Fast Refresh keeps the state of a page when you edit it.
- Before it starts, `bun bake` checks the project's React for the
default framework. It exits with a one-line error when React is not
installed, when the version is not 18 or 19, or when `react` and
`react-dom` are different versions. Each error gives the `bun add`
command that fixes it.
- A plugin in `bake.config.ts` can bring its own framework instead. Its
`framework` is the same object as the `framework` of the `app` option of
`Bun.serve()`, read by the same parser: its routers
(`fileSystemRouterTypes`: folder, style, entry points, extensions,
layouts), `serverComponents`, `reactFastRefresh`, `builtInModules` and
`plugins`.

  ```ts
  export default {
    plugins: [{
      name: "my-framework",
      framework: {
        fileSystemRouterTypes: [{
          root: "src/routes",
          style: "nextjs-pages",
          serverEntryPoint: "./framework/server.ts",
          clientEntryPoint: "./framework/client.ts",
          clientPages: true,
        }],
      },
    }],
  };
  ```

A plugin with a `framework` needs no `setup()`. Two plugins with a
framework are an error. `.html` pages in `app/pages/` and
`app/server.ts` work with every framework.
- A router type has a new option, `clientPages`. With it, the pages and
layouts also go into the browser build, and the client entry point loads
those of the current route from `route` of `bun:bake/client`, to hydrate
the server's HTML. It cannot be used with `serverComponents`, where the
browser gets the client components instead. The default framework sets
it.

How it works

- `internal/bake.ts` checks the `framework` of a plugin when it loads
`bake.config.ts`: it runs the parser of `Bun.serve()` on it
(`checkFramework`), and resolves its entry points from the project root.
A mistake is then a one-line error of `bake.config.ts`, and `bun bake`
waits for a fix.
- `internal/bake.ts` gives the framework to its own `Bun.serve()`
through a private function (`setPages`), not a new public option.
`ServerConfig` parses the plugin's object with `Framework::from_js`, or
makes the default with `Framework::bake_pages`.
- With `clientPages`, the dev server adds the page and its layouts to
the client graph, traces them into the route's browser bundle, and
writes their IDs into `config.route` of the bundle. The browser runtime
gives them to `bun:bake/client` as `route.page()` and `route.layouts()`.
- A change to a page changes both its server copy and its browser copy.
The browser runtime used to see only "server code changed" and dropped
the JavaScript part of the update. When `config.route` is set, it now
applies that part too, so Fast Refresh updates the page in place.
- In the dev server, `bun:bake/client` and `bun:bake/server` now resolve
for every framework. Before, they resolved only with server components.
The HMR runtime always provides them.

Fixes in the same code

- Two pages with the same URL pattern (`[a].tsx` and `[b].tsx`) panicked
with "TODO: propagate aliased route error". The router now reports its
usual "Multiple pages matching the same route pattern is ambiguous"
error, with both files, and keeps serving.
- A route of the server (`app/server.ts` routes, `.html` pages) with the
same path as a framework page hid the page with no message. The dev
server now prints `error: The server has a route "/about", which hides
the page app/pages/about.ts`, and keeps serving. It checks when the
server sets its routes (at the start and on a reload of `app/server.ts`)
and when the router adds a page; the test covers the start.
- A request for the browser bundle of a route whose router has no client
entry point panicked (`assertion failed: self.current_chunk_len > 0`).
It now gets a 404, and `meta.modules` is empty for such a route.

Not in this PR

- `bun bake build` (a production build).
- A custom routing style: `style` is one of the three Next.js styles.
The function form in the types was never implemented.
- The docs of frameworks in `bake.config.ts` and of `route`. They go in
the docs PR (#45751).

Tests

- `test/cli/bake/bake.test.ts`: the default framework with a layout and
a param, next to an `.html` page and `app/server.ts` routes; the three
React errors; a framework plugin with its own folder (`src/routes`) and
extension, no React and no client entry point; `clientPages` puts only
the current route's page in its browser bundle; two pages with the same
pattern; a page that an `app/server.ts` route hides; two frameworks; a
framework that the parser rejects; a framework entry point that cannot
be found; `clientPages` with `serverComponents`.
- `test/bake/dev/react-spa.test.ts`: in the test browser, a page of the
default framework hydrates (a click changes the state), then an edit to
the page keeps the state (`clicks 2`, not a reload).
- All of the new tests fail on the released Bun. React 18 (the test
suite's) and React 19 both pass the hydration test locally.
- In `test/bake/`, 31 tests fail locally: the `production`,
`server-sourcemap`, `react-response`, `ssg-pages-router` and
`request-cookies` tests install the experimental React of the server
components framework first, and that install fails on my machine's
package mirror. The rest pass. CI runs them all.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants