aboutsummaryrefslogtreecommitdiff
path: root/docs/bundler
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--docs/bundler/intro.md75
-rw-r--r--docs/bundler/loaders.md136
-rw-r--r--docs/bundler/plugins.md (renamed from docs/runtime/plugins.md)42
3 files changed, 249 insertions, 4 deletions
diff --git a/docs/bundler/intro.md b/docs/bundler/intro.md
new file mode 100644
index 000000000..5056c26a9
--- /dev/null
+++ b/docs/bundler/intro.md
@@ -0,0 +1,75 @@
+<!-- This document is a work in progress. It's not currently included in the actual docs. -->
+
+The goal of this document is to break down why bundling is necessary, how it works, and how the bundler became such a key part of modern JavaScript development. The content is not specific to Bun's bundler, but is rather aimed at anyone looking for a greater understanding of how bundlers work and, by extension, how most modern frameworks are implemented.
+
+## What is bundling
+
+With the adoption of ECMAScript modules (ESM), browsers can now resolve `import`/`export` statements in JavaScript files loaded via `<script>` tags.
+
+{% codetabs %}
+
+```html#index.html
+<html>
+ <head>
+ <script type="module" src="/index.js" ></script>
+ </head>
+</html>
+```
+
+```js#index.js
+import {sayHello} from "./hello.js";
+
+sayHello();
+```
+
+```js#hello.js
+export function sayHello() {
+ console.log("Hello, world!");
+}
+```
+
+{% /codetabs %}
+
+When a user visits this website, the files are loaded in the following order:
+
+{% image src="/images/module_loading_unbundled.png" /%}
+
+{% callout %}
+**Relative imports** — Relative imports are resolved relative to the URL of the importing file. Because we're importing `./hello.js` from `/index.js`, the browser resolves it to `/hello.js`. If instead we'd imported `./hello.js` from `/src/index.js`, the browser would have resolved it to `/src/hello.js`.
+{% /callout %}
+
+This approach works, it requires three round-trip HTTP requests before the browser is ready to render the page. On slow internet connections, this may add up to a non-trivial delay.
+
+This example is extremely simplistic. A modern app may be loading dozens of modules from `node_modules`, each consisting of hundrends of files. Loading each of these files with a separate HTTP request becomes untenable very quickly. While most of these requests will be running in parallel, the number of round-trip requests can still be very high; plus, there are limits on how many simultaneous requests a browser can make.
+
+{% callout %}
+Some recent advances like modulepreload and HTTP/3 are intended to solve some of these problems, but at the moment bundling is still the most performant approach.
+{% /callout %}
+
+The answer: bundling.
+
+## Entrypoints
+
+A bundler accepts an "entrypoint" to your source code (in this case, `/index.js`) and outputs a single file containing all of the code needed to run your app. If does so by parsing your source code, reading the `import`/`export` statements, and building a "module graph" of your app's dependencies.
+
+{% image src="/images/bundling.png" /%}
+
+We can now load `/bundle.js` from our `index.html` file and eliminate a round trip request, decreasing load times for our app.
+
+{% image src="/images/module_loading_bundled.png" /%}
+
+## Loaders
+
+Bundlers typically have some set of built-in "loaders".
+
+## Transpilation
+
+The JavaScript files above are just that: plain JavaScript. They can be directly executed by any modern browser.
+
+But modern tooling goes far beyond HTML, JavaScript, and CSS. JSX, TypeScript, and PostCSS/CSS-in-JS are all popular technologies that involve non-standard syntax that must be converted into vanilla JavaScript and CSS before if can be consumed by a browser.
+
+## Chunking
+
+## Module resolution
+
+## Plugins
diff --git a/docs/bundler/loaders.md b/docs/bundler/loaders.md
new file mode 100644
index 000000000..c8f067385
--- /dev/null
+++ b/docs/bundler/loaders.md
@@ -0,0 +1,136 @@
+The Bun bundler implements a set of default loaders out of the box. As a rule of thumb, the bundler and the runtime both support the same set of file types out of the box.
+
+`.js` `.cjs` `.mjs` `.mts` `.cts` `.ts` `.tsx` `.jsx` `.toml` `.json`
+
+{% callout %}
+The runtime also supports `.wasm` and `.node` binaries, which are not easily embedded in a bundle. These are effectively not supported by Bun's bundler.
+{% /callout %}
+
+This document describes how these extensions map onto Bun's set of built-in _loaders_. Every loader has a name, such as `js`, `tsx`, or `json`. These names are used when building [plugins](/docs/bundler/plugins) that extend Bun with custom loaders.
+
+{% table %}
+
+- Loader
+- Extensions
+- Description
+
+---
+
+- `js`
+- `.cjs` `.mjs`
+- **JavaScript.** Parses the code and applies a set if default transforms, like dead-code elimination, tree shaking, and environment variable inlining. Note that Bun does not attempt to down-convert syntax at the moment.
+
+---
+
+- `jsx`
+- `.js` `.jsx`
+- **JavaScript + JSX.** Same as the `js` loader, but JSX syntax is supported. By default, JSX is downconverted to `createElement` syntax and a `jsx` factory is injected into the bundle. This can be configured using the relevant `jsx*` compiler options in `tsconfig.json`.
+
+---
+
+- `ts`
+- `.mts` `.cts`
+- **TypeScript.** Strips out all TypeScript syntax, then behaves identically to the `js` loader. Bun does not perform typechecking.
+
+---
+
+- `tsx`
+- `.ts` `.tsx`
+- **TypeScript + JSX**. Transpiles both TypeScript and JSX to vanilla JavaScript.
+
+---
+
+- `json`
+- `.json`
+- **JSON**. JSON files are parsed and inlined into the bundle as a JavaScript object.
+
+ ```ts
+ import pkg from "./package.json";
+ pkg.name; // => "my-package"
+ ```
+
+ If a `.json` file is passed as an entrypoint, it will be converted to a `.js` with the parsed object as a default export.
+
+ {% codetabs %}
+
+ ```json#Input
+ {
+ "name": "John Doe",
+ "age": 35,
+ "email": "johndoe@example.com"
+ }
+ ```
+
+ ```js#Output
+ export default {
+ name: "John Doe",
+ age: 35,
+ email: "johndoe@example.com"
+ }
+ ```
+
+ {% /codetabs %}
+
+---
+
+- `toml`
+- `.toml`
+- **TOML**. TOML files are parsed and inlined into the bundle as a JavaScript object.
+
+ ```ts
+ import config from "./bunfig.toml";
+ config.logLevel; // => "debug"
+ ```
+
+ If a `.toml` file is passed as an entrypoint, it will be converted to a `.js` with the parsed object as a default export.
+
+ {% codetabs %}
+
+ ```toml#Input
+ name = "John Doe"
+ age = 35
+ email = "johndoe@example.com"
+ ```
+
+ ```js#Output
+ export default {
+ name: "John Doe",
+ age: 35,
+ email: "johndoe@example.com"
+ }
+ ```
+
+ {% /codetabs %}
+
+---
+
+- `text`
+- `.txt`
+- **Text files**. The contents of the text file are read and inlined into the bundle as a string.
+
+ ```ts
+ import contents from "./file.txt";
+ console.log(contents); // => "Hello, world!"
+ ```
+
+ If a `.txt` file is passed as an entrypoint, it will be converted to a `.js` with the contents of the file as a default export.
+
+ {% codetabs %}
+
+ ```txt#Input
+ Hello, world!
+ ```
+
+ ```js#Output
+ export default "Hello, world!";
+ ```
+
+ {% /codetabs %}
+
+---
+
+- `file`
+- `.*`
+- **File loader**. Any unrecognized file type is handled using the `file` loader. The file is copied into the `outdir` as-is. The name of the copied file is determined using the value of `naming.asset`.
+
+{% /table %}
diff --git a/docs/runtime/plugins.md b/docs/bundler/plugins.md
index 788bfb3ef..68d2b3107 100644
--- a/docs/runtime/plugins.md
+++ b/docs/bundler/plugins.md
@@ -2,29 +2,54 @@
**Note** — Introduced in Bun v0.1.11.
{% /callout %}
-Bun's runtime can be extended to support additional file types using _plugins_. Plugins can intercept imports and perform custom loading logic: reading files, transpiling code, etc. They can be used to extend Bun's runtime with _loaders_ for additional file types.
+Bun provides a universal plugin API that can be used to extend both the _runtime_ and _bundler_.
+
+Plugins intercept imports and perform custom loading logic: reading files, transpiling code, etc. They can be used to add support for additional file types, like `.scss` or `.yaml`. In the context of Bun's bundler, plugins can be used to implement framework-level features like CSS extraction, macros, and client-server code co-location.
## Usage
A plugin is defined as simple JavaScript object containing a `name` property and a `setup` function. Register a plugin with Bun using the `plugin` function.
```tsx#yamlPlugin.ts
-import { plugin } from "bun";
+import type { BunPlugin } from "bun";
-plugin({
+const myPlugin: BunPlugin = {
name: "YAML loader",
setup(build) {
// implementation
},
+};
+```
+
+This plugin can be passed into the `plugins` array when calling `Bun.build`.
+
+```ts
+Bun.build({
+ entrypoints: ["./app.ts"],
+ outdir: "./out",
+ plugins: [myPlugin],
});
```
+It can also be "registered" with the Bun runtime using the `Bun.plugin()` function. Once registered, the currently executing `bun` process will incorporate the plugin into its module resolution algorithm.
+
+```ts
+Bun.plugin(myPlugin);
+```
+
To consume this plugin, add this file to the `preload` option in your [`bunfig.toml`](/docs/runtime/configuration). Bun automatically loads the files/modules specified in `preload` before running a file.
```toml
preload = ["./yamlPlugin.ts"]
```
+To preload files during `bun test`:
+
+```toml
+[test]
+preload = ["./loader.ts"]
+```
+
{% details summary="Usage without preload" %}
Alternatively, you can import this file manually at the top of your project's entrypoint, before any application code is imported.
@@ -243,15 +268,24 @@ namespace Bun {
}
type PluginBuilder = {
+ onResolve: (
+ args: { filter: RegExp; namespace?: string },
+ callback: (args: { path: string; importer: string }) => {
+ path: string;
+ namespace?: string;
+ } | void,
+ ) => void;
onLoad: (
args: { filter: RegExp; namespace?: string },
callback: (args: { path: string }) => {
- loader?: "js" | "jsx" | "ts" | "tsx" | "json" | "toml" | "object";
+ loader?: Loader;
contents?: string;
exports?: Record<string, any>;
},
) => void;
};
+
+type Loader = "js" | "jsx" | "ts" | "tsx" | "json" | "toml" | "object";
```
The `onLoad` method optionally accepts a `namespace` in addition to the `filter` regex. This namespace will be be used to prefix the import in transpiled code; for instance, a loader with a `filter: /\.yaml$/` and `namespace: "yaml:"` will transform an import from `./myfile.yaml` into `yaml:./myfile.yaml`.