diff options
author | 2023-04-28 08:35:20 -0700 | |
---|---|---|
committer | 2023-04-28 08:35:20 -0700 | |
commit | 1483d73c3a9a4a045287df62c85b2173d80e8ceb (patch) | |
tree | 77aa6307d7b452b2759c3e7d53f3ebdc8761dd78 | |
parent | 52c50e37371ed0bf208de2b2e8dbf7c2cc1bd97c (diff) | |
download | bun-1483d73c3a9a4a045287df62c85b2173d80e8ceb.tar.gz bun-1483d73c3a9a4a045287df62c85b2173d80e8ceb.tar.zst bun-1483d73c3a9a4a045287df62c85b2173d80e8ceb.zip |
Bundler docs updates + support for `naming` string (#2767)
* Bundler docs updates
* Remove comments
* Updates
* Fix bunx usages
* Add info about metafile
-rw-r--r-- | docs/bundler/loaders.md | 284 | ||||
-rw-r--r-- | docs/cli/build.md | 299 | ||||
-rw-r--r-- | packages/bun-types/bun.d.ts | 20 | ||||
-rw-r--r-- | src/bun.js/api/JSBundler.zig | 41 | ||||
-rw-r--r-- | src/cli.zig | 2 |
5 files changed, 394 insertions, 252 deletions
diff --git a/docs/bundler/loaders.md b/docs/bundler/loaders.md index c8f067385..7cac75648 100644 --- a/docs/bundler/loaders.md +++ b/docs/bundler/loaders.md @@ -1,136 +1,234 @@ 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` +`.js` `.cjs` `.mjs` `.mts` `.cts` `.ts` `.tsx` `.jsx` `.toml` `.json` `.txt` `.wasm` `.node` -{% 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 %} +Bun uses the file extension to determine which built-in _loader_ should be used to parse the file. 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. -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. +## Built-in loaders -{% table %} +### `js` -- Loader -- Extensions -- Description +**JavaScript**. Default for `.cjs` and `.mjs`. ---- +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. -- `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` ---- +**JavaScript + JSX.**. Default for `.js` and `.jsx`. -- `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`. +Same as the `js` loader, but JSX syntax is supported. By default, JSX is downconverted to plain JavaScript; the details of how this is done depends on the `jsx*` compiler options in your `tsconfig.json`. Refer to the TypeScript documentation [on JSX](https://www.typescriptlang.org/docs/handbook/jsx.html) for more information. ---- +### `ts` -- `ts` -- `.mts` `.cts` -- **TypeScript.** Strips out all TypeScript syntax, then behaves identically to the `js` loader. Bun does not perform typechecking. +**TypeScript loader**. Default for `.ts`, `.mts`, and `.cts`. ---- +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. +### `tsx` ---- +**TypeScript + JSX loader**. Default for `.tsx`. Transpiles both TypeScript and JSX to vanilla JavaScript. -- `json` -- `.json` -- **JSON**. JSON files are parsed and inlined into the bundle as a JavaScript object. +### `json` - ```ts - import pkg from "./package.json"; - pkg.name; // => "my-package" - ``` +**JSON loader**. Default for `.json`. - If a `.json` file is passed as an entrypoint, it will be converted to a `.js` with the parsed object as a default export. +JSON files can be directly imported. - {% codetabs %} +```ts +import pkg from "./package.json"; +pkg.name; // => "my-package" +``` - ```json#Input - { - "name": "John Doe", - "age": 35, - "email": "johndoe@example.com" - } - ``` +During bundling, the parsed JSON is inlined into the bundle as a JavaScript object. - ```js#Output - export default { - name: "John Doe", - age: 35, - email: "johndoe@example.com" - } - ``` +```ts +var pkg = { + name: "my-package", + // ... other fields +}; +pkg.name; +``` - {% /codetabs %} +If a `.json` file is passed as an entrypoint to the bundler, it will be converted to a `.js` module that `export default`s the parsed object. ---- +{% codetabs %} -- `toml` -- `.toml` -- **TOML**. TOML files are parsed and inlined into the bundle as a JavaScript object. +```json#Input +{ + "name": "John Doe", + "age": 35, + "email": "johndoe@example.com" +} +``` - ```ts - import config from "./bunfig.toml"; - config.logLevel; // => "debug" - ``` +```js#Output +export default { + name: "John Doe", + age: 35, + email: "johndoe@example.com" +} +``` - 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 %} - {% codetabs %} +### `toml` - ```toml#Input - name = "John Doe" - age = 35 - email = "johndoe@example.com" - ``` +**TOML loader**. Default for `.toml`. - ```js#Output - export default { - name: "John Doe", - age: 35, - email: "johndoe@example.com" - } - ``` +TOML files can be directly imported. Bun will parse them with its fast native TOML parser. - {% /codetabs %} +```ts +import config from "./bunfig.toml"; +config.logLevel; // => "debug" +``` ---- +During bundling, the parsed TOML is inlined into the bundle as a JavaScript object. + +```ts +var config = { + logLevel: "debug", + // ...other fields +}; +config.logLevel; +``` + +If a `.toml` file is passed as an entrypoint, it will be converted to a `.js` module that `export default`s the parsed object. + +{% 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` + +**Text loader**. Default for `.txt`. + +The contents of the text file are read and inlined into the bundle as a string. +Text files can be directly imported. The file is read and returned as a string. + +```ts +import contents from "./file.txt"; +console.log(contents); // => "Hello, world!" +``` + +When referenced during a build, the contents are into the bundle as a string. + +```ts +var contents = `Hello, world!`; +console.log(contents); +``` + +If a `.txt` file is passed as an entrypoint, it will be converted to a `.js` module that `export default`s the file contents. + +{% codetabs %} + +```txt#Input +Hello, world! +``` + +```js#Output +export default "Hello, world!"; +``` + +{% /codetabs %} -- `text` -- `.txt` -- **Text files**. The contents of the text file are read and inlined into the bundle as a string. +### `wasm` - ```ts - import contents from "./file.txt"; - console.log(contents); // => "Hello, world!" - ``` +**WebAssembly loader**. Default for `.wasm`. - 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. +In the runtime, WebAssembly files can be directly imported. The file is read and returned as a `WebAssembly.Module`. - {% codetabs %} +```ts +import wasm from "./module.wasm"; +console.log(wasm); // => WebAssembly.Module +``` - ```txt#Input - Hello, world! - ``` +In the bundler, `.wasm` files are handled using the [`file`](#file) loader. - ```js#Output - export default "Hello, world!"; - ``` +### `napi` - {% /codetabs %} +**Native addon loader**. Default for `.node`. + +In the runtime, native addons can be directly imported. + +```ts +import addon from "./addon.node"; +console.log(addon); +``` + +In the bundler, `.node` files are handled using the [`file`](#file) loader. + +### `file` + +**File loader**. Default for all unrecognized file types. + +The file loader resolves the import as a _path/URL_ to the imported file. It's commonly used for referencing media or font assets. + +```ts#logo.ts +import logo from "./logo.svg"; +console.log(logo); +``` + +_In the runtime_, Bun checks that the `logo.svg` file exists and converts it to an absolute path to the location of `logo.svg` on disk. + +```bash +$ bun run logo.ts +/path/to/project/logo.svg +``` + +_In the bundler_, things are slightly different. The file is copied into `outdir` as-is, and the import is resolved as a relative path pointing to the copied file. + +```ts#Output +var logo = "./logo.svg"; +console.log(logo); +``` + +If a value is specified for `publicPath`, the import will use value as a prefix to construct an absolute path/URL. + +{% table %} + +- Public path +- Resolved import --- -- `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`. +- `""` (default) +- `/logo.svg` + +--- + +- `"/assets"` +- `/assets/logo.svg` + +--- + +- `"https://cdn.example.com/"` +- `https://cdn.example.com/` {% /table %} + +{% callout %} +The location and file name of the copied file is determined by the value of [`naming.asset`](/docs/cli/build#naming). +{% callout %} +This loader is copied into the `outdir` as-is. The name of the copied file is determined using the value of `naming.asset`. + +{% details summary="Fixing TypeScript import errors" %} +If you're using TypeScript, you may get an error like this: + +```ts +// TypeScript error +// Cannot find module './logo.svg' or its corresponding type declarations. +``` diff --git a/docs/cli/build.md b/docs/cli/build.md index 2c7c78d4f..b4c7984dc 100644 --- a/docs/cli/build.md +++ b/docs/cli/build.md @@ -1,5 +1,5 @@ {% callout %} -**Note** — Added in Bun v0.6.0 +**Note** — Available in the Bun v0.6.0 nightly. Run `bun upgrade --canary` to try it out. {% /callout %} 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. @@ -58,7 +58,7 @@ To create our bundle: {% codetabs %} ```ts#JavaScript -Bun.build({ +await Bun.build({ entrypoints: ['./index.tsx'], outdir: './out', }) @@ -70,12 +70,7 @@ $ bun build ./index.tsx --outdir ./out {% /codetabs %} -Let's break that down. - -- `entrypoints` — **Required.** An array of paths corresponding to the entrypoints of our application. In this case, we just have one. -- `outdir` — **Required.** The directory where output files will be written. - -Running this build will generate a new file `./out/index.js`. +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: ```ts . @@ -85,18 +80,18 @@ Running this build will generate a new file `./out/index.js`. └── index.js ``` -It looks something like this: +The contents of the `out/index.js` file looks something like this: ```js#out/index.js // ... // ~20k lines of code // including the contents of `react-dom/client` and all its dependencies -// this is where the $jsx and $createRoot functions are defined +// this is where the $jsxDEV and $createRoot functions are defined // Component.tsx function Component(props) { - return $jsx("p", { + return $jsxDEV("p", { children: props.message }, undefined, false, undefined, this); } @@ -104,7 +99,7 @@ function Component(props) { // index.tsx var rootNode = document.getElementById("root"); var root = $createRoot(rootNode); -root.render($jsx(Component, { +root.render($jsxDEV(Component, { message: "Sup!" }, undefined, false, undefined, this)); ``` @@ -181,58 +176,42 @@ Like the Bun runtime, the bundler supports an array of file types out of the box console.log(contents); // => "Hello, world!" ``` ---- - -- `.*` -- If the bundler encounters a file with an unsupported extension, it treats it as an _external file_. That means the import is converted into a path, and the referenced file is copied into the `outdir` as-is. - - {% codetabs %} - - ```ts#Build_file - Bun.build({ - entrypoints: ['./index.ts'], - outdir: './out', - origin: 'https://example.com', - }) - ``` +{% /table %} - ```ts#Input - import logo from "./logo.svg"; - console.log(logo); - ``` +### Assets - ```ts#Output - var logo = "./logo-ab237dfe.svg"; - console.log(logo); - ``` +If the bundler encounters an import with an unrecognized extension, it treats the imported file as an _external file_. The referenced file is copied as-is into `outdir`, and the import is resolved as a _path_ to the file. - {% /codetabs %} +{% codetabs %} - By default, a hash is added to the file name to avoid collisions; this behavior can be overridden with the [`naming.asset`](#naming) option. +```ts#Build_file +await Bun.build({ + entrypoints: ['./index.ts'], + outdir: './out' +}) +``` - If a value is provided for `origin`, the bundler will construct an absolute URL instead of using a relative path. +```ts#Input +import logo from "./logo.svg"; +console.log(logo); +``` - {% codetabs %} +```ts#Output +var logo = "./logo-ab237dfe.svg"; +console.log(logo); +``` - ```ts-diff#Build_file - Bun.build({ - entrypoints: ['./index.ts'], - outdir: './out', - + origin: 'https://example.com', - }) - ``` +{% /codetabs %} - ```ts-diff#Output - - var logo = "./logo-ab237dfe.svg"; - + var logo = "https://example.com/logo-ab237dfe.svg"; - console.log(logo); - ``` +{% callout %} +The exact behavior of the file loader is also impacted by [`naming`](#naming) and [`publicPath`](#publicpath). +{% /callout %} - {% /codetabs %} +Refer to the [Bundler > Loaders](/docs/bundler/loaders#file) page for more complete documentation on the file loader. -{% /table %} +### Plugins -The behavior described in this table can be overridden with [plugins](/docs/bundler/plugins). Refer to the [Bundler > Loaders](/docs/bundler/loaders) page for complete documentation on Bun's built-in loaders. +The behavior described in this table can be overridden with [plugins](/docs/bundler/plugins). Refer to the [Bundler > Loaders](/docs/bundler/plugins) page for complete documentation. ## API @@ -240,9 +219,65 @@ The behavior described in this table can be overridden with [plugins](/docs/bund **Required.** An array of paths corresponding to the entrypoints of our application. One bundle will be generated for each entrypoint. +{% codetabs %} + +```ts#JavaScript +const result = await Bun.build({ + entrypoints: ['./index.ts'] +}); // => Promise + +await result; +// => { outputs: Array<{ path: string; result: Blob }> } +``` + +```bash#CLI +$ bun build --entrypoints ./index.ts +# the bundle will be printed to stdout +# <bundled code> +``` + +{% /codetabs %} + ### `outdir` -**Required.** The directory where output files will be written. +The directory where output files will be written. + +{% codetabs %} + +```ts#JavaScript +const result = await Bun.build({ + entrypoints: ['./index.ts'], + outdir: './out' +}); + +result; +// => { outputs: Array<{ path: string; result: BunFile }> } +``` + +```bash#CLI +$ bun build --entrypoints ./index.ts --outdir ./out +# the bundle will be printed to stdout +# ... +``` + +{% /codetabs %} + +When `outdir` is specified: + +- The JavaScript API will write the generated bundles to the appropriate location in `outdir`. The result of the `Bun.build()` call will contain `BunFile` instances corresponding to the new files. + + ```ts + const result = await Bun.build({ + /* ... */ + }); + // => { outputs: Array<{ path: string; result: BunFile }> } + + for (const { path, result } of result.outputs) { + console.log(`Wrote file: ${path}`); + } + ``` + +- The CLI will print a summary of the written files. The bundled code will not be written to `stdout`. ### `target` @@ -251,7 +286,7 @@ The intended execution environment for the bundle. {% codetabs %} ```ts#JavaScript -Bun.build({ +await Bun.build({ entrypoints: ['./index.ts'], outdir: './out', target: 'browser', // default @@ -259,7 +294,7 @@ Bun.build({ ``` ```bash#CLI -$ bunx build --entrypoints ./index.ts --outdir ./out --target browser +$ bun build --entrypoints ./index.ts --outdir ./out --target browser ``` {% /codetabs %} @@ -298,27 +333,27 @@ Currently the bundler only supports one module format: `"esm"`. Support for `"cj {% codetabs %} ```ts#Bun.build -Bun.build({ +await Bun.build({ entrypoints: ['./index.tsx'], outdir: './out', - module: "esm", + format: "esm", }) ``` ```bash#CLI -$ bun build ./index.tsx --outdir ./out --module esm +$ bun build ./index.tsx --outdir ./out --format esm ``` {% /codetabs %} --> -### `bundling` +<!-- ### `bundling` Whether to enable bundling. {% codetabs %} ```ts#JavaScript -Bun.build({ +await Bun.build({ entrypoints: ['./index.tsx'], outdir: './out', bundling: true, // default @@ -337,7 +372,7 @@ Set to `false` to disable bundling. Instead, files will be transpiled and indivi {% codetabs %} ```ts#JavaScript -Bun.build({ +await Bun.build({ entrypoints: ['./index.tsx'], outdir: './out', bundling: false, @@ -348,7 +383,7 @@ Bun.build({ $ bun build ./index.tsx --outdir ./out --no-bundling ``` -{% /codetabs %} +{% /codetabs %} --> ### `splitting` @@ -357,7 +392,7 @@ Whether to enable code splitting. {% codetabs %} ```ts#JavaScript -Bun.build({ +await Bun.build({ entrypoints: ['./index.tsx'], outdir: './out', splitting: false, // default @@ -393,7 +428,7 @@ To bundle `entry-a.ts` and `entry-b.ts` with code-splitting enabled: {% codetabs %} ```ts#JavaScript -Bun.build({ +await Bun.build({ entrypoints: ['./entry-a.ts', './entry-b.ts'], outdir: './out', splitting: true, @@ -429,7 +464,7 @@ A list of plugins to use during bundling. {% codetabs %} ```ts#JavaScript -Bun.build({ +await Bun.build({ entrypoints: ['./index.tsx'], outdir: './out', plugins: [/* ... */], @@ -458,6 +493,7 @@ const result = await Bun.build({ console.log(result.manifest); ``` +The manifest takes the following form: {% details summary="Manifest structure" %} The manifest has the following form: @@ -503,6 +539,8 @@ export type ImportKind = {% /details %} +By design, the manifest is a simple JSON object that can easily be serialized or written to disk. It is also compatible with esbuild's [`metafile`](https://esbuild.github.io/api/#metafile) format. + ### `sourcemap` Specifies the type of sourcemap to generate. @@ -510,7 +548,7 @@ Specifies the type of sourcemap to generate. {% codetabs %} ```ts#JavaScript -Bun.build({ +await Bun.build({ entrypoints: ['./index.tsx'], outdir: './out', sourcemap: "inline", // default "none" @@ -544,12 +582,18 @@ $ bun build ./index.tsx --outdir ./out --sourcemap=inline ### `minify` -Whether to enable minification. Default `false`. To enable minification: +Whether to enable minification. Default `false`. + +{% callout %} +When targeting `bun`, identifiers will be minified by default. +{% /callout %} + +To enable all minification options: {% codetabs %} ```ts#JavaScript -Bun.build({ +await Bun.build({ entrypoints: ['./index.tsx'], outdir: './out', minify: true, // default false @@ -562,12 +606,12 @@ $ bun build ./index.tsx --outdir ./out --minify {% /codetabs %} -This will enable all minification options. To granularly enable certain minifications: +To granularly enable certain minifications: {% codetabs %} ```ts#JavaScript -Bun.build({ +await Bun.build({ entrypoints: ['./index.tsx'], outdir: './out', minify: { @@ -595,7 +639,7 @@ A list of import paths to consider _external_. Defaults to `[]`. {% codetabs %} ```ts#JavaScript -Bun.build({ +await Bun.build({ entrypoints: ['./index.tsx'], outdir: './out', external: ["lodash", "react"], // default: [] @@ -618,7 +662,6 @@ import {z} from "zod"; const value = z.string().parse("Hello world!") console.log(_.upperCase(value)); - ``` Normally, bundling `index.tsx` would generate a bundle containing the entire source code of the `"zod"` package. If instead, we want to leave the `import` statement as-is, we can mark it as external: @@ -626,7 +669,7 @@ Normally, bundling `index.tsx` would generate a bundle containing the entire sou {% codetabs %} ```ts#JavaScript -Bun.build({ +await Bun.build({ entrypoints: ['./index.tsx'], outdir: './out', external: ['zod'], @@ -659,10 +702,10 @@ Customizes the generated file names. Defaults to `./[dir]/[name].[ext]`. {% codetabs %} ```ts#JavaScript -Bun.build({ +await Bun.build({ entrypoints: ['./index.tsx'], outdir: './out', - naming: "[dir]/[name].[ext]", // default + naming: "./[dir]/[name].[ext]", // default }) ``` @@ -694,12 +737,12 @@ With multiple entrypoints, the generated file hierarchy will reflect the directo └── index.js ``` -The names of these files can be customized with the `naming` field. This field accepts a template string that is used to generate the filenames for all bundles corresponding to entrypoints. where the following tokens are replaced with their corresponding values: +The names and locations of the generated files can be customized with the `naming` field. This field accepts a template string that is used to generate the filenames for all bundles corresponding to entrypoints. where the following tokens are replaced with their corresponding values: -- `[name]` - The name of the entrypoint file, without the extension, e.g. `index` -- `[ext]` - The extension of the generated bundle, e.g. `js` -- `[hash]` - A hash of the bundle contents, e.g. `a1b2c3d4` -- `[dir]` - The relative path from the build [`root`](#root) to the parent directory of the file, e.g. `nested` +- `[name]` - The name of the entrypoint file, without the extension. +- `[ext]` - The extension of the generated bundle. +- `[hash]` - A hash of the bundle contents. +- `[dir]` - The relative path from the build root to the parent directory of the file. For example: @@ -734,10 +777,10 @@ We can combine these tokens to create a template string. For instance, to includ {% codetabs %} ```ts#JavaScript -Bun.build({ +await Bun.build({ entrypoints: ['./index.tsx'], outdir: './out', - naming: '[dir]/[name]-[hash].[ext]', + naming: 'files/[dir]/[name]-[hash].[ext]', }) ``` @@ -753,7 +796,8 @@ This build would result in the following file structure: . ├── index.tsx └── out - └── index-a1b2c3d4.js + └── files + └── index-a1b2c3d4.js ``` When a `string` is provided for the `naming` field, it is used only for bundles _that correspond to entrypoints_. The names of [chunks](#splitting) and copied assets are not affected. Using the JavaScript API, separate template strings can be specified for each type of generated file. @@ -761,7 +805,7 @@ When a `string` is provided for the `naming` field, it is used only for bundles {% codetabs %} ```ts#JavaScript -Bun.build({ +await Bun.build({ entrypoints: ['./index.tsx'], outdir: './out', naming: { @@ -778,14 +822,14 @@ n/a {% /codetabs %} -### `root` +<!-- ### `root` The root directory of the project. {% codetabs %} ```ts#JavaScript -Bun.build({ +await Bun.build({ entrypoints: ['./pages/a.tsx', './pages/b.tsx'], outdir: './out', root: '.', @@ -812,7 +856,7 @@ We can build both entrypoints in the `pages` directory: {% codetabs %} ```ts#JavaScript -Bun.build({ +await Bun.build({ entrypoints: ['./pages/index.tsx', './pages/settings.tsx'], outdir: './out', }) @@ -843,7 +887,7 @@ This behavior can be overridden by specifying the `root` option: {% codetabs %} ```ts#JavaScript -Bun.build({ +await Bun.build({ entrypoints: ['./pages/index.tsx', './pages/settings.tsx'], outdir: './out', root: '.', @@ -867,92 +911,80 @@ By specifying `.` as `root`, the generated file structure will look like this: └── pages └── index.js └── settings.js -``` +``` --> -### `origin` +### `publicPath` -Used to generate absolute asset URLs. +A prefix to be appended to any import paths in bundled code. -{% codetabs %} - -```ts#JavaScript -Bun.build({ - entrypoints: ['./index.tsx'], - outdir: './out', - origin: 'https://cdn.example.com', // default is undefined -}) -``` +<!-- $ bun build ./index.tsx --outdir ./out --publicPath https://cdn.example.com --> -```bash#CLI -$ bun build ./index.tsx --outdir ./out --origin https://cdn.example.com -``` +In many cases, generated bundles will contain no `import` statements. After all, the goal of bundling is to combine all of the code into a single file. However there are a number of cases with the generated bundles will contain `import` statements. -{% /codetabs %} +- **Asset imports** — When importing an unrecognized file type like `*.svg`, the bundler defers to the [`file` loader](/docs/bundler/loaders#file), which copies the file into `outdir` as is. The import is converted into a variable +- **External modules** — Files and modules can be marked as [`external`](#external), in which case they will not be included in the bundle. Instead, the `import` statement will be left in the final bundle. +- **Chunking**. When [`splitting`](#splitting) is enabled, the bundler may generate separate "chunk" files that represent code that is shared among multiple entrypoints. -When the bundler encounters an unknown file type, it defaults to using the `"file"` loader. This converts the import path to an absolute URL that can be referenced in the file. This is useful for referencing images, fonts, and other static assets. +In any of these cases, the final bundles may contain paths to other files. By default these imports are _relative_. Here is an example of a simple asset import: -```tsx#Input -import logo from "./images/logo.svg"; +{% codetabs %} -export function Logo(){ - return <img src={logo} /> -} +```ts#Input +import logo from './logo.svg'; +console.log(logo); ``` -In the absence of a plugin that overrides `*.svg` loading, the `logo` import will be converted to a relative path: - -```ts -var logo = "./logo.svg"; - +```ts#Output +// logo.svg is copied into <outdir> +// and hash is added to the filename to prevent collisions +var logo = './logo-a7305bdef.svg'; console.log(logo); ``` -This is fine for local development, but in production, we may want these imports to correspond to absolute URLs. To do this, we can specify the `origin` option: +{% /codetabs %} + +Setting `publicPath` will prefix all file paths with the specified value. {% codetabs %} ```ts#JavaScript -Bun.build({ +await Bun.build({ entrypoints: ['./index.tsx'], outdir: './out', - origin: 'https://cdn.mydomain.com', + publicPath: 'https://cdn.example.com/', // default is undefined }) ``` ```bash#CLI -$ bun build ./index.tsx --outdir ./out --origin https://cdn.mydomain.com +n/a ``` {% /codetabs %} -With `origin` set to this value, the generated bundle will now be something like this: +The output file would now look something like this. -```ts-diff -- var logo = "./logo.svg"; -+ var logo = "https://cdn.mydomain.com/logo.svg"; - -console.log(logo); +```ts-diff#Output +- var logo = './logo-a7305bdef.svg'; ++ var logo = 'https://cdn.example.com/logo-a7305bdef.svg'; ``` ## Reference ```ts -Bun.build({ +await Bun.build({ entrypoints: string[]; // list of file path - outdir: string; // output directory + outdir?: string; // output directory target?: "browser" | "bun" | "node"; // default: "browser" - bundling?: boolean, // default: false, transform instead of bundling splitting?: boolean, // default true, enable code splitting plugins?: BunPlugin[]; manifest?: boolean; // whether to return manifest - external?: Array<string | RegExp>; + external?: Array<string>; naming?: string | { entrypoint?: string; chunk?: string; asset?: string; }, // default './[dir]/[name].[ext]' - root?: string; // project root - origin?: string; // e.g. http://mydomain.com + publicPath?: string; // e.g. http://mydomain.com/ minify?: boolean | { identifiers?: boolean; whitespace?: boolean; @@ -962,7 +994,8 @@ Bun.build({ ``` <!-- -module?: "esm"; // later: "cjs", "iife" +root?: string; // project root +format?: "esm"; // later: "cjs", "iife" loader?: { [k in string]: Loader }; sourcemap?: "none" | "inline" | "external"; // default: "none" treeshaking?: boolean; diff --git a/packages/bun-types/bun.d.ts b/packages/bun-types/bun.d.ts index ce7894b4e..14b9d3a38 100644 --- a/packages/bun-types/bun.d.ts +++ b/packages/bun-types/bun.d.ts @@ -966,23 +966,23 @@ declare module "bun" { interface BuildConfig { entrypoints: string[]; // list of file path - target?: Target; // default: "browser" - // module?: ModuleFormat; // later: "cjs", "iife" outdir?: string; // output directory + target?: Target; // default: "browser" + // format?: ModuleFormat; // later: "cjs", "iife" - naming?: { - chunk?: string; - entrypoint?: string; - asset?: string; - }; // | string; + naming?: + | string + | { + chunk?: string; + entrypoint?: string; + asset?: string; + }; // | string; // root?: string; // project root - // transform?: boolean; // default: false, transform instead of bundling splitting?: boolean; // default true, enable code splitting - bundling?: boolean; // default true, enable bundling plugins?: BunPlugin[]; // manifest?: boolean; // whether to return manifest external?: Array<string>; - publicPath: string; + publicPath?: string; // origin?: string; // e.g. http://mydomain.com // loaders?: { [k in string]: Loader }; // sourcemap?: "none" | "inline" | "external"; // default: "none" diff --git a/src/bun.js/api/JSBundler.zig b/src/bun.js/api/JSBundler.zig index 50160f1dc..eae4da646 100644 --- a/src/bun.js/api/JSBundler.zig +++ b/src/bun.js/api/JSBundler.zig @@ -167,23 +167,34 @@ pub const JSBundler = struct { this.public_path.appendSliceExact(slice.slice()) catch unreachable; } - if (try config.getObject(globalThis, "naming")) |naming| { - if (try naming.getOptional(globalThis, "entrypoint", ZigString.Slice)) |slice| { - defer slice.deinit(); - this.names.owned_entry_point.appendSliceExact(slice.slice()) catch unreachable; - this.names.entry_point.data = this.names.owned_entry_point.list.items; - } + if (config.getTruthy(globalThis, "naming")) |naming| { + if (naming.isString()) { + if (try config.getOptional(globalThis, "naming", ZigString.Slice)) |slice| { + defer slice.deinit(); + this.names.owned_entry_point.appendSliceExact(slice.slice()) catch unreachable; + this.names.entry_point.data = this.names.owned_entry_point.list.items; + } + } else if (naming.isObject()) { + if (try naming.getOptional(globalThis, "entrypoint", ZigString.Slice)) |slice| { + defer slice.deinit(); + this.names.owned_entry_point.appendSliceExact(slice.slice()) catch unreachable; + this.names.entry_point.data = this.names.owned_entry_point.list.items; + } - if (try naming.getOptional(globalThis, "chunk", ZigString.Slice)) |slice| { - defer slice.deinit(); - this.names.owned_chunk.appendSliceExact(slice.slice()) catch unreachable; - this.names.chunk.data = this.names.owned_chunk.list.items; - } + if (try naming.getOptional(globalThis, "chunk", ZigString.Slice)) |slice| { + defer slice.deinit(); + this.names.owned_chunk.appendSliceExact(slice.slice()) catch unreachable; + this.names.chunk.data = this.names.owned_chunk.list.items; + } - if (try naming.getOptional(globalThis, "asset", ZigString.Slice)) |slice| { - defer slice.deinit(); - this.names.owned_asset.appendSliceExact(slice.slice()) catch unreachable; - this.names.asset.data = this.names.owned_asset.list.items; + if (try naming.getOptional(globalThis, "asset", ZigString.Slice)) |slice| { + defer slice.deinit(); + this.names.owned_asset.appendSliceExact(slice.slice()) catch unreachable; + this.names.asset.data = this.names.owned_asset.list.items; + } + } else { + globalThis.throwInvalidArguments("Expected naming to be a string or an object", .{}); + return error.JSException; } } diff --git a/src/cli.zig b/src/cli.zig index 142ab06e3..86dce4dca 100644 --- a/src/cli.zig +++ b/src/cli.zig @@ -162,7 +162,7 @@ pub const Arguments = struct { clap.parseParam("-d, --define <STR>... Substitute K:V while parsing, e.g. --define process.env.NODE_ENV:\"development\". Values are parsed as JSON.") catch unreachable, clap.parseParam("-e, --external <STR>... Exclude module from transpilation (can use * wildcards). ex: -e react") catch unreachable, clap.parseParam("-h, --help Display this help and exit. ") catch unreachable, - clap.parseParam("-l, --loader <STR>... Parse files with .ext:loader, e.g. --loader .js:jsx. Valid loaders: jsx, js, json, tsx, ts, css") catch unreachable, + clap.parseParam("-l, --loader <STR>... Parse files with .ext:loader, e.g. --loader .js:jsx. Valid loaders: js, jsx, ts, tsx, json, toml, text, file, wasm, napi") catch unreachable, clap.parseParam("-u, --origin <STR> Rewrite import URLs to start with --origin. Default: \"\"") catch unreachable, clap.parseParam("-p, --port <STR> Port to serve bun's dev server on. Default: \"3000\"") catch unreachable, clap.parseParam("--minify Minify (experimental)") catch unreachable, |