1 Commits

Author SHA1 Message Date
eric
10758aa20a docs: Refactor code structure for improved readability
Some checks failed
CI / test (macos-14, darwin-arm64) (push) Has been cancelled
CI / test (windows-latest, windows) (push) Has been cancelled
CI / test (ubuntu-latest, linux-x64) (push) Has been cancelled
Docs Pages / deploy (push) Failing after 33s
2026-03-06 20:54:28 +01:00
19 changed files with 232 additions and 2341 deletions

View File

@@ -1,19 +0,0 @@
#!/usr/bin/env bash
set -e
if [[ ! -d "/Users/eric/Projects/rules_bun" ]]; then
echo "Cannot find source directory; Did you move it?"
echo "(Looking for "/Users/eric/Projects/rules_bun")"
echo 'Cannot force reload with this script - use "direnv reload" manually and then try again'
exit 1
fi
# rebuild the cache forcefully
_nix_direnv_force_reload=1 direnv exec "/Users/eric/Projects/rules_bun" true
# Update the mtime for .envrc.
# This will cause direnv to reload again - but without re-building.
touch "/Users/eric/Projects/rules_bun/.envrc"
# Also update the timestamp of whatever profile_rc we have.
# This makes sure that we know we are up to date.
touch -r "/Users/eric/Projects/rules_bun/.envrc" "/Users/eric/Projects/rules_bun/.direnv"/*.rc

View File

@@ -1 +0,0 @@
/nix/store/7f0478ddr51i3r708dpkljnvmzwc2fhn-source

View File

@@ -1 +0,0 @@
/nix/store/affmc6lhad8f6q3iaa3iydcdjwr8lwgp-source

View File

@@ -1 +0,0 @@
/nix/store/g5v3sgqy6a0fsmas7mnapc196flrplix-source

View File

@@ -1 +0,0 @@
/nix/store/jzfmmjnq1cip816awnliw7ir69pcyg00-source

View File

@@ -1 +0,0 @@
/nix/store/kx00h535s3jzb9803vnylxllij3zhix5-source

View File

@@ -1 +0,0 @@
/nix/store/ngdfag0pfs1h54pbjs9ywah4zhqsphf1-source

View File

@@ -1 +0,0 @@
/nix/store/nk13680f34w3q01a1q69c48my6fi7cxz-source

View File

@@ -1 +0,0 @@
/nix/store/k647bkhh6fv8rw9pnxgzyyv3r4y8fsin-nix-shell-env

File diff suppressed because one or more lines are too long

1
.gitignore vendored
View File

@@ -23,3 +23,4 @@ node_modules/
.env .env
!tests/.env !tests/.env
!examples/.env !examples/.env
.direnv

View File

@@ -3,11 +3,13 @@ module(
version = "0.2.0", version = "0.2.0",
) )
# Core ruleset dependencies.
bazel_dep(name = "platforms", version = "1.0.0") bazel_dep(name = "platforms", version = "1.0.0")
bazel_dep(name = "rules_shell", version = "0.6.1") bazel_dep(name = "rules_shell", version = "0.6.1")
bazel_dep(name = "bazel_skylib", version = "1.8.2") bazel_dep(name = "bazel_skylib", version = "1.8.2")
bazel_dep(name = "stardoc", version = "0.7.2") bazel_dep(name = "stardoc", version = "0.7.2")
# Repository-local setup for this ruleset's own tests and examples.
bun_ext = use_extension("//bun:extensions.bzl", "bun") bun_ext = use_extension("//bun:extensions.bzl", "bun")
use_repo( use_repo(
bun_ext, bun_ext,
@@ -18,6 +20,7 @@ use_repo(
"bun_windows_x64", "bun_windows_x64",
) )
# Test fixture dependency installation used by //tests/script_test.
bun_install_ext = use_extension("//bun:extensions.bzl", "bun_install") bun_install_ext = use_extension("//bun:extensions.bzl", "bun_install")
bun_install_ext.install( bun_install_ext.install(
name = "script_test_vite_node_modules", name = "script_test_vite_node_modules",
@@ -26,6 +29,7 @@ bun_install_ext.install(
) )
use_repo(bun_install_ext, "script_test_vite_node_modules") use_repo(bun_install_ext, "script_test_vite_node_modules")
# Register the published Bun toolchains for this repository.
register_toolchains( register_toolchains(
"//bun:darwin_aarch64_toolchain", "//bun:darwin_aarch64_toolchain",
"//bun:darwin_x64_toolchain", "//bun:darwin_x64_toolchain",

4
MODULE.bazel.lock generated
View File

@@ -190,7 +190,7 @@
"//bun:extensions.bzl%bun": { "//bun:extensions.bzl%bun": {
"general": { "general": {
"bzlTransitiveDigest": "83P5DLnVhSu3AwYrHPGqYVY/L6twnM4Jnlhc8uAOOls=", "bzlTransitiveDigest": "83P5DLnVhSu3AwYrHPGqYVY/L6twnM4Jnlhc8uAOOls=",
"usagesDigest": "qk1PDh3WICa0VONYKXJLsmWCesNJxz3Jkb/aH/voIeI=", "usagesDigest": "SXT282ETYzRFxBNaQrX/Ym1rZzueKzjlll/D0arFoYs=",
"recordedInputs": [ "recordedInputs": [
"REPO_MAPPING:,bazel_tools bazel_tools" "REPO_MAPPING:,bazel_tools bazel_tools"
], ],
@@ -251,7 +251,7 @@
"//bun:extensions.bzl%bun_install": { "//bun:extensions.bzl%bun_install": {
"general": { "general": {
"bzlTransitiveDigest": "83P5DLnVhSu3AwYrHPGqYVY/L6twnM4Jnlhc8uAOOls=", "bzlTransitiveDigest": "83P5DLnVhSu3AwYrHPGqYVY/L6twnM4Jnlhc8uAOOls=",
"usagesDigest": "r8Bik2FdMIUMCNUMdEfwvoy+Yaru70lPjHGWflG1wIc=", "usagesDigest": "BSZJHRsUZpMHTxqlUZZaq4MH8kfqrzFfF+50EqSPx/Q=",
"recordedInputs": [ "recordedInputs": [
"REPO_MAPPING:,bazel_tools bazel_tools" "REPO_MAPPING:,bazel_tools bazel_tools"
], ],

252
README.md
View File

@@ -1,191 +1,197 @@
# rules_bun # Bun rules for [Bazel](https://bazel.build)
Bazel rules for bun. `rules_bun` provides Bazel rules for running, testing, bundling, and developing
JavaScript and TypeScript code with Bun.
## Rule reference ## Repository layout
This repository follows the standard Bazel ruleset layout:
```text
/
MODULE.bazel
README.md
bun/
BUILD.bazel
defs.bzl
extensions.bzl
repositories.bzl
toolchain.bzl
docs/
examples/
tests/
```
The public entrypoint for rule authors and users is `@rules_bun//bun:defs.bzl`.
## Public API
`rules_bun` exports these primary rules:
- `bun_binary`
- `bun_bundle`
- `bun_dev`
- `bun_script`
- `bun_test`
- `js_library`
- `ts_library`
Reference documentation:
- Published docs site: https://eriyc.github.io/rules_bun/ - Published docs site: https://eriyc.github.io/rules_bun/
- Generated API docs: [docs/rules.md](docs/rules.md) - Generated rule reference: [docs/rules.md](docs/rules.md)
- Regenerate: `bazel build //docs:rules_md && cp bazel-bin/docs/rules.md docs/rules.md` - Docs index: [docs/index.md](docs/index.md)
## Use To refresh generated rule docs:
These steps show how to consume a tagged release of `rules_bun` in a separate Bazel workspace. ```bash
bazel build //docs:rules_md && cp bazel-bin/docs/rules.md docs/rules.md
```
### 1) Add the module dependency ## Bzlmod usage
In your project's `MODULE.bazel`, add: Release announcements should provide a copy-pasteable module snippet in the
standard ruleset form:
```starlark ```starlark
bazel_dep(name = "rules_bun", version = "0.2.0") bazel_dep(name = "rules_bun", version = "0.2.0")
archive_override(
module_name = "rules_bun",
urls = ["https://github.com/Eriyc/rules_bun/archiv0.0.5.tar.gz"],
strip_prefix = "rules_bun-v0.2.0",
)
``` ```
For channel/pre-release tags (for example `v0.2.0-rc.1`), use the matching folder prefix: Then add the Bun repositories and register the toolchains in `MODULE.bazel`:
```starlark
bazel_dep(name = "rules_bun", version = "0.2.0-rc.1")
archive_override(
module_name = "rules_bun",
urls = ["https://github.com/Eriyc/rules_bun/archiv0.0.5-rc.1.tar.gz"],
strip_prefix = "rules_bun-v0.2.0-rc.1",
)
```
Note: keep the `v` prefix in the Git tag URL and `strip_prefix`; for `bazel_dep(..., version = ...)`, use the module version string without the leading `v`.
### 2) Create Bun repositories with the extension
Still in `MODULE.bazel`, add:
```starlark ```starlark
bun_ext = use_extension("@rules_bun//bun:extensions.bzl", "bun") bun_ext = use_extension("@rules_bun//bun:extensions.bzl", "bun")
use_repo( use_repo(
bun_ext, bun_ext,
"bun_linux_x64", "bun_linux_x64",
"bun_linux_aarch64", "bun_linux_aarch64",
"bun_darwin_x64", "bun_darwin_x64",
"bun_darwin_aarch64", "bun_darwin_aarch64",
"bun_windows_x64", "bun_windows_x64",
) )
```
### 3) Register toolchains
Also in `MODULE.bazel`, register:
```starlark
register_toolchains( register_toolchains(
"@rules_bun//bun:darwin_aarch64_toolchain", "@rules_bun//bun:darwin_aarch64_toolchain",
"@rules_bun//bun:darwin_x64_toolchain", "@rules_bun//bun:darwin_x64_toolchain",
"@rules_bun//bun:linux_aarch64_toolchain", "@rules_bun//bun:linux_aarch64_toolchain",
"@rules_bun//bun:linux_x64_toolchain", "@rules_bun//bun:linux_x64_toolchain",
"@rules_bun//bun:windows_x64_toolchain", "@rules_bun//bun:windows_x64_toolchain",
) )
``` ```
### 4) Load rules in `BUILD.bazel` If you want Bazel-managed dependency installation, also add the module
extension for `bun_install`:
```starlark
load(
"@rules_bun//bun:defs.bzl",
"bun_binary",
"bun_bundle",
"bun_dev",
"bun_script",
"bun_test",
"js_library",
"ts_library",
)
```
### 5) (Optional) Use `bun_install` module extension
If you want Bazel-managed install repositories, add:
```starlark ```starlark
bun_install_ext = use_extension("@rules_bun//bun:extensions.bzl", "bun_install") bun_install_ext = use_extension("@rules_bun//bun:extensions.bzl", "bun_install")
bun_install_ext.install( bun_install_ext.install(
name = "npm", name = "npm",
package_json = "//:package.json", package_json = "//:package.json",
bun_lockfile = "//:bun.lock", bun_lockfile = "//:bun.lock",
) )
use_repo(bun_install_ext, "npm") use_repo(bun_install_ext, "npm")
``` ```
### 6) Verify setup ## Legacy WORKSPACE usage
Run one of your bun-backed targets, for example: For non-Bzlmod consumers, the repository exposes a legacy setup macro in
`@rules_bun//bun:repositories.bzl`:
```bash ```starlark
bazel test //path/to:your_bun_test load("@rules_bun//bun:repositories.bzl", "bun_register_toolchains")
bun_register_toolchains()
``` ```
All `rules_bun` rule-driven Bun invocations pass `--bun`. ## Loading rules in BUILD files
## Package scripts (`bun_script`) ```starlark
load(
"@rules_bun//bun:defs.bzl",
"bun_binary",
"bun_bundle",
"bun_dev",
"bun_script",
"bun_test",
"js_library",
"ts_library",
)
```
Use `bun_script` to expose a `package.json` script as a Bazel executable target. ## Common workflows
### `bun_script` for package scripts
Use `bun_script` to expose a `package.json` script as a Bazel executable.
This is the recommended way to run Vite-style `dev`, `build`, and `preview`
scripts.
```starlark ```starlark
load("@rules_bun//bun:defs.bzl", "bun_script") load("@rules_bun//bun:defs.bzl", "bun_script")
bun_script( bun_script(
name = "web_dev", name = "web_dev",
script = "dev", script = "dev",
package_json = "package.json", package_json = "package.json",
node_modules = "@npm//:node_modules", node_modules = "@npm//:node_modules",
data = glob([ data = glob([
"src/**", "src/**",
"static/**", "static/**",
"vite.config.*", "vite.config.*",
"svelte.config.*", "svelte.config.*",
"tsconfig*.json", "tsconfig*.json",
]), ]),
) )
``` ```
Run it with: When `node_modules` is provided, executables from `node_modules/.bin` are added
to `PATH`.
```bash ### `bun_dev` for local development
bazel run //path/to:web_dev -- --host
```
`bun_script` defaults to running from the directory containing `package.json`, which matches the usual expectations for `vite`, `svelte-kit`, and similar package scripts. Use `bun_dev` for long-running watch or hot-reload development targets.
## Development mode (`bun_dev`)
Use `bun_dev` for long-running local development with Bun watch mode.
```starlark ```starlark
load("@rules_bun//bun:defs.bzl", "bun_dev") load("@rules_bun//bun:defs.bzl", "bun_dev")
bun_dev( bun_dev(
name = "web_dev", name = "web_dev",
entry_point = "src/main.ts", entry_point = "src/main.ts",
# Optional: run from the entry point directory so Bun auto-loads colocated .env files. # Optional: run from the entry point directory so Bun auto-loads colocated .env files.
# working_dir = "entry_point", # working_dir = "entry_point",
) )
``` ```
Run it with: Supported development options include:
```bash - `watch_mode = "watch"`
bazel run //path/to:web_dev - `watch_mode = "hot"`
``` - `restart_on = [...]`
- `working_dir = "workspace" | "entry_point"`
`bun_dev` supports: ### Working directory behavior
- `watch_mode = "watch"` (default) for `bun --watch`
- `watch_mode = "hot"` for `bun --hot`
- `restart_on = [...]` to force full process restarts when specific files change
- `working_dir = "workspace" | "entry_point"` (default: `workspace`)
## Runtime working directory (`bun_binary`, `bun_dev`)
`bun_binary` and `bun_dev` support `working_dir`: `bun_binary` and `bun_dev` support `working_dir`:
- `"workspace"` (default): runs from the Bazel runfiles workspace root. - `"workspace"`: run from the Bazel runfiles workspace root.
- `"entry_point"`: runs from the nearest ancestor of the entry point that contains `.env` or `package.json` (falls back to the entry point directory). - `"entry_point"`: run from the nearest ancestor of the entry point that
contains `.env` or `package.json`.
Use `"entry_point"` when Bun should resolve local files such as colocated `.env` files relative to the program directory. ## Tests and examples
### Hybrid Go + Bun + protobuf workflow The repository keeps conformance and integration coverage in [tests/](tests/) and
usage samples in [examples/](examples/).
For monorepos that mix Go and Bun (including FFI): Representative example docs:
1. Run Bun app with native watch/HMR via `bun_dev`. - [examples/basic/README.md](examples/basic/README.md)
2. Put generated artifacts or bridge files in `restart_on` (for example generated JS/TS files from proto/go steps). - [examples/workspace/README.md](examples/workspace/README.md)
3. Rebuild Go/proto artifacts separately (for example with `ibazel build`) so their output files change.
4. `bun_dev` detects those `restart_on` changes and restarts Bun, while ordinary JS edits continue to use Bun watch/HMR without full Bazel restarts.
This keeps the fast Bun JS loop while still supporting full restarts when non-JS dependencies change. To validate the ruleset locally:
```bash
bazel test //tests/...
```

View File

@@ -5,6 +5,9 @@ load(":version.bzl", "BUN_VERSION")
exports_files([ exports_files([
"defs.bzl", "defs.bzl",
"extensions.bzl", "extensions.bzl",
"repositories.bzl",
"toolchain.bzl",
"version.bzl",
]) ])
bzl_library( bzl_library(
@@ -13,6 +16,19 @@ bzl_library(
visibility = ["//visibility:public"], visibility = ["//visibility:public"],
) )
bzl_library(
name = "extensions_bzl",
srcs = ["extensions.bzl"],
visibility = ["//visibility:public"],
deps = ["//internal:bun_install_bzl"],
)
bzl_library(
name = "repositories_bzl",
srcs = ["repositories.bzl"],
visibility = ["//visibility:public"],
)
bzl_library( bzl_library(
name = "defs_bzl", name = "defs_bzl",
srcs = ["defs.bzl"], srcs = ["defs.bzl"],

View File

@@ -1,14 +1,80 @@
# rules_bun docs # rules_bun docs
Documentation site for `rules_bun`. Documentation for `rules_bun`, a Bazel ruleset for Bun.
## Ruleset layout
The repository exposes its public Bazel API from the [bun/](../bun/) package:
- `@rules_bun//bun:defs.bzl` for build rules
- `@rules_bun//bun:extensions.bzl` for Bzlmod extensions
- `@rules_bun//bun:repositories.bzl` for legacy WORKSPACE setup
Supporting material lives in:
- [examples/](../examples/) for usage samples
- [tests/](../tests/) for repository conformance and integration tests
- [docs/rules.md](rules.md) for generated rule reference
## Rule reference ## Rule reference
- [rules.md](rules.md) - [rules.md](rules.md)
## Typical Bzlmod setup
```starlark
bazel_dep(name = "rules_bun", version = "0.2.0")
bun_ext = use_extension("@rules_bun//bun:extensions.bzl", "bun")
use_repo(
bun_ext,
"bun_linux_x64",
"bun_linux_aarch64",
"bun_darwin_x64",
"bun_darwin_aarch64",
"bun_windows_x64",
)
register_toolchains(
"@rules_bun//bun:darwin_aarch64_toolchain",
"@rules_bun//bun:darwin_x64_toolchain",
"@rules_bun//bun:linux_aarch64_toolchain",
"@rules_bun//bun:linux_x64_toolchain",
"@rules_bun//bun:windows_x64_toolchain",
)
```
## Vite package scripts
Use `bun_script` for package-script driven workflows such as `dev`, `build`,
and `preview`.
```starlark
load("@rules_bun//bun:defs.bzl", "bun_script")
bun_script(
name = "web_dev",
script = "dev",
package_json = "package.json",
node_modules = "@npm//:node_modules",
data = glob([
"src/**",
"public/**",
"index.html",
"vite.config.*",
"tsconfig*.json",
]),
)
```
`bun_script` runs from the package directory by default and adds
`node_modules/.bin` to `PATH`.
## Regeneration ## Regeneration
The rule reference is generated from Starlark rule docstrings: The rule reference is generated from the public Starlark symbols in
`@rules_bun//bun:defs.bzl`:
```bash ```bash
bazel build //docs:rules_md bazel build //docs:rules_md

View File

@@ -30,13 +30,17 @@ Attributes:
Runs a named `package.json` script with Bun as an executable target (`bazel run`). Runs a named `package.json` script with Bun as an executable target (`bazel run`).
Recommended for package-script based tools such as Vite (`dev`, `build`, `preview`).
When `node_modules` is provided, executables from `node_modules/.bin` are added
to `PATH`, so scripts like `vite` work without wrapper scripts.
Attributes: Attributes:
- `script` (string, required): package script name passed to `bun run <script>`. - `script` (string, required): package script name passed to `bun run <script>`.
- `package_json` (label, required): `package.json` file containing the named script. - `package_json` (label, required): `package.json` file containing the named script.
- `node_modules` (label, optional): Bun/npm package files in runfiles. - `node_modules` (label, optional): Bun/npm package files in runfiles.
- `data` (label_list, optional): additional runtime files for the script. - `data` (label_list, optional): additional runtime files for the script.
- `working_dir` (string, default: `"package"`, values: `"workspace" | "package"`): runtime working directory. - `working_dir` (string, default: `"package"`, values: `"workspace" | "package"`): runtime working directory. The default is a good fit for Vite and similar package-script based tools.
## bun_bundle ## bun_bundle

View File

@@ -28,17 +28,9 @@
system: system:
let let
pkgs = import nixpkgs { inherit system; }; pkgs = import nixpkgs { inherit system; };
bazelDefaultArgs =
if pkgs.stdenv.hostPlatform.isDarwin then
[
"--macos_minimum_os=10.12"
"--host_macos_minimum_os=10.12"
]
else
[ ];
bazel9 = pkgs.writeShellScriptBin "bazel" '' bazel9 = pkgs.writeShellScriptBin "bazel" ''
export USE_BAZEL_VERSION="''${USE_BAZEL_VERSION:-9.0.0}" export USE_BAZEL_VERSION="''${USE_BAZEL_VERSION:-9.0.0}"
exec ${pkgs.bazelisk}/bin/bazelisk ${pkgs.lib.escapeShellArgs bazelDefaultArgs} "$@" exec ${pkgs.bazelisk}/bin/bazelisk "$@"
''; '';
env = devshell-lib.lib.mkDevShell { env = devshell-lib.lib.mkDevShell {
inherit system; inherit system;

View File

@@ -70,7 +70,10 @@ bun_script = rule(
doc = """Runs a named `package.json` script with Bun as an executable target. doc = """Runs a named `package.json` script with Bun as an executable target.
Use this rule to expose existing package scripts such as `dev`, `build`, or Use this rule to expose existing package scripts such as `dev`, `build`, or
`check` via `bazel run` without adding wrapper shell scripts. `check` via `bazel run` without adding wrapper shell scripts. This is a good fit
for Vite-style workflows, where scripts like `vite dev` or `vite build` are
declared in `package.json` and expect to run from the package directory with
`node_modules/.bin` available on `PATH`.
""", """,
attrs = { attrs = {
"script": attr.string( "script": attr.string(
@@ -83,7 +86,7 @@ Use this rule to expose existing package scripts such as `dev`, `build`, or
doc = "Label of the `package.json` file containing the named script.", doc = "Label of the `package.json` file containing the named script.",
), ),
"node_modules": attr.label( "node_modules": attr.label(
doc = "Optional label providing Bun/npm package files in runfiles.", doc = "Optional label providing Bun/npm package files in runfiles. Executables from `node_modules/.bin` are added to `PATH`, which is useful for scripts such as `vite`.",
), ),
"data": attr.label_list( "data": attr.label_list(
allow_files = True, allow_files = True,
@@ -92,7 +95,7 @@ Use this rule to expose existing package scripts such as `dev`, `build`, or
"working_dir": attr.string( "working_dir": attr.string(
default = "package", default = "package",
values = ["workspace", "package"], values = ["workspace", "package"],
doc = "Working directory at runtime: Bazel runfiles `workspace` root or the directory containing `package.json`.", doc = "Working directory at runtime: Bazel runfiles `workspace` root or the directory containing `package.json`. The default `package` mode matches tools such as Vite that resolve config and assets relative to the package directory.",
), ),
}, },
executable = True, executable = True,