--- url: /docs/guide/workspaces.md --- # Bunup Workspaces Effortlessly manage **multiple packages in a monorepo** with Bunup’s built-in workspace support. This eliminates the need for separate build configurations and multiple commands for each package. With a single configuration file and a single command, you can build all your packages at once. ## Creating a Workspace Configuration Define your workspace using the `defineWorkspace` function: ```ts [bunup.config.ts] import { defineWorkspace } from "bunup"; export default defineWorkspace([ // Package configurations go here ]); ``` ## Package Configuration Each package requires three properties: | Property | Type | Description | | -------- | ------------------------------ | ------------------------------------------------------------ | | `name` | `string` | Unique identifier for the package | | `root` | `string` | Path to the package directory, relative to the monorepo root | | `config` | `BunupConfig \| BunupConfig[]` | Optional build configuration(s) for this package | πŸ‘‰ If you omit `config`, Bunup will use **defaults**: * Build as ESM-only * Use [default entry points](/#default-entry-points) (e.g. `src/index.ts`) * Generate TypeScript declaration files (`.d.ts`) for entry points that need them ## Basic Usage A minimal workspace with two packages: ```ts [bunup.config.ts] import { defineWorkspace } from "bunup"; export default defineWorkspace([ { name: "core", root: "packages/core", config: { // Bunup finds 'src/index.ts' by default // Or specify exactly which files to build // entry: ["src/index.ts", "src/plugins.ts"], format: ["esm", "cjs"], }, }, { name: "utils", root: "packages/utils", // Uses default entry points // Uses default format: esm // Generates .d.ts declaration files }, ]); ``` Here, **`core`** has custom formats, while **`utils`** works out of the box with defaults. ## Shared Options You can define **shared options** for all packages, reducing repetition: ```ts [bunup.config.ts] export default defineWorkspace( [ { name: "core", root: "packages/core", config: { format: ["esm"], // overrides shared format }, }, { name: "utils", root: "packages/utils", // config is optional, shared options apply }, ], { // Shared options format: ["esm", "cjs"], exports: true, } ); ``` ## Multiple Build Configurations Each package can have multiple builds by passing an array. ::: info Named Configurations When using an array of build configurations, the `name` property is **required** for each configuration to identify the builds in logs and reports. ::: ```ts [bunup.config.ts] export default defineWorkspace([ { name: "web", root: "packages/web", config: [ { entry: "src/index.ts", name: "node", format: "esm", target: "node", }, { entry: "src/browser.ts", name: "browser", format: ["esm", "iife"], target: "browser", outDir: "dist/browser", }, ], }, ]); ``` **Another example:** if you have different entry points that need different build configurations, you can specify them separately. For instance, your main module might need both ESM and CJS formats, while a CLI entry point might only need ESM: ```ts [bunup.config.ts] export default defineWorkspace([ { name: "main", root: "packages/main", config: [ { entry: "src/index.ts", name: "main", format: ["esm", "cjs"], }, { entry: "src/cli.ts", name: "cli", format: ["esm"], }, ], }, ]); ``` ## Path Resolution All paths in package configs are **relative to the package root**: ``` myproject/ β”œβ”€β”€ packages/ β”‚ β”œβ”€β”€ core/ <- package root β”‚ β”‚ β”œβ”€β”€ src/ <- entries resolved here β”‚ β”‚ └── dist/ <- outputs here β”‚ └── utils/ β”œβ”€β”€ bunup.config.ts └── package.json ``` Example: ```ts { name: "core", root: "packages/core", config: { entry: "src/index.ts", // resolves to packages/core/src/index.ts outDir: "dist", // outputs to packages/core/dist/ }, } ``` ::: tip Plugin Paths When using plugins (like [`copy`](/docs/builtin-plugins/copy)), paths are also resolved relative to the **package root**. For example, `copy("assets/**/*.svg")` in the `core` package will copy from `packages/core/assets`. ::: ## Building Packages ### Build all packages ```sh bunx bunup ``` ### Watch mode ```sh bunx bunup --watch ``` Bunup will watch **all packages** and rebuild only those that change. ### Build specific packages Use the `--filter` option with package names: ```sh bunx bunup --filter core,utils # or in watch mode bunx bunup --filter core,utils --watch ``` ::: info Incremental Builds Workspaces are **incremental**: only changed packages are rebuilt. ::: --- --- url: /docs/guide/cli-options.md --- # CLI Options --- --- url: /docs/advanced/compile.md --- # Compile to Executable Bunup supports creating standalone executables using the `compile` option. This allows you to bundle your code and the Bun runtime into a single executable file that can be distributed and run without requiring Bun to be installed. ## Use Case The `compile` option is useful when you want to: * Distribute CLI tools that users can run without installing Bun * Create standalone executables for scripts and utilities * Package your code with all dependencies included ## Output Directory Compiled executables output to `bin/` by default, unlike normal builds which output to `dist/` by default. If you want to change the output location, you can use the `outDir` option to specify a different directory like `dist/` or any other location. ## Basic Usage ```typescript [bunup.config.ts] export default defineConfig({ entry: 'src/cli.ts', compile: true, // Create executable for current platform }); ``` This will output the executable to the `bin/` directory. ## Customizing Output Directory You can change the output directory using the `outDir` option: ```typescript [bunup.config.ts] export default defineConfig({ entry: 'src/cli.ts', compile: true, outDir: 'dist', // Output to dist/ instead of bin/ }); ``` ## Cross-Compilation Target specific platforms: ```typescript [bunup.config.ts] export default defineConfig({ entry: 'src/cli.ts', compile: 'bun-linux-x64', // Cross-compile for Linux }); ``` ## Advanced Configuration ```typescript [bunup.config.ts] export default defineConfig({ entry: 'src/cli.ts', compile: { target: 'bun-linux-x64', outfile: './bin/my-app', windows: { hideConsole: true, icon: './icon.ico', }, }, }); ``` ## Multiple Entrypoints Only one entrypoint can be compiled at a time. If you want to compile multiple entrypoints, you need to use a build config array with separate configurations: ```typescript [bunup.config.ts] export default defineConfig([ { name: 'main', entry: 'src/main.ts', compile: true, }, { name: 'cli', entry: 'src/cli.ts', compile: { outfile: 'my-cli', }, }, ]); ``` This will create separate executables for each entrypoint. ## Cross-Compiling for Multiple Targets If you want to cross-compile the same entrypoint for multiple targets, or use different configurations for different targets, you can create separate objects in the config array: ```typescript [bunup.config.ts] export default defineConfig([ { name: 'cli-linux', entry: 'src/cli.ts', compile: 'bun-linux-x64', }, { name: 'cli-windows', entry: 'src/cli.ts', compile: { target: 'bun-windows-x64', outfile: './bin/my-app-windows.exe', windows: { hideConsole: true, icon: './icon.ico', }, }, }, { name: 'cli-macos', entry: 'src/cli.ts', compile: { target: 'bun-darwin-arm64', outfile: './bin/my-app-macos', }, }, ]); ``` This approach allows you to: * Build executables for multiple platforms in a single build command * Apply platform-specific configurations (like Windows console hiding or custom icons) * Customize the output filename for each target platform ## Mixing Executables and Library Builds You can also mix executable compilation with normal library builds in the same config array. This is useful when you want to build both a library and CLI tools: ```typescript [bunup.config.ts] export default defineConfig([ { name: 'library', entry: 'src/index.ts', format: ['esm', 'cjs'], dts: true, // Normal library build }, { name: 'cli', entry: 'src/cli.ts', compile: true, // Compile to executable }, ]); ``` This allows you to build your library and create executables in a single build process. ## Learn More For detailed information about available targets and configuration options, see the [Bun documentation on executables](https://bun.com/docs/bundler/executables). --- --- url: /docs/guide/config-file.md --- # Config File Centralize your build settings with a configuration file when CLI options aren't enough. ## Getting Started Create a `bunup.config.ts` file in your project root: ```ts [bunup.config.ts] import { defineConfig } from 'bunup'; export default defineConfig({ // ...your configuration options go here }); ``` This is the simplest way to centralize and reuse your build configuration. See [Options](/docs/guide/options) for all the available options. ## Multiple Configurations Bunup supports exporting an **array of configurations**, useful when you want to build for multiple environments or formats in a single run. ::: info Named Configurations When using an array of configurations, the `name` property is **required** for each configuration to identify the builds in logs and reports. ::: ```ts [bunup.config.ts] export default defineConfig([ { entry: "src/index.ts", name: 'node', format: 'esm', target: 'node', }, { entry: "src/browser.ts", name: 'browser', format: ['esm', 'iife'], target: 'browser', outDir: 'dist/browser', }, ]); ``` With this setup, Bunup will build both Node.js and browser bundles. **Another example:** if you have different entry points that need different build configurations, you can specify them separately. For instance, your main module might need both ESM and CJS formats, while a CLI entry point might only need ESM: ```ts [bunup.config.ts] export default defineConfig([ { entry: "src/index.ts", name: 'main', format: ['esm', 'cjs'], }, { entry: "src/cli.ts", name: 'cli', format: ['esm'], }, { entry: "src/browser.ts", name: 'browser', format: ['esm', 'iife'], outDir: 'dist/browser' }, ]); ``` ## Filtering Configurations When you have multiple configurations in an array, you can use the `--filter` option to build only specific configurations by name: ```sh [CLI] # Single bunup --filter main # Multiple bunup --filter main,browser ``` Only the configurations matching these names will be built - perfect for testing specific builds without running the entire suite. ## Custom Configuration Path If you need to use a configuration file with a non-standard name or location, you can specify its path using the `--config` CLI option: ::: code-group ```sh [CLI] bunup --config ./configs/custom.bunup.config.ts # or using alias bunup -c ./configs/custom.bunup.config.ts ``` ::: This allows you to keep your configuration files organized in custom locations or use different configuration files for different environments. ## Disabling Configuration Files To explicitly disable config file usage and rely only on CLI options: ```sh [CLI] bunup --no-config ``` --- --- url: /docs/builtin-plugins/copy.md --- # Copy The copy plugin copies files and directories to your build output. It supports glob patterns, direct folder copying, file transformation with filename changes, and can copy to specific destinations or rename files and folders. ## Basic Usage ```ts [bunup.config.ts] import { defineConfig } from 'bunup'; import { copy } from 'bunup/plugins'; export default defineConfig({ plugins: [copy(['README.md', 'assets/**/*'])], }); ``` This will copy the `README.md` file and all files in the `assets` directory to your build output directory. Use `copy(pattern)` to copy files or folders. Optionally, add `.to(destination)` to set the output name or location, `.with(options)` for extra settings, and `.transform(fn)` to modify files during copy. By default, everything is copied to your build output directory. ## Examples Below are some examples of how to use the copy plugin. ### Basic File Operations ```ts // Copy single file copy('README.md') // Copy multiple specific files copy(['README.md', 'LICENSE', 'CHANGELOG.md']) // Copy and rename a file copy('README.md').to('documentation.md') ``` ### Directory Operations ```ts // Copy entire directory as is (preserves structure) copy('assets') // β†’ dist/assets/ // Copy and rename directory copy('assets').to('static') // β†’ dist/static/ // Copy multiple directories copy(['assets', 'public', 'docs']) ``` ### Glob Patterns ```ts // Copy all markdown files recursively copy('**/*.md') // Copy all files in assets directory copy('assets/**/*') // Copy with multiple patterns copy([ 'assets/**/*', // All files in assets 'docs/**/*.md', // Markdown files in docs 'src/**/*.css', // CSS files in src ]) ``` ### Pattern Exclusions ```ts // Exclude specific files and patterns copy([ 'assets/**/*', // Include all assets '!**/*.tmp', // Exclude temporary files '!**/*.log', // Exclude log files '!**/node_modules', // Exclude node_modules '!**/.DS_Store' // Exclude system files ]) ``` ### Flattening Structure ```ts // Flatten all files from subdirectories copy('assets/**/*').to('static') // All files β†’ dist/static/ // Flatten specific file types copy('src/**/*.css').to('styles') // All CSS β†’ dist/styles/ copy('images/**/*.{png,jpg,svg}').to('assets') // All images β†’ dist/assets/ ``` ### Multiple Copy Operations You can add multiple copy plugins for different copy operations: ```ts export default defineConfig({ plugins: [ copy('README.md'), copy('assets/**/*').to('static'), copy('docs/**/*.md').to('documentation'), ], }); ``` ## Transform Files Transform files on the fly during the copy operation using the `transform()` method. The transform function receives a context object with file content, paths, and build options. ```ts // Simple transformation - minify JSON files copy('data/**/*.json') .transform(({ content }) => { // Return content only - keeps original filename return JSON.stringify(JSON.parse(content.toString())) }) // Transform with filename change - TypeScript to JavaScript copy('scripts/**/*.ts') .transform(async ({ content, path }) => { const transpiler = new Bun.Transpiler({ loader: 'ts' }) // Return object to change both content and filename return { content: transpiler.transformSync(content.toString()), filename: basename(path).replace('.ts', '.js') } }) // Access full context including build options copy('config/**/*') .transform(({ content, path, destination, options }) => { // options contains build configuration (outDir, minify, etc.) // destination is where the file will be written const processed = content.toString() .replace('__BUILD_MODE__', options.watch ? 'development' : 'production') .replace('__OUT_DIR__', options.outDir) return processed }) ``` ## Options The copy plugin supports additional options via the `with()` method to customize copy behavior. ### `followSymlinks` Whether to follow symbolic links when copying files. By default, symbolic links are not followed. ```ts [bunup.config.ts] copy('assets/**/*').with({ followSymlinks: true }) ``` ### `excludeDotfiles` Whether to exclude dotfiles (files starting with a dot) from being copied. By default, dotfiles are included in the copy operation. ```ts [bunup.config.ts] copy('assets/**/*').with({ excludeDotfiles: true }) ``` ### `override` Whether to override existing files in the destination. By default, existing files are overwritten. ```ts [bunup.config.ts] // Skip files that already exist in the destination copy('assets/**/*').with({ override: false }) ``` ### `watchMode` Controls the behavior of the copy plugin in watch mode. Available options: * `'changed'` (default): Only copy files that have been modified since the last build * `'always'`: Copy all files on every build, regardless of changes * `'skip'`: Skip copying entirely in watch mode ```ts [bunup.config.ts] // Only copy changed files in watch mode (default) copy('assets/**/*').with({ watchMode: 'changed' }) // Always copy all files, even in watch mode copy('config/**/*').with({ watchMode: 'always' }) // Skip copying in watch mode (useful for large static assets) copy('videos/**/*').with({ watchMode: 'skip' }) ``` --- --- url: /docs/guide/css.md --- # CSS Bunup handles CSS automatically. Just import it and it works. ## Quick Start Import CSS in your TypeScript files: ```typescript [src/index.ts] import './styles.css'; import { Button } from './components/button'; export { Button }; ``` ```css [src/styles.css] .button { background-color: #007bff; color: white; padding: 8px 16px; border: none; border-radius: 4px; } ``` Bunup automatically bundles your CSS into `dist/index.css` with cross-browser compatibility. Any CSS imports encountered in your files will be bundled together into `dist/index.css`. To generate separate CSS files instead of a single `index.css` output, add them as entry points rather than importing them in your files: ```typescript [bunup.config.ts] import { defineConfig } from 'bunup'; export default defineConfig({ entry: [ 'src/index.ts', 'src/components/button.css' 'src/components/alert.css' ], }); ``` This creates individual CSS files in your build output: ```plaintext dist/ β”œβ”€β”€ index.js └── components/ β”œβ”€β”€ button.css └── alert.css ``` ## CSS Modules CSS modules prevent style conflicts by automatically scoping class names. Just add `.module.css` to your filename: ::: tip New to CSS modules? Check out [this guide](https://css-tricks.com/css-modules-part-1-need/) to learn what they are and why they're useful. ::: ```css [src/components/button.module.css] .primary { background-color: #007bff; color: white; padding: 8px 16px; border: none; border-radius: 4px; } ``` ```tsx [src/components/button.tsx] import styles from "./button.module.css"; export function Button({ children }) { return ( ); } ``` That's it! Bunup handles the rest automatically. ### Sharing Styles Reuse styles with the `composes` property: ```css [src/components/button.module.css] {9,15} .base { padding: 8px 16px; border: none; border-radius: 4px; cursor: pointer; } .primary { composes: base; background-color: #007bff; color: white; } .secondary { composes: base; background-color: transparent; color: #007bff; border: 1px solid #007bff; } ``` **Rules:** * `composes` must come first in the class * Works only with single class selectors (not `#id` or `.class1, .class2`) **From other files:** ```css [src/components/button.module.css] {2} .primary { composes: base from "../shared.module.css"; background-color: #007bff; color: white; } ``` ::: warning Avoid conflicting properties when composing from separate files. ::: ## Distributing CSS Export CSS files for package consumers: ```json [package.json] { "exports": { ".": { "import": "./dist/index.js", "types": "./dist/index.d.ts" }, "./styles.css": "./dist/index.css" // [!code ++] } } ``` Users can then import your styles: ```javascript import 'your-package/styles.css'; import { Button } from 'your-package';