18 Commits

Author SHA1 Message Date
eric
4f8e27cd74 chore(release): v0.5.0
Some checks failed
CI / test (ubuntu-latest, linux-x64) (push) Failing after 34s
Docs Pages / deploy (push) Failing after 44s
Copilot Setup Steps / copilot-setup-steps (push) Failing after 25s
CI / test (macos-14, darwin-arm64) (push) Has been cancelled
CI / test (windows-latest, windows) (push) Has been cancelled
2026-03-15 09:34:22 +01:00
eric
31c42a8638 fix: tests 2026-03-15 09:34:07 +01:00
eric
769b95d05b chore(release): v0.4.0
Some checks failed
CI / test (ubuntu-latest, linux-x64) (push) Failing after 31s
Copilot Setup Steps / copilot-setup-steps (push) Failing after 36s
CI / test (macos-14, darwin-arm64) (push) Has been cancelled
CI / test (windows-latest, windows) (push) Has been cancelled
2026-03-15 01:20:57 +01:00
eric
2a25cfb91a fix: include .env files 2026-03-15 01:20:45 +01:00
eric
2a9bd09aa4 fix: include .env files 2026-03-15 01:13:52 +01:00
eric
4b7ebb1536 fix: include .env files 2026-03-15 01:07:36 +01:00
73d4625420 Merge pull request 'feature/add-build-compile-rules' (#4) from feature/add-build-compile-rules into main
Some checks failed
CI / test (ubuntu-latest, linux-x64) (push) Failing after 37s
Docs Pages / deploy (push) Failing after 43s
CI / test (macos-14, darwin-arm64) (push) Has been cancelled
CI / test (windows-latest, windows) (push) Has been cancelled
Reviewed-on: #4
2026-03-15 00:01:19 +00:00
eric
b35f03872c test: add more tests
Some checks failed
CI / test (ubuntu-latest, linux-x64) (pull_request) Failing after 32s
CI / test (macos-14, darwin-arm64) (pull_request) Has been cancelled
CI / test (windows-latest, windows) (pull_request) Has been cancelled
2026-03-15 00:59:58 +01:00
eric
a0bc998bd2 feat: new bun_build and bun_compile, extend bun_install 2026-03-15 00:11:55 +01:00
eric
c446f23a35 feat: improve rules_js parity 2026-03-14 23:50:26 +01:00
eric
d7a6d6b0ba fix: bun monorepo complex deps 2026-03-08 03:29:54 +01:00
eric
81e9b93914 fix: bun install symlinks 4
Some checks failed
CI / test (ubuntu-latest, linux-x64) (push) Failing after 31s
CI / test (macos-14, darwin-arm64) (push) Has been cancelled
CI / test (windows-latest, windows) (push) Has been cancelled
2026-03-07 00:34:14 +01:00
eric
9cbd027e7e fix: bun install symlinks 4 2026-03-07 00:32:40 +01:00
eric
6b5343fdf3 fix: bun install symlinks 4 2026-03-07 00:30:13 +01:00
eric
9487fafe88 fix: bun install symlinks 3 2026-03-07 00:27:51 +01:00
eric
9e64fd1369 fix: bun install symlinks
Some checks failed
Copilot Setup Steps / copilot-setup-steps (push) Failing after 36s
2026-03-07 00:23:39 +01:00
eric
d04f489b0e fix: bun install symlinks
Some checks failed
CI / test (ubuntu-latest, linux-x64) (push) Failing after 39s
Docs Pages / deploy (push) Failing after 33s
CI / test (macos-14, darwin-arm64) (push) Has been cancelled
CI / test (windows-latest, windows) (push) Has been cancelled
2026-03-07 00:00:00 +01:00
eric
40d621d1cf fix: bun install symlinks 2026-03-06 23:52:27 +01:00
142 changed files with 6537 additions and 546 deletions

4
.gitignore vendored
View File

@@ -22,6 +22,6 @@ node_modules/
.env
!tests/.env
!tests/**/.env
!examples/.env
.direnv
!examples/**/.env

3
.gitleaksignore Normal file
View File

@@ -0,0 +1,3 @@
tests/binary_test/.env:environment-file:1
tests/script_test/.env:environment-file:1
tests/binary_test/env_parent/.env:environment-file:1

View File

@@ -1 +0,0 @@
/nix/store/vq88dl8yys4hbw4gq6kwypah5ykqr8xs-pre-commit-config.json

View File

@@ -1 +1,9 @@
package(default_visibility = ["//visibility:public"])
filegroup(
name = "repo_runtime_files",
srcs = [
"BUILD.bazel",
"MODULE.bazel",
],
)

View File

@@ -1,6 +1,6 @@
module(
name = "rules_bun",
version = "0.2.2",
version = "0.5.0",
)
# Core ruleset dependencies.
@@ -8,6 +8,7 @@ bazel_dep(name = "platforms", version = "1.0.0")
bazel_dep(name = "rules_shell", version = "0.6.1")
bazel_dep(name = "bazel_skylib", version = "1.8.2")
bazel_dep(name = "stardoc", version = "0.7.2")
bazel_dep(name = "rules_multirun", version = "0.9.0", dev_dependency = True)
# Repository-local setup for this ruleset's own tests and examples.
bun_ext = use_extension("//bun:extensions.bzl", "bun")
@@ -21,27 +22,34 @@ use_repo(
)
# 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", dev_dependency = True)
bun_install_ext.install(
name = "script_test_vite_node_modules",
package_json = "//tests/script_test:vite_app/package.json",
bun_lockfile = "//tests/script_test:vite_app/bun.lock",
package_json = "//tests/script_test:vite_app/package.json",
)
use_repo(bun_install_ext, "script_test_vite_node_modules")
bun_install_ext.install(
name = "script_test_vite_monorepo_node_modules",
package_json = "//tests/script_test:vite_monorepo/package.json",
bun_lockfile = "//tests/script_test:vite_monorepo/bun.lock",
package_json = "//tests/script_test:vite_monorepo/package.json",
)
use_repo(bun_install_ext, "script_test_vite_monorepo_node_modules")
bun_install_ext.install(
name = "examples_vite_monorepo_node_modules",
package_json = "//examples/vite_monorepo:package.json",
bun_lockfile = "//examples/vite_monorepo:bun.lock",
name = "script_test_paraglide_monorepo_node_modules",
bun_lockfile = "//tests/script_test:paraglide_monorepo/bun.lock",
package_json = "//tests/script_test:paraglide_monorepo/package.json",
)
use_repo(bun_install_ext, "examples_vite_monorepo_node_modules")
use_repo(bun_install_ext, "script_test_paraglide_monorepo_node_modules")
bun_install_ext.install(
name = "examples_vite_monorepo_node_modules",
bun_lockfile = "//examples/vite_monorepo:bun.lock",
package_json = "//examples/vite_monorepo:package.json",
)
use_repo(bun_install_ext, node_modules = "examples_vite_monorepo_node_modules")
# Register the published Bun toolchains for this repository.
register_toolchains(

90
MODULE.bazel.lock generated
View File

@@ -141,6 +141,8 @@
"https://bcr.bazel.build/modules/rules_license/0.0.7/MODULE.bazel": "088fbeb0b6a419005b89cf93fe62d9517c0a2b8bb56af3244af65ecfe37e7d5d",
"https://bcr.bazel.build/modules/rules_license/1.0.0/MODULE.bazel": "a7fda60eefdf3d8c827262ba499957e4df06f659330bbe6cdbdb975b768bb65c",
"https://bcr.bazel.build/modules/rules_license/1.0.0/source.json": "a52c89e54cc311196e478f8382df91c15f7a2bfdf4c6cd0e2675cc2ff0b56efb",
"https://bcr.bazel.build/modules/rules_multirun/0.9.0/MODULE.bazel": "32d628ef586b5b23f67e55886b7bc38913ea4160420d66ae90521dda2ff37df0",
"https://bcr.bazel.build/modules/rules_multirun/0.9.0/source.json": "e882ba77962fa6c5fe68619e5c7d0374ec9a219fb8d03c42eadaf6d0243771bd",
"https://bcr.bazel.build/modules/rules_pkg/0.7.0/MODULE.bazel": "df99f03fc7934a4737122518bb87e667e62d780b610910f0447665a7e2be62dc",
"https://bcr.bazel.build/modules/rules_pkg/1.0.1/MODULE.bazel": "5b1df97dbc29623bccdf2b0dcd0f5cb08e2f2c9050aab1092fd39a41e82686ff",
"https://bcr.bazel.build/modules/rules_pkg/1.0.1/source.json": "bd82e5d7b9ce2d31e380dd9f50c111d678c3bdaca190cb76b0e1c71b05e1ba8a",
@@ -153,6 +155,7 @@
"https://bcr.bazel.build/modules/rules_python/0.10.2/MODULE.bazel": "cc82bc96f2997baa545ab3ce73f196d040ffb8756fd2d66125a530031cd90e5f",
"https://bcr.bazel.build/modules/rules_python/0.23.1/MODULE.bazel": "49ffccf0511cb8414de28321f5fcf2a31312b47c40cc21577144b7447f2bf300",
"https://bcr.bazel.build/modules/rules_python/0.25.0/MODULE.bazel": "72f1506841c920a1afec76975b35312410eea3aa7b63267436bfb1dd91d2d382",
"https://bcr.bazel.build/modules/rules_python/0.27.1/MODULE.bazel": "65dc875cc1a06c30d5bbdba7ab021fd9e551a6579e408a3943a61303e2228a53",
"https://bcr.bazel.build/modules/rules_python/0.28.0/MODULE.bazel": "cba2573d870babc976664a912539b320cbaa7114cd3e8f053c720171cde331ed",
"https://bcr.bazel.build/modules/rules_python/0.31.0/MODULE.bazel": "93a43dc47ee570e6ec9f5779b2e64c1476a6ce921c48cc9a1678a91dd5f8fd58",
"https://bcr.bazel.build/modules/rules_python/0.33.2/MODULE.bazel": "3e036c4ad8d804a4dad897d333d8dce200d943df4827cb849840055be8d2e937",
@@ -189,8 +192,8 @@
"moduleExtensions": {
"//bun:extensions.bzl%bun": {
"general": {
"bzlTransitiveDigest": "dy4VVPGLJrnr4plt8TA/E8PGKCOMrDWu6M9D5NWfNJo=",
"usagesDigest": "pUJFcZPlt7todXukySNaxd4bl0KGB8biTd2Ygx044co=",
"bzlTransitiveDigest": "lzOUyaXDbkH922ruNkkwEF2cnI4m0XpzrOti0qypwtA=",
"usagesDigest": "iOEuN6Lhr5Bejh9GRDT3yaCI7umBsNZo5CLQSzt+tNQ=",
"recordedInputs": [
"REPO_MAPPING:,bazel_tools bazel_tools"
],
@@ -215,6 +218,26 @@
"build_file_content": "\nexports_files([\"bun-linux-aarch64/bun\"])\n\nfilegroup(\n name = \"bun\",\n srcs = [\"bun-linux-aarch64/bun\"],\n visibility = [\"//visibility:public\"],\n)\n"
}
},
"bun_linux_x64_musl": {
"repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
"urls": [
"https://github.com/oven-sh/bun/releases/download/bun-v1.3.10/bun-linux-x64-musl.zip"
],
"sha256": "48a6c32277d343db0148ce066336472ffd380358a4d26bb1329714742492d824",
"build_file_content": "\nexports_files([\"bun-linux-x64-musl/bun\"])\n\nfilegroup(\n name = \"bun\",\n srcs = [\"bun-linux-x64-musl/bun\"],\n visibility = [\"//visibility:public\"],\n)\n"
}
},
"bun_linux_aarch64_musl": {
"repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
"urls": [
"https://github.com/oven-sh/bun/releases/download/bun-v1.3.10/bun-linux-aarch64-musl.zip"
],
"sha256": "d2c81365a2e529b78a42330d3a0056e8dbd7896b4a6782c8e392b6532141e34d",
"build_file_content": "\nexports_files([\"bun-linux-aarch64-musl/bun\"])\n\nfilegroup(\n name = \"bun\",\n srcs = [\"bun-linux-aarch64-musl/bun\"],\n visibility = [\"//visibility:public\"],\n)\n"
}
},
"bun_darwin_x64": {
"repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
@@ -244,14 +267,24 @@
"sha256": "7a77b3e245e2e26965c93089a4a1332e8a326d3364c89fae1d1fd99cdd3cd73d",
"build_file_content": "\nexports_files([\"bun-windows-x64/bun.exe\"])\n\nfilegroup(\n name = \"bun\",\n srcs = [\"bun-windows-x64/bun.exe\"],\n visibility = [\"//visibility:public\"],\n)\n"
}
},
"bun_windows_aarch64": {
"repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
"urls": [
"https://github.com/oven-sh/bun/releases/download/bun-v1.3.10/bun-windows-aarch64.zip"
],
"sha256": "6822f3aa7bd2be40fb94c194a1185aae1c6fade54ca4fc2efdc722e37f3257d2",
"build_file_content": "\nexports_files([\"bun-windows-aarch64/bun.exe\"])\n\nfilegroup(\n name = \"bun\",\n srcs = [\"bun-windows-aarch64/bun.exe\"],\n visibility = [\"//visibility:public\"],\n)\n"
}
}
}
}
},
"//bun:extensions.bzl%bun_install": {
"general": {
"bzlTransitiveDigest": "dy4VVPGLJrnr4plt8TA/E8PGKCOMrDWu6M9D5NWfNJo=",
"usagesDigest": "xWWY9DPXS5FKvNZvUbM6w+PBr8q9KbWKiP8wfD4RHLU=",
"bzlTransitiveDigest": "lzOUyaXDbkH922ruNkkwEF2cnI4m0XpzrOti0qypwtA=",
"usagesDigest": "eD/dECSVWIFS/qMgEXZ6501CimKmsHt7yTIFPX4XdLo=",
"recordedInputs": [
"REPO_MAPPING:,bazel_tools bazel_tools"
],
@@ -260,21 +293,64 @@
"repoRuleId": "@@//internal:bun_install.bzl%bun_install_repository",
"attributes": {
"package_json": "@@//tests/script_test:vite_app/package.json",
"bun_lockfile": "@@//tests/script_test:vite_app/bun.lock"
"bun_lockfile": "@@//tests/script_test:vite_app/bun.lock",
"install_inputs": [],
"isolated_home": true,
"production": false,
"omit": [],
"linker": "",
"backend": "",
"ignore_scripts": false,
"install_flags": [],
"visible_repo_name": "script_test_vite_node_modules"
}
},
"script_test_vite_monorepo_node_modules": {
"repoRuleId": "@@//internal:bun_install.bzl%bun_install_repository",
"attributes": {
"package_json": "@@//tests/script_test:vite_monorepo/package.json",
"bun_lockfile": "@@//tests/script_test:vite_monorepo/bun.lock"
"bun_lockfile": "@@//tests/script_test:vite_monorepo/bun.lock",
"install_inputs": [],
"isolated_home": true,
"production": false,
"omit": [],
"linker": "",
"backend": "",
"ignore_scripts": false,
"install_flags": [],
"visible_repo_name": "script_test_vite_monorepo_node_modules"
}
},
"script_test_paraglide_monorepo_node_modules": {
"repoRuleId": "@@//internal:bun_install.bzl%bun_install_repository",
"attributes": {
"package_json": "@@//tests/script_test:paraglide_monorepo/package.json",
"bun_lockfile": "@@//tests/script_test:paraglide_monorepo/bun.lock",
"install_inputs": [],
"isolated_home": true,
"production": false,
"omit": [],
"linker": "",
"backend": "",
"ignore_scripts": false,
"install_flags": [],
"visible_repo_name": "script_test_paraglide_monorepo_node_modules"
}
},
"examples_vite_monorepo_node_modules": {
"repoRuleId": "@@//internal:bun_install.bzl%bun_install_repository",
"attributes": {
"package_json": "@@//examples/vite_monorepo:package.json",
"bun_lockfile": "@@//examples/vite_monorepo:bun.lock"
"bun_lockfile": "@@//examples/vite_monorepo:bun.lock",
"install_inputs": [],
"isolated_home": true,
"production": false,
"omit": [],
"linker": "",
"backend": "",
"ignore_scripts": false,
"install_flags": [],
"visible_repo_name": "examples_vite_monorepo_node_modules"
}
}
}

View File

@@ -1,7 +1,7 @@
# Bun rules for [Bazel](https://bazel.build)
`rules_bun` provides Bazel rules for running, testing, bundling, and developing
JavaScript and TypeScript code with Bun.
`rules_bun` provides Bazel rules for running, testing, building, compiling,
bundling, and developing JavaScript and TypeScript code with Bun.
## Repository layout
@@ -29,10 +29,15 @@ The public entrypoint for rule authors and users is `@rules_bun//bun:defs.bzl`.
`rules_bun` exports these primary rules:
- `bun_binary`
- `bun_build`
- `bun_compile`
- `bun_bundle`
- `bun_dev`
- `bun_script`
- `bun_test`
- `js_binary`
- `js_test`
- `js_run_devserver`
- `js_library`
- `ts_library`
@@ -55,7 +60,7 @@ Release announcements should provide a copy-pasteable module snippet in the
standard ruleset form:
```starlark
bazel_dep(name = "rules_bun", version = "0.2.2")
bazel_dep(name = "rules_bun", version = "0.5.0")
```
Then add the Bun repositories and register the toolchains in `MODULE.bazel`:
@@ -67,9 +72,12 @@ use_repo(
bun_ext,
"bun_linux_x64",
"bun_linux_aarch64",
"bun_linux_x64_musl",
"bun_linux_aarch64_musl",
"bun_darwin_x64",
"bun_darwin_aarch64",
"bun_windows_x64",
"bun_windows_aarch64",
)
register_toolchains(
@@ -96,11 +104,45 @@ bun_install_ext.install(
name = "bun_deps",
package_json = "//:package.json",
bun_lockfile = "//:bun.lock",
# Optional: include extra install-time files or allow Bun to reuse the
# host HOME/cache.
# install_inputs = ["//:.npmrc"],
# isolated_home = False,
)
use_repo(bun_install_ext, "bun_deps")
```
## `rules_js` compatibility layer
`rules_bun` now exposes a Bun-backed compatibility layer for the most common
`rules_js` entrypoints:
- `@rules_bun//js:defs.bzl` exports `js_binary`, `js_test`, `js_run_devserver`,
`js_library`, `ts_library`, and `JsInfo`.
- `@rules_bun//npm:extensions.bzl` exports `npm_translate_lock`, which creates a
Bun-installed external repo and generates `@<repo>//:defs.bzl` with
`npm_link_all_packages()`.
Example:
```starlark
load("@rules_bun//js:defs.bzl", "js_binary")
load("@npm//:defs.bzl", "npm_link_all_packages")
npm_link_all_packages()
js_binary(
name = "app",
entry_point = "src/main.ts",
node_modules = ":node_modules",
)
```
This is a compatibility subset, not a full reimplementation of `rules_js`.
Package aliases created by `npm_link_all_packages()` use sanitized target names
such as `npm__vite` or `npm__at_types_node`.
## Legacy WORKSPACE usage
For non-Bzlmod consumers, the repository exposes a legacy setup macro in
@@ -118,6 +160,8 @@ bun_register_toolchains()
load(
"@rules_bun//bun:defs.bzl",
"bun_binary",
"bun_build",
"bun_compile",
"bun_bundle",
"bun_dev",
"bun_script",
@@ -157,6 +201,34 @@ When `node_modules` is provided, executables from `node_modules/.bin` are added
to `PATH`. This label typically comes from `bun_install`, which still produces a
standard `node_modules/` directory.
### `bun_build` and `bun_compile`
Use `bun_build` for general-purpose `bun build` output directories and
`bun_compile` for standalone executables built with `bun build --compile`.
```starlark
load("@rules_bun//bun:defs.bzl", "bun_build", "bun_compile")
bun_build(
name = "site",
entry_points = ["src/index.html"],
data = glob(["src/**"]),
splitting = True,
metafile = True,
)
bun_compile(
name = "cli",
entry_point = "src/cli.ts",
)
```
`bun_build` exposes a directory output so Bun can emit HTML, CSS, assets, and
split chunks. `bun_compile` produces a single executable artifact and supports
explicit cross-compilation via `compile_executable`. When `root` is omitted,
`bun_build` derives a stable default from the entry point parent directory so
HTML and asset output stays inside Bazel's declared output tree.
### `bun_dev` for local development
Use `bun_dev` for long-running watch or hot-reload development targets.

View File

@@ -1,3 +1,3 @@
0.2.2
0.5.0
stable
0

View File

@@ -10,6 +10,19 @@ exports_files([
"version.bzl",
])
filegroup(
name = "repo_runtime_files",
srcs = [
"BUILD.bazel",
"defs.bzl",
"extensions.bzl",
"repositories.bzl",
"toolchain.bzl",
"version.bzl",
],
visibility = ["//visibility:public"],
)
bzl_library(
name = "toolchain_bzl",
srcs = ["toolchain.bzl"],
@@ -35,11 +48,14 @@ bzl_library(
visibility = ["//visibility:public"],
deps = [
":toolchain_bzl",
"//internal:bun_build_support_bzl",
"//internal:bun_binary_bzl",
"//internal:bun_compile_bzl",
"//internal:bun_bundle_bzl",
"//internal:bun_dev_bzl",
"//internal:bun_script_bzl",
"//internal:bun_test_bzl",
"//internal:js_compat_bzl",
"//internal:js_library_bzl",
],
)

View File

@@ -1,21 +1,29 @@
"""Public API surface for Bun Bazel rules."""
load("//internal:bun_compile.bzl", _bun_build = "bun_build", _bun_compile = "bun_compile")
load("//internal:bun_binary.bzl", _bun_binary = "bun_binary")
load("//internal:bun_bundle.bzl", _bun_bundle = "bun_bundle")
load("//internal:bun_dev.bzl", _bun_dev = "bun_dev")
load("//internal:bun_script.bzl", _bun_script = "bun_script")
load("//internal:bun_test.bzl", _bun_test = "bun_test")
load("//internal:js_compat.bzl", _JsInfo = "JsInfo", _js_binary = "js_binary", _js_run_devserver = "js_run_devserver", _js_test = "js_test")
load("//internal:js_library.bzl", _js_library = "js_library", _ts_library = "ts_library")
load(":toolchain.bzl", _BunToolchainInfo = "BunToolchainInfo", _bun_toolchain = "bun_toolchain")
visibility("public")
bun_binary = _bun_binary
bun_build = _bun_build
bun_compile = _bun_compile
bun_bundle = _bun_bundle
bun_dev = _bun_dev
bun_script = _bun_script
bun_test = _bun_test
js_binary = _js_binary
js_test = _js_test
js_run_devserver = _js_run_devserver
js_library = _js_library
ts_library = _ts_library
JsInfo = _JsInfo
BunToolchainInfo = _BunToolchainInfo
bun_toolchain = _bun_toolchain

View File

@@ -13,6 +13,16 @@ _BUN_ARCHIVES = {
"asset": "bun-linux-aarch64.zip",
"binary": "bun-linux-aarch64/bun",
},
"bun_linux_x64_musl": {
"sha256": "48a6c32277d343db0148ce066336472ffd380358a4d26bb1329714742492d824",
"asset": "bun-linux-x64-musl.zip",
"binary": "bun-linux-x64-musl/bun",
},
"bun_linux_aarch64_musl": {
"sha256": "d2c81365a2e529b78a42330d3a0056e8dbd7896b4a6782c8e392b6532141e34d",
"asset": "bun-linux-aarch64-musl.zip",
"binary": "bun-linux-aarch64-musl/bun",
},
"bun_darwin_x64": {
"sha256": "c1d90bf6140f20e572c473065dc6b37a4b036349b5e9e4133779cc642ad94323",
"asset": "bun-darwin-x64.zip",
@@ -28,6 +38,11 @@ _BUN_ARCHIVES = {
"asset": "bun-windows-x64.zip",
"binary": "bun-windows-x64/bun.exe",
},
"bun_windows_aarch64": {
"sha256": "6822f3aa7bd2be40fb94c194a1185aae1c6fade54ca4fc2efdc722e37f3257d2",
"asset": "bun-windows-aarch64.zip",
"binary": "bun-windows-aarch64/bun.exe",
},
}
_BUN_GITHUB_RELEASE_URL_TEMPLATE = "https://github.com/oven-sh/bun/releases/download/bun-v{}/{}"
@@ -58,8 +73,16 @@ bun = module_extension(
_install = tag_class(
attrs = {
"name": attr.string(mandatory = True),
"package_json": attr.string(mandatory = True),
"bun_lockfile": attr.string(mandatory = True),
"package_json": attr.label(mandatory = True),
"bun_lockfile": attr.label(mandatory = True),
"install_inputs": attr.label_list(allow_files = True),
"isolated_home": attr.bool(default = True),
"production": attr.bool(default = False),
"omit": attr.string_list(),
"linker": attr.string(),
"backend": attr.string(),
"ignore_scripts": attr.bool(default = False),
"install_flags": attr.string_list(),
},
)
@@ -71,6 +94,15 @@ def _bun_install_impl(ctx):
name = install.name,
package_json = install.package_json,
bun_lockfile = install.bun_lockfile,
install_inputs = install.install_inputs,
isolated_home = install.isolated_home,
production = install.production,
omit = install.omit,
linker = install.linker,
backend = install.backend,
ignore_scripts = install.ignore_scripts,
install_flags = install.install_flags,
visible_repo_name = install.name,
)

View File

@@ -12,6 +12,16 @@ _BUN_ARCHIVES = {
"asset": "bun-linux-aarch64.zip",
"binary": "bun-linux-aarch64/bun",
},
"bun_linux_x64_musl": {
"sha256": "48a6c32277d343db0148ce066336472ffd380358a4d26bb1329714742492d824",
"asset": "bun-linux-x64-musl.zip",
"binary": "bun-linux-x64-musl/bun",
},
"bun_linux_aarch64_musl": {
"sha256": "d2c81365a2e529b78a42330d3a0056e8dbd7896b4a6782c8e392b6532141e34d",
"asset": "bun-linux-aarch64-musl.zip",
"binary": "bun-linux-aarch64-musl/bun",
},
"bun_darwin_x64": {
"sha256": "c1d90bf6140f20e572c473065dc6b37a4b036349b5e9e4133779cc642ad94323",
"asset": "bun-darwin-x64.zip",
@@ -27,6 +37,11 @@ _BUN_ARCHIVES = {
"asset": "bun-windows-x64.zip",
"binary": "bun-windows-x64/bun.exe",
},
"bun_windows_aarch64": {
"sha256": "6822f3aa7bd2be40fb94c194a1185aae1c6fade54ca4fc2efdc722e37f3257d2",
"asset": "bun-windows-aarch64.zip",
"binary": "bun-windows-aarch64/bun.exe",
},
}
_BUN_GITHUB_RELEASE_URL_TEMPLATE = "https://github.com/oven-sh/bun/releases/download/bun-v{}/{}"

View File

@@ -8,13 +8,17 @@ stardoc(
input = "//bun:defs.bzl",
symbol_names = [
"bun_binary",
"bun_build",
"bun_compile",
"bun_bundle",
"bun_dev",
"bun_script",
"bun_test",
"js_binary",
"js_run_devserver",
"js_test",
"js_library",
"ts_library",
],
deps = ["//bun:defs_bzl"],
target_compatible_with = ["@platforms//os:linux"],
)

View File

@@ -14,6 +14,7 @@ Unlike the build rules in [rules.md](rules.md), `bun_install` is not loaded from
- runs `bun install --frozen-lockfile`
- uses your checked-in `package.json` and `bun.lock` or `bun.lockb`
- creates an external Bazel repository exposing `:node_modules`
- generates `:defs.bzl` with `npm_link_all_packages()` and `package_target_name()`
- keeps dependency installation under Bun rather than npm
The generated repository can then be passed to rules such as `bun_script`,
@@ -28,6 +29,8 @@ bun_install_ext.install(
name = "bun_deps",
package_json = "//:package.json",
bun_lockfile = "//:bun.lock",
production = True,
omit = ["peer"],
)
use_repo(bun_install_ext, "bun_deps")
@@ -80,6 +83,77 @@ Example:
bun_lockfile = "//:bun.lock"
```
### `install_inputs`
Optional list of additional files under the same package root to copy into the
install repository before Bun runs.
Use this for install-time config or patch files that Bun needs to see, for
example `.npmrc`, `bunfig.toml`, or patch files referenced by your manifest.
Example:
```starlark
install_inputs = [
"//:.npmrc",
"//:patches/react.patch",
]
```
`bun_install` also copies these root-level files automatically when present:
- `.npmrc`
- `bunfig.json`
- `bunfig.toml`
### `isolated_home`
Optional boolean controlling whether Bun runs with `HOME` set to the generated
repository root.
- `True` (default): more isolated install environment
- `False`: lets Bun use the host `HOME`, which can improve repeated-install
performance when Bun's cache is home-scoped
### `production`
Optional boolean controlling whether Bun installs only production dependencies.
Example:
```starlark
production = True
```
### `omit`
Optional list of dependency groups to omit, forwarded as repeated
`--omit` flags. Common values are `dev`, `optional`, and `peer`.
### `linker`
Optional Bun linker strategy, forwarded as `--linker`.
Common values:
- `isolated`
- `hoisted`
### `backend`
Optional Bun install backend, forwarded as `--backend`.
Examples include `hardlink`, `symlink`, and `copyfile`.
### `ignore_scripts`
Optional boolean controlling whether Bun skips lifecycle scripts in the project
manifest.
### `install_flags`
Optional list of additional raw flags forwarded to `bun install`.
## Notes
- `bun_install` runs Bun, not npm.
@@ -88,3 +162,5 @@ bun_lockfile = "//:bun.lock"
is the dependency layout Bun installs.
- `--frozen-lockfile` is used, so the lockfile must already be in sync with
`package.json`.
- Additional `install_inputs` must be files under the same package root as the
selected `package_json`.

View File

@@ -36,9 +36,12 @@ use_repo(
bun_ext,
"bun_linux_x64",
"bun_linux_aarch64",
"bun_linux_x64_musl",
"bun_linux_aarch64_musl",
"bun_darwin_x64",
"bun_darwin_aarch64",
"bun_windows_x64",
"bun_windows_aarch64",
)
register_toolchains(
@@ -79,6 +82,12 @@ bun_script(
`bun_script` runs from the package directory by default and adds
`node_modules/.bin` to `PATH`.
## Build and compile
Use `bun_build` when Bun may emit a directory of outputs such as HTML, CSS,
chunks, and static assets. Use `bun_compile` for standalone executables created
with `bun build --compile`.
## Regeneration
The rule reference is generated from the public Starlark symbols in

View File

@@ -1,88 +1,483 @@
# rules_bun rule reference
<!-- Generated with Stardoc: http://skydoc.bazel.build -->
This file documents the public rules exported from `@rules_bun//bun:defs.bzl`.
Public API surface for Bun Bazel rules.
<a id="bun_binary"></a>
## bun_binary
Runs a JS/TS entry point with Bun as an executable target (`bazel run`).
<pre>
load("@rules_bun//bun:defs.bzl", "bun_binary")
Attributes:
bun_binary(<a href="#bun_binary-name">name</a>, <a href="#bun_binary-deps">deps</a>, <a href="#bun_binary-data">data</a>, <a href="#bun_binary-conditions">conditions</a>, <a href="#bun_binary-entry_point">entry_point</a>, <a href="#bun_binary-env_files">env_files</a>, <a href="#bun_binary-install_mode">install_mode</a>, <a href="#bun_binary-no_env_file">no_env_file</a>,
<a href="#bun_binary-node_modules">node_modules</a>, <a href="#bun_binary-preload">preload</a>, <a href="#bun_binary-run_flags">run_flags</a>, <a href="#bun_binary-smol">smol</a>, <a href="#bun_binary-working_dir">working_dir</a>)
</pre>
- `entry_point` (label, required): path to the main JS/TS file to execute.
- `node_modules` (label, optional): package files from a `node_modules` tree, typically produced by `bun_install`, made available in runfiles.
- `data` (label_list, optional): additional runtime files.
- `working_dir` (string, default: `"workspace"`, values: `"workspace" | "entry_point"`): runtime working directory.
Runs a JS/TS entry point with Bun as an executable target.
## bun_dev
Use this rule for non-test scripts and CLIs that should run via `bazel run`.
Runs a JS/TS entry point in Bun development watch mode (`bazel run`).
**ATTRIBUTES**
Attributes:
- `entry_point` (label, required): path to the main JS/TS file.
- `watch_mode` (string, default: `"watch"`, values: `"watch" | "hot"`): Bun live-reload mode.
- `restart_on` (label_list, optional): files that trigger full process restart when changed.
- `node_modules` (label, optional): package files from a `node_modules` tree, typically produced by `bun_install`, made available in runfiles.
- `data` (label_list, optional): additional runtime files for dev process.
- `working_dir` (string, default: `"workspace"`, values: `"workspace" | "entry_point"`): runtime working directory.
| Name | Description | Type | Mandatory | Default |
| :------------- | :------------- | :------------- | :------------- | :------------- |
| <a id="bun_binary-name"></a>name | A unique name for this target. | <a href="https://bazel.build/concepts/labels#target-names">Name</a> | required | |
| <a id="bun_binary-deps"></a>deps | Library dependencies required by the program. | <a href="https://bazel.build/concepts/labels">List of labels</a> | optional | `[]` |
| <a id="bun_binary-data"></a>data | Additional runtime files required by the program. | <a href="https://bazel.build/concepts/labels">List of labels</a> | optional | `[]` |
| <a id="bun_binary-conditions"></a>conditions | Custom package resolve conditions passed to Bun. | List of strings | optional | `[]` |
| <a id="bun_binary-entry_point"></a>entry_point | Path to the main JS/TS file to execute. | <a href="https://bazel.build/concepts/labels">Label</a> | required | |
| <a id="bun_binary-env_files"></a>env_files | Additional environment files loaded with `--env-file`. | <a href="https://bazel.build/concepts/labels">List of labels</a> | optional | `[]` |
| <a id="bun_binary-install_mode"></a>install_mode | Whether Bun may auto-install missing packages at runtime. | String | optional | `"disable"` |
| <a id="bun_binary-no_env_file"></a>no_env_file | If true, disables Bun's automatic `.env` loading. | Boolean | optional | `False` |
| <a id="bun_binary-node_modules"></a>node_modules | Optional label providing package files from a `node_modules` tree, typically produced by `bun_install`, in runfiles. | <a href="https://bazel.build/concepts/labels">Label</a> | optional | `None` |
| <a id="bun_binary-preload"></a>preload | Modules to preload with `--preload` before running the entry point. | <a href="https://bazel.build/concepts/labels">List of labels</a> | optional | `[]` |
| <a id="bun_binary-run_flags"></a>run_flags | Additional raw flags forwarded to `bun run` before the entry point. | List of strings | optional | `[]` |
| <a id="bun_binary-smol"></a>smol | If true, enables Bun's lower-memory runtime mode. | Boolean | optional | `False` |
| <a id="bun_binary-working_dir"></a>working_dir | Working directory at runtime: `workspace` root or nearest `entry_point` ancestor containing `.env`/`package.json`. | String | optional | `"workspace"` |
## bun_script
Runs a named `package.json` script with Bun as an executable target (`bazel run`).
<a id="bun_build"></a>
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.
## bun_build
Attributes:
<pre>
load("@rules_bun//bun:defs.bzl", "bun_build")
- `script` (string, required): package script name passed to `bun run <script>`.
- `package_json` (label, required): `package.json` file containing the named script.
- `node_modules` (label, optional): package files from a `node_modules` tree, typically produced by `bun_install`, made available in runfiles.
- `data` (label_list, optional): additional runtime files for the script.
- `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_build(<a href="#bun_build-name">name</a>, <a href="#bun_build-deps">deps</a>, <a href="#bun_build-data">data</a>, <a href="#bun_build-asset_naming">asset_naming</a>, <a href="#bun_build-banner">banner</a>, <a href="#bun_build-build_flags">build_flags</a>, <a href="#bun_build-chunk_naming">chunk_naming</a>, <a href="#bun_build-conditions">conditions</a>,
<a href="#bun_build-css_chunking">css_chunking</a>, <a href="#bun_build-define">define</a>, <a href="#bun_build-drop">drop</a>, <a href="#bun_build-emit_dce_annotations">emit_dce_annotations</a>, <a href="#bun_build-entry_naming">entry_naming</a>, <a href="#bun_build-entry_points">entry_points</a>, <a href="#bun_build-env">env</a>, <a href="#bun_build-external">external</a>,
<a href="#bun_build-feature">feature</a>, <a href="#bun_build-footer">footer</a>, <a href="#bun_build-format">format</a>, <a href="#bun_build-install_mode">install_mode</a>, <a href="#bun_build-jsx_factory">jsx_factory</a>, <a href="#bun_build-jsx_fragment">jsx_fragment</a>, <a href="#bun_build-jsx_import_source">jsx_import_source</a>,
<a href="#bun_build-jsx_runtime">jsx_runtime</a>, <a href="#bun_build-jsx_side_effects">jsx_side_effects</a>, <a href="#bun_build-keep_names">keep_names</a>, <a href="#bun_build-loader">loader</a>, <a href="#bun_build-metafile">metafile</a>, <a href="#bun_build-metafile_md">metafile_md</a>, <a href="#bun_build-minify">minify</a>,
<a href="#bun_build-minify_identifiers">minify_identifiers</a>, <a href="#bun_build-minify_syntax">minify_syntax</a>, <a href="#bun_build-minify_whitespace">minify_whitespace</a>, <a href="#bun_build-no_bundle">no_bundle</a>, <a href="#bun_build-node_modules">node_modules</a>, <a href="#bun_build-packages">packages</a>,
<a href="#bun_build-production">production</a>, <a href="#bun_build-public_path">public_path</a>, <a href="#bun_build-react_fast_refresh">react_fast_refresh</a>, <a href="#bun_build-root">root</a>, <a href="#bun_build-sourcemap">sourcemap</a>, <a href="#bun_build-splitting">splitting</a>, <a href="#bun_build-target">target</a>)
</pre>
Builds one or more entry points with `bun build`.
The rule emits a directory artifact so Bun can materialize multi-file output
graphs such as HTML, CSS, assets, and split chunks. Optional metafile outputs
may be requested with `metafile` and `metafile_md`.
**ATTRIBUTES**
| Name | Description | Type | Mandatory | Default |
| :------------- | :------------- | :------------- | :------------- | :------------- |
| <a id="bun_build-name"></a>name | A unique name for this target. | <a href="https://bazel.build/concepts/labels#target-names">Name</a> | required | |
| <a id="bun_build-deps"></a>deps | Source/library dependencies that provide transitive inputs. | <a href="https://bazel.build/concepts/labels">List of labels</a> | optional | `[]` |
| <a id="bun_build-data"></a>data | Additional non-source files needed during building. | <a href="https://bazel.build/concepts/labels">List of labels</a> | optional | `[]` |
| <a id="bun_build-asset_naming"></a>asset_naming | Optional asset naming template. | String | optional | `""` |
| <a id="bun_build-banner"></a>banner | Optional bundle banner text. | String | optional | `""` |
| <a id="bun_build-build_flags"></a>build_flags | Additional raw flags forwarded to `bun build`. | List of strings | optional | `[]` |
| <a id="bun_build-chunk_naming"></a>chunk_naming | Optional chunk naming template. | String | optional | `""` |
| <a id="bun_build-conditions"></a>conditions | Custom resolve conditions passed to Bun. | List of strings | optional | `[]` |
| <a id="bun_build-css_chunking"></a>css_chunking | If true, Bun chunks CSS across multiple entry points. | Boolean | optional | `False` |
| <a id="bun_build-define"></a>define | Repeated `--define` values such as `process.env.NODE_ENV:"production"`. | List of strings | optional | `[]` |
| <a id="bun_build-drop"></a>drop | Repeated `--drop` values, for example `console`. | List of strings | optional | `[]` |
| <a id="bun_build-emit_dce_annotations"></a>emit_dce_annotations | If true, re-emits DCE annotations in the bundle. | Boolean | optional | `False` |
| <a id="bun_build-entry_naming"></a>entry_naming | Optional entry naming template. | String | optional | `""` |
| <a id="bun_build-entry_points"></a>entry_points | Entry files to build, including JS/TS or HTML entry points. | <a href="https://bazel.build/concepts/labels">List of labels</a> | required | |
| <a id="bun_build-env"></a>env | Inline environment variable behavior passed to `--env`. | String | optional | `""` |
| <a id="bun_build-external"></a>external | Modules treated as externals (not bundled). | List of strings | optional | `[]` |
| <a id="bun_build-feature"></a>feature | Repeated `--feature` values for dead-code elimination. | List of strings | optional | `[]` |
| <a id="bun_build-footer"></a>footer | Optional bundle footer text. | String | optional | `""` |
| <a id="bun_build-format"></a>format | Output module format. | String | optional | `"esm"` |
| <a id="bun_build-install_mode"></a>install_mode | Whether Bun may auto-install missing packages while executing the build. | String | optional | `"disable"` |
| <a id="bun_build-jsx_factory"></a>jsx_factory | Optional JSX factory override. | String | optional | `""` |
| <a id="bun_build-jsx_fragment"></a>jsx_fragment | Optional JSX fragment override. | String | optional | `""` |
| <a id="bun_build-jsx_import_source"></a>jsx_import_source | Optional JSX import source override. | String | optional | `""` |
| <a id="bun_build-jsx_runtime"></a>jsx_runtime | Optional JSX runtime override. | String | optional | `""` |
| <a id="bun_build-jsx_side_effects"></a>jsx_side_effects | If true, treats JSX as having side effects. | Boolean | optional | `False` |
| <a id="bun_build-keep_names"></a>keep_names | If true, preserves function and class names when minifying. | Boolean | optional | `False` |
| <a id="bun_build-loader"></a>loader | Repeated `--loader` values such as `.svg:file`. | List of strings | optional | `[]` |
| <a id="bun_build-metafile"></a>metafile | If true, emits Bun's JSON metafile alongside the output directory. | Boolean | optional | `False` |
| <a id="bun_build-metafile_md"></a>metafile_md | If true, emits Bun's markdown metafile alongside the output directory. | Boolean | optional | `False` |
| <a id="bun_build-minify"></a>minify | If true, enables all Bun minification passes. | Boolean | optional | `False` |
| <a id="bun_build-minify_identifiers"></a>minify_identifiers | If true, minifies identifiers only. | Boolean | optional | `False` |
| <a id="bun_build-minify_syntax"></a>minify_syntax | If true, minifies syntax only. | Boolean | optional | `False` |
| <a id="bun_build-minify_whitespace"></a>minify_whitespace | If true, minifies whitespace only. | Boolean | optional | `False` |
| <a id="bun_build-no_bundle"></a>no_bundle | If true, transpiles without bundling. | Boolean | optional | `False` |
| <a id="bun_build-node_modules"></a>node_modules | Optional label providing package files from a `node_modules` tree, typically produced by `bun_install`, for package resolution. | <a href="https://bazel.build/concepts/labels">Label</a> | optional | `None` |
| <a id="bun_build-packages"></a>packages | Whether packages stay bundled or are treated as external. | String | optional | `"bundle"` |
| <a id="bun_build-production"></a>production | If true, sets `NODE_ENV=production` and enables Bun production mode. | Boolean | optional | `False` |
| <a id="bun_build-public_path"></a>public_path | Optional public path prefix for emitted imports. | String | optional | `""` |
| <a id="bun_build-react_fast_refresh"></a>react_fast_refresh | If true, enables Bun's React fast refresh transform. | Boolean | optional | `False` |
| <a id="bun_build-root"></a>root | Optional root directory for multiple entry points. When omitted, `bun_build` derives one from the entry point parent directories to keep emitted files inside the declared output tree. | String | optional | `""` |
| <a id="bun_build-sourcemap"></a>sourcemap | Sourcemap emission mode. | String | optional | `"none"` |
| <a id="bun_build-splitting"></a>splitting | If true, enables code splitting. | Boolean | optional | `False` |
| <a id="bun_build-target"></a>target | Bun build target environment. | String | optional | `"browser"` |
<a id="bun_bundle"></a>
## bun_bundle
Bundles one or more JS/TS entry points with Bun build.
<pre>
load("@rules_bun//bun:defs.bzl", "bun_bundle")
Attributes:
bun_bundle(<a href="#bun_bundle-name">name</a>, <a href="#bun_bundle-deps">deps</a>, <a href="#bun_bundle-data">data</a>, <a href="#bun_bundle-build_flags">build_flags</a>, <a href="#bun_bundle-entry_points">entry_points</a>, <a href="#bun_bundle-external">external</a>, <a href="#bun_bundle-format">format</a>, <a href="#bun_bundle-install_mode">install_mode</a>, <a href="#bun_bundle-minify">minify</a>,
<a href="#bun_bundle-node_modules">node_modules</a>, <a href="#bun_bundle-sourcemap">sourcemap</a>, <a href="#bun_bundle-target">target</a>)
</pre>
- `entry_points` (label_list, required): entry files to bundle.
- `node_modules` (label, optional): package files from a `node_modules` tree, typically produced by `bun_install`, used for package resolution.
- `deps` (label_list, optional): source/library dependencies for transitive inputs.
- `data` (label_list, optional): additional non-source files needed during bundling.
- `target` (string, default: `"browser"`, values: `"browser" | "node" | "bun"`): Bun build target.
- `format` (string, default: `"esm"`, values: `"esm" | "cjs" | "iife"`): module format.
- `minify` (bool, default: `False`): minifies bundle output.
- `sourcemap` (bool, default: `False`): emits source maps.
- `external` (string_list, optional): package names treated as external (not bundled).
Bundles one or more JS/TS entry points using Bun build.
Each entry point produces one output JavaScript artifact.
**ATTRIBUTES**
| Name | Description | Type | Mandatory | Default |
| :------------- | :------------- | :------------- | :------------- | :------------- |
| <a id="bun_bundle-name"></a>name | A unique name for this target. | <a href="https://bazel.build/concepts/labels#target-names">Name</a> | required | |
| <a id="bun_bundle-deps"></a>deps | Source/library dependencies that provide transitive inputs. | <a href="https://bazel.build/concepts/labels">List of labels</a> | optional | `[]` |
| <a id="bun_bundle-data"></a>data | Additional non-source files needed during bundling. | <a href="https://bazel.build/concepts/labels">List of labels</a> | optional | `[]` |
| <a id="bun_bundle-build_flags"></a>build_flags | Additional raw flags forwarded to `bun build`. | List of strings | optional | `[]` |
| <a id="bun_bundle-entry_points"></a>entry_points | Entry files to bundle. | <a href="https://bazel.build/concepts/labels">List of labels</a> | required | |
| <a id="bun_bundle-external"></a>external | Package names to treat as externals (not bundled). | List of strings | optional | `[]` |
| <a id="bun_bundle-format"></a>format | Output module format. | String | optional | `"esm"` |
| <a id="bun_bundle-install_mode"></a>install_mode | Whether Bun may auto-install missing packages during bundling. | String | optional | `"disable"` |
| <a id="bun_bundle-minify"></a>minify | If true, minifies bundle output. | Boolean | optional | `False` |
| <a id="bun_bundle-node_modules"></a>node_modules | Optional label providing package files from a `node_modules` tree, typically produced by `bun_install`, for package resolution. | <a href="https://bazel.build/concepts/labels">Label</a> | optional | `None` |
| <a id="bun_bundle-sourcemap"></a>sourcemap | If true, emits source maps. | Boolean | optional | `False` |
| <a id="bun_bundle-target"></a>target | Bun build target environment. | String | optional | `"browser"` |
<a id="bun_compile"></a>
## bun_compile
<pre>
load("@rules_bun//bun:defs.bzl", "bun_compile")
bun_compile(<a href="#bun_compile-name">name</a>, <a href="#bun_compile-deps">deps</a>, <a href="#bun_compile-data">data</a>, <a href="#bun_compile-asset_naming">asset_naming</a>, <a href="#bun_compile-banner">banner</a>, <a href="#bun_compile-build_flags">build_flags</a>, <a href="#bun_compile-bytecode">bytecode</a>, <a href="#bun_compile-chunk_naming">chunk_naming</a>,
<a href="#bun_compile-compile_autoload_bunfig">compile_autoload_bunfig</a>, <a href="#bun_compile-compile_autoload_dotenv">compile_autoload_dotenv</a>, <a href="#bun_compile-compile_autoload_package_json">compile_autoload_package_json</a>,
<a href="#bun_compile-compile_autoload_tsconfig">compile_autoload_tsconfig</a>, <a href="#bun_compile-compile_exec_argv">compile_exec_argv</a>, <a href="#bun_compile-compile_executable">compile_executable</a>, <a href="#bun_compile-conditions">conditions</a>,
<a href="#bun_compile-css_chunking">css_chunking</a>, <a href="#bun_compile-define">define</a>, <a href="#bun_compile-drop">drop</a>, <a href="#bun_compile-emit_dce_annotations">emit_dce_annotations</a>, <a href="#bun_compile-entry_naming">entry_naming</a>, <a href="#bun_compile-entry_point">entry_point</a>, <a href="#bun_compile-env">env</a>,
<a href="#bun_compile-external">external</a>, <a href="#bun_compile-feature">feature</a>, <a href="#bun_compile-footer">footer</a>, <a href="#bun_compile-format">format</a>, <a href="#bun_compile-install_mode">install_mode</a>, <a href="#bun_compile-jsx_factory">jsx_factory</a>, <a href="#bun_compile-jsx_fragment">jsx_fragment</a>,
<a href="#bun_compile-jsx_import_source">jsx_import_source</a>, <a href="#bun_compile-jsx_runtime">jsx_runtime</a>, <a href="#bun_compile-jsx_side_effects">jsx_side_effects</a>, <a href="#bun_compile-keep_names">keep_names</a>, <a href="#bun_compile-loader">loader</a>, <a href="#bun_compile-minify">minify</a>,
<a href="#bun_compile-minify_identifiers">minify_identifiers</a>, <a href="#bun_compile-minify_syntax">minify_syntax</a>, <a href="#bun_compile-minify_whitespace">minify_whitespace</a>, <a href="#bun_compile-no_bundle">no_bundle</a>, <a href="#bun_compile-node_modules">node_modules</a>, <a href="#bun_compile-packages">packages</a>,
<a href="#bun_compile-production">production</a>, <a href="#bun_compile-public_path">public_path</a>, <a href="#bun_compile-react_fast_refresh">react_fast_refresh</a>, <a href="#bun_compile-root">root</a>, <a href="#bun_compile-sourcemap">sourcemap</a>, <a href="#bun_compile-splitting">splitting</a>, <a href="#bun_compile-target">target</a>,
<a href="#bun_compile-windows_copyright">windows_copyright</a>, <a href="#bun_compile-windows_description">windows_description</a>, <a href="#bun_compile-windows_hide_console">windows_hide_console</a>, <a href="#bun_compile-windows_icon">windows_icon</a>,
<a href="#bun_compile-windows_publisher">windows_publisher</a>, <a href="#bun_compile-windows_title">windows_title</a>, <a href="#bun_compile-windows_version">windows_version</a>)
</pre>
Compiles a Bun program into a standalone executable with `bun build --compile`.
**ATTRIBUTES**
| Name | Description | Type | Mandatory | Default |
| :------------- | :------------- | :------------- | :------------- | :------------- |
| <a id="bun_compile-name"></a>name | A unique name for this target. | <a href="https://bazel.build/concepts/labels#target-names">Name</a> | required | |
| <a id="bun_compile-deps"></a>deps | Source/library dependencies that provide transitive inputs. | <a href="https://bazel.build/concepts/labels">List of labels</a> | optional | `[]` |
| <a id="bun_compile-data"></a>data | Additional non-source files needed during building. | <a href="https://bazel.build/concepts/labels">List of labels</a> | optional | `[]` |
| <a id="bun_compile-asset_naming"></a>asset_naming | Optional asset naming template. | String | optional | `""` |
| <a id="bun_compile-banner"></a>banner | Optional bundle banner text. | String | optional | `""` |
| <a id="bun_compile-build_flags"></a>build_flags | Additional raw flags forwarded to `bun build`. | List of strings | optional | `[]` |
| <a id="bun_compile-bytecode"></a>bytecode | If true, enables Bun bytecode caching in the compiled executable. | Boolean | optional | `False` |
| <a id="bun_compile-chunk_naming"></a>chunk_naming | Optional chunk naming template. | String | optional | `""` |
| <a id="bun_compile-compile_autoload_bunfig"></a>compile_autoload_bunfig | Whether the compiled executable auto-loads `bunfig.toml` at runtime. | Boolean | optional | `True` |
| <a id="bun_compile-compile_autoload_dotenv"></a>compile_autoload_dotenv | Whether the compiled executable auto-loads `.env` files at runtime. | Boolean | optional | `True` |
| <a id="bun_compile-compile_autoload_package_json"></a>compile_autoload_package_json | Whether the compiled executable auto-loads `package.json` at runtime. | Boolean | optional | `False` |
| <a id="bun_compile-compile_autoload_tsconfig"></a>compile_autoload_tsconfig | Whether the compiled executable auto-loads `tsconfig.json` at runtime. | Boolean | optional | `False` |
| <a id="bun_compile-compile_exec_argv"></a>compile_exec_argv | Repeated `--compile-exec-argv` values prepended to the executable's `execArgv`. | List of strings | optional | `[]` |
| <a id="bun_compile-compile_executable"></a>compile_executable | Optional Bun executable used for cross-compilation via `--compile-executable-path`. | <a href="https://bazel.build/concepts/labels">Label</a> | optional | `None` |
| <a id="bun_compile-conditions"></a>conditions | Custom resolve conditions passed to Bun. | List of strings | optional | `[]` |
| <a id="bun_compile-css_chunking"></a>css_chunking | If true, Bun chunks CSS across multiple entry points. | Boolean | optional | `False` |
| <a id="bun_compile-define"></a>define | Repeated `--define` values such as `process.env.NODE_ENV:"production"`. | List of strings | optional | `[]` |
| <a id="bun_compile-drop"></a>drop | Repeated `--drop` values, for example `console`. | List of strings | optional | `[]` |
| <a id="bun_compile-emit_dce_annotations"></a>emit_dce_annotations | If true, re-emits DCE annotations in the bundle. | Boolean | optional | `False` |
| <a id="bun_compile-entry_naming"></a>entry_naming | Optional entry naming template. | String | optional | `""` |
| <a id="bun_compile-entry_point"></a>entry_point | Entry file to compile into an executable. | <a href="https://bazel.build/concepts/labels">Label</a> | required | |
| <a id="bun_compile-env"></a>env | Inline environment variable behavior passed to `--env`. | String | optional | `""` |
| <a id="bun_compile-external"></a>external | Modules treated as externals (not bundled). | List of strings | optional | `[]` |
| <a id="bun_compile-feature"></a>feature | Repeated `--feature` values for dead-code elimination. | List of strings | optional | `[]` |
| <a id="bun_compile-footer"></a>footer | Optional bundle footer text. | String | optional | `""` |
| <a id="bun_compile-format"></a>format | Output module format. | String | optional | `"esm"` |
| <a id="bun_compile-install_mode"></a>install_mode | Whether Bun may auto-install missing packages while executing the build. | String | optional | `"disable"` |
| <a id="bun_compile-jsx_factory"></a>jsx_factory | Optional JSX factory override. | String | optional | `""` |
| <a id="bun_compile-jsx_fragment"></a>jsx_fragment | Optional JSX fragment override. | String | optional | `""` |
| <a id="bun_compile-jsx_import_source"></a>jsx_import_source | Optional JSX import source override. | String | optional | `""` |
| <a id="bun_compile-jsx_runtime"></a>jsx_runtime | Optional JSX runtime override. | String | optional | `""` |
| <a id="bun_compile-jsx_side_effects"></a>jsx_side_effects | If true, treats JSX as having side effects. | Boolean | optional | `False` |
| <a id="bun_compile-keep_names"></a>keep_names | If true, preserves function and class names when minifying. | Boolean | optional | `False` |
| <a id="bun_compile-loader"></a>loader | Repeated `--loader` values such as `.svg:file`. | List of strings | optional | `[]` |
| <a id="bun_compile-minify"></a>minify | If true, enables all Bun minification passes. | Boolean | optional | `False` |
| <a id="bun_compile-minify_identifiers"></a>minify_identifiers | If true, minifies identifiers only. | Boolean | optional | `False` |
| <a id="bun_compile-minify_syntax"></a>minify_syntax | If true, minifies syntax only. | Boolean | optional | `False` |
| <a id="bun_compile-minify_whitespace"></a>minify_whitespace | If true, minifies whitespace only. | Boolean | optional | `False` |
| <a id="bun_compile-no_bundle"></a>no_bundle | If true, transpiles without bundling. | Boolean | optional | `False` |
| <a id="bun_compile-node_modules"></a>node_modules | Optional label providing package files from a `node_modules` tree, typically produced by `bun_install`, for package resolution. | <a href="https://bazel.build/concepts/labels">Label</a> | optional | `None` |
| <a id="bun_compile-packages"></a>packages | Whether packages stay bundled or are treated as external. | String | optional | `"bundle"` |
| <a id="bun_compile-production"></a>production | If true, sets `NODE_ENV=production` and enables Bun production mode. | Boolean | optional | `False` |
| <a id="bun_compile-public_path"></a>public_path | Optional public path prefix for emitted imports. | String | optional | `""` |
| <a id="bun_compile-react_fast_refresh"></a>react_fast_refresh | If true, enables Bun's React fast refresh transform. | Boolean | optional | `False` |
| <a id="bun_compile-root"></a>root | Optional root directory for multiple entry points. | String | optional | `""` |
| <a id="bun_compile-sourcemap"></a>sourcemap | Sourcemap emission mode. | String | optional | `"none"` |
| <a id="bun_compile-splitting"></a>splitting | If true, enables code splitting. | Boolean | optional | `False` |
| <a id="bun_compile-target"></a>target | Bun build target environment for the compiled executable. | String | optional | `"bun"` |
| <a id="bun_compile-windows_copyright"></a>windows_copyright | Optional Windows copyright metadata. | String | optional | `""` |
| <a id="bun_compile-windows_description"></a>windows_description | Optional Windows description metadata. | String | optional | `""` |
| <a id="bun_compile-windows_hide_console"></a>windows_hide_console | When targeting Windows, hides the console window for GUI-style executables. | Boolean | optional | `False` |
| <a id="bun_compile-windows_icon"></a>windows_icon | Optional Windows icon path passed directly to Bun. | String | optional | `""` |
| <a id="bun_compile-windows_publisher"></a>windows_publisher | Optional Windows publisher metadata. | String | optional | `""` |
| <a id="bun_compile-windows_title"></a>windows_title | Optional Windows executable title. | String | optional | `""` |
| <a id="bun_compile-windows_version"></a>windows_version | Optional Windows version metadata. | String | optional | `""` |
<a id="bun_dev"></a>
## bun_dev
<pre>
load("@rules_bun//bun:defs.bzl", "bun_dev")
bun_dev(<a href="#bun_dev-name">name</a>, <a href="#bun_dev-data">data</a>, <a href="#bun_dev-conditions">conditions</a>, <a href="#bun_dev-entry_point">entry_point</a>, <a href="#bun_dev-env_files">env_files</a>, <a href="#bun_dev-install_mode">install_mode</a>, <a href="#bun_dev-no_clear_screen">no_clear_screen</a>, <a href="#bun_dev-no_env_file">no_env_file</a>,
<a href="#bun_dev-node_modules">node_modules</a>, <a href="#bun_dev-preload">preload</a>, <a href="#bun_dev-restart_on">restart_on</a>, <a href="#bun_dev-run_flags">run_flags</a>, <a href="#bun_dev-smol">smol</a>, <a href="#bun_dev-watch_mode">watch_mode</a>, <a href="#bun_dev-working_dir">working_dir</a>)
</pre>
Runs a JS/TS entry point in Bun development watch mode.
This rule is intended for local dev loops (`bazel run`) and supports Bun
watch/HMR plus optional full restarts on selected file changes.
**ATTRIBUTES**
| Name | Description | Type | Mandatory | Default |
| :------------- | :------------- | :------------- | :------------- | :------------- |
| <a id="bun_dev-name"></a>name | A unique name for this target. | <a href="https://bazel.build/concepts/labels#target-names">Name</a> | required | |
| <a id="bun_dev-data"></a>data | Additional runtime files required by the dev process. | <a href="https://bazel.build/concepts/labels">List of labels</a> | optional | `[]` |
| <a id="bun_dev-conditions"></a>conditions | Custom package resolve conditions passed to Bun. | List of strings | optional | `[]` |
| <a id="bun_dev-entry_point"></a>entry_point | Path to the main JS/TS file to execute in dev mode. | <a href="https://bazel.build/concepts/labels">Label</a> | required | |
| <a id="bun_dev-env_files"></a>env_files | Additional environment files loaded with `--env-file`. | <a href="https://bazel.build/concepts/labels">List of labels</a> | optional | `[]` |
| <a id="bun_dev-install_mode"></a>install_mode | Whether Bun may auto-install missing packages in dev mode. | String | optional | `"disable"` |
| <a id="bun_dev-no_clear_screen"></a>no_clear_screen | If true, disables terminal clearing on Bun reloads. | Boolean | optional | `False` |
| <a id="bun_dev-no_env_file"></a>no_env_file | If true, disables Bun's automatic `.env` loading. | Boolean | optional | `False` |
| <a id="bun_dev-node_modules"></a>node_modules | Optional label providing package files from a `node_modules` tree, typically produced by `bun_install`, in runfiles. | <a href="https://bazel.build/concepts/labels">Label</a> | optional | `None` |
| <a id="bun_dev-preload"></a>preload | Modules to preload with `--preload` before running the entry point. | <a href="https://bazel.build/concepts/labels">List of labels</a> | optional | `[]` |
| <a id="bun_dev-restart_on"></a>restart_on | Files that trigger a full Bun process restart when they change. | <a href="https://bazel.build/concepts/labels">List of labels</a> | optional | `[]` |
| <a id="bun_dev-run_flags"></a>run_flags | Additional raw flags forwarded to `bun run` before the entry point. | List of strings | optional | `[]` |
| <a id="bun_dev-smol"></a>smol | If true, enables Bun's lower-memory runtime mode. | Boolean | optional | `False` |
| <a id="bun_dev-watch_mode"></a>watch_mode | Bun live-reload mode: `watch` (default) or `hot`. | String | optional | `"watch"` |
| <a id="bun_dev-working_dir"></a>working_dir | Working directory at runtime: `workspace` root or nearest `entry_point` ancestor containing `.env`/`package.json`. | String | optional | `"workspace"` |
<a id="bun_script"></a>
## bun_script
<pre>
load("@rules_bun//bun:defs.bzl", "bun_script")
bun_script(<a href="#bun_script-name">name</a>, <a href="#bun_script-data">data</a>, <a href="#bun_script-conditions">conditions</a>, <a href="#bun_script-env_files">env_files</a>, <a href="#bun_script-execution_mode">execution_mode</a>, <a href="#bun_script-filters">filters</a>, <a href="#bun_script-install_mode">install_mode</a>, <a href="#bun_script-no_env_file">no_env_file</a>,
<a href="#bun_script-no_exit_on_error">no_exit_on_error</a>, <a href="#bun_script-node_modules">node_modules</a>, <a href="#bun_script-package_json">package_json</a>, <a href="#bun_script-preload">preload</a>, <a href="#bun_script-run_flags">run_flags</a>, <a href="#bun_script-script">script</a>, <a href="#bun_script-shell">shell</a>, <a href="#bun_script-silent">silent</a>,
<a href="#bun_script-smol">smol</a>, <a href="#bun_script-working_dir">working_dir</a>, <a href="#bun_script-workspaces">workspaces</a>)
</pre>
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
`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`.
**ATTRIBUTES**
| Name | Description | Type | Mandatory | Default |
| :------------- | :------------- | :------------- | :------------- | :------------- |
| <a id="bun_script-name"></a>name | A unique name for this target. | <a href="https://bazel.build/concepts/labels#target-names">Name</a> | required | |
| <a id="bun_script-data"></a>data | Additional runtime files required by the script. | <a href="https://bazel.build/concepts/labels">List of labels</a> | optional | `[]` |
| <a id="bun_script-conditions"></a>conditions | Custom package resolve conditions passed to Bun. | List of strings | optional | `[]` |
| <a id="bun_script-env_files"></a>env_files | Additional environment files loaded with `--env-file`. | <a href="https://bazel.build/concepts/labels">List of labels</a> | optional | `[]` |
| <a id="bun_script-execution_mode"></a>execution_mode | How Bun should execute matching workspace scripts. | String | optional | `"single"` |
| <a id="bun_script-filters"></a>filters | Workspace package filters passed via repeated `--filter` flags. | List of strings | optional | `[]` |
| <a id="bun_script-install_mode"></a>install_mode | Whether Bun may auto-install missing packages while running the script. | String | optional | `"disable"` |
| <a id="bun_script-no_env_file"></a>no_env_file | If true, disables Bun's automatic `.env` loading. | Boolean | optional | `False` |
| <a id="bun_script-no_exit_on_error"></a>no_exit_on_error | If true, Bun keeps running other workspace scripts when one fails. | Boolean | optional | `False` |
| <a id="bun_script-node_modules"></a>node_modules | Optional label providing package files from a `node_modules` tree, typically produced by `bun_install`, in runfiles. Executables from `node_modules/.bin` are added to `PATH`, which is useful for scripts such as `vite`. | <a href="https://bazel.build/concepts/labels">Label</a> | optional | `None` |
| <a id="bun_script-package_json"></a>package_json | Label of the `package.json` file containing the named script. | <a href="https://bazel.build/concepts/labels">Label</a> | required | |
| <a id="bun_script-preload"></a>preload | Modules to preload with `--preload` before running the script. | <a href="https://bazel.build/concepts/labels">List of labels</a> | optional | `[]` |
| <a id="bun_script-run_flags"></a>run_flags | Additional raw flags forwarded to `bun run` before the script name. | List of strings | optional | `[]` |
| <a id="bun_script-script"></a>script | Name of the `package.json` script to execute via `bun run <script>`. | String | required | |
| <a id="bun_script-shell"></a>shell | Optional shell implementation for package scripts. | String | optional | `""` |
| <a id="bun_script-silent"></a>silent | If true, suppresses Bun's command echo for package scripts. | Boolean | optional | `False` |
| <a id="bun_script-smol"></a>smol | If true, enables Bun's lower-memory runtime mode. | Boolean | optional | `False` |
| <a id="bun_script-working_dir"></a>working_dir | 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. | String | optional | `"package"` |
| <a id="bun_script-workspaces"></a>workspaces | If true, runs the script in all workspace packages. | Boolean | optional | `False` |
<a id="bun_test"></a>
## bun_test
Runs Bun tests as a Bazel test target (`bazel test`).
<pre>
load("@rules_bun//bun:defs.bzl", "bun_test")
Attributes:
bun_test(<a href="#bun_test-name">name</a>, <a href="#bun_test-deps">deps</a>, <a href="#bun_test-srcs">srcs</a>, <a href="#bun_test-data">data</a>, <a href="#bun_test-bail">bail</a>, <a href="#bun_test-concurrent">concurrent</a>, <a href="#bun_test-coverage">coverage</a>, <a href="#bun_test-coverage_reporters">coverage_reporters</a>, <a href="#bun_test-env_files">env_files</a>,
<a href="#bun_test-install_mode">install_mode</a>, <a href="#bun_test-max_concurrency">max_concurrency</a>, <a href="#bun_test-no_env_file">no_env_file</a>, <a href="#bun_test-node_modules">node_modules</a>, <a href="#bun_test-only">only</a>, <a href="#bun_test-pass_with_no_tests">pass_with_no_tests</a>, <a href="#bun_test-preload">preload</a>,
<a href="#bun_test-randomize">randomize</a>, <a href="#bun_test-reporter">reporter</a>, <a href="#bun_test-rerun_each">rerun_each</a>, <a href="#bun_test-retry">retry</a>, <a href="#bun_test-seed">seed</a>, <a href="#bun_test-smol">smol</a>, <a href="#bun_test-test_flags">test_flags</a>, <a href="#bun_test-timeout_ms">timeout_ms</a>, <a href="#bun_test-todo">todo</a>,
<a href="#bun_test-update_snapshots">update_snapshots</a>)
</pre>
- `srcs` (label_list, required): test source files passed to `bun test`.
- `node_modules` (label, optional): package files from a `node_modules` tree, typically produced by `bun_install`, made available in runfiles.
- `deps` (label_list, optional): library dependencies required by tests.
- `data` (label_list, optional): additional runtime files needed by tests.
Runs Bun tests as a Bazel test target.
Supports Bazel test filtering (`--test_filter`) and coverage integration.
**ATTRIBUTES**
| Name | Description | Type | Mandatory | Default |
| :------------- | :------------- | :------------- | :------------- | :------------- |
| <a id="bun_test-name"></a>name | A unique name for this target. | <a href="https://bazel.build/concepts/labels#target-names">Name</a> | required | |
| <a id="bun_test-deps"></a>deps | Library dependencies required by test sources. | <a href="https://bazel.build/concepts/labels">List of labels</a> | optional | `[]` |
| <a id="bun_test-srcs"></a>srcs | Test source files passed to `bun test`. | <a href="https://bazel.build/concepts/labels">List of labels</a> | required | |
| <a id="bun_test-data"></a>data | Additional runtime files needed by tests. | <a href="https://bazel.build/concepts/labels">List of labels</a> | optional | `[]` |
| <a id="bun_test-bail"></a>bail | Optional failure count after which Bun exits the test run. | Integer | optional | `0` |
| <a id="bun_test-concurrent"></a>concurrent | If true, treats all tests as concurrent tests. | Boolean | optional | `False` |
| <a id="bun_test-coverage"></a>coverage | If true, always enables Bun coverage output. | Boolean | optional | `False` |
| <a id="bun_test-coverage_reporters"></a>coverage_reporters | Repeated Bun coverage reporters such as `text` or `lcov`. | List of strings | optional | `[]` |
| <a id="bun_test-env_files"></a>env_files | Additional environment files loaded with `--env-file`. | <a href="https://bazel.build/concepts/labels">List of labels</a> | optional | `[]` |
| <a id="bun_test-install_mode"></a>install_mode | Whether Bun may auto-install missing packages while testing. | String | optional | `"disable"` |
| <a id="bun_test-max_concurrency"></a>max_concurrency | Optional maximum number of concurrent tests. | Integer | optional | `0` |
| <a id="bun_test-no_env_file"></a>no_env_file | If true, disables Bun's automatic `.env` loading. | Boolean | optional | `False` |
| <a id="bun_test-node_modules"></a>node_modules | Optional label providing package files from a `node_modules` tree, typically produced by `bun_install`, in runfiles. | <a href="https://bazel.build/concepts/labels">Label</a> | optional | `None` |
| <a id="bun_test-only"></a>only | If true, runs only tests marked with `test.only()` or `describe.only()`. | Boolean | optional | `False` |
| <a id="bun_test-pass_with_no_tests"></a>pass_with_no_tests | If true, exits successfully when no tests are found. | Boolean | optional | `False` |
| <a id="bun_test-preload"></a>preload | Modules to preload with `--preload` before running tests. | <a href="https://bazel.build/concepts/labels">List of labels</a> | optional | `[]` |
| <a id="bun_test-randomize"></a>randomize | If true, runs tests in random order. | Boolean | optional | `False` |
| <a id="bun_test-reporter"></a>reporter | Test reporter format. | String | optional | `"console"` |
| <a id="bun_test-rerun_each"></a>rerun_each | Optional number of times to rerun each test file. | Integer | optional | `0` |
| <a id="bun_test-retry"></a>retry | Optional default retry count for all tests. | Integer | optional | `0` |
| <a id="bun_test-seed"></a>seed | Optional randomization seed. | Integer | optional | `0` |
| <a id="bun_test-smol"></a>smol | If true, enables Bun's lower-memory runtime mode. | Boolean | optional | `False` |
| <a id="bun_test-test_flags"></a>test_flags | Additional raw flags forwarded to `bun test` before the test source list. | List of strings | optional | `[]` |
| <a id="bun_test-timeout_ms"></a>timeout_ms | Optional per-test timeout in milliseconds. | Integer | optional | `0` |
| <a id="bun_test-todo"></a>todo | If true, includes tests marked with `test.todo()`. | Boolean | optional | `False` |
| <a id="bun_test-update_snapshots"></a>update_snapshots | If true, updates Bun snapshot files. | Boolean | optional | `False` |
<a id="js_library"></a>
## js_library
<pre>
load("@rules_bun//bun:defs.bzl", "js_library")
js_library(<a href="#js_library-name">name</a>, <a href="#js_library-deps">deps</a>, <a href="#js_library-srcs">srcs</a>, <a href="#js_library-data">data</a>, <a href="#js_library-types">types</a>)
</pre>
Aggregates JavaScript sources and transitive Bun source dependencies.
Attributes:
**ATTRIBUTES**
- `srcs` (label_list, optional): `.js`, `.jsx`, `.mjs`, `.cjs` files.
- `deps` (label_list, optional): dependent source libraries.
| Name | Description | Type | Mandatory | Default |
| :------------- | :------------- | :------------- | :------------- | :------------- |
| <a id="js_library-name"></a>name | A unique name for this target. | <a href="https://bazel.build/concepts/labels#target-names">Name</a> | required | |
| <a id="js_library-deps"></a>deps | Other Bun source libraries to include transitively. | <a href="https://bazel.build/concepts/labels">List of labels</a> | optional | `[]` |
| <a id="js_library-srcs"></a>srcs | JavaScript source files in this library. | <a href="https://bazel.build/concepts/labels">List of labels</a> | optional | `[]` |
| <a id="js_library-data"></a>data | Optional runtime files propagated to dependents. | <a href="https://bazel.build/concepts/labels">List of labels</a> | optional | `[]` |
| <a id="js_library-types"></a>types | Optional declaration files associated with this library. | <a href="https://bazel.build/concepts/labels">List of labels</a> | optional | `[]` |
<a id="js_run_devserver"></a>
## js_run_devserver
<pre>
load("@rules_bun//bun:defs.bzl", "js_run_devserver")
js_run_devserver(<a href="#js_run_devserver-name">name</a>, <a href="#js_run_devserver-deps">deps</a>, <a href="#js_run_devserver-data">data</a>, <a href="#js_run_devserver-node_modules">node_modules</a>, <a href="#js_run_devserver-package_dir_hint">package_dir_hint</a>, <a href="#js_run_devserver-package_json">package_json</a>, <a href="#js_run_devserver-tool">tool</a>, <a href="#js_run_devserver-working_dir">working_dir</a>)
</pre>
Runs an executable target from a staged JS workspace.
This is a Bun-backed compatibility adapter for `rules_js`-style devserver
targets. It stages the same runtime workspace as the Bun rules, then executes
the provided tool with any default arguments.
**ATTRIBUTES**
| Name | Description | Type | Mandatory | Default |
| :------------- | :------------- | :------------- | :------------- | :------------- |
| <a id="js_run_devserver-name"></a>name | A unique name for this target. | <a href="https://bazel.build/concepts/labels#target-names">Name</a> | required | |
| <a id="js_run_devserver-deps"></a>deps | Library dependencies required by the dev server. | <a href="https://bazel.build/concepts/labels">List of labels</a> | optional | `[]` |
| <a id="js_run_devserver-data"></a>data | Additional runtime files required by the dev server. | <a href="https://bazel.build/concepts/labels">List of labels</a> | optional | `[]` |
| <a id="js_run_devserver-node_modules"></a>node_modules | Optional label providing package files from a node_modules tree, typically produced by bun_install or npm_translate_lock, in runfiles. | <a href="https://bazel.build/concepts/labels">Label</a> | optional | `None` |
| <a id="js_run_devserver-package_dir_hint"></a>package_dir_hint | Optional package-relative directory hint when package_json is not supplied. | String | optional | `"."` |
| <a id="js_run_devserver-package_json"></a>package_json | Optional package.json used to resolve the package working directory. | <a href="https://bazel.build/concepts/labels">Label</a> | optional | `None` |
| <a id="js_run_devserver-tool"></a>tool | Executable target to launch as the dev server. | <a href="https://bazel.build/concepts/labels">Label</a> | required | |
| <a id="js_run_devserver-working_dir"></a>working_dir | Working directory at runtime: Bazel runfiles workspace root or the resolved package directory. | String | optional | `"workspace"` |
<a id="ts_library"></a>
## ts_library
<pre>
load("@rules_bun//bun:defs.bzl", "ts_library")
ts_library(<a href="#ts_library-name">name</a>, <a href="#ts_library-deps">deps</a>, <a href="#ts_library-srcs">srcs</a>, <a href="#ts_library-data">data</a>, <a href="#ts_library-types">types</a>)
</pre>
Aggregates TypeScript sources and transitive Bun source dependencies.
Attributes:
**ATTRIBUTES**
| Name | Description | Type | Mandatory | Default |
| :------------- | :------------- | :------------- | :------------- | :------------- |
| <a id="ts_library-name"></a>name | A unique name for this target. | <a href="https://bazel.build/concepts/labels#target-names">Name</a> | required | |
| <a id="ts_library-deps"></a>deps | Other Bun source libraries to include transitively. | <a href="https://bazel.build/concepts/labels">List of labels</a> | optional | `[]` |
| <a id="ts_library-srcs"></a>srcs | TypeScript source files in this library. | <a href="https://bazel.build/concepts/labels">List of labels</a> | optional | `[]` |
| <a id="ts_library-data"></a>data | Optional runtime files propagated to dependents. | <a href="https://bazel.build/concepts/labels">List of labels</a> | optional | `[]` |
| <a id="ts_library-types"></a>types | Optional declaration files associated with this library. | <a href="https://bazel.build/concepts/labels">List of labels</a> | optional | `[]` |
<a id="js_binary"></a>
## js_binary
<pre>
load("@rules_bun//bun:defs.bzl", "js_binary")
js_binary(<a href="#js_binary-name">name</a>, <a href="#js_binary-kwargs">**kwargs</a>)
</pre>
**PARAMETERS**
| Name | Description | Default Value |
| :------------- | :------------- | :------------- |
| <a id="js_binary-name"></a>name | <p align="center"> - </p> | none |
| <a id="js_binary-kwargs"></a>kwargs | <p align="center"> - </p> | none |
<a id="js_test"></a>
## js_test
<pre>
load("@rules_bun//bun:defs.bzl", "js_test")
js_test(<a href="#js_test-name">name</a>, <a href="#js_test-entry_point">entry_point</a>, <a href="#js_test-srcs">srcs</a>, <a href="#js_test-kwargs">**kwargs</a>)
</pre>
**PARAMETERS**
| Name | Description | Default Value |
| :------------- | :------------- | :------------- |
| <a id="js_test-name"></a>name | <p align="center"> - </p> | none |
| <a id="js_test-entry_point"></a>entry_point | <p align="center"> - </p> | `None` |
| <a id="js_test-srcs"></a>srcs | <p align="center"> - </p> | `None` |
| <a id="js_test-kwargs"></a>kwargs | <p align="center"> - </p> | none |
- `srcs` (label_list, optional): `.ts`, `.tsx` files.
- `deps` (label_list, optional): dependent source libraries.

View File

@@ -11,3 +11,11 @@ bun_dev(
name = "web_dev",
entry_point = "main.ts",
)
bun_dev(
name = "web_dev_hot_restart",
entry_point = "main.ts",
no_clear_screen = True,
restart_on = ["README.md"],
watch_mode = "hot",
)

View File

@@ -9,3 +9,12 @@ bazel run //examples/basic:web_dev
```
This starts Bun in watch mode for `main.ts`.
For the hot-reload launcher variant:
```bash
bazel run //examples/basic:web_dev_hot_restart
```
This starts Bun with `watch_mode = "hot"`, disables screen clearing, and wires
`README.md` through `restart_on` to exercise the custom restart launcher path.

1
examples/vite_monorepo/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
bazel-*

View File

@@ -1,4 +1,8 @@
load("//bun:defs.bzl", "bun_script")
load("@rules_bun//bun:defs.bzl", "bun_script")
# //:BUILD.bazel
load("@rules_multirun//:defs.bzl", "multirun")
load("@rules_shell//shell:sh_test.bzl", "sh_test")
package(default_visibility = ["//visibility:public"])
@@ -10,24 +14,48 @@ exports_files([
"apps/app-b/package.json",
])
multirun(
name = "dev",
commands = [
":app_a_dev",
":app_b_dev",
],
jobs = 0,
)
bun_script(
name = "app_a_dev",
script = "dev",
package_json = "apps/app-a/package.json",
node_modules = "@examples_vite_monorepo_node_modules//:node_modules",
data = [
"apps/app-a/index.html",
"apps/app-a/main.js",
"apps/app-a/vite.config.js",
],
node_modules = "@node_modules//:node_modules",
package_json = "apps/app-a/package.json",
script = "dev",
)
bun_script(
name = "app_b_dev",
script = "dev",
package_json = "apps/app-b/package.json",
node_modules = "@examples_vite_monorepo_node_modules//:node_modules",
data = [
"apps/app-b/index.html",
"apps/app-b/main.js",
"apps/app-b/vite.config.js",
],
node_modules = "@node_modules//:node_modules",
package_json = "apps/app-b/package.json",
script = "dev",
)
sh_test(
name = "vite_monorepo_workspace_resolution_test",
srcs = ["run_vite_monorepo_apps.sh"],
args = [
"$(location :app_a_dev)",
"$(location :app_b_dev)",
],
data = [
":app_a_dev",
":app_b_dev",
],
)

View File

@@ -0,0 +1,39 @@
module(
name = "rules_bun_example_vite_monorepo",
version = "0.1.0",
)
bazel_dep(name = "rules_bun", version = "0.2.2")
bazel_dep(name = "rules_multirun", version = "0.9.0")
bazel_dep(name = "rules_shell", version = "0.6.1")
local_path_override(
module_name = "rules_bun",
path = "../..",
)
bun_ext = use_extension("@rules_bun//bun:extensions.bzl", "bun")
use_repo(
bun_ext,
"bun_darwin_aarch64",
"bun_darwin_x64",
"bun_linux_aarch64",
"bun_linux_x64",
"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",
)
bun_install_ext = use_extension("@rules_bun//bun:extensions.bzl", "bun_install")
bun_install_ext.install(
name = "npm",
bun_lockfile = "//:bun.lock",
package_json = "//:package.json",
)
use_repo(bun_install_ext, node_modules = "npm")

531
examples/vite_monorepo/MODULE.bazel.lock generated Normal file
View File

@@ -0,0 +1,531 @@
{
"lockFileVersion": 26,
"registryFileHashes": {
"https://bcr.bazel.build/bazel_registry.json": "8a28e4aff06ee60aed2a8c281907fb8bcbf3b753c91fb5a5c57da3215d5b3497",
"https://bcr.bazel.build/modules/abseil-cpp/20210324.2/MODULE.bazel": "7cd0312e064fde87c8d1cd79ba06c876bd23630c83466e9500321be55c96ace2",
"https://bcr.bazel.build/modules/abseil-cpp/20211102.0/MODULE.bazel": "70390338f7a5106231d20620712f7cccb659cd0e9d073d1991c038eb9fc57589",
"https://bcr.bazel.build/modules/abseil-cpp/20230125.1/MODULE.bazel": "89047429cb0207707b2dface14ba7f8df85273d484c2572755be4bab7ce9c3a0",
"https://bcr.bazel.build/modules/abseil-cpp/20230802.0.bcr.1/MODULE.bazel": "1c8cec495288dccd14fdae6e3f95f772c1c91857047a098fad772034264cc8cb",
"https://bcr.bazel.build/modules/abseil-cpp/20230802.0/MODULE.bazel": "d253ae36a8bd9ee3c5955384096ccb6baf16a1b1e93e858370da0a3b94f77c16",
"https://bcr.bazel.build/modules/abseil-cpp/20230802.1/MODULE.bazel": "fa92e2eb41a04df73cdabeec37107316f7e5272650f81d6cc096418fe647b915",
"https://bcr.bazel.build/modules/abseil-cpp/20240116.1/MODULE.bazel": "37bcdb4440fbb61df6a1c296ae01b327f19e9bb521f9b8e26ec854b6f97309ed",
"https://bcr.bazel.build/modules/abseil-cpp/20240116.2/MODULE.bazel": "73939767a4686cd9a520d16af5ab440071ed75cec1a876bf2fcfaf1f71987a16",
"https://bcr.bazel.build/modules/abseil-cpp/20250127.1/MODULE.bazel": "c4a89e7ceb9bf1e25cf84a9f830ff6b817b72874088bf5141b314726e46a57c1",
"https://bcr.bazel.build/modules/abseil-cpp/20250512.1/MODULE.bazel": "d209fdb6f36ffaf61c509fcc81b19e81b411a999a934a032e10cd009a0226215",
"https://bcr.bazel.build/modules/abseil-cpp/20250814.1/MODULE.bazel": "51f2312901470cdab0dbdf3b88c40cd21c62a7ed58a3de45b365ddc5b11bcab2",
"https://bcr.bazel.build/modules/abseil-cpp/20250814.1/source.json": "cea3901d7e299da7320700abbaafe57a65d039f10d0d7ea601c4a66938ea4b0c",
"https://bcr.bazel.build/modules/apple_support/1.11.1/MODULE.bazel": "1843d7cd8a58369a444fc6000e7304425fba600ff641592161d9f15b179fb896",
"https://bcr.bazel.build/modules/apple_support/1.15.1/MODULE.bazel": "a0556fefca0b1bb2de8567b8827518f94db6a6e7e7d632b4c48dc5f865bc7c85",
"https://bcr.bazel.build/modules/apple_support/1.21.0/MODULE.bazel": "ac1824ed5edf17dee2fdd4927ada30c9f8c3b520be1b5fd02a5da15bc10bff3e",
"https://bcr.bazel.build/modules/apple_support/1.21.1/MODULE.bazel": "5809fa3efab15d1f3c3c635af6974044bac8a4919c62238cce06acee8a8c11f1",
"https://bcr.bazel.build/modules/apple_support/1.24.2/MODULE.bazel": "0e62471818affb9f0b26f128831d5c40b074d32e6dda5a0d3852847215a41ca4",
"https://bcr.bazel.build/modules/apple_support/1.24.2/source.json": "2c22c9827093250406c5568da6c54e6fdf0ef06238def3d99c71b12feb057a8d",
"https://bcr.bazel.build/modules/bazel_features/1.1.1/MODULE.bazel": "27b8c79ef57efe08efccbd9dd6ef70d61b4798320b8d3c134fd571f78963dbcd",
"https://bcr.bazel.build/modules/bazel_features/1.10.0/MODULE.bazel": "f75e8807570484a99be90abcd52b5e1f390362c258bcb73106f4544957a48101",
"https://bcr.bazel.build/modules/bazel_features/1.11.0/MODULE.bazel": "f9382337dd5a474c3b7d334c2f83e50b6eaedc284253334cf823044a26de03e8",
"https://bcr.bazel.build/modules/bazel_features/1.15.0/MODULE.bazel": "d38ff6e517149dc509406aca0db3ad1efdd890a85e049585b7234d04238e2a4d",
"https://bcr.bazel.build/modules/bazel_features/1.17.0/MODULE.bazel": "039de32d21b816b47bd42c778e0454217e9c9caac4a3cf8e15c7231ee3ddee4d",
"https://bcr.bazel.build/modules/bazel_features/1.18.0/MODULE.bazel": "1be0ae2557ab3a72a57aeb31b29be347bcdc5d2b1eb1e70f39e3851a7e97041a",
"https://bcr.bazel.build/modules/bazel_features/1.19.0/MODULE.bazel": "59adcdf28230d220f0067b1f435b8537dd033bfff8db21335ef9217919c7fb58",
"https://bcr.bazel.build/modules/bazel_features/1.21.0/MODULE.bazel": "675642261665d8eea09989aa3b8afb5c37627f1be178382c320d1b46afba5e3b",
"https://bcr.bazel.build/modules/bazel_features/1.23.0/MODULE.bazel": "fd1ac84bc4e97a5a0816b7fd7d4d4f6d837b0047cf4cbd81652d616af3a6591a",
"https://bcr.bazel.build/modules/bazel_features/1.27.0/MODULE.bazel": "621eeee06c4458a9121d1f104efb80f39d34deff4984e778359c60eaf1a8cb65",
"https://bcr.bazel.build/modules/bazel_features/1.28.0/MODULE.bazel": "4b4200e6cbf8fa335b2c3f43e1d6ef3e240319c33d43d60cc0fbd4b87ece299d",
"https://bcr.bazel.build/modules/bazel_features/1.3.0/MODULE.bazel": "cdcafe83ec318cda34e02948e81d790aab8df7a929cec6f6969f13a489ccecd9",
"https://bcr.bazel.build/modules/bazel_features/1.30.0/MODULE.bazel": "a14b62d05969a293b80257e72e597c2da7f717e1e69fa8b339703ed6731bec87",
"https://bcr.bazel.build/modules/bazel_features/1.33.0/MODULE.bazel": "8b8dc9d2a4c88609409c3191165bccec0e4cb044cd7a72ccbe826583303459f6",
"https://bcr.bazel.build/modules/bazel_features/1.33.0/source.json": "13617db3930328c2cd2807a0f13d52ca870ac05f96db9668655113265147b2a6",
"https://bcr.bazel.build/modules/bazel_features/1.4.1/MODULE.bazel": "e45b6bb2350aff3e442ae1111c555e27eac1d915e77775f6fdc4b351b758b5d7",
"https://bcr.bazel.build/modules/bazel_features/1.9.1/MODULE.bazel": "8f679097876a9b609ad1f60249c49d68bfab783dd9be012faf9d82547b14815a",
"https://bcr.bazel.build/modules/bazel_skylib/1.0.3/MODULE.bazel": "bcb0fd896384802d1ad283b4e4eb4d718eebd8cb820b0a2c3a347fb971afd9d8",
"https://bcr.bazel.build/modules/bazel_skylib/1.1.1/MODULE.bazel": "1add3e7d93ff2e6998f9e118022c84d163917d912f5afafb3058e3d2f1545b5e",
"https://bcr.bazel.build/modules/bazel_skylib/1.2.0/MODULE.bazel": "44fe84260e454ed94ad326352a698422dbe372b21a1ac9f3eab76eb531223686",
"https://bcr.bazel.build/modules/bazel_skylib/1.2.1/MODULE.bazel": "f35baf9da0efe45fa3da1696ae906eea3d615ad41e2e3def4aeb4e8bc0ef9a7a",
"https://bcr.bazel.build/modules/bazel_skylib/1.3.0/MODULE.bazel": "20228b92868bf5cfc41bda7afc8a8ba2a543201851de39d990ec957b513579c5",
"https://bcr.bazel.build/modules/bazel_skylib/1.4.1/MODULE.bazel": "a0dcb779424be33100dcae821e9e27e4f2901d9dfd5333efe5ac6a8d7ab75e1d",
"https://bcr.bazel.build/modules/bazel_skylib/1.4.2/MODULE.bazel": "3bd40978e7a1fac911d5989e6b09d8f64921865a45822d8b09e815eaa726a651",
"https://bcr.bazel.build/modules/bazel_skylib/1.5.0/MODULE.bazel": "32880f5e2945ce6a03d1fbd588e9198c0a959bb42297b2cfaf1685b7bc32e138",
"https://bcr.bazel.build/modules/bazel_skylib/1.6.1/MODULE.bazel": "8fdee2dbaace6c252131c00e1de4b165dc65af02ea278476187765e1a617b917",
"https://bcr.bazel.build/modules/bazel_skylib/1.7.0/MODULE.bazel": "0db596f4563de7938de764cc8deeabec291f55e8ec15299718b93c4423e9796d",
"https://bcr.bazel.build/modules/bazel_skylib/1.7.1/MODULE.bazel": "3120d80c5861aa616222ec015332e5f8d3171e062e3e804a2a0253e1be26e59b",
"https://bcr.bazel.build/modules/bazel_skylib/1.8.1/MODULE.bazel": "88ade7293becda963e0e3ea33e7d54d3425127e0a326e0d17da085a5f1f03ff6",
"https://bcr.bazel.build/modules/bazel_skylib/1.8.2/MODULE.bazel": "69ad6927098316848b34a9142bcc975e018ba27f08c4ff403f50c1b6e646ca67",
"https://bcr.bazel.build/modules/bazel_skylib/1.8.2/source.json": "34a3c8bcf233b835eb74be9d628899bb32999d3e0eadef1947a0a562a2b16ffb",
"https://bcr.bazel.build/modules/buildozer/8.2.1/MODULE.bazel": "61e9433c574c2bd9519cad7fa66b9c1d2b8e8d5f3ae5d6528a2c2d26e68d874d",
"https://bcr.bazel.build/modules/buildozer/8.2.1/source.json": "7c33f6a26ee0216f85544b4bca5e9044579e0219b6898dd653f5fb449cf2e484",
"https://bcr.bazel.build/modules/google_benchmark/1.8.2/MODULE.bazel": "a70cf1bba851000ba93b58ae2f6d76490a9feb74192e57ab8e8ff13c34ec50cb",
"https://bcr.bazel.build/modules/googletest/1.11.0/MODULE.bazel": "3a83f095183f66345ca86aa13c58b59f9f94a2f81999c093d4eeaa2d262d12f4",
"https://bcr.bazel.build/modules/googletest/1.14.0.bcr.1/MODULE.bazel": "22c31a561553727960057361aa33bf20fb2e98584bc4fec007906e27053f80c6",
"https://bcr.bazel.build/modules/googletest/1.14.0/MODULE.bazel": "cfbcbf3e6eac06ef9d85900f64424708cc08687d1b527f0ef65aa7517af8118f",
"https://bcr.bazel.build/modules/googletest/1.15.2/MODULE.bazel": "6de1edc1d26cafb0ea1a6ab3f4d4192d91a312fd2d360b63adaa213cd00b2108",
"https://bcr.bazel.build/modules/googletest/1.17.0/MODULE.bazel": "dbec758171594a705933a29fcf69293d2468c49ec1f2ebca65c36f504d72df46",
"https://bcr.bazel.build/modules/googletest/1.17.0/source.json": "38e4454b25fc30f15439c0378e57909ab1fd0a443158aa35aec685da727cd713",
"https://bcr.bazel.build/modules/jsoncpp/1.9.5/MODULE.bazel": "31271aedc59e815656f5736f282bb7509a97c7ecb43e927ac1a37966e0578075",
"https://bcr.bazel.build/modules/jsoncpp/1.9.6/MODULE.bazel": "2f8d20d3b7d54143213c4dfc3d98225c42de7d666011528dc8fe91591e2e17b0",
"https://bcr.bazel.build/modules/jsoncpp/1.9.6/source.json": "a04756d367a2126c3541682864ecec52f92cdee80a35735a3cb249ce015ca000",
"https://bcr.bazel.build/modules/libpfm/4.11.0/MODULE.bazel": "45061ff025b301940f1e30d2c16bea596c25b176c8b6b3087e92615adbd52902",
"https://bcr.bazel.build/modules/nlohmann_json/3.6.1/MODULE.bazel": "6f7b417dcc794d9add9e556673ad25cb3ba835224290f4f848f8e2db1e1fca74",
"https://bcr.bazel.build/modules/nlohmann_json/3.6.1/source.json": "f448c6e8963fdfa7eb831457df83ad63d3d6355018f6574fb017e8169deb43a9",
"https://bcr.bazel.build/modules/platforms/0.0.10/MODULE.bazel": "8cb8efaf200bdeb2150d93e162c40f388529a25852b332cec879373771e48ed5",
"https://bcr.bazel.build/modules/platforms/0.0.11/MODULE.bazel": "0daefc49732e227caa8bfa834d65dc52e8cc18a2faf80df25e8caea151a9413f",
"https://bcr.bazel.build/modules/platforms/0.0.4/MODULE.bazel": "9b328e31ee156f53f3c416a64f8491f7eb731742655a47c9eec4703a71644aee",
"https://bcr.bazel.build/modules/platforms/0.0.5/MODULE.bazel": "5733b54ea419d5eaf7997054bb55f6a1d0b5ff8aedf0176fef9eea44f3acda37",
"https://bcr.bazel.build/modules/platforms/0.0.6/MODULE.bazel": "ad6eeef431dc52aefd2d77ed20a4b353f8ebf0f4ecdd26a807d2da5aa8cd0615",
"https://bcr.bazel.build/modules/platforms/0.0.7/MODULE.bazel": "72fd4a0ede9ee5c021f6a8dd92b503e089f46c227ba2813ff183b71616034814",
"https://bcr.bazel.build/modules/platforms/0.0.8/MODULE.bazel": "9f142c03e348f6d263719f5074b21ef3adf0b139ee4c5133e2aa35664da9eb2d",
"https://bcr.bazel.build/modules/platforms/0.0.9/MODULE.bazel": "4a87a60c927b56ddd67db50c89acaa62f4ce2a1d2149ccb63ffd871d5ce29ebc",
"https://bcr.bazel.build/modules/platforms/1.0.0/MODULE.bazel": "f05feb42b48f1b3c225e4ccf351f367be0371411a803198ec34a389fb22aa580",
"https://bcr.bazel.build/modules/platforms/1.0.0/source.json": "f4ff1fd412e0246fd38c82328eb209130ead81d62dcd5a9e40910f867f733d96",
"https://bcr.bazel.build/modules/protobuf/21.7/MODULE.bazel": "a5a29bb89544f9b97edce05642fac225a808b5b7be74038ea3640fae2f8e66a7",
"https://bcr.bazel.build/modules/protobuf/27.0/MODULE.bazel": "7873b60be88844a0a1d8f80b9d5d20cfbd8495a689b8763e76c6372998d3f64c",
"https://bcr.bazel.build/modules/protobuf/29.0-rc2/MODULE.bazel": "6241d35983510143049943fc0d57937937122baf1b287862f9dc8590fc4c37df",
"https://bcr.bazel.build/modules/protobuf/29.0-rc3/MODULE.bazel": "33c2dfa286578573afc55a7acaea3cada4122b9631007c594bf0729f41c8de92",
"https://bcr.bazel.build/modules/protobuf/29.1/MODULE.bazel": "557c3457560ff49e122ed76c0bc3397a64af9574691cb8201b4e46d4ab2ecb95",
"https://bcr.bazel.build/modules/protobuf/3.19.0/MODULE.bazel": "6b5fbb433f760a99a22b18b6850ed5784ef0e9928a72668b66e4d7ccd47db9b0",
"https://bcr.bazel.build/modules/protobuf/32.1/MODULE.bazel": "89cd2866a9cb07fee9ff74c41ceace11554f32e0d849de4e23ac55515cfada4d",
"https://bcr.bazel.build/modules/protobuf/33.4/MODULE.bazel": "114775b816b38b6d0ca620450d6b02550c60ceedfdc8d9a229833b34a223dc42",
"https://bcr.bazel.build/modules/protobuf/33.4/source.json": "555f8686b4c7d6b5ba731fbea13bf656b4bfd9a7ff629c1d9d3f6e1d6155de79",
"https://bcr.bazel.build/modules/pybind11_bazel/2.11.1/MODULE.bazel": "88af1c246226d87e65be78ed49ecd1e6f5e98648558c14ce99176da041dc378e",
"https://bcr.bazel.build/modules/pybind11_bazel/2.12.0/MODULE.bazel": "e6f4c20442eaa7c90d7190d8dc539d0ab422f95c65a57cc59562170c58ae3d34",
"https://bcr.bazel.build/modules/pybind11_bazel/2.12.0/source.json": "6900fdc8a9e95866b8c0d4ad4aba4d4236317b5c1cd04c502df3f0d33afed680",
"https://bcr.bazel.build/modules/re2/2023-09-01/MODULE.bazel": "cb3d511531b16cfc78a225a9e2136007a48cf8a677e4264baeab57fe78a80206",
"https://bcr.bazel.build/modules/re2/2024-07-02.bcr.1/MODULE.bazel": "b4963dda9b31080be1905ef085ecd7dd6cd47c05c79b9cdf83ade83ab2ab271a",
"https://bcr.bazel.build/modules/re2/2024-07-02.bcr.1/source.json": "2ff292be6ef3340325ce8a045ecc326e92cbfab47c7cbab4bd85d28971b97ac4",
"https://bcr.bazel.build/modules/re2/2024-07-02/MODULE.bazel": "0eadc4395959969297cbcf31a249ff457f2f1d456228c67719480205aa306daa",
"https://bcr.bazel.build/modules/rules_android/0.1.1/MODULE.bazel": "48809ab0091b07ad0182defb787c4c5328bd3a278938415c00a7b69b50c4d3a8",
"https://bcr.bazel.build/modules/rules_android/0.1.1/source.json": "e6986b41626ee10bdc864937ffb6d6bf275bb5b9c65120e6137d56e6331f089e",
"https://bcr.bazel.build/modules/rules_apple/3.16.0/MODULE.bazel": "0d1caf0b8375942ce98ea944be754a18874041e4e0459401d925577624d3a54a",
"https://bcr.bazel.build/modules/rules_apple/4.1.0/MODULE.bazel": "76e10fd4a48038d3fc7c5dc6e63b7063bbf5304a2e3bd42edda6ec660eebea68",
"https://bcr.bazel.build/modules/rules_apple/4.1.0/source.json": "8ee81e1708756f81b343a5eb2b2f0b953f1d25c4ab3d4a68dc02754872e80715",
"https://bcr.bazel.build/modules/rules_cc/0.0.1/MODULE.bazel": "cb2aa0747f84c6c3a78dad4e2049c154f08ab9d166b1273835a8174940365647",
"https://bcr.bazel.build/modules/rules_cc/0.0.10/MODULE.bazel": "ec1705118f7eaedd6e118508d3d26deba2a4e76476ada7e0e3965211be012002",
"https://bcr.bazel.build/modules/rules_cc/0.0.13/MODULE.bazel": "0e8529ed7b323dad0775ff924d2ae5af7640b23553dfcd4d34344c7e7a867191",
"https://bcr.bazel.build/modules/rules_cc/0.0.15/MODULE.bazel": "6704c35f7b4a72502ee81f61bf88706b54f06b3cbe5558ac17e2e14666cd5dcc",
"https://bcr.bazel.build/modules/rules_cc/0.0.16/MODULE.bazel": "7661303b8fc1b4d7f532e54e9d6565771fea666fbdf839e0a86affcd02defe87",
"https://bcr.bazel.build/modules/rules_cc/0.0.17/MODULE.bazel": "2ae1d8f4238ec67d7185d8861cb0a2cdf4bc608697c331b95bf990e69b62e64a",
"https://bcr.bazel.build/modules/rules_cc/0.0.2/MODULE.bazel": "6915987c90970493ab97393024c156ea8fb9f3bea953b2f3ec05c34f19b5695c",
"https://bcr.bazel.build/modules/rules_cc/0.0.6/MODULE.bazel": "abf360251023dfe3efcef65ab9d56beefa8394d4176dd29529750e1c57eaa33f",
"https://bcr.bazel.build/modules/rules_cc/0.0.8/MODULE.bazel": "964c85c82cfeb6f3855e6a07054fdb159aced38e99a5eecf7bce9d53990afa3e",
"https://bcr.bazel.build/modules/rules_cc/0.0.9/MODULE.bazel": "836e76439f354b89afe6a911a7adf59a6b2518fafb174483ad78a2a2fde7b1c5",
"https://bcr.bazel.build/modules/rules_cc/0.1.1/MODULE.bazel": "2f0222a6f229f0bf44cd711dc13c858dad98c62d52bd51d8fc3a764a83125513",
"https://bcr.bazel.build/modules/rules_cc/0.1.2/MODULE.bazel": "557ddc3a96858ec0d465a87c0a931054d7dcfd6583af2c7ed3baf494407fd8d0",
"https://bcr.bazel.build/modules/rules_cc/0.1.5/MODULE.bazel": "88dfc9361e8b5ae1008ac38f7cdfd45ad738e4fa676a3ad67d19204f045a1fd8",
"https://bcr.bazel.build/modules/rules_cc/0.2.0/MODULE.bazel": "b5c17f90458caae90d2ccd114c81970062946f49f355610ed89bebf954f5783c",
"https://bcr.bazel.build/modules/rules_cc/0.2.13/MODULE.bazel": "eecdd666eda6be16a8d9dc15e44b5c75133405e820f620a234acc4b1fdc5aa37",
"https://bcr.bazel.build/modules/rules_cc/0.2.14/MODULE.bazel": "353c99ed148887ee89c54a17d4100ae7e7e436593d104b668476019023b58df8",
"https://bcr.bazel.build/modules/rules_cc/0.2.14/source.json": "55d0a4587c5592fad350f6e698530f4faf0e7dd15e69d43f8d87e220c78bea54",
"https://bcr.bazel.build/modules/rules_cc/0.2.8/MODULE.bazel": "f1df20f0bf22c28192a794f29b501ee2018fa37a3862a1a2132ae2940a23a642",
"https://bcr.bazel.build/modules/rules_foreign_cc/0.9.0/MODULE.bazel": "c9e8c682bf75b0e7c704166d79b599f93b72cfca5ad7477df596947891feeef6",
"https://bcr.bazel.build/modules/rules_fuzzing/0.5.2/MODULE.bazel": "40c97d1144356f52905566c55811f13b299453a14ac7769dfba2ac38192337a8",
"https://bcr.bazel.build/modules/rules_java/4.0.0/MODULE.bazel": "5a78a7ae82cd1a33cef56dc578c7d2a46ed0dca12643ee45edbb8417899e6f74",
"https://bcr.bazel.build/modules/rules_java/5.3.5/MODULE.bazel": "a4ec4f2db570171e3e5eb753276ee4b389bae16b96207e9d3230895c99644b86",
"https://bcr.bazel.build/modules/rules_java/6.5.2/MODULE.bazel": "1d440d262d0e08453fa0c4d8f699ba81609ed0e9a9a0f02cd10b3e7942e61e31",
"https://bcr.bazel.build/modules/rules_java/7.10.0/MODULE.bazel": "530c3beb3067e870561739f1144329a21c851ff771cd752a49e06e3dc9c2e71a",
"https://bcr.bazel.build/modules/rules_java/7.12.2/MODULE.bazel": "579c505165ee757a4280ef83cda0150eea193eed3bef50b1004ba88b99da6de6",
"https://bcr.bazel.build/modules/rules_java/7.2.0/MODULE.bazel": "06c0334c9be61e6cef2c8c84a7800cef502063269a5af25ceb100b192453d4ab",
"https://bcr.bazel.build/modules/rules_java/7.6.1/MODULE.bazel": "2f14b7e8a1aa2f67ae92bc69d1ec0fa8d9f827c4e17ff5e5f02e91caa3b2d0fe",
"https://bcr.bazel.build/modules/rules_java/8.3.2/MODULE.bazel": "7336d5511ad5af0b8615fdc7477535a2e4e723a357b6713af439fe8cf0195017",
"https://bcr.bazel.build/modules/rules_java/8.5.1/MODULE.bazel": "d8a9e38cc5228881f7055a6079f6f7821a073df3744d441978e7a43e20226939",
"https://bcr.bazel.build/modules/rules_java/8.6.1/MODULE.bazel": "f4808e2ab5b0197f094cabce9f4b006a27766beb6a9975931da07099560ca9c2",
"https://bcr.bazel.build/modules/rules_java/9.0.3/MODULE.bazel": "1f98ed015f7e744a745e0df6e898a7c5e83562d6b759dfd475c76456dda5ccea",
"https://bcr.bazel.build/modules/rules_java/9.0.3/source.json": "b038c0c07e12e658135bbc32cc1a2ded6e33785105c9d41958014c592de4593e",
"https://bcr.bazel.build/modules/rules_jvm_external/4.4.2/MODULE.bazel": "a56b85e418c83eb1839819f0b515c431010160383306d13ec21959ac412d2fe7",
"https://bcr.bazel.build/modules/rules_jvm_external/5.1/MODULE.bazel": "33f6f999e03183f7d088c9be518a63467dfd0be94a11d0055fe2d210f89aa909",
"https://bcr.bazel.build/modules/rules_jvm_external/5.2/MODULE.bazel": "d9351ba35217ad0de03816ef3ed63f89d411349353077348a45348b096615036",
"https://bcr.bazel.build/modules/rules_jvm_external/6.3/MODULE.bazel": "c998e060b85f71e00de5ec552019347c8bca255062c990ac02d051bb80a38df0",
"https://bcr.bazel.build/modules/rules_jvm_external/6.7/MODULE.bazel": "e717beabc4d091ecb2c803c2d341b88590e9116b8bf7947915eeb33aab4f96dd",
"https://bcr.bazel.build/modules/rules_jvm_external/6.7/source.json": "5426f412d0a7fc6b611643376c7e4a82dec991491b9ce5cb1cfdd25fe2e92be4",
"https://bcr.bazel.build/modules/rules_kotlin/1.9.6/MODULE.bazel": "d269a01a18ee74d0335450b10f62c9ed81f2321d7958a2934e44272fe82dcef3",
"https://bcr.bazel.build/modules/rules_kotlin/1.9.6/source.json": "2faa4794364282db7c06600b7e5e34867a564ae91bda7cae7c29c64e9466b7d5",
"https://bcr.bazel.build/modules/rules_license/0.0.3/MODULE.bazel": "627e9ab0247f7d1e05736b59dbb1b6871373de5ad31c3011880b4133cafd4bd0",
"https://bcr.bazel.build/modules/rules_license/0.0.7/MODULE.bazel": "088fbeb0b6a419005b89cf93fe62d9517c0a2b8bb56af3244af65ecfe37e7d5d",
"https://bcr.bazel.build/modules/rules_license/1.0.0/MODULE.bazel": "a7fda60eefdf3d8c827262ba499957e4df06f659330bbe6cdbdb975b768bb65c",
"https://bcr.bazel.build/modules/rules_license/1.0.0/source.json": "a52c89e54cc311196e478f8382df91c15f7a2bfdf4c6cd0e2675cc2ff0b56efb",
"https://bcr.bazel.build/modules/rules_multirun/0.9.0/MODULE.bazel": "32d628ef586b5b23f67e55886b7bc38913ea4160420d66ae90521dda2ff37df0",
"https://bcr.bazel.build/modules/rules_multirun/0.9.0/source.json": "e882ba77962fa6c5fe68619e5c7d0374ec9a219fb8d03c42eadaf6d0243771bd",
"https://bcr.bazel.build/modules/rules_pkg/0.7.0/MODULE.bazel": "df99f03fc7934a4737122518bb87e667e62d780b610910f0447665a7e2be62dc",
"https://bcr.bazel.build/modules/rules_pkg/1.0.1/MODULE.bazel": "5b1df97dbc29623bccdf2b0dcd0f5cb08e2f2c9050aab1092fd39a41e82686ff",
"https://bcr.bazel.build/modules/rules_pkg/1.0.1/source.json": "bd82e5d7b9ce2d31e380dd9f50c111d678c3bdaca190cb76b0e1c71b05e1ba8a",
"https://bcr.bazel.build/modules/rules_proto/4.0.0/MODULE.bazel": "a7a7b6ce9bee418c1a760b3d84f83a299ad6952f9903c67f19e4edd964894e06",
"https://bcr.bazel.build/modules/rules_proto/5.3.0-21.7/MODULE.bazel": "e8dff86b0971688790ae75528fe1813f71809b5afd57facb44dad9e8eca631b7",
"https://bcr.bazel.build/modules/rules_proto/6.0.0-rc1/MODULE.bazel": "1e5b502e2e1a9e825eef74476a5a1ee524a92297085015a052510b09a1a09483",
"https://bcr.bazel.build/modules/rules_proto/6.0.2/MODULE.bazel": "ce916b775a62b90b61888052a416ccdda405212b6aaeb39522f7dc53431a5e73",
"https://bcr.bazel.build/modules/rules_proto/7.1.0/MODULE.bazel": "002d62d9108f75bb807cd56245d45648f38275cb3a99dcd45dfb864c5d74cb96",
"https://bcr.bazel.build/modules/rules_proto/7.1.0/source.json": "39f89066c12c24097854e8f57ab8558929f9c8d474d34b2c00ac04630ad8940e",
"https://bcr.bazel.build/modules/rules_python/0.10.2/MODULE.bazel": "cc82bc96f2997baa545ab3ce73f196d040ffb8756fd2d66125a530031cd90e5f",
"https://bcr.bazel.build/modules/rules_python/0.23.1/MODULE.bazel": "49ffccf0511cb8414de28321f5fcf2a31312b47c40cc21577144b7447f2bf300",
"https://bcr.bazel.build/modules/rules_python/0.25.0/MODULE.bazel": "72f1506841c920a1afec76975b35312410eea3aa7b63267436bfb1dd91d2d382",
"https://bcr.bazel.build/modules/rules_python/0.27.1/MODULE.bazel": "65dc875cc1a06c30d5bbdba7ab021fd9e551a6579e408a3943a61303e2228a53",
"https://bcr.bazel.build/modules/rules_python/0.28.0/MODULE.bazel": "cba2573d870babc976664a912539b320cbaa7114cd3e8f053c720171cde331ed",
"https://bcr.bazel.build/modules/rules_python/0.31.0/MODULE.bazel": "93a43dc47ee570e6ec9f5779b2e64c1476a6ce921c48cc9a1678a91dd5f8fd58",
"https://bcr.bazel.build/modules/rules_python/0.33.2/MODULE.bazel": "3e036c4ad8d804a4dad897d333d8dce200d943df4827cb849840055be8d2e937",
"https://bcr.bazel.build/modules/rules_python/0.4.0/MODULE.bazel": "9208ee05fd48bf09ac60ed269791cf17fb343db56c8226a720fbb1cdf467166c",
"https://bcr.bazel.build/modules/rules_python/1.3.0/MODULE.bazel": "8361d57eafb67c09b75bf4bbe6be360e1b8f4f18118ab48037f2bd50aa2ccb13",
"https://bcr.bazel.build/modules/rules_python/1.4.1/MODULE.bazel": "8991ad45bdc25018301d6b7e1d3626afc3c8af8aaf4bc04f23d0b99c938b73a6",
"https://bcr.bazel.build/modules/rules_python/1.6.0/MODULE.bazel": "7e04ad8f8d5bea40451cf80b1bd8262552aa73f841415d20db96b7241bd027d8",
"https://bcr.bazel.build/modules/rules_python/1.7.0/MODULE.bazel": "d01f995ecd137abf30238ad9ce97f8fc3ac57289c8b24bd0bf53324d937a14f8",
"https://bcr.bazel.build/modules/rules_python/1.7.0/source.json": "028a084b65dcf8f4dc4f82f8778dbe65df133f234b316828a82e060d81bdce32",
"https://bcr.bazel.build/modules/rules_shell/0.2.0/MODULE.bazel": "fda8a652ab3c7d8fee214de05e7a9916d8b28082234e8d2c0094505c5268ed3c",
"https://bcr.bazel.build/modules/rules_shell/0.3.0/MODULE.bazel": "de4402cd12f4cc8fda2354fce179fdb068c0b9ca1ec2d2b17b3e21b24c1a937b",
"https://bcr.bazel.build/modules/rules_shell/0.6.1/MODULE.bazel": "72e76b0eea4e81611ef5452aa82b3da34caca0c8b7b5c0c9584338aa93bae26b",
"https://bcr.bazel.build/modules/rules_shell/0.6.1/source.json": "20ec05cd5e592055e214b2da8ccb283c7f2a421ea0dc2acbf1aa792e11c03d0c",
"https://bcr.bazel.build/modules/rules_swift/1.16.0/MODULE.bazel": "4a09f199545a60d09895e8281362b1ff3bb08bbde69c6fc87aff5b92fcc916ca",
"https://bcr.bazel.build/modules/rules_swift/2.1.1/MODULE.bazel": "494900a80f944fc7aa61500c2073d9729dff0b764f0e89b824eb746959bc1046",
"https://bcr.bazel.build/modules/rules_swift/2.4.0/MODULE.bazel": "1639617eb1ede28d774d967a738b4a68b0accb40650beadb57c21846beab5efd",
"https://bcr.bazel.build/modules/rules_swift/3.1.2/MODULE.bazel": "72c8f5cf9d26427cee6c76c8e3853eb46ce6b0412a081b2b6db6e8ad56267400",
"https://bcr.bazel.build/modules/rules_swift/3.1.2/source.json": "e85761f3098a6faf40b8187695e3de6d97944e98abd0d8ce579cb2daf6319a66",
"https://bcr.bazel.build/modules/stardoc/0.5.1/MODULE.bazel": "1a05d92974d0c122f5ccf09291442580317cdd859f07a8655f1db9a60374f9f8",
"https://bcr.bazel.build/modules/stardoc/0.5.3/MODULE.bazel": "c7f6948dae6999bf0db32c1858ae345f112cacf98f174c7a8bb707e41b974f1c",
"https://bcr.bazel.build/modules/stardoc/0.7.0/MODULE.bazel": "05e3d6d30c099b6770e97da986c53bd31844d7f13d41412480ea265ac9e8079c",
"https://bcr.bazel.build/modules/stardoc/0.7.2/MODULE.bazel": "fc152419aa2ea0f51c29583fab1e8c99ddefd5b3778421845606ee628629e0e5",
"https://bcr.bazel.build/modules/stardoc/0.7.2/source.json": "58b029e5e901d6802967754adf0a9056747e8176f017cfe3607c0851f4d42216",
"https://bcr.bazel.build/modules/swift_argument_parser/1.3.1.1/MODULE.bazel": "5e463fbfba7b1701d957555ed45097d7f984211330106ccd1352c6e0af0dcf91",
"https://bcr.bazel.build/modules/swift_argument_parser/1.3.1.2/MODULE.bazel": "75aab2373a4bbe2a1260b9bf2a1ebbdbf872d3bd36f80bff058dccd82e89422f",
"https://bcr.bazel.build/modules/swift_argument_parser/1.3.1.2/source.json": "5fba48bbe0ba48761f9e9f75f92876cafb5d07c0ce059cc7a8027416de94a05b",
"https://bcr.bazel.build/modules/upb/0.0.0-20220923-a547704/MODULE.bazel": "7298990c00040a0e2f121f6c32544bab27d4452f80d9ce51349b1a28f3005c43",
"https://bcr.bazel.build/modules/zlib/1.2.11/MODULE.bazel": "07b389abc85fdbca459b69e2ec656ae5622873af3f845e1c9d80fe179f3effa0",
"https://bcr.bazel.build/modules/zlib/1.3.1.bcr.5/MODULE.bazel": "eec517b5bbe5492629466e11dae908d043364302283de25581e3eb944326c4ca",
"https://bcr.bazel.build/modules/zlib/1.3.1.bcr.5/source.json": "22bc55c47af97246cfc093d0acf683a7869377de362b5d1c552c2c2e16b7a806",
"https://bcr.bazel.build/modules/zlib/1.3.1/MODULE.bazel": "751c9940dcfe869f5f7274e1295422a34623555916eb98c174c1e945594bf198"
},
"selectedYankedVersions": {},
"moduleExtensions": {
"@@rules_bun+//bun:extensions.bzl%bun": {
"general": {
"bzlTransitiveDigest": "64B4fTkEHdAlieIOkE/Wi2M/R9lMNZhFxeI1eXEFHRs=",
"usagesDigest": "NKGlTDuQz8QyvtynGiqPQZ47pqfh7gwmp164c1kruoc=",
"recordedInputs": [
"REPO_MAPPING:rules_bun+,bazel_tools bazel_tools"
],
"generatedRepoSpecs": {
"bun_linux_x64": {
"repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
"urls": [
"https://github.com/oven-sh/bun/releases/download/bun-v1.3.10/bun-linux-x64.zip"
],
"sha256": "f57bc0187e39623de716ba3a389fda5486b2d7be7131a980ba54dc7b733d2e08",
"build_file_content": "\nexports_files([\"bun-linux-x64/bun\"])\n\nfilegroup(\n name = \"bun\",\n srcs = [\"bun-linux-x64/bun\"],\n visibility = [\"//visibility:public\"],\n)\n"
}
},
"bun_linux_aarch64": {
"repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
"urls": [
"https://github.com/oven-sh/bun/releases/download/bun-v1.3.10/bun-linux-aarch64.zip"
],
"sha256": "fa5ecb25cafa8e8f5c87a0f833719d46dd0af0a86c7837d806531212d55636d3",
"build_file_content": "\nexports_files([\"bun-linux-aarch64/bun\"])\n\nfilegroup(\n name = \"bun\",\n srcs = [\"bun-linux-aarch64/bun\"],\n visibility = [\"//visibility:public\"],\n)\n"
}
},
"bun_darwin_x64": {
"repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
"urls": [
"https://github.com/oven-sh/bun/releases/download/bun-v1.3.10/bun-darwin-x64.zip"
],
"sha256": "c1d90bf6140f20e572c473065dc6b37a4b036349b5e9e4133779cc642ad94323",
"build_file_content": "\nexports_files([\"bun-darwin-x64/bun\"])\n\nfilegroup(\n name = \"bun\",\n srcs = [\"bun-darwin-x64/bun\"],\n visibility = [\"//visibility:public\"],\n)\n"
}
},
"bun_darwin_aarch64": {
"repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
"urls": [
"https://github.com/oven-sh/bun/releases/download/bun-v1.3.10/bun-darwin-aarch64.zip"
],
"sha256": "82034e87c9d9b4398ea619aee2eed5d2a68c8157e9a6ae2d1052d84d533ccd8d",
"build_file_content": "\nexports_files([\"bun-darwin-aarch64/bun\"])\n\nfilegroup(\n name = \"bun\",\n srcs = [\"bun-darwin-aarch64/bun\"],\n visibility = [\"//visibility:public\"],\n)\n"
}
},
"bun_windows_x64": {
"repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
"urls": [
"https://github.com/oven-sh/bun/releases/download/bun-v1.3.10/bun-windows-x64.zip"
],
"sha256": "7a77b3e245e2e26965c93089a4a1332e8a326d3364c89fae1d1fd99cdd3cd73d",
"build_file_content": "\nexports_files([\"bun-windows-x64/bun.exe\"])\n\nfilegroup(\n name = \"bun\",\n srcs = [\"bun-windows-x64/bun.exe\"],\n visibility = [\"//visibility:public\"],\n)\n"
}
}
}
}
},
"@@rules_bun+//bun:extensions.bzl%bun_install": {
"general": {
"bzlTransitiveDigest": "64B4fTkEHdAlieIOkE/Wi2M/R9lMNZhFxeI1eXEFHRs=",
"usagesDigest": "7SehYeU297FzlGHTRg/G5aakC/abC14POtOr8Qy38vA=",
"recordedInputs": [
"REPO_MAPPING:rules_bun+,bazel_tools bazel_tools"
],
"generatedRepoSpecs": {
"npm": {
"repoRuleId": "@@rules_bun+//internal:bun_install.bzl%bun_install_repository",
"attributes": {
"package_json": "@@//:package.json",
"bun_lockfile": "@@//:bun.lock",
"install_inputs": [],
"isolated_home": true
}
}
}
}
},
"@@rules_kotlin+//src/main/starlark/core/repositories:bzlmod_setup.bzl%rules_kotlin_extensions": {
"general": {
"bzlTransitiveDigest": "ABI1D/sbS1ovwaW/kHDoj8nnXjQ0oKU9fzmzEG4iT8o=",
"usagesDigest": "QI2z8ZUR+mqtbwsf2fLqYdJAkPOHdOV+tF2yVAUgRzw=",
"recordedInputs": [
"REPO_MAPPING:rules_kotlin+,bazel_tools bazel_tools"
],
"generatedRepoSpecs": {
"com_github_jetbrains_kotlin_git": {
"repoRuleId": "@@rules_kotlin+//src/main/starlark/core/repositories:compiler.bzl%kotlin_compiler_git_repository",
"attributes": {
"urls": [
"https://github.com/JetBrains/kotlin/releases/download/v1.9.23/kotlin-compiler-1.9.23.zip"
],
"sha256": "93137d3aab9afa9b27cb06a824c2324195c6b6f6179d8a8653f440f5bd58be88"
}
},
"com_github_jetbrains_kotlin": {
"repoRuleId": "@@rules_kotlin+//src/main/starlark/core/repositories:compiler.bzl%kotlin_capabilities_repository",
"attributes": {
"git_repository_name": "com_github_jetbrains_kotlin_git",
"compiler_version": "1.9.23"
}
},
"com_github_google_ksp": {
"repoRuleId": "@@rules_kotlin+//src/main/starlark/core/repositories:ksp.bzl%ksp_compiler_plugin_repository",
"attributes": {
"urls": [
"https://github.com/google/ksp/releases/download/1.9.23-1.0.20/artifacts.zip"
],
"sha256": "ee0618755913ef7fd6511288a232e8fad24838b9af6ea73972a76e81053c8c2d",
"strip_version": "1.9.23-1.0.20"
}
},
"com_github_pinterest_ktlint": {
"repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_file",
"attributes": {
"sha256": "01b2e0ef893383a50dbeb13970fe7fa3be36ca3e83259e01649945b09d736985",
"urls": [
"https://github.com/pinterest/ktlint/releases/download/1.3.0/ktlint"
],
"executable": true
}
},
"rules_android": {
"repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
"sha256": "cd06d15dd8bb59926e4d65f9003bfc20f9da4b2519985c27e190cddc8b7a7806",
"strip_prefix": "rules_android-0.1.1",
"urls": [
"https://github.com/bazelbuild/rules_android/archive/v0.1.1.zip"
]
}
}
}
}
},
"@@rules_python+//python/extensions:config.bzl%config": {
"general": {
"bzlTransitiveDigest": "2hLgIvNVTLgxus0ZuXtleBe70intCfo0cHs8qvt6cdM=",
"usagesDigest": "ZVSXMAGpD+xzVNPuvF1IoLBkty7TROO0+akMapt1pAg=",
"recordedInputs": [
"REPO_MAPPING:rules_python+,bazel_tools bazel_tools",
"REPO_MAPPING:rules_python+,pypi__build rules_python++config+pypi__build",
"REPO_MAPPING:rules_python+,pypi__click rules_python++config+pypi__click",
"REPO_MAPPING:rules_python+,pypi__colorama rules_python++config+pypi__colorama",
"REPO_MAPPING:rules_python+,pypi__importlib_metadata rules_python++config+pypi__importlib_metadata",
"REPO_MAPPING:rules_python+,pypi__installer rules_python++config+pypi__installer",
"REPO_MAPPING:rules_python+,pypi__more_itertools rules_python++config+pypi__more_itertools",
"REPO_MAPPING:rules_python+,pypi__packaging rules_python++config+pypi__packaging",
"REPO_MAPPING:rules_python+,pypi__pep517 rules_python++config+pypi__pep517",
"REPO_MAPPING:rules_python+,pypi__pip rules_python++config+pypi__pip",
"REPO_MAPPING:rules_python+,pypi__pip_tools rules_python++config+pypi__pip_tools",
"REPO_MAPPING:rules_python+,pypi__pyproject_hooks rules_python++config+pypi__pyproject_hooks",
"REPO_MAPPING:rules_python+,pypi__setuptools rules_python++config+pypi__setuptools",
"REPO_MAPPING:rules_python+,pypi__tomli rules_python++config+pypi__tomli",
"REPO_MAPPING:rules_python+,pypi__wheel rules_python++config+pypi__wheel",
"REPO_MAPPING:rules_python+,pypi__zipp rules_python++config+pypi__zipp"
],
"generatedRepoSpecs": {
"rules_python_internal": {
"repoRuleId": "@@rules_python+//python/private:internal_config_repo.bzl%internal_config_repo",
"attributes": {
"transition_setting_generators": {},
"transition_settings": []
}
},
"pypi__build": {
"repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
"url": "https://files.pythonhosted.org/packages/e2/03/f3c8ba0a6b6e30d7d18c40faab90807c9bb5e9a1e3b2fe2008af624a9c97/build-1.2.1-py3-none-any.whl",
"sha256": "75e10f767a433d9a86e50d83f418e83efc18ede923ee5ff7df93b6cb0306c5d4",
"type": "zip",
"build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n"
}
},
"pypi__click": {
"repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
"url": "https://files.pythonhosted.org/packages/00/2e/d53fa4befbf2cfa713304affc7ca780ce4fc1fd8710527771b58311a3229/click-8.1.7-py3-none-any.whl",
"sha256": "ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28",
"type": "zip",
"build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n"
}
},
"pypi__colorama": {
"repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
"url": "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl",
"sha256": "4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6",
"type": "zip",
"build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n"
}
},
"pypi__importlib_metadata": {
"repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
"url": "https://files.pythonhosted.org/packages/2d/0a/679461c511447ffaf176567d5c496d1de27cbe34a87df6677d7171b2fbd4/importlib_metadata-7.1.0-py3-none-any.whl",
"sha256": "30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570",
"type": "zip",
"build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n"
}
},
"pypi__installer": {
"repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
"url": "https://files.pythonhosted.org/packages/e5/ca/1172b6638d52f2d6caa2dd262ec4c811ba59eee96d54a7701930726bce18/installer-0.7.0-py3-none-any.whl",
"sha256": "05d1933f0a5ba7d8d6296bb6d5018e7c94fa473ceb10cf198a92ccea19c27b53",
"type": "zip",
"build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n"
}
},
"pypi__more_itertools": {
"repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
"url": "https://files.pythonhosted.org/packages/50/e2/8e10e465ee3987bb7c9ab69efb91d867d93959095f4807db102d07995d94/more_itertools-10.2.0-py3-none-any.whl",
"sha256": "686b06abe565edfab151cb8fd385a05651e1fdf8f0a14191e4439283421f8684",
"type": "zip",
"build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n"
}
},
"pypi__packaging": {
"repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
"url": "https://files.pythonhosted.org/packages/49/df/1fceb2f8900f8639e278b056416d49134fb8d84c5942ffaa01ad34782422/packaging-24.0-py3-none-any.whl",
"sha256": "2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5",
"type": "zip",
"build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n"
}
},
"pypi__pep517": {
"repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
"url": "https://files.pythonhosted.org/packages/25/6e/ca4a5434eb0e502210f591b97537d322546e4833dcb4d470a48c375c5540/pep517-0.13.1-py3-none-any.whl",
"sha256": "31b206f67165b3536dd577c5c3f1518e8fbaf38cbc57efff8369a392feff1721",
"type": "zip",
"build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n"
}
},
"pypi__pip": {
"repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
"url": "https://files.pythonhosted.org/packages/8a/6a/19e9fe04fca059ccf770861c7d5721ab4c2aebc539889e97c7977528a53b/pip-24.0-py3-none-any.whl",
"sha256": "ba0d021a166865d2265246961bec0152ff124de910c5cc39f1156ce3fa7c69dc",
"type": "zip",
"build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n"
}
},
"pypi__pip_tools": {
"repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
"url": "https://files.pythonhosted.org/packages/0d/dc/38f4ce065e92c66f058ea7a368a9c5de4e702272b479c0992059f7693941/pip_tools-7.4.1-py3-none-any.whl",
"sha256": "4c690e5fbae2f21e87843e89c26191f0d9454f362d8acdbd695716493ec8b3a9",
"type": "zip",
"build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n"
}
},
"pypi__pyproject_hooks": {
"repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
"url": "https://files.pythonhosted.org/packages/ae/f3/431b9d5fe7d14af7a32340792ef43b8a714e7726f1d7b69cc4e8e7a3f1d7/pyproject_hooks-1.1.0-py3-none-any.whl",
"sha256": "7ceeefe9aec63a1064c18d939bdc3adf2d8aa1988a510afec15151578b232aa2",
"type": "zip",
"build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n"
}
},
"pypi__setuptools": {
"repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
"url": "https://files.pythonhosted.org/packages/90/99/158ad0609729111163fc1f674a5a42f2605371a4cf036d0441070e2f7455/setuptools-78.1.1-py3-none-any.whl",
"sha256": "c3a9c4211ff4c309edb8b8c4f1cbfa7ae324c4ba9f91ff254e3d305b9fd54561",
"type": "zip",
"build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n"
}
},
"pypi__tomli": {
"repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
"url": "https://files.pythonhosted.org/packages/97/75/10a9ebee3fd790d20926a90a2547f0bf78f371b2f13aa822c759680ca7b9/tomli-2.0.1-py3-none-any.whl",
"sha256": "939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc",
"type": "zip",
"build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n"
}
},
"pypi__wheel": {
"repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
"url": "https://files.pythonhosted.org/packages/7d/cd/d7460c9a869b16c3dd4e1e403cce337df165368c71d6af229a74699622ce/wheel-0.43.0-py3-none-any.whl",
"sha256": "55c570405f142630c6b9f72fe09d9b67cf1477fcf543ae5b8dcb1f5b7377da81",
"type": "zip",
"build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n"
}
},
"pypi__zipp": {
"repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
"url": "https://files.pythonhosted.org/packages/da/55/a03fd7240714916507e1fcf7ae355bd9d9ed2e6db492595f1a67f61681be/zipp-3.18.2-py3-none-any.whl",
"sha256": "dce197b859eb796242b0622af1b8beb0a722d52aa2f57133ead08edd5bf5374e",
"type": "zip",
"build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n"
}
}
}
}
},
"@@rules_python+//python/uv:uv.bzl%uv": {
"general": {
"bzlTransitiveDigest": "ijW9KS7qsIY+yBVvJ+Nr1mzwQox09j13DnE3iIwaeTM=",
"usagesDigest": "H8dQoNZcoqP+Mu0tHZTi4KHATzvNkM5ePuEqoQdklIU=",
"recordedInputs": [
"REPO_MAPPING:rules_python+,bazel_tools bazel_tools",
"REPO_MAPPING:rules_python+,platforms platforms"
],
"generatedRepoSpecs": {
"uv": {
"repoRuleId": "@@rules_python+//python/uv/private:uv_toolchains_repo.bzl%uv_toolchains_repo",
"attributes": {
"toolchain_type": "'@@rules_python+//python/uv:uv_toolchain_type'",
"toolchain_names": [
"none"
],
"toolchain_implementations": {
"none": "'@@rules_python+//python:none'"
},
"toolchain_compatible_with": {
"none": [
"@platforms//:incompatible"
]
},
"toolchain_target_settings": {}
}
}
}
}
}
},
"facts": {}
}

View File

@@ -23,6 +23,6 @@ bazel run //examples/vite_monorepo:app_a_dev -- --host 127.0.0.1 --port 5173 --s
bazel run //examples/vite_monorepo:app_b_dev -- --host 127.0.0.1 --port 5174 --strictPort
```
This example relies on a `bun_install` repository named
`examples_vite_monorepo_node_modules` defined in the repo's `MODULE.bazel` and
`WORKSPACE` files.
This example maps its `bun_install` output to the canonical `@node_modules`
repository name in `MODULE.bazel`, so `bun_script` targets don't need to hard-
code a generated repository name.

View File

@@ -0,0 +1 @@
export default {};

View File

@@ -1,7 +1,9 @@
import { nanoid } from "nanoid";
const app = document.querySelector("#app");
if (app) {
app.textContent = "Hello from monorepo app B";
const id = nanoid();
app.textContent = "Hello from monorepo app B " + id;
}
console.log("Hello from monorepo app B");

View File

@@ -3,7 +3,10 @@
"private": true,
"type": "module",
"scripts": {
"dev": "vite"
"dev": "vite --port 5174"
},
"dependencies": {
"nanoid": "5.1.6"
},
"devDependencies": {
"vite": "catalog:",

View File

@@ -0,0 +1 @@
export default {};

View File

@@ -13,6 +13,9 @@
},
"apps/app-b": {
"name": "vite-monorepo-app-b",
"dependencies": {
"nanoid": "5.1.6",
},
"devDependencies": {
"vite": "catalog:",
"vitest": "catalog:testing",
@@ -178,7 +181,7 @@
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
"nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
"nanoid": ["nanoid@5.1.6", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg=="],
"pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
@@ -225,5 +228,7 @@
"vitest": ["vitest@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.4", "@vitest/mocker": "3.2.4", "@vitest/pretty-format": "^3.2.4", "@vitest/runner": "3.2.4", "@vitest/snapshot": "3.2.4", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "debug": "^4.4.1", "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", "picomatch": "^4.0.2", "std-env": "^3.9.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.14", "tinypool": "^1.1.1", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", "vite-node": "3.2.4", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "@vitest/browser": "3.2.4", "@vitest/ui": "3.2.4", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/debug", "@types/node", "@vitest/browser", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A=="],
"why-is-node-running": ["why-is-node-running@2.3.0", "", { "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" }, "bin": { "why-is-node-running": "cli.js" } }, "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w=="],
"postcss/nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
}
}

View File

@@ -0,0 +1,90 @@
#!/usr/bin/env bash
set -euo pipefail
app_a_binary="$1"
app_b_binary="$2"
workdir="$(mktemp -d)"
server_pid=""
log_file=""
cleanup() {
if [[ -n ${server_pid} ]] && kill -0 "${server_pid}" 2>/dev/null; then
kill "${server_pid}" 2>/dev/null || true
wait "${server_pid}" 2>/dev/null || true
fi
rm -rf "${workdir}"
}
trap cleanup EXIT
pick_port() {
python3 - <<'PY'
import socket
sock = socket.socket()
sock.bind(("127.0.0.1", 0))
print(sock.getsockname()[1])
sock.close()
PY
}
matches_expected_js() {
local content="$1"
local expected="$2"
if [[ ${expected} == *"*"* ]]; then
local regex
regex="$(printf '%s' "${expected}" | sed -e 's/[][(){}.+?^$|\\]/\\&/g' -e 's/\*/.*/g')"
printf '%s' "${content}" | grep -Eq "${regex}"
else
printf '%s' "${content}" | grep -Fq "${expected}"
fi
}
verify_vite_app() {
local binary="$1"
local expected_title="$2"
local expected_js="$3"
local log_name="$4"
local port
local main_js
port="$(pick_port)"
log_file="${workdir}/${log_name}.log"
"${binary}" --host 127.0.0.1 --port "${port}" --strictPort >"${log_file}" 2>&1 &
server_pid=$!
for _ in {1..60}; do
if ! kill -0 "${server_pid}" 2>/dev/null; then
cat "${log_file}" >&2
echo "Vite server exited unexpectedly for ${log_name}" >&2
exit 1
fi
if curl --fail --silent "http://127.0.0.1:${port}/" | grep -Fq "${expected_title}"; then
break
fi
sleep 0.5
done
if ! curl --fail --silent "http://127.0.0.1:${port}/" | grep -Fq "${expected_title}"; then
cat "${log_file}" >&2
echo "Timed out waiting for Vite index page for ${log_name}" >&2
exit 1
fi
main_js="$(curl --fail --silent "http://127.0.0.1:${port}/main.js")"
if ! matches_expected_js "${main_js}" "${expected_js}"; then
cat "${log_file}" >&2
echo "Expected Vite module output was not served for ${log_name}" >&2
exit 1
fi
kill "${server_pid}" 2>/dev/null || true
wait "${server_pid}" 2>/dev/null || true
server_pid=""
}
verify_vite_app "${app_a_binary}" "Vite monorepo app A" "Hello from monorepo app A" "app-a"
verify_vite_app "${app_b_binary}" "Vite monorepo app B" "Hello from monorepo app B *" "app-b"

80
flake.lock generated
View File

@@ -1,28 +1,5 @@
{
"nodes": {
"devshell-lib": {
"inputs": {
"git-hooks": "git-hooks",
"nixpkgs": [
"nixpkgs"
],
"treefmt-nix": "treefmt-nix"
},
"locked": {
"lastModified": 1772815059,
"narHash": "sha256-9Mn8t/a7b43omtmKRsF0HmFpCkNpTsvYEq0y85KLL5s=",
"ref": "v2.0.1",
"rev": "80cc529de7060e079d89a69d8daaf0347b53d8f9",
"revCount": 43,
"type": "git",
"url": "https://git.dgren.dev/eric/nix-flake-lib"
},
"original": {
"ref": "v2.0.1",
"type": "git",
"url": "https://git.dgren.dev/eric/nix-flake-lib"
}
},
"flake-compat": {
"flake": false,
"locked": {
@@ -43,7 +20,7 @@
"inputs": {
"flake-compat": "flake-compat",
"gitignore": "gitignore",
"nixpkgs": "nixpkgs"
"nixpkgs": "nixpkgs_2"
},
"locked": {
"lastModified": 1772024342,
@@ -62,7 +39,7 @@
"gitignore": {
"inputs": {
"nixpkgs": [
"devshell-lib",
"repo-lib",
"git-hooks",
"nixpkgs"
]
@@ -82,6 +59,22 @@
}
},
"nixpkgs": {
"locked": {
"lastModified": 1772542754,
"narHash": "sha256-WGV2hy+VIeQsYXpsLjdr4GvHv5eECMISX1zKLTedhdg=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "8c809a146a140c5c8806f13399592dbcb1bb5dc4",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1770073757,
"narHash": "sha256-Vy+G+F+3E/Tl+GMNgiHl9Pah2DgShmIUBJXmbiQPHbI=",
@@ -97,7 +90,7 @@
"type": "github"
}
},
"nixpkgs_2": {
"nixpkgs_3": {
"locked": {
"lastModified": 1770107345,
"narHash": "sha256-tbS0Ebx2PiA1FRW8mt8oejR0qMXmziJmPaU1d4kYY9g=",
@@ -113,31 +106,38 @@
"type": "github"
}
},
"nixpkgs_3": {
"repo-lib": {
"inputs": {
"git-hooks": "git-hooks",
"nixpkgs": [
"nixpkgs"
],
"treefmt-nix": "treefmt-nix"
},
"locked": {
"lastModified": 1772542754,
"narHash": "sha256-WGV2hy+VIeQsYXpsLjdr4GvHv5eECMISX1zKLTedhdg=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "8c809a146a140c5c8806f13399592dbcb1bb5dc4",
"type": "github"
"lastModified": 1772866275,
"narHash": "sha256-lsJrFIbq6OO5wUC648VnvOmJm3qgJrlEugbdjeZsP34=",
"ref": "refs/tags/v3.0.0",
"rev": "96d2d190466dddcb9e652c38b70152f09b9fcb05",
"revCount": 50,
"type": "git",
"url": "https://git.dgren.dev/eric/nix-flake-lib"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
"ref": "refs/tags/v3.0.0",
"type": "git",
"url": "https://git.dgren.dev/eric/nix-flake-lib"
}
},
"root": {
"inputs": {
"devshell-lib": "devshell-lib",
"nixpkgs": "nixpkgs_3"
"nixpkgs": "nixpkgs",
"repo-lib": "repo-lib"
}
},
"treefmt-nix": {
"inputs": {
"nixpkgs": "nixpkgs_2"
"nixpkgs": "nixpkgs_3"
},
"locked": {
"lastModified": 1770228511,

191
flake.nix
View File

@@ -3,58 +3,34 @@
inputs = {
nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
devshell-lib.url = "git+https://git.dgren.dev/eric/nix-flake-lib?ref=v2.0.1";
devshell-lib.inputs.nixpkgs.follows = "nixpkgs";
repo-lib.url = "git+https://git.dgren.dev/eric/nix-flake-lib?ref=refs/tags/v3.0.0";
repo-lib.inputs.nixpkgs.follows = "nixpkgs";
};
outputs =
{
self,
nixpkgs,
devshell-lib,
repo-lib,
...
}:
let
supportedSystems = [
"x86_64-linux"
"aarch64-linux"
"x86_64-darwin"
"aarch64-darwin"
];
forAllSystems = nixpkgs.lib.genAttrs supportedSystems;
bazelVersion = "9.0.0";
in
{
devShells = forAllSystems (
system:
let
pkgs = import nixpkgs { inherit system; };
bazel9 = pkgs.writeShellScriptBin "bazel" ''
export USE_BAZEL_VERSION="''${USE_BAZEL_VERSION:-9.0.0}"
exec ${pkgs.bazelisk}/bin/bazelisk "$@"
repo-lib.lib.mkRepo {
inherit self nixpkgs;
src = ./.;
config = {
shell.extraShellText = ''
export USE_BAZEL_VERSION="''${USE_BAZEL_VERSION:-${bazelVersion}}"
export BUN_INSTALL="''${BUN_INSTALL:-$HOME/.bun}"
export PATH="$BUN_INSTALL/bin:$PATH"
'';
env = devshell-lib.lib.mkDevShell {
inherit system;
extraPackages = with pkgs; [
go
gopls
gotools
bun
bazel9
bazel-buildtools
self.packages.${system}.release
];
features = {
oxfmt = false;
};
formatters = {
shfmt.enable = true;
};
formatterSettings = {
shfmt.options = [
formatting = {
programs.shfmt.enable = true;
settings.shfmt.options = [
"-i"
"2"
"-s"
@@ -62,82 +38,15 @@
];
};
additionalHooks = {
tests = {
enable = true;
entry = "echo 'No tests defined yet.'";
pass_filenames = false;
stages = [ "pre-push" ];
};
};
tools = [
release = {
steps = [
{
name = "Bun";
bin = "${pkgs.bun}/bin/bun";
versionCmd = "--version";
color = "YELLOW";
}
{
name = "Go";
bin = "${pkgs.go}/bin/go";
versionCmd = "version";
color = "CYAN";
}
{
name = "Bazel";
bin = "${bazel9}/bin/bazel";
versionCmd = "--version";
color = "GREEN";
}
];
extraShellHook = ''
export USE_BAZEL_VERSION="''${USE_BAZEL_VERSION:-9.0.0}"
export BUN_INSTALL="''${BUN_INSTALL:-$HOME/.bun}"
export PATH="$BUN_INSTALL/bin:$PATH"
'';
};
in
{
default = env.shell;
}
);
checks = forAllSystems (
system:
let
env = devshell-lib.lib.mkDevShell { inherit system; };
in
{
inherit (env) pre-commit-check;
}
);
formatter = forAllSystems (system: (devshell-lib.lib.mkDevShell { inherit system; }).formatter);
# Optional: release command (`release`)
#
# The release script always updates VERSION first, then:
# 1) runs release steps in order (file writes and scripts)
# 2) runs postVersion hook
# 3) formats, stages, commits, tags, and pushes
#
# Runtime env vars available in release.run/postVersion:
# BASE_VERSION, CHANNEL, PRERELEASE_NUM, FULL_VERSION, FULL_TAG
#
packages = forAllSystems (system: {
release = devshell-lib.lib.mkRelease {
inherit system;
release = [
{
run = ''
run.script = ''
sed -E -i 's#^([[:space:]]*version[[:space:]]*=[[:space:]]*")[^"]*(",)$#\1'"$FULL_VERSION"'\2#' "$ROOT_DIR/MODULE.bazel"
'';
}
{
run = ''
run.script = ''
README="$ROOT_DIR/README.md"
TMP="$README.tmp"
@@ -166,14 +75,70 @@
' "$README" > "$TMP" && mv "$TMP" "$README"
'';
}
{
run.script = ''
bazel cquery //tests/... >/dev/null
'';
}
];
postVersion = ''
echo "Released $FULL_TAG"
'';
};
});
};
perSystem =
{
pkgs,
system,
...
}:
let
bazel9 = pkgs.writeShellScriptBin "bazel" ''
export USE_BAZEL_VERSION="''${USE_BAZEL_VERSION:-${bazelVersion}}"
exec ${pkgs.bazelisk}/bin/bazelisk "$@"
'';
in
{
tools = [
(repo-lib.lib.tools.fromPackage {
name = "Bun";
package = pkgs.bun;
version.args = [ "--version" ];
banner.color = "YELLOW";
})
(repo-lib.lib.tools.fromPackage {
name = "Go";
package = pkgs.go;
version.args = [ "version" ];
banner.color = "CYAN";
})
(repo-lib.lib.tools.fromPackage {
name = "Bazel";
package = bazel9;
version.args = [ "--version" ];
banner.color = "GREEN";
})
];
shell.packages = [
pkgs.gopls
pkgs.gotools
pkgs.bazel-buildtools
self.packages.${system}.release
];
checks.tests = {
command = "bazel test //tests/...";
stage = "pre-push";
passFilenames = false;
runtimeInputs = [
bazel9
pkgs.bun
pkgs.go
];
};
};
};
}

View File

@@ -3,29 +3,89 @@ load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
package(default_visibility = ["//visibility:public"])
exports_files([
"bun_build_support.bzl",
"bun_binary.bzl",
"bun_command.bzl",
"bun_compile.bzl",
"bun_bundle.bzl",
"bun_dev.bzl",
"bun_install.bzl",
"bun_script.bzl",
"bun_test.bzl",
"js_compat.bzl",
"js_library.bzl",
"js_run_devserver.bzl",
"workspace.bzl",
])
filegroup(
name = "repo_runtime_files",
srcs = [
"BUILD.bazel",
"bun_binary.bzl",
"bun_build_support.bzl",
"bun_bundle.bzl",
"bun_command.bzl",
"bun_compile.bzl",
"bun_dev.bzl",
"bun_install.bzl",
"bun_script.bzl",
"bun_test.bzl",
"js_compat.bzl",
"js_library.bzl",
"js_run_devserver.bzl",
"workspace.bzl",
],
visibility = ["//visibility:public"],
)
bzl_library(
name = "bun_command_bzl",
srcs = ["bun_command.bzl"],
)
bzl_library(
name = "bun_build_support_bzl",
srcs = ["bun_build_support.bzl"],
deps = [
":bun_command_bzl",
":js_library_bzl",
],
)
bzl_library(
name = "bun_binary_bzl",
srcs = ["bun_binary.bzl"],
deps = [
":bun_command_bzl",
":js_library_bzl",
":workspace_bzl",
],
)
bzl_library(
name = "bun_compile_bzl",
srcs = ["bun_compile.bzl"],
deps = [
":bun_build_support_bzl",
],
)
bzl_library(
name = "bun_bundle_bzl",
srcs = ["bun_bundle.bzl"],
deps = [":js_library_bzl"],
deps = [
":bun_build_support_bzl",
],
)
bzl_library(
name = "bun_dev_bzl",
srcs = ["bun_dev.bzl"],
deps = [
":bun_command_bzl",
":workspace_bzl",
],
)
bzl_library(
@@ -36,15 +96,48 @@ bzl_library(
bzl_library(
name = "bun_script_bzl",
srcs = ["bun_script.bzl"],
deps = [
":bun_command_bzl",
":workspace_bzl",
],
)
bzl_library(
name = "bun_test_bzl",
srcs = ["bun_test.bzl"],
deps = [":js_library_bzl"],
deps = [
":bun_command_bzl",
":js_library_bzl",
":workspace_bzl",
],
)
bzl_library(
name = "js_compat_bzl",
srcs = ["js_compat.bzl"],
deps = [
":bun_binary_bzl",
":bun_test_bzl",
":js_library_bzl",
":js_run_devserver_bzl",
],
)
bzl_library(
name = "js_library_bzl",
srcs = ["js_library.bzl"],
)
bzl_library(
name = "js_run_devserver_bzl",
srcs = ["js_run_devserver.bzl"],
deps = [
":js_library_bzl",
":workspace_bzl",
],
)
bzl_library(
name = "workspace_bzl",
srcs = ["workspace.bzl"],
)

View File

@@ -1,67 +1,61 @@
"""Rule for running JS/TS scripts with Bun."""
load("//internal:bun_command.bzl", "append_shell_flag", "append_shell_flag_files", "append_shell_flag_values", "append_shell_install_mode", "append_shell_raw_flags", "render_shell_array", "shell_quote")
load("//internal:js_library.bzl", "collect_js_runfiles")
load("//internal:workspace.bzl", "create_bun_workspace_info", "render_workspace_setup", "workspace_runfiles")
def _bun_binary_impl(ctx):
toolchain = ctx.toolchains["//bun:toolchain_type"]
bun_bin = toolchain.bun.bun_bin
entry_point = ctx.file.entry_point
dep_runfiles = [collect_js_runfiles(dep) for dep in ctx.attr.deps]
workspace_info = create_bun_workspace_info(
ctx,
extra_files = ctx.files.data + ctx.files.preload + ctx.files.env_files + [bun_bin],
primary_file = entry_point,
)
launcher_lines = [render_shell_array("bun_args", ["--bun", "run"])]
append_shell_install_mode(launcher_lines, "bun_args", ctx.attr.install_mode)
append_shell_flag_files(launcher_lines, "bun_args", "--preload", ctx.files.preload)
append_shell_flag_files(launcher_lines, "bun_args", "--env-file", ctx.files.env_files)
append_shell_flag(launcher_lines, "bun_args", "--no-env-file", ctx.attr.no_env_file)
append_shell_flag(launcher_lines, "bun_args", "--smol", ctx.attr.smol)
append_shell_flag_values(launcher_lines, "bun_args", "--conditions", ctx.attr.conditions)
append_shell_raw_flags(launcher_lines, "bun_args", ctx.attr.run_flags)
launcher_lines.append('bun_args+=("${primary_source}")')
for arg in ctx.attr.args:
launcher_lines.append("bun_args+=(%s)" % shell_quote(arg))
command = """
trap cleanup_runtime_workspace EXIT
cd "${runtime_exec_dir}"
__BUN_ARGS__
exec "${bun_bin}" "${bun_args[@]}" "$@"
""".replace("__BUN_ARGS__", "\n".join(launcher_lines))
launcher = ctx.actions.declare_file(ctx.label.name)
ctx.actions.write(
output = launcher,
is_executable = True,
content = """#!/usr/bin/env bash
set -euo pipefail
runfiles_dir="${{RUNFILES_DIR:-$0.runfiles}}"
workspace_root="${{runfiles_dir}}/_main"
bun_bin="${{runfiles_dir}}/_main/{bun_short_path}"
entry_point="${{runfiles_dir}}/_main/{entry_short_path}"
resolve_entrypoint_workdir() {{
local dir
dir="$(dirname "${{entry_point}}")"
while [[ "${{dir}}" == "${{workspace_root}}"* ]]; do
if [[ -f "${{dir}}/.env" || -f "${{dir}}/package.json" ]]; then
echo "${{dir}}"
return 0
fi
if [[ "${{dir}}" == "${{workspace_root}}" ]]; then
break
fi
dir="$(dirname "${{dir}}")"
done
echo "$(dirname "${{entry_point}}")"
}}
working_dir="{working_dir}"
if [[ "${{working_dir}}" == "entry_point" ]]; then
cd "$(resolve_entrypoint_workdir)"
else
cd "${{workspace_root}}"
fi
exec "${{bun_bin}}" --bun run "${{entry_point}}" "$@"
""".format(
content = render_workspace_setup(
bun_short_path = bun_bin.short_path,
entry_short_path = entry_point.short_path,
working_dir = ctx.attr.working_dir,
),
)
transitive_files = []
if ctx.attr.node_modules:
transitive_files.append(ctx.attr.node_modules[DefaultInfo].files)
runfiles = ctx.runfiles(
files = [bun_bin, entry_point] + ctx.files.data,
transitive_files = depset(transitive = transitive_files),
install_metadata_short_path = workspace_info.install_metadata_file.short_path if workspace_info.install_metadata_file else "",
primary_source_short_path = entry_point.short_path,
working_dir_mode = ctx.attr.working_dir,
) + command,
)
return [
workspace_info,
DefaultInfo(
executable = launcher,
runfiles = runfiles,
runfiles = workspace_runfiles(
ctx,
workspace_info,
direct_files = [launcher],
transitive_files = dep_runfiles,
),
),
]
@@ -85,6 +79,36 @@ Use this rule for non-test scripts and CLIs that should run via `bazel run`.
allow_files = True,
doc = "Additional runtime files required by the program.",
),
"deps": attr.label_list(
doc = "Library dependencies required by the program.",
),
"preload": attr.label_list(
allow_files = True,
doc = "Modules to preload with `--preload` before running the entry point.",
),
"env_files": attr.label_list(
allow_files = True,
doc = "Additional environment files loaded with `--env-file`.",
),
"no_env_file": attr.bool(
default = False,
doc = "If true, disables Bun's automatic `.env` loading.",
),
"smol": attr.bool(
default = False,
doc = "If true, enables Bun's lower-memory runtime mode.",
),
"conditions": attr.string_list(
doc = "Custom package resolve conditions passed to Bun.",
),
"install_mode": attr.string(
default = "disable",
values = ["disable", "auto", "fallback", "force"],
doc = "Whether Bun may auto-install missing packages at runtime.",
),
"run_flags": attr.string_list(
doc = "Additional raw flags forwarded to `bun run` before the entry point.",
),
"working_dir": attr.string(
default = "workspace",
values = ["workspace", "entry_point"],

View File

@@ -0,0 +1,114 @@
"""Shared helpers for Bun build- and compile-style rules."""
load("//internal:bun_command.bzl", "add_flag", "add_flag_value", "add_flag_values", "add_install_mode", "add_raw_flags")
load("//internal:js_library.bzl", "collect_js_sources")
def infer_entry_point_root(entries):
if not entries:
return None
common_segments = entries[0].path.split("/")[:-1]
for entry in entries[1:]:
entry_segments = entry.path.split("/")[:-1]
common_length = min(len(common_segments), len(entry_segments))
idx = common_length
for segment_idx in range(common_length):
if common_segments[segment_idx] != entry_segments[segment_idx]:
idx = segment_idx
break
common_segments = common_segments[:idx]
if not common_segments:
return "."
return "/".join(common_segments)
def bun_build_transitive_inputs(ctx):
transitive_inputs = []
if getattr(ctx.attr, "node_modules", None):
transitive_inputs.append(ctx.attr.node_modules[DefaultInfo].files)
for dep in getattr(ctx.attr, "deps", []):
transitive_inputs.append(collect_js_sources(dep))
return transitive_inputs
def add_bun_build_common_flags(args, attr, metafile = None, metafile_md = None, root = None):
build_root = root
if build_root == None:
build_root = getattr(attr, "root", None)
add_install_mode(args, getattr(attr, "install_mode", "disable"))
add_flag_value(args, "--target", getattr(attr, "target", None))
add_flag_value(args, "--format", getattr(attr, "format", None))
add_flag(args, "--production", getattr(attr, "production", False))
add_flag(args, "--splitting", getattr(attr, "splitting", False))
add_flag_value(args, "--root", build_root)
sourcemap = getattr(attr, "sourcemap", None)
if sourcemap == True:
args.add("--sourcemap")
elif sourcemap and sourcemap != "none":
add_flag_value(args, "--sourcemap", sourcemap)
add_flag_value(args, "--banner", getattr(attr, "banner", None))
add_flag_value(args, "--footer", getattr(attr, "footer", None))
add_flag_value(args, "--public-path", getattr(attr, "public_path", None))
add_flag_value(args, "--packages", getattr(attr, "packages", None))
add_flag_values(args, "--external", getattr(attr, "external", []))
add_flag_value(args, "--entry-naming", getattr(attr, "entry_naming", None))
add_flag_value(args, "--chunk-naming", getattr(attr, "chunk_naming", None))
add_flag_value(args, "--asset-naming", getattr(attr, "asset_naming", None))
add_flag(args, "--minify", getattr(attr, "minify", False))
add_flag(args, "--minify-syntax", getattr(attr, "minify_syntax", False))
add_flag(args, "--minify-whitespace", getattr(attr, "minify_whitespace", False))
add_flag(args, "--minify-identifiers", getattr(attr, "minify_identifiers", False))
add_flag(args, "--keep-names", getattr(attr, "keep_names", False))
add_flag(args, "--css-chunking", getattr(attr, "css_chunking", False))
add_flag_values(args, "--conditions", getattr(attr, "conditions", []))
add_flag_value(args, "--env", getattr(attr, "env", None))
add_flag_values(args, "--define", getattr(attr, "define", []))
add_flag_values(args, "--drop", getattr(attr, "drop", []))
add_flag_values(args, "--feature", getattr(attr, "feature", []))
add_flag_values(args, "--loader", getattr(attr, "loader", []))
add_flag_value(args, "--jsx-factory", getattr(attr, "jsx_factory", None))
add_flag_value(args, "--jsx-fragment", getattr(attr, "jsx_fragment", None))
add_flag_value(args, "--jsx-import-source", getattr(attr, "jsx_import_source", None))
add_flag_value(args, "--jsx-runtime", getattr(attr, "jsx_runtime", None))
add_flag(args, "--jsx-side-effects", getattr(attr, "jsx_side_effects", False))
add_flag(args, "--react-fast-refresh", getattr(attr, "react_fast_refresh", False))
add_flag(args, "--emit-dce-annotations", getattr(attr, "emit_dce_annotations", False))
add_flag(args, "--no-bundle", getattr(attr, "no_bundle", False))
if metafile:
args.add("--metafile=%s" % metafile.path)
if metafile_md:
args.add("--metafile-md=%s" % metafile_md.path)
add_raw_flags(args, getattr(attr, "build_flags", []))
def add_bun_compile_flags(args, attr, compile_executable = None):
add_flag(args, "--compile", True)
add_flag(args, "--bytecode", getattr(attr, "bytecode", False))
add_flag_values(args, "--compile-exec-argv", getattr(attr, "compile_exec_argv", []))
if getattr(attr, "compile_autoload_dotenv", True):
args.add("--compile-autoload-dotenv")
else:
args.add("--no-compile-autoload-dotenv")
if getattr(attr, "compile_autoload_bunfig", True):
args.add("--compile-autoload-bunfig")
else:
args.add("--no-compile-autoload-bunfig")
if getattr(attr, "compile_autoload_tsconfig", False):
args.add("--compile-autoload-tsconfig")
else:
args.add("--no-compile-autoload-tsconfig")
if getattr(attr, "compile_autoload_package_json", False):
args.add("--compile-autoload-package-json")
else:
args.add("--no-compile-autoload-package-json")
if compile_executable:
add_flag_value(args, "--compile-executable-path", compile_executable.path)
add_flag(args, "--windows-hide-console", getattr(attr, "windows_hide_console", False))
add_flag_value(args, "--windows-icon", getattr(attr, "windows_icon", None))
add_flag_value(args, "--windows-title", getattr(attr, "windows_title", None))
add_flag_value(args, "--windows-publisher", getattr(attr, "windows_publisher", None))
add_flag_value(args, "--windows-version", getattr(attr, "windows_version", None))
add_flag_value(args, "--windows-description", getattr(attr, "windows_description", None))
add_flag_value(args, "--windows-copyright", getattr(attr, "windows_copyright", None))

View File

@@ -1,6 +1,6 @@
"""Rule for bundling JS/TS sources with Bun."""
load("//internal:js_library.bzl", "BunSourcesInfo")
load("//internal:bun_build_support.bzl", "add_bun_build_common_flags", "bun_build_transitive_inputs")
def _output_name(target_name, entry):
@@ -12,14 +12,7 @@ def _bun_bundle_impl(ctx):
toolchain = ctx.toolchains["//bun:toolchain_type"]
bun_bin = toolchain.bun.bun_bin
transitive_inputs = []
if ctx.attr.node_modules:
transitive_inputs.append(ctx.attr.node_modules[DefaultInfo].files)
for dep in ctx.attr.deps:
if BunSourcesInfo in dep:
transitive_inputs.append(dep[BunSourcesInfo].transitive_sources)
else:
transitive_inputs.append(dep[DefaultInfo].files)
transitive_inputs = bun_build_transitive_inputs(ctx)
outputs = []
for entry in ctx.files.entry_points:
@@ -29,20 +22,10 @@ def _bun_bundle_impl(ctx):
args = ctx.actions.args()
args.add("--bun")
args.add("build")
args.add(entry.path)
add_bun_build_common_flags(args, ctx.attr)
args.add("--outfile")
args.add(output.path)
args.add("--target")
args.add(ctx.attr.target)
args.add("--format")
args.add(ctx.attr.format)
if ctx.attr.minify:
args.add("--minify")
if ctx.attr.sourcemap:
args.add("--sourcemap")
for package in ctx.attr.external:
args.add("--external")
args.add(package)
args.add(entry.path)
ctx.actions.run(
executable = bun_bin,
@@ -81,6 +64,11 @@ Each entry point produces one output JavaScript artifact.
allow_files = True,
doc = "Additional non-source files needed during bundling.",
),
"install_mode": attr.string(
default = "disable",
values = ["disable", "auto", "fallback", "force"],
doc = "Whether Bun may auto-install missing packages during bundling.",
),
"target": attr.string(
default = "browser",
values = ["browser", "node", "bun"],
@@ -102,6 +90,9 @@ Each entry point produces one output JavaScript artifact.
"external": attr.string_list(
doc = "Package names to treat as externals (not bundled).",
),
"build_flags": attr.string_list(
doc = "Additional raw flags forwarded to `bun build`.",
),
},
toolchains = ["//bun:toolchain_type"],
)

84
internal/bun_command.bzl Normal file
View File

@@ -0,0 +1,84 @@
"""Shared Bun CLI flag builders for rules and launchers."""
def shell_quote(value):
return "'" + str(value).replace("'", "'\"'\"'") + "'"
def _runfiles_workspace(file):
workspace_name = file.owner.workspace_name
if workspace_name:
return workspace_name
return "_main"
def runfiles_path_expr(file):
return '"${runfiles_dir}/%s/%s"' % (_runfiles_workspace(file), file.short_path)
def render_shell_array(name, values):
rendered = [shell_quote(value) for value in values]
return "%s=(%s)" % (name, " ".join(rendered))
def append_shell_arg(lines, name, value):
lines.append("%s+=(%s)" % (name, shell_quote(value)))
def append_shell_expr(lines, name, expr):
lines.append("%s+=(%s)" % (name, expr))
def append_shell_flag(lines, name, flag, enabled):
if enabled:
append_shell_arg(lines, name, flag)
def append_shell_flag_value(lines, name, flag, value):
if value == None:
return
if type(value) == type("") and not value:
return
append_shell_arg(lines, name, flag)
append_shell_arg(lines, name, value)
def append_shell_flag_values(lines, name, flag, values):
for value in values:
append_shell_flag_value(lines, name, flag, value)
def append_shell_flag_files(lines, name, flag, files):
for file in files:
append_shell_arg(lines, name, flag)
append_shell_expr(lines, name, runfiles_path_expr(file))
def append_shell_raw_flags(lines, name, values):
for value in values:
append_shell_arg(lines, name, value)
def append_shell_install_mode(lines, name, install_mode):
if install_mode == "disable":
append_shell_arg(lines, name, "--no-install")
elif install_mode in ["fallback", "force"]:
append_shell_flag_value(lines, name, "--install", install_mode)
def add_flag(args, flag, enabled):
if enabled:
args.add(flag)
def add_flag_value(args, flag, value):
if value == None:
return
if type(value) == type("") and not value:
return
args.add(flag)
args.add(value)
def add_flag_values(args, flag, values):
for value in values:
add_flag_value(args, flag, value)
def add_flag_files(args, flag, files):
for file in files:
args.add(flag)
args.add(file.path)
def add_raw_flags(args, values):
args.add_all(values)
def add_install_mode(args, install_mode):
if install_mode == "disable":
args.add("--no-install")
elif install_mode in ["fallback", "force"]:
add_flag_value(args, "--install", install_mode)

393
internal/bun_compile.bzl Normal file
View File

@@ -0,0 +1,393 @@
"""Rules for Bun build outputs and standalone executables."""
load("//internal:bun_build_support.bzl", "add_bun_build_common_flags", "add_bun_compile_flags", "bun_build_transitive_inputs", "infer_entry_point_root")
def _bun_build_impl(ctx):
toolchain = ctx.toolchains["//bun:toolchain_type"]
bun_bin = toolchain.bun.bun_bin
output_dir = ctx.actions.declare_directory(ctx.label.name)
metafile = None
if ctx.attr.metafile:
metafile = ctx.actions.declare_file(ctx.label.name + ".meta.json")
metafile_md = None
if ctx.attr.metafile_md:
metafile_md = ctx.actions.declare_file(ctx.label.name + ".meta.md")
build_root = ctx.attr.root
if not build_root:
build_root = infer_entry_point_root(ctx.files.entry_points)
transitive_inputs = bun_build_transitive_inputs(ctx)
build_inputs = depset(
direct = ctx.files.entry_points + ctx.files.data,
transitive = transitive_inputs,
)
input_manifest = ctx.actions.declare_file(ctx.label.name + ".inputs")
runner = ctx.actions.declare_file(ctx.label.name + "_runner.sh")
args = ctx.actions.args()
args.add(input_manifest.path)
args.add(bun_bin.path)
args.add("--bun")
args.add("build")
add_bun_build_common_flags(args, ctx.attr, metafile = metafile, metafile_md = metafile_md, root = build_root)
args.add("--outdir")
args.add(output_dir.path)
args.add_all(ctx.files.entry_points)
outputs = [output_dir]
if metafile:
outputs.append(metafile)
if metafile_md:
outputs.append(metafile_md)
ctx.actions.write(
output = input_manifest,
content = "".join([file.path + "\n" for file in build_inputs.to_list()]),
)
ctx.actions.write(
output = runner,
is_executable = True,
content = """#!/usr/bin/env bash
set -euo pipefail
manifest="$1"
execroot="$(pwd -P)"
bun_bin="$2"
if [[ "${bun_bin}" != /* ]]; then
bun_bin="${execroot}/${bun_bin}"
fi
shift 2
stage_dir="$(mktemp -d "${TMPDIR:-/tmp}/rules_bun_build.XXXXXX")"
cleanup() {
rm -rf "${stage_dir}"
}
trap cleanup EXIT
while IFS= read -r relpath; do
if [[ -z "${relpath}" ]]; then
continue
fi
src="${execroot}/${relpath}"
dest="${stage_dir}/${relpath}"
mkdir -p "$(dirname "${dest}")"
cp -L "${src}" "${dest}"
done < "${manifest}"
forwarded_args=()
while (($#)); do
case "$1" in
--outdir)
forwarded_args+=("$1" "${execroot}/$2")
shift 2
;;
--metafile=*)
forwarded_args+=("--metafile=${execroot}/${1#--metafile=}")
shift
;;
--metafile-md=*)
forwarded_args+=("--metafile-md=${execroot}/${1#--metafile-md=}")
shift
;;
*)
forwarded_args+=("$1")
shift
;;
esac
done
cd "${stage_dir}"
exec "${bun_bin}" "${forwarded_args[@]}"
""",
)
ctx.actions.run(
executable = runner,
arguments = [args],
inputs = depset(
direct = [input_manifest, bun_bin],
transitive = [build_inputs],
),
outputs = outputs,
mnemonic = "BunBuild",
progress_message = "Building {} with Bun".format(ctx.label.name),
)
return [DefaultInfo(files = depset(outputs))]
def _bun_compile_impl(ctx):
toolchain = ctx.toolchains["//bun:toolchain_type"]
bun_bin = toolchain.bun.bun_bin
output = ctx.actions.declare_file(ctx.label.name)
compile_executable = ctx.file.compile_executable
args = ctx.actions.args()
args.add("--bun")
args.add("build")
add_bun_build_common_flags(args, ctx.attr)
add_bun_compile_flags(args, ctx.attr, compile_executable = compile_executable)
args.add("--outfile")
args.add(output.path)
args.add(ctx.file.entry_point.path)
direct_inputs = [ctx.file.entry_point] + ctx.files.data
if compile_executable:
direct_inputs.append(compile_executable)
ctx.actions.run(
executable = bun_bin,
arguments = [args],
inputs = depset(
direct = direct_inputs,
transitive = bun_build_transitive_inputs(ctx),
),
outputs = [output],
mnemonic = "BunCompile",
progress_message = "Compiling {} with Bun".format(ctx.file.entry_point.short_path),
)
return [
DefaultInfo(
executable = output,
files = depset([output]),
),
]
_COMMON_BUILD_ATTRS = {
"node_modules": attr.label(
doc = "Optional label providing package files from a `node_modules` tree, typically produced by `bun_install`, for package resolution.",
),
"deps": attr.label_list(
doc = "Source/library dependencies that provide transitive inputs.",
),
"data": attr.label_list(
allow_files = True,
doc = "Additional non-source files needed during building.",
),
"install_mode": attr.string(
default = "disable",
values = ["disable", "auto", "fallback", "force"],
doc = "Whether Bun may auto-install missing packages while executing the build.",
),
"target": attr.string(
default = "browser",
values = ["browser", "node", "bun"],
doc = "Bun build target environment.",
),
"format": attr.string(
default = "esm",
values = ["esm", "cjs", "iife"],
doc = "Output module format.",
),
"production": attr.bool(
default = False,
doc = "If true, sets `NODE_ENV=production` and enables Bun production mode.",
),
"splitting": attr.bool(
default = False,
doc = "If true, enables code splitting.",
),
"root": attr.string(
doc = "Optional root directory for multiple entry points.",
),
"sourcemap": attr.string(
default = "none",
values = ["none", "linked", "inline", "external"],
doc = "Sourcemap emission mode.",
),
"banner": attr.string(
doc = "Optional bundle banner text.",
),
"footer": attr.string(
doc = "Optional bundle footer text.",
),
"public_path": attr.string(
doc = "Optional public path prefix for emitted imports.",
),
"packages": attr.string(
default = "bundle",
values = ["bundle", "external"],
doc = "Whether packages stay bundled or are treated as external.",
),
"external": attr.string_list(
doc = "Modules treated as externals (not bundled).",
),
"entry_naming": attr.string(
doc = "Optional entry naming template.",
),
"chunk_naming": attr.string(
doc = "Optional chunk naming template.",
),
"asset_naming": attr.string(
doc = "Optional asset naming template.",
),
"minify": attr.bool(
default = False,
doc = "If true, enables all Bun minification passes.",
),
"minify_syntax": attr.bool(
default = False,
doc = "If true, minifies syntax only.",
),
"minify_whitespace": attr.bool(
default = False,
doc = "If true, minifies whitespace only.",
),
"minify_identifiers": attr.bool(
default = False,
doc = "If true, minifies identifiers only.",
),
"keep_names": attr.bool(
default = False,
doc = "If true, preserves function and class names when minifying.",
),
"css_chunking": attr.bool(
default = False,
doc = "If true, Bun chunks CSS across multiple entry points.",
),
"conditions": attr.string_list(
doc = "Custom resolve conditions passed to Bun.",
),
"env": attr.string(
doc = "Inline environment variable behavior passed to `--env`.",
),
"define": attr.string_list(
doc = "Repeated `--define` values such as `process.env.NODE_ENV:\"production\"`.",
),
"drop": attr.string_list(
doc = "Repeated `--drop` values, for example `console`.",
),
"feature": attr.string_list(
doc = "Repeated `--feature` values for dead-code elimination.",
),
"loader": attr.string_list(
doc = "Repeated `--loader` values such as `.svg:file`.",
),
"jsx_factory": attr.string(
doc = "Optional JSX factory override.",
),
"jsx_fragment": attr.string(
doc = "Optional JSX fragment override.",
),
"jsx_import_source": attr.string(
doc = "Optional JSX import source override.",
),
"jsx_runtime": attr.string(
values = ["", "automatic", "classic"],
default = "",
doc = "Optional JSX runtime override.",
),
"jsx_side_effects": attr.bool(
default = False,
doc = "If true, treats JSX as having side effects.",
),
"react_fast_refresh": attr.bool(
default = False,
doc = "If true, enables Bun's React fast refresh transform.",
),
"emit_dce_annotations": attr.bool(
default = False,
doc = "If true, re-emits DCE annotations in the bundle.",
),
"no_bundle": attr.bool(
default = False,
doc = "If true, transpiles without bundling.",
),
"build_flags": attr.string_list(
doc = "Additional raw flags forwarded to `bun build`.",
),
}
bun_build = rule(
implementation = _bun_build_impl,
doc = """Builds one or more entry points with `bun build`.
The rule emits a directory artifact so Bun can materialize multi-file output
graphs such as HTML, CSS, assets, and split chunks. Optional metafile outputs
may be requested with `metafile` and `metafile_md`.
""",
attrs = dict(_COMMON_BUILD_ATTRS, **{
"entry_points": attr.label_list(
mandatory = True,
allow_files = True,
doc = "Entry files to build, including JS/TS or HTML entry points.",
),
"metafile": attr.bool(
default = False,
doc = "If true, emits Bun's JSON metafile alongside the output directory.",
),
"metafile_md": attr.bool(
default = False,
doc = "If true, emits Bun's markdown metafile alongside the output directory.",
),
}),
toolchains = ["//bun:toolchain_type"],
)
bun_compile = rule(
implementation = _bun_compile_impl,
doc = """Compiles a Bun program into a standalone executable with `bun build --compile`.""",
attrs = dict(_COMMON_BUILD_ATTRS, **{
"target": attr.string(
default = "bun",
values = ["browser", "node", "bun"],
doc = "Bun build target environment for the compiled executable.",
),
"entry_point": attr.label(
mandatory = True,
allow_single_file = True,
doc = "Entry file to compile into an executable.",
),
"bytecode": attr.bool(
default = False,
doc = "If true, enables Bun bytecode caching in the compiled executable.",
),
"compile_exec_argv": attr.string_list(
doc = "Repeated `--compile-exec-argv` values prepended to the executable's `execArgv`.",
),
"compile_executable": attr.label(
allow_single_file = True,
doc = "Optional Bun executable used for cross-compilation via `--compile-executable-path`.",
),
"compile_autoload_dotenv": attr.bool(
default = True,
doc = "Whether the compiled executable auto-loads `.env` files at runtime.",
),
"compile_autoload_bunfig": attr.bool(
default = True,
doc = "Whether the compiled executable auto-loads `bunfig.toml` at runtime.",
),
"compile_autoload_tsconfig": attr.bool(
default = False,
doc = "Whether the compiled executable auto-loads `tsconfig.json` at runtime.",
),
"compile_autoload_package_json": attr.bool(
default = False,
doc = "Whether the compiled executable auto-loads `package.json` at runtime.",
),
"windows_hide_console": attr.bool(
default = False,
doc = "When targeting Windows, hides the console window for GUI-style executables.",
),
"windows_icon": attr.string(
doc = "Optional Windows icon path passed directly to Bun.",
),
"windows_title": attr.string(
doc = "Optional Windows executable title.",
),
"windows_publisher": attr.string(
doc = "Optional Windows publisher metadata.",
),
"windows_version": attr.string(
doc = "Optional Windows version metadata.",
),
"windows_description": attr.string(
doc = "Optional Windows description metadata.",
),
"windows_copyright": attr.string(
doc = "Optional Windows copyright metadata.",
),
}),
executable = True,
toolchains = ["//bun:toolchain_type"],
)

View File

@@ -1,101 +1,91 @@
"""Rule for running JS/TS scripts with Bun in watch mode for development."""
load("//internal:bun_command.bzl", "append_shell_flag", "append_shell_flag_files", "append_shell_flag_values", "append_shell_install_mode", "append_shell_raw_flags", "render_shell_array", "shell_quote")
load("//internal:workspace.bzl", "create_bun_workspace_info", "render_workspace_setup", "workspace_runfiles")
def _bun_dev_impl(ctx):
toolchain = ctx.toolchains["//bun:toolchain_type"]
bun_bin = toolchain.bun.bun_bin
entry_point = ctx.file.entry_point
workspace_info = create_bun_workspace_info(
ctx,
extra_files = ctx.files.data + ctx.files.restart_on + ctx.files.preload + ctx.files.env_files + [bun_bin],
primary_file = entry_point,
)
restart_watch_paths = "\n".join([path.short_path for path in ctx.files.restart_on])
launcher_lines = [render_shell_array("bun_args", ["--bun", "run"])]
append_shell_install_mode(launcher_lines, "bun_args", ctx.attr.install_mode)
append_shell_flag_files(launcher_lines, "bun_args", "--preload", ctx.files.preload)
append_shell_flag_files(launcher_lines, "bun_args", "--env-file", ctx.files.env_files)
append_shell_flag(launcher_lines, "bun_args", "--no-env-file", ctx.attr.no_env_file)
append_shell_flag(launcher_lines, "bun_args", "--smol", ctx.attr.smol)
append_shell_flag_values(launcher_lines, "bun_args", "--conditions", ctx.attr.conditions)
append_shell_flag(launcher_lines, "bun_args", "--no-clear-screen", ctx.attr.no_clear_screen)
append_shell_raw_flags(launcher_lines, "bun_args", ctx.attr.run_flags)
launcher_lines.append('bun_args+=("${primary_source}")')
for arg in ctx.attr.args:
launcher_lines.append("bun_args+=(%s)" % shell_quote(arg))
launcher = ctx.actions.declare_file(ctx.label.name)
ctx.actions.write(
output = launcher,
is_executable = True,
content = """#!/usr/bin/env bash
set -euo pipefail
runfiles_dir="${{RUNFILES_DIR:-$0.runfiles}}"
workspace_root="${{runfiles_dir}}/_main"
bun_bin="${{runfiles_dir}}/_main/{bun_short_path}"
entry_point="${{runfiles_dir}}/_main/{entry_short_path}"
resolve_entrypoint_workdir() {{
local dir
dir="$(dirname "${{entry_point}}")"
while [[ "${{dir}}" == "${{workspace_root}}"* ]]; do
if [[ -f "${{dir}}/.env" || -f "${{dir}}/package.json" ]]; then
echo "${{dir}}"
return 0
fi
if [[ "${{dir}}" == "${{workspace_root}}" ]]; then
break
fi
dir="$(dirname "${{dir}}")"
done
echo "$(dirname "${{entry_point}}")"
}}
working_dir="{working_dir}"
if [[ "${{working_dir}}" == "entry_point" ]]; then
cd "$(resolve_entrypoint_workdir)"
command = """
__BUN_ARGS__
watch_mode="__WATCH_MODE__"
if [[ "${watch_mode}" == "hot" ]]; then
bun_args+=("--hot")
else
cd "${{workspace_root}}"
bun_args+=("--watch")
fi
watch_mode="{watch_mode}"
if [[ "${{watch_mode}}" == "hot" ]]; then
dev_flag="--hot"
else
dev_flag="--watch"
fi
run_dev() {{
exec "${{bun_bin}}" --bun "${{dev_flag}}" run "${{entry_point}}" "$@"
}}
if [[ {restart_count} -eq 0 ]]; then
run_dev "$@"
if [[ __RESTART_COUNT__ -eq 0 ]]; then
trap cleanup_runtime_workspace EXIT
cd "${runtime_exec_dir}"
exec "${bun_bin}" "${bun_args[@]}" "$@"
fi
readarray -t restart_paths <<'EOF_RESTART_PATHS'
{restart_watch_paths}
__RESTART_PATHS__
EOF_RESTART_PATHS
file_mtime() {{
local p="$1"
if stat -f '%m' "${{p}}" >/dev/null 2>&1; then
stat -f '%m' "${{p}}"
file_mtime() {
local path="$1"
if stat -f '%m' "${path}" >/dev/null 2>&1; then
stat -f '%m' "${path}"
return 0
fi
stat -c '%Y' "${{p}}"
}}
stat -c '%Y' "${path}"
}
declare -A mtimes
for rel in "${{restart_paths[@]}}"; do
path="${{runfiles_dir}}/_main/${{rel}}"
if [[ -e "${{path}}" ]]; then
mtimes["${{rel}}"]="$(file_mtime "${{path}}")"
for rel in "${restart_paths[@]}"; do
path="${runfiles_dir}/_main/${rel}"
if [[ -e "${path}" ]]; then
mtimes["${rel}"]="$(file_mtime "${path}")"
else
mtimes["${{rel}}"]="missing"
mtimes["${rel}"]="missing"
fi
done
child_pid=""
restart_child() {{
if [[ -n "${{child_pid}}" ]] && kill -0 "${{child_pid}}" 2>/dev/null; then
kill "${{child_pid}}"
wait "${{child_pid}}" || true
restart_child() {
if [[ -n "${child_pid}" ]] && kill -0 "${child_pid}" 2>/dev/null; then
kill "${child_pid}"
wait "${child_pid}" || true
fi
"${{bun_bin}}" --bun "${{dev_flag}}" run "${{entry_point}}" "$@" &
child_pid=$!
}}
cleanup() {{
if [[ -n "${{child_pid}}" ]] && kill -0 "${{child_pid}}" 2>/dev/null; then
kill "${{child_pid}}"
wait "${{child_pid}}" || true
(
cd "${runtime_exec_dir}"
exec "${bun_bin}" "${bun_args[@]}" "$@"
) &
child_pid=$!
}
cleanup() {
if [[ -n "${child_pid}" ]] && kill -0 "${child_pid}" 2>/dev/null; then
kill "${child_pid}"
wait "${child_pid}" || true
fi
}}
cleanup_runtime_workspace
}
trap cleanup EXIT INT TERM
@@ -104,45 +94,50 @@ restart_child "$@"
while true; do
sleep 1
changed=0
for rel in "${{restart_paths[@]}}"; do
path="${{runfiles_dir}}/_main/${{rel}}"
if [[ -e "${{path}}" ]]; then
current="$(file_mtime "${{path}}")"
for rel in "${restart_paths[@]}"; do
path="${runfiles_dir}/_main/${rel}"
if [[ -e "${path}" ]]; then
current="$(file_mtime "${path}")"
else
current="missing"
fi
if [[ "${{current}}" != "${{mtimes[${{rel}}]}}" ]]; then
mtimes["${{rel}}"]="${{current}}"
if [[ "${current}" != "${mtimes[${rel}]}" ]]; then
mtimes["${rel}"]="${current}"
changed=1
fi
done
if [[ "${{changed}}" -eq 1 ]]; then
if [[ "${changed}" -eq 1 ]]; then
restart_child "$@"
fi
done
""".format(
bun_short_path = bun_bin.short_path,
entry_short_path = entry_point.short_path,
watch_mode = ctx.attr.watch_mode,
working_dir = ctx.attr.working_dir,
restart_count = len(ctx.files.restart_on),
restart_watch_paths = restart_watch_paths,
),
""".replace("__WATCH_MODE__", ctx.attr.watch_mode).replace(
"__RESTART_COUNT__",
str(len(ctx.files.restart_on)),
).replace(
"__RESTART_PATHS__",
restart_watch_paths,
).replace(
"__BUN_ARGS__",
"\n".join(launcher_lines),
)
transitive_files = []
if ctx.attr.node_modules:
transitive_files.append(ctx.attr.node_modules[DefaultInfo].files)
runfiles = ctx.runfiles(
files = [bun_bin, entry_point] + ctx.files.data + ctx.files.restart_on,
transitive_files = depset(transitive = transitive_files),
launcher = ctx.actions.declare_file(ctx.label.name)
ctx.actions.write(
output = launcher,
is_executable = True,
content = render_workspace_setup(
bun_short_path = bun_bin.short_path,
install_metadata_short_path = workspace_info.install_metadata_file.short_path if workspace_info.install_metadata_file else "",
primary_source_short_path = entry_point.short_path,
working_dir_mode = ctx.attr.working_dir,
) + command,
)
return [
workspace_info,
DefaultInfo(
executable = launcher,
runfiles = runfiles,
runfiles = workspace_runfiles(ctx, workspace_info, direct_files = [launcher]),
),
]
@@ -175,6 +170,37 @@ watch/HMR plus optional full restarts on selected file changes.
allow_files = True,
doc = "Additional runtime files required by the dev process.",
),
"preload": attr.label_list(
allow_files = True,
doc = "Modules to preload with `--preload` before running the entry point.",
),
"env_files": attr.label_list(
allow_files = True,
doc = "Additional environment files loaded with `--env-file`.",
),
"no_env_file": attr.bool(
default = False,
doc = "If true, disables Bun's automatic `.env` loading.",
),
"smol": attr.bool(
default = False,
doc = "If true, enables Bun's lower-memory runtime mode.",
),
"conditions": attr.string_list(
doc = "Custom package resolve conditions passed to Bun.",
),
"install_mode": attr.string(
default = "disable",
values = ["disable", "auto", "fallback", "force"],
doc = "Whether Bun may auto-install missing packages in dev mode.",
),
"no_clear_screen": attr.bool(
default = False,
doc = "If true, disables terminal clearing on Bun reloads.",
),
"run_flags": attr.string_list(
doc = "Additional raw flags forwarded to `bun run` before the entry point.",
),
"working_dir": attr.string(
default = "workspace",
values = ["workspace", "entry_point"],

View File

@@ -1,5 +1,37 @@
"""Repository-rule based bun_install implementation."""
_DEFAULT_INSTALL_INPUTS = [
".npmrc",
"bunfig.json",
"bunfig.toml",
]
_MANIFEST_DEP_FIELDS = [
"dependencies",
"devDependencies",
"optionalDependencies",
"peerDependencies",
]
def _normalize_path(path):
normalized = path.replace("\\", "/")
if normalized.endswith("/") and normalized != "/":
normalized = normalized[:-1]
return normalized
def _relative_to_root(root, child):
normalized_root = _normalize_path(root)
normalized_child = _normalize_path(child)
if normalized_child == normalized_root:
return ""
prefix = normalized_root + "/"
if not normalized_child.startswith(prefix):
fail("bun_install: expected install input {} to be under {}".format(child, root))
return normalized_child[len(prefix):]
def _segment_matches(name, pattern):
if pattern == "*":
return True
@@ -71,10 +103,82 @@ def _workspace_patterns(repository_ctx, package_json):
return patterns
def _validate_catalog_shape(field, value):
if value == None:
return
if type(value) != type({}):
fail("bun_install: `{}` must be an object".format(field))
if field not in ["catalogs", "workspaces.catalogs"]:
return
for name, catalog in value.items():
if type(name) != type(""):
fail("bun_install: `catalogs` keys must be strings, got {}".format(type(name)))
if type(catalog) != type({}):
fail("bun_install: `catalogs.{}` must be an object".format(name))
def _copy_json_value(value):
return json.decode(json.encode(value))
def _package_target_name(package_name):
sanitized = package_name
sanitized = sanitized.replace("@", "at_")
sanitized = sanitized.replace("/", "_")
sanitized = sanitized.replace("-", "_")
sanitized = sanitized.replace(".", "_")
sanitized = sanitized.replace("__", "_").replace("__", "_").replace("__", "_")
sanitized = sanitized.strip("_")
if not sanitized:
sanitized = "package"
return "npm__" + sanitized
def _manifest_dependency_names(manifest):
names = {}
for field in _MANIFEST_DEP_FIELDS:
dependencies = manifest.get(field)
if dependencies == None:
continue
if type(dependencies) != type({}):
fail("bun_install: `{}` must be an object when present".format(field))
for name in dependencies.keys():
names[name] = True
return names
def _normalized_root_manifest(repository_ctx, package_json):
manifest = json.decode(repository_ctx.read(package_json))
workspaces = manifest.get("workspaces")
for field in ["catalog", "catalogs"]:
manifest_value = manifest.get(field)
_validate_catalog_shape(field, manifest_value)
if type(workspaces) != type({}):
continue
workspace_value = workspaces.get(field)
_validate_catalog_shape("workspaces.{}".format(field), workspace_value)
if workspace_value == None:
continue
if manifest_value == None:
manifest[field] = _copy_json_value(workspace_value)
continue
if manifest_value != workspace_value:
fail(
"bun_install: `{}` conflicts with `workspaces.{}`; use one source of truth or keep both values identical".format(field, field),
)
return json.encode(manifest)
def _materialize_workspace_packages(repository_ctx, package_json):
package_root = package_json.dirname
package_root_str = str(package_root)
written = {}
workspace_packages = {}
for pattern in _workspace_patterns(repository_ctx, package_json):
segments = pattern.split("/")
@@ -87,7 +191,7 @@ def _materialize_workspace_packages(repository_ctx, package_json):
if workspace_dir_str == package_root_str:
continue
relative_dir = workspace_dir_str[len(package_root_str) + 1:]
relative_dir = _relative_to_root(package_root_str, workspace_dir_str)
if relative_dir in written:
continue
@@ -96,6 +200,45 @@ def _materialize_workspace_packages(repository_ctx, package_json):
repository_ctx.read(workspace_package_json),
)
written[relative_dir] = True
manifest = json.decode(repository_ctx.read(workspace_package_json))
package_name = manifest.get("name")
workspace_packages[relative_dir] = package_name if type(package_name) == type("") else ""
package_dirs = sorted(workspace_packages.keys())
return struct(
package_dirs = package_dirs,
package_names = [workspace_packages[package_dir] for package_dir in package_dirs if workspace_packages[package_dir]],
)
def _materialize_install_inputs(repository_ctx, package_json):
package_root = package_json.dirname
package_root_str = str(package_root)
written = {}
for relative_path in _DEFAULT_INSTALL_INPUTS:
source_path = repository_ctx.path(str(package_root) + "/" + relative_path)
if source_path.exists and not source_path.is_dir:
repository_ctx.file(relative_path, repository_ctx.read(source_path))
written[relative_path] = True
for install_input in repository_ctx.attr.install_inputs:
source_path = repository_ctx.path(install_input)
if not source_path.exists:
fail("bun_install: install input not found: {}".format(install_input))
if source_path.is_dir:
fail("bun_install: install_inputs must be files under the package root: {}".format(install_input))
relative_path = _relative_to_root(package_root_str, str(source_path))
if not relative_path:
fail("bun_install: install input must be a file under the package root: {}".format(install_input))
if relative_path in written:
continue
repository_ctx.file(relative_path, repository_ctx.read(source_path))
written[relative_path] = True
def _select_bun_binary(repository_ctx):
os_name = repository_ctx.os.name.lower()
@@ -116,6 +259,68 @@ def _select_bun_binary(repository_ctx):
fail("Unsupported host platform: os={}, arch={}".format(repository_ctx.os.name, repository_ctx.os.arch))
def _render_package_targets_file(package_names):
lines = ["NPM_PACKAGE_TARGETS = {"]
for package_name in package_names:
lines.append(' "{}": "{}",'.format(package_name, _package_target_name(package_name)))
lines.extend([
"}",
"",
])
return "\n".join(lines)
def _render_repo_defs_bzl(repo_name):
return """load(":packages.bzl", "NPM_PACKAGE_TARGETS")
def package_target_name(package_name):
return NPM_PACKAGE_TARGETS.get(package_name)
def npm_link_all_packages(name = "node_modules", imported_links = []):
if not native.existing_rule(name):
native.alias(
name = name,
actual = "@{repo_name}//:node_modules",
)
requested = {{}}
for package_name in imported_links:
requested[package_name] = True
for package_name, target_name in NPM_PACKAGE_TARGETS.items():
if imported_links and package_name not in requested:
continue
if native.existing_rule(target_name):
continue
native.alias(
name = target_name,
actual = "@{repo_name}//:%s" % target_name,
)
""".format(repo_name = repo_name)
def _render_repo_build(package_names):
lines = [
'exports_files(["defs.bzl", "packages.bzl"])',
"",
"filegroup(",
' name = "node_modules",',
' srcs = glob(["**/node_modules/**"], allow_empty = False),',
' visibility = ["//visibility:public"],',
")",
"",
]
for package_name in package_names:
lines.extend([
"filegroup(",
' name = "{}",'.format(_package_target_name(package_name)),
' srcs = glob(["node_modules/{}/**"], allow_empty = True),'.format(package_name),
' visibility = ["//visibility:public"],',
")",
"",
])
return "\n".join(lines)
def _bun_install_repository_impl(repository_ctx):
package_json = repository_ctx.path(repository_ctx.attr.package_json)
bun_lockfile = repository_ctx.path(repository_ctx.attr.bun_lockfile)
@@ -128,20 +333,41 @@ def _bun_install_repository_impl(repository_ctx):
bun_bin = _select_bun_binary(repository_ctx)
lockfile_name = bun_lockfile.basename
root_manifest = json.decode(repository_ctx.read(package_json))
if lockfile_name not in ["bun.lock", "bun.lockb"]:
lockfile_name = "bun.lock"
repository_ctx.file("package.json", repository_ctx.read(package_json))
repository_ctx.file("package.json", _normalized_root_manifest(repository_ctx, package_json))
repository_ctx.symlink(bun_lockfile, lockfile_name)
_materialize_workspace_packages(repository_ctx, package_json)
_materialize_install_inputs(repository_ctx, package_json)
workspace_packages = _materialize_workspace_packages(repository_ctx, package_json)
install_args = [str(bun_bin), "--bun", "install", "--frozen-lockfile", "--no-progress"]
if repository_ctx.attr.production:
install_args.append("--production")
for omit in repository_ctx.attr.omit:
install_args.extend(["--omit", omit])
if repository_ctx.attr.linker:
install_args.extend(["--linker", repository_ctx.attr.linker])
if repository_ctx.attr.backend:
install_args.extend(["--backend", repository_ctx.attr.backend])
if repository_ctx.attr.ignore_scripts:
install_args.append("--ignore-scripts")
install_args.extend(repository_ctx.attr.install_flags)
if repository_ctx.attr.isolated_home:
result = repository_ctx.execute(
[str(bun_bin), "--bun", "install", "--frozen-lockfile", "--no-progress"],
install_args,
timeout = 600,
quiet = False,
environment = {"HOME": str(repository_ctx.path("."))},
)
else:
result = repository_ctx.execute(
install_args,
timeout = 600,
quiet = False,
)
if result.return_code:
fail("""bun_install failed running `bun --bun install --frozen-lockfile`.
@@ -152,20 +378,40 @@ stderr:
""".format(result.stdout, result.stderr))
repository_ctx.file(
"BUILD.bazel",
"""filegroup(
name = "node_modules",
srcs = glob(["node_modules/**"], allow_empty = False),
visibility = ["//visibility:public"],
)
""",
"node_modules/.rules_bun/install.json",
json.encode({
"bun_lockfile": lockfile_name,
"package_json": "package.json",
"workspace_package_dirs": workspace_packages.package_dirs,
}) + "\n",
)
package_names = {}
for package_name in _manifest_dependency_names(root_manifest).keys():
package_names[package_name] = True
for package_name in workspace_packages.package_names:
package_names[package_name] = True
sorted_package_names = sorted(package_names.keys())
visible_repo_name = repository_ctx.attr.visible_repo_name or repository_ctx.name
repository_ctx.file("packages.bzl", _render_package_targets_file(sorted_package_names))
repository_ctx.file("defs.bzl", _render_repo_defs_bzl(visible_repo_name))
repository_ctx.file("BUILD.bazel", _render_repo_build(sorted_package_names))
bun_install_repository = repository_rule(
implementation = _bun_install_repository_impl,
attrs = {
"package_json": attr.label(mandatory = True, allow_single_file = True),
"bun_lockfile": attr.label(mandatory = True, allow_single_file = True),
"install_inputs": attr.label_list(allow_files = True),
"isolated_home": attr.bool(default = True),
"production": attr.bool(default = False),
"omit": attr.string_list(),
"linker": attr.string(),
"backend": attr.string(),
"ignore_scripts": attr.bool(default = False),
"install_flags": attr.string_list(),
"visible_repo_name": attr.string(),
"bun_linux_x64": attr.label(default = "@bun_linux_x64//:bun-linux-x64/bun", allow_single_file = True),
"bun_linux_aarch64": attr.label(default = "@bun_linux_aarch64//:bun-linux-aarch64/bun", allow_single_file = True),
"bun_darwin_x64": attr.label(default = "@bun_darwin_x64//:bun-darwin-x64/bun", allow_single_file = True),
@@ -174,13 +420,34 @@ bun_install_repository = repository_rule(
},
)
def bun_install(name, package_json, bun_lockfile):
def bun_install(
name,
package_json,
bun_lockfile,
install_inputs = [],
isolated_home = True,
production = False,
omit = [],
linker = "",
backend = "",
ignore_scripts = False,
install_flags = []):
"""Create an external repository containing installed node_modules.
Args:
name: Repository name to create.
package_json: Label to a package.json file.
bun_lockfile: Label to a bun.lockb file.
install_inputs: Optional additional files under the package root to copy
into the install context, such as patch files or auth/config files.
isolated_home: Whether to run Bun with HOME set to the generated
repository root for a more isolated install context.
production: Whether to omit devDependencies during install.
omit: Optional Bun dependency groups to omit, such as `dev` or `peer`.
linker: Optional Bun linker strategy, such as `isolated` or `hoisted`.
backend: Optional Bun install backend, such as `hardlink` or `copyfile`.
ignore_scripts: Whether to skip lifecycle scripts in the project manifest.
install_flags: Additional raw flags forwarded to `bun install`.
Usage (WORKSPACE):
bun_install(
@@ -194,4 +461,13 @@ def bun_install(name, package_json, bun_lockfile):
name = name,
package_json = package_json,
bun_lockfile = bun_lockfile,
install_inputs = install_inputs,
isolated_home = isolated_home,
production = production,
omit = omit,
linker = linker,
backend = backend,
ignore_scripts = ignore_scripts,
install_flags = install_flags,
visible_repo_name = name,
)

View File

@@ -1,66 +1,68 @@
"""Rule for running package.json scripts with Bun."""
def _shell_quote(value):
return "'" + value.replace("'", "'\"'\"'") + "'"
load("//internal:bun_command.bzl", "append_shell_flag", "append_shell_flag_files", "append_shell_flag_value", "append_shell_flag_values", "append_shell_install_mode", "append_shell_raw_flags", "render_shell_array", "shell_quote")
load("//internal:workspace.bzl", "create_bun_workspace_info", "render_workspace_setup", "workspace_runfiles")
def _bun_script_impl(ctx):
toolchain = ctx.toolchains["//bun:toolchain_type"]
bun_bin = toolchain.bun.bun_bin
package_json = ctx.file.package_json
workspace_info = create_bun_workspace_info(
ctx,
extra_files = ctx.files.data + ctx.files.preload + ctx.files.env_files + [bun_bin],
package_dir_hint = package_json.dirname or ".",
package_json = package_json,
primary_file = package_json,
)
launcher_lines = [render_shell_array("bun_args", ["--bun", "run"])]
append_shell_install_mode(launcher_lines, "bun_args", ctx.attr.install_mode)
append_shell_flag_files(launcher_lines, "bun_args", "--preload", ctx.files.preload)
append_shell_flag_files(launcher_lines, "bun_args", "--env-file", ctx.files.env_files)
append_shell_flag(launcher_lines, "bun_args", "--no-env-file", ctx.attr.no_env_file)
append_shell_flag(launcher_lines, "bun_args", "--smol", ctx.attr.smol)
append_shell_flag_values(launcher_lines, "bun_args", "--conditions", ctx.attr.conditions)
append_shell_flag(launcher_lines, "bun_args", "--workspaces", ctx.attr.workspaces)
append_shell_flag_values(launcher_lines, "bun_args", "--filter", ctx.attr.filters)
if ctx.attr.execution_mode == "parallel":
append_shell_flag(launcher_lines, "bun_args", "--parallel", True)
elif ctx.attr.execution_mode == "sequential":
append_shell_flag(launcher_lines, "bun_args", "--sequential", True)
append_shell_flag(launcher_lines, "bun_args", "--no-exit-on-error", ctx.attr.no_exit_on_error)
append_shell_flag_value(launcher_lines, "bun_args", "--shell", ctx.attr.shell)
append_shell_flag(launcher_lines, "bun_args", "--silent", ctx.attr.silent)
append_shell_raw_flags(launcher_lines, "bun_args", ctx.attr.run_flags)
launcher_lines.append('bun_args+=(%s)' % shell_quote(ctx.attr.script))
for arg in ctx.attr.args:
launcher_lines.append("bun_args+=(%s)" % shell_quote(arg))
command = """
trap cleanup_runtime_workspace EXIT
cd "${runtime_exec_dir}"
__BUN_ARGS__
exec "${bun_bin}" "${bun_args[@]}" "$@"
""".replace("__BUN_ARGS__", "\n".join(launcher_lines))
launcher = ctx.actions.declare_file(ctx.label.name)
ctx.actions.write(
output = launcher,
is_executable = True,
content = """#!/usr/bin/env bash
set -euo pipefail
runfiles_dir="${{RUNFILES_DIR:-$0.runfiles}}"
workspace_root="${{runfiles_dir}}/_main"
bun_bin="${{runfiles_dir}}/_main/{bun_short_path}"
package_json="${{runfiles_dir}}/_main/{package_json_short_path}"
package_dir="$(dirname "${{package_json}}")"
node_modules_bin_dirs=()
while IFS= read -r node_modules_bin; do
node_modules_bin_dirs+=("${{node_modules_bin}}")
done < <(find "${{runfiles_dir}}" -type d -path '*/node_modules/.bin' 2>/dev/null | sort)
if [[ ${{#node_modules_bin_dirs[@]}} -gt 0 ]]; then
export PATH="$(IFS=:; echo "${{node_modules_bin_dirs[*]}}"):${{PATH}}"
fi
working_dir="{working_dir}"
if [[ "${{working_dir}}" == "package" ]]; then
cd "${{package_dir}}"
else
cd "${{workspace_root}}"
fi
exec "${{bun_bin}}" --bun run {script} "$@"
""".format(
content = render_workspace_setup(
bun_short_path = bun_bin.short_path,
package_dir_hint = package_json.dirname or ".",
package_json_short_path = package_json.short_path,
working_dir = ctx.attr.working_dir,
script = _shell_quote(ctx.attr.script),
),
)
transitive_files = []
if ctx.attr.node_modules:
transitive_files.append(ctx.attr.node_modules[DefaultInfo].files)
runfiles = ctx.runfiles(
files = [bun_bin, package_json] + ctx.files.data,
transitive_files = depset(transitive = transitive_files),
primary_source_short_path = package_json.short_path,
install_metadata_short_path = workspace_info.install_metadata_file.short_path if workspace_info.install_metadata_file else "",
working_dir_mode = ctx.attr.working_dir,
) + command,
)
return [
workspace_info,
DefaultInfo(
executable = launcher,
runfiles = runfiles,
runfiles = workspace_runfiles(ctx, workspace_info, direct_files = [launcher]),
),
]
@@ -92,6 +94,58 @@ declared in `package.json` and expect to run from the package directory with
allow_files = True,
doc = "Additional runtime files required by the script.",
),
"preload": attr.label_list(
allow_files = True,
doc = "Modules to preload with `--preload` before running the script.",
),
"env_files": attr.label_list(
allow_files = True,
doc = "Additional environment files loaded with `--env-file`.",
),
"no_env_file": attr.bool(
default = False,
doc = "If true, disables Bun's automatic `.env` loading.",
),
"smol": attr.bool(
default = False,
doc = "If true, enables Bun's lower-memory runtime mode.",
),
"conditions": attr.string_list(
doc = "Custom package resolve conditions passed to Bun.",
),
"install_mode": attr.string(
default = "disable",
values = ["disable", "auto", "fallback", "force"],
doc = "Whether Bun may auto-install missing packages while running the script.",
),
"filters": attr.string_list(
doc = "Workspace package filters passed via repeated `--filter` flags.",
),
"workspaces": attr.bool(
default = False,
doc = "If true, runs the script in all workspace packages.",
),
"execution_mode": attr.string(
default = "single",
values = ["single", "parallel", "sequential"],
doc = "How Bun should execute matching workspace scripts.",
),
"no_exit_on_error": attr.bool(
default = False,
doc = "If true, Bun keeps running other workspace scripts when one fails.",
),
"shell": attr.string(
default = "",
values = ["", "bun", "system"],
doc = "Optional shell implementation for package scripts.",
),
"silent": attr.bool(
default = False,
doc = "If true, suppresses Bun's command echo for package scripts.",
),
"run_flags": attr.string_list(
doc = "Additional raw flags forwarded to `bun run` before the script name.",
),
"working_dir": attr.string(
default = "package",
values = ["workspace", "package"],

View File

@@ -1,62 +1,101 @@
"""Rule for running test suites with Bun."""
load("//internal:js_library.bzl", "BunSourcesInfo")
def _shell_quote(value):
return "'" + value.replace("'", "'\"'\"'") + "'"
load("//internal:bun_command.bzl", "append_shell_flag", "append_shell_flag_files", "append_shell_flag_value", "append_shell_flag_values", "append_shell_install_mode", "append_shell_raw_flags", "render_shell_array", "shell_quote")
load("//internal:js_library.bzl", "collect_js_runfiles")
load("//internal:workspace.bzl", "create_bun_workspace_info", "render_workspace_setup", "workspace_runfiles")
def _bun_test_impl(ctx):
toolchain = ctx.toolchains["//bun:toolchain_type"]
bun_bin = toolchain.bun.bun_bin
primary_file = ctx.files.srcs[0]
dep_runfiles = [collect_js_runfiles(dep) for dep in ctx.attr.deps]
workspace_info = create_bun_workspace_info(
ctx,
extra_files = ctx.files.srcs + ctx.files.data + ctx.files.preload + ctx.files.env_files + [bun_bin],
primary_file = primary_file,
)
launcher_lines = [render_shell_array("bun_args", ["--bun", "test"])]
append_shell_install_mode(launcher_lines, "bun_args", ctx.attr.install_mode)
append_shell_flag_files(launcher_lines, "bun_args", "--preload", ctx.files.preload)
append_shell_flag_files(launcher_lines, "bun_args", "--env-file", ctx.files.env_files)
append_shell_flag(launcher_lines, "bun_args", "--no-env-file", ctx.attr.no_env_file)
append_shell_flag(launcher_lines, "bun_args", "--smol", ctx.attr.smol)
append_shell_flag_value(launcher_lines, "bun_args", "--timeout", str(ctx.attr.timeout_ms) if ctx.attr.timeout_ms > 0 else None)
append_shell_flag(launcher_lines, "bun_args", "--update-snapshots", ctx.attr.update_snapshots)
append_shell_flag_value(launcher_lines, "bun_args", "--rerun-each", str(ctx.attr.rerun_each) if ctx.attr.rerun_each > 0 else None)
append_shell_flag_value(launcher_lines, "bun_args", "--retry", str(ctx.attr.retry) if ctx.attr.retry > 0 else None)
append_shell_flag(launcher_lines, "bun_args", "--todo", ctx.attr.todo)
append_shell_flag(launcher_lines, "bun_args", "--only", ctx.attr.only)
append_shell_flag(launcher_lines, "bun_args", "--pass-with-no-tests", ctx.attr.pass_with_no_tests)
append_shell_flag(launcher_lines, "bun_args", "--concurrent", ctx.attr.concurrent)
append_shell_flag(launcher_lines, "bun_args", "--randomize", ctx.attr.randomize)
append_shell_flag_value(launcher_lines, "bun_args", "--seed", str(ctx.attr.seed) if ctx.attr.seed > 0 else None)
append_shell_flag_value(launcher_lines, "bun_args", "--bail", str(ctx.attr.bail) if ctx.attr.bail > 0 else None)
append_shell_flag_value(launcher_lines, "bun_args", "--max-concurrency", str(ctx.attr.max_concurrency) if ctx.attr.max_concurrency > 0 else None)
append_shell_raw_flags(launcher_lines, "bun_args", ctx.attr.test_flags)
launcher_lines.append('coverage_requested="0"')
launcher_lines.append('coverage_dir=""')
launcher_lines.append('if [[ "${COVERAGE_DIR:-}" != "" ]]; then')
launcher_lines.append(' coverage_requested="1"')
launcher_lines.append(' coverage_dir="${COVERAGE_DIR}"')
launcher_lines.append('elif [[ "%s" == "1" ]]; then' % ("1" if ctx.attr.coverage else "0"))
launcher_lines.append(' coverage_requested="1"')
launcher_lines.append(' coverage_dir="${TEST_UNDECLARED_OUTPUTS_DIR:-${runtime_workspace}/coverage}"')
launcher_lines.append('fi')
launcher_lines.append('if [[ "${coverage_requested}" == "1" ]]; then')
launcher_lines.append(' bun_args+=("--coverage")')
launcher_lines.append(' bun_args+=("--coverage-dir" "${coverage_dir}")')
if ctx.attr.coverage_reporters:
for reporter in ctx.attr.coverage_reporters:
launcher_lines.append(' bun_args+=("--coverage-reporter" %s)' % shell_quote(reporter))
else:
launcher_lines.append(' if [[ "${COVERAGE_DIR:-}" != "" ]]; then')
launcher_lines.append(' bun_args+=("--coverage-reporter" "lcov")')
launcher_lines.append(' fi')
launcher_lines.append('fi')
launcher_lines.append('if [[ -n "${TESTBRIDGE_TEST_ONLY:-}" ]]; then')
launcher_lines.append(' bun_args+=("--test-name-pattern" "${TESTBRIDGE_TEST_ONLY}")')
launcher_lines.append('fi')
if ctx.attr.reporter == "junit":
launcher_lines.append('reporter_out="${XML_OUTPUT_FILE:-${runtime_workspace}/junit.xml}"')
launcher_lines.append('bun_args+=("--reporter" "junit" "--reporter-outfile" "${reporter_out}")')
elif ctx.attr.reporter == "dots":
launcher_lines.append('bun_args+=("--reporter" "dots")')
for src in ctx.files.srcs:
launcher_lines.append("bun_args+=(%s)" % shell_quote(src.short_path))
for arg in ctx.attr.args:
launcher_lines.append("bun_args+=(%s)" % shell_quote(arg))
command = """
trap cleanup_runtime_workspace EXIT
cd "${runtime_workspace}"
__BUN_ARGS__
exec "${bun_bin}" "${bun_args[@]}" "$@"
""".replace("__BUN_ARGS__", "\n".join(launcher_lines))
src_args = " ".join([_shell_quote(src.short_path) for src in ctx.files.srcs])
launcher = ctx.actions.declare_file(ctx.label.name)
ctx.actions.write(
output = launcher,
is_executable = True,
content = """#!/usr/bin/env bash
set -euo pipefail
runfiles_dir="${{RUNFILES_DIR:-$0.runfiles}}"
bun_bin="${{runfiles_dir}}/_main/{bun_short_path}"
cd "${{runfiles_dir}}/_main"
if [[ -n "${{TESTBRIDGE_TEST_ONLY:-}}" && -n "${{COVERAGE_DIR:-}}" ]]; then
exec "${{bun_bin}}" --bun test {src_args} --test-name-pattern "${{TESTBRIDGE_TEST_ONLY}}" --coverage "$@"
fi
if [[ -n "${{TESTBRIDGE_TEST_ONLY:-}}" ]]; then
exec "${{bun_bin}}" --bun test {src_args} --test-name-pattern "${{TESTBRIDGE_TEST_ONLY}}" "$@"
fi
if [[ -n "${{COVERAGE_DIR:-}}" ]]; then
exec "${{bun_bin}}" --bun test {src_args} --coverage "$@"
fi
exec "${{bun_bin}}" --bun test {src_args} "$@"
""".format(
content = render_workspace_setup(
bun_short_path = bun_bin.short_path,
src_args = src_args,
),
install_metadata_short_path = workspace_info.install_metadata_file.short_path if workspace_info.install_metadata_file else "",
primary_source_short_path = primary_file.short_path,
working_dir_mode = "workspace",
) + command,
)
transitive_files = []
if ctx.attr.node_modules:
transitive_files.append(ctx.attr.node_modules[DefaultInfo].files)
for dep in ctx.attr.deps:
if BunSourcesInfo in dep:
transitive_files.append(dep[BunSourcesInfo].transitive_sources)
else:
transitive_files.append(dep[DefaultInfo].files)
runfiles = ctx.runfiles(
files = [bun_bin] + ctx.files.srcs + ctx.files.data,
transitive_files = depset(transitive = transitive_files),
)
return [
workspace_info,
DefaultInfo(
executable = launcher,
runfiles = runfiles,
runfiles = workspace_runfiles(
ctx,
workspace_info,
direct_files = [launcher],
transitive_files = dep_runfiles,
),
),
]
@@ -83,6 +122,90 @@ Supports Bazel test filtering (`--test_filter`) and coverage integration.
allow_files = True,
doc = "Additional runtime files needed by tests.",
),
"preload": attr.label_list(
allow_files = True,
doc = "Modules to preload with `--preload` before running tests.",
),
"env_files": attr.label_list(
allow_files = True,
doc = "Additional environment files loaded with `--env-file`.",
),
"no_env_file": attr.bool(
default = False,
doc = "If true, disables Bun's automatic `.env` loading.",
),
"smol": attr.bool(
default = False,
doc = "If true, enables Bun's lower-memory runtime mode.",
),
"install_mode": attr.string(
default = "disable",
values = ["disable", "auto", "fallback", "force"],
doc = "Whether Bun may auto-install missing packages while testing.",
),
"timeout_ms": attr.int(
default = 0,
doc = "Optional per-test timeout in milliseconds.",
),
"update_snapshots": attr.bool(
default = False,
doc = "If true, updates Bun snapshot files.",
),
"rerun_each": attr.int(
default = 0,
doc = "Optional number of times to rerun each test file.",
),
"retry": attr.int(
default = 0,
doc = "Optional default retry count for all tests.",
),
"todo": attr.bool(
default = False,
doc = "If true, includes tests marked with `test.todo()`.",
),
"only": attr.bool(
default = False,
doc = "If true, runs only tests marked with `test.only()` or `describe.only()`.",
),
"pass_with_no_tests": attr.bool(
default = False,
doc = "If true, exits successfully when no tests are found.",
),
"concurrent": attr.bool(
default = False,
doc = "If true, treats all tests as concurrent tests.",
),
"randomize": attr.bool(
default = False,
doc = "If true, runs tests in random order.",
),
"seed": attr.int(
default = 0,
doc = "Optional randomization seed.",
),
"bail": attr.int(
default = 0,
doc = "Optional failure count after which Bun exits the test run.",
),
"reporter": attr.string(
default = "console",
values = ["console", "dots", "junit"],
doc = "Test reporter format.",
),
"max_concurrency": attr.int(
default = 0,
doc = "Optional maximum number of concurrent tests.",
),
"coverage": attr.bool(
default = False,
doc = "If true, always enables Bun coverage output.",
),
"coverage_reporters": attr.string_list(
doc = "Repeated Bun coverage reporters such as `text` or `lcov`.",
),
"test_flags": attr.string_list(
doc = "Additional raw flags forwarded to `bun test` before the test source list.",
),
},
test = True,
toolchains = ["//bun:toolchain_type"],

29
internal/js_compat.bzl Normal file
View File

@@ -0,0 +1,29 @@
"""rules_js-style compatibility exports backed by Bun."""
load("//internal:bun_binary.bzl", _bun_binary = "bun_binary")
load("//internal:bun_test.bzl", _bun_test = "bun_test")
load("//internal:js_library.bzl", _JsInfo = "JsInfo", _js_library = "js_library", _ts_library = "ts_library")
load("//internal:js_run_devserver.bzl", _js_run_devserver = "js_run_devserver")
JsInfo = _JsInfo
js_library = _js_library
ts_library = _ts_library
js_run_devserver = _js_run_devserver
def js_binary(name, **kwargs):
_bun_binary(name = name, **kwargs)
def js_test(name, entry_point = None, srcs = None, **kwargs):
if entry_point != None:
if srcs != None:
fail("js_test accepts either `entry_point` or `srcs`, but not both")
srcs = [entry_point]
if srcs == None:
fail("js_test requires `entry_point` or `srcs`")
_bun_test(
name = name,
srcs = srcs,
**kwargs
)

View File

@@ -1,23 +1,74 @@
"""Lightweight JS/TS source grouping rules."""
JsInfo = provider(
doc = "Provides transitive JavaScript/TypeScript metadata for Bun and JS compatibility rules.",
fields = {
"sources": "Direct source files owned by this target.",
"transitive_sources": "Transitive source files from this target and its deps.",
"types": "Direct type files owned by this target.",
"transitive_types": "Transitive type files from this target and its deps.",
"data_files": "Direct runtime data files owned by this target.",
"transitive_runfiles": "Transitive runtime files from this target and its deps.",
},
)
BunSourcesInfo = provider(
"Provides transitive sources for Bun libraries.",
fields = ["transitive_sources"],
)
def collect_js_sources(dep):
if JsInfo in dep:
return dep[JsInfo].transitive_sources
if BunSourcesInfo in dep:
return dep[BunSourcesInfo].transitive_sources
return dep[DefaultInfo].files
def collect_js_runfiles(dep):
if JsInfo in dep:
return dep[JsInfo].transitive_runfiles
if BunSourcesInfo in dep:
return dep[BunSourcesInfo].transitive_sources
return dep[DefaultInfo].files
def _bun_library_impl(ctx):
transitive_sources = [
dep[BunSourcesInfo].transitive_sources
transitive_sources = [collect_js_sources(dep) for dep in ctx.attr.deps]
transitive_types = [
dep[JsInfo].transitive_types
for dep in ctx.attr.deps
if BunSourcesInfo in dep
if JsInfo in dep
]
transitive_runfiles = [collect_js_runfiles(dep) for dep in ctx.attr.deps]
all_sources = depset(
direct = ctx.files.srcs,
transitive = transitive_sources,
)
all_types = depset(
direct = ctx.files.types,
transitive = transitive_types,
)
all_runfiles = depset(
direct = ctx.files.srcs + ctx.files.types + ctx.files.data,
transitive = transitive_runfiles,
)
default_files = depset(
direct = ctx.files.srcs + ctx.files.types + ctx.files.data,
transitive = transitive_sources + transitive_types + transitive_runfiles,
)
js_info = JsInfo(
sources = depset(ctx.files.srcs),
transitive_sources = all_sources,
types = depset(ctx.files.types),
transitive_types = all_types,
data_files = depset(ctx.files.data),
transitive_runfiles = all_runfiles,
)
return [
js_info,
BunSourcesInfo(transitive_sources = all_sources),
DefaultInfo(files = all_sources),
DefaultInfo(files = default_files),
]
js_library = rule(
@@ -28,6 +79,14 @@ js_library = rule(
allow_files = [".js", ".jsx", ".mjs", ".cjs"],
doc = "JavaScript source files in this library.",
),
"types": attr.label_list(
allow_files = [".d.ts"],
doc = "Optional declaration files associated with this library.",
),
"data": attr.label_list(
allow_files = True,
doc = "Optional runtime files propagated to dependents.",
),
"deps": attr.label_list(
doc = "Other Bun source libraries to include transitively.",
),
@@ -42,6 +101,14 @@ ts_library = rule(
allow_files = [".ts", ".tsx"],
doc = "TypeScript source files in this library.",
),
"types": attr.label_list(
allow_files = [".d.ts"],
doc = "Optional declaration files associated with this library.",
),
"data": attr.label_list(
allow_files = True,
doc = "Optional runtime files propagated to dependents.",
),
"deps": attr.label_list(
doc = "Other Bun source libraries to include transitively.",
),

View File

@@ -0,0 +1,101 @@
"""Compatibility rule for running an executable target as a dev server."""
load("//internal:js_library.bzl", "collect_js_runfiles")
load("//internal:workspace.bzl", "create_bun_workspace_info", "render_workspace_setup", "workspace_runfiles")
def _shell_quote(value):
return "'" + value.replace("'", "'\"'\"'") + "'"
def _js_run_devserver_impl(ctx):
toolchain = ctx.toolchains["//bun:toolchain_type"]
bun_bin = toolchain.bun.bun_bin
package_json = ctx.file.package_json
dep_runfiles = [collect_js_runfiles(dep) for dep in ctx.attr.deps]
tool_default_info = ctx.attr.tool[DefaultInfo]
workspace_info = create_bun_workspace_info(
ctx,
primary_file = package_json or tool_default_info.files_to_run.executable,
package_json = package_json,
package_dir_hint = ctx.attr.package_dir_hint,
extra_files = ctx.files.data + [bun_bin, tool_default_info.files_to_run.executable],
)
tool_workspace = ctx.attr.tool.label.workspace_name or "_main"
tool_path = "{}/{}".format(tool_workspace, tool_default_info.files_to_run.executable.short_path)
default_args = " ".join([_shell_quote(arg) for arg in ctx.attr.args])
launcher = ctx.actions.declare_file(ctx.label.name)
ctx.actions.write(
output = launcher,
is_executable = True,
content = render_workspace_setup(
bun_short_path = bun_bin.short_path,
install_metadata_short_path = workspace_info.install_metadata_file.short_path if workspace_info.install_metadata_file else "",
primary_source_short_path = package_json.short_path if package_json else tool_default_info.files_to_run.executable.short_path,
package_json_short_path = package_json.short_path if package_json else "",
package_dir_hint = ctx.attr.package_dir_hint,
working_dir_mode = ctx.attr.working_dir,
) + """
trap cleanup_runtime_workspace EXIT
cd "${runtime_exec_dir}"
tool="${runfiles_dir}/__TOOL_SHORT_PATH__"
exec "${tool}" __DEFAULT_ARGS__ "$@"
""".replace("__TOOL_SHORT_PATH__", tool_path).replace("__DEFAULT_ARGS__", default_args),
)
return [
workspace_info,
DefaultInfo(
executable = launcher,
runfiles = workspace_runfiles(
ctx,
workspace_info,
direct_files = [launcher, tool_default_info.files_to_run.executable],
transitive_files = dep_runfiles,
).merge(tool_default_info.default_runfiles),
),
]
js_run_devserver = rule(
implementation = _js_run_devserver_impl,
doc = """Runs an executable target from a staged JS workspace.
This is a Bun-backed compatibility adapter for `rules_js`-style devserver
targets. It stages the same runtime workspace as the Bun rules, then executes
the provided tool with any default arguments.
""",
attrs = {
"tool": attr.label(
mandatory = True,
executable = True,
cfg = "target",
doc = "Executable target to launch as the dev server.",
),
"package_json": attr.label(
allow_single_file = True,
doc = "Optional package.json used to resolve the package working directory.",
),
"package_dir_hint": attr.string(
default = ".",
doc = "Optional package-relative directory hint when package_json is not supplied.",
),
"node_modules": attr.label(
doc = "Optional label providing package files from a node_modules tree, typically produced by bun_install or npm_translate_lock, in runfiles.",
),
"deps": attr.label_list(
doc = "Library dependencies required by the dev server.",
),
"data": attr.label_list(
allow_files = True,
doc = "Additional runtime files required by the dev server.",
),
"working_dir": attr.string(
default = "workspace",
values = ["workspace", "package"],
doc = "Working directory at runtime: Bazel runfiles workspace root or the resolved package directory.",
),
},
executable = True,
toolchains = ["//bun:toolchain_type"],
)

776
internal/workspace.bzl Normal file
View File

@@ -0,0 +1,776 @@
"""Shared Bun workspace metadata and launcher helpers."""
BunWorkspaceInfo = provider(
doc = "Workspace/runtime metadata shared by Bun rules and adapters.",
fields = {
"install_metadata_file": "Optional install metadata file from bun_install.",
"metadata_file": "Rule-local metadata file describing the staged workspace inputs.",
"node_modules_files": "Depset of node_modules files from bun_install.",
"package_dir_hint": "Package-relative directory when known at analysis time.",
"package_json": "Package manifest file when explicitly provided.",
"primary_file": "Primary source file used to resolve the runtime package context.",
"runtime_files": "Depset of runtime files required to stage the workspace.",
},
)
_WORKSPACE_SETUP_TEMPLATE = """#!/usr/bin/env bash
set -euo pipefail
runfiles_dir="${RUNFILES_DIR:-$0.runfiles}"
workspace_root="${runfiles_dir}/_main"
workspace_root="$(cd "${workspace_root}" && pwd -P)"
bun_bin="${runfiles_dir}/_main/__BUN_SHORT_PATH__"
primary_source=""
if [[ -n "__PRIMARY_SOURCE_SHORT_PATH__" ]]; then
primary_source="${runfiles_dir}/_main/__PRIMARY_SOURCE_SHORT_PATH__"
fi
package_json=""
if [[ -n "__PACKAGE_JSON_SHORT_PATH__" ]]; then
package_json="${runfiles_dir}/_main/__PACKAGE_JSON_SHORT_PATH__"
fi
package_rel_dir_hint="__PACKAGE_DIR_HINT__"
install_root_rel_dir_hint="__INSTALL_ROOT_REL_DIR__"
install_metadata=""
if [[ -n "__INSTALL_METADATA_SHORT_PATH__" ]]; then
install_metadata="${runfiles_dir}/_main/__INSTALL_METADATA_SHORT_PATH__"
fi
working_dir_mode="__WORKING_DIR_MODE__"
normalize_rel_dir() {
local value="$1"
if [[ -z "${value}" || "${value}" == "." ]]; then
echo "."
else
echo "${value#./}"
fi
}
dirname_rel_dir() {
local value
value="$(normalize_rel_dir "$1")"
if [[ "${value}" == "." || "${value}" != */* ]]; then
echo "."
return 0
fi
echo "${value%/*}"
}
first_path_component() {
local value
value="$(normalize_rel_dir "$1")"
if [[ "${value}" == "." ]]; then
echo ""
return 0
fi
echo "${value%%/*}"
}
rel_dir_from_abs_path() {
local absolute_path="$1"
if [[ "${absolute_path}" == "${workspace_root}" ]]; then
echo "."
return 0
fi
echo "${absolute_path#"${workspace_root}/"}"
}
find_package_rel_dir_for_path() {
local path="$1"
local dir="$1"
if [[ -f "${dir}" ]]; then
dir="$(dirname "${dir}")"
fi
while [[ "${dir}" == "${workspace_root}"* ]]; do
if [[ -f "${dir}/package.json" ]]; then
rel_dir_from_abs_path "${dir}"
return 0
fi
if [[ "${dir}" == "${workspace_root}" ]]; then
break
fi
dir="$(dirname "${dir}")"
done
rel_dir_from_abs_path "$(dirname "${path}")"
}
find_working_rel_dir_for_path() {
local path="$1"
local dir="$1"
if [[ -f "${dir}" ]]; then
dir="$(dirname "${dir}")"
fi
while [[ "${dir}" == "${workspace_root}"* ]]; do
if [[ -f "${dir}/.env" || -f "${dir}/package.json" ]]; then
rel_dir_from_abs_path "${dir}"
return 0
fi
if [[ "${dir}" == "${workspace_root}" ]]; then
break
fi
dir="$(dirname "${dir}")"
done
rel_dir_from_abs_path "$(dirname "${path}")"
}
strip_rel_prefix() {
local child
child="$(normalize_rel_dir "$1")"
local parent
parent="$(normalize_rel_dir "$2")"
if [[ "${parent}" == "." ]]; then
echo "${child}"
return 0
fi
if [[ "${child}" == "${parent}" ]]; then
echo "."
return 0
fi
if [[ "${child}" == "${parent}/"* ]]; then
echo "${child#"${parent}/"}"
return 0
fi
echo "${child}"
}
select_primary_node_modules() {
local selected=""
local fallback=""
while IFS= read -r node_modules_dir; do
if [[ -z "${fallback}" ]]; then
fallback="${node_modules_dir}"
fi
if [[ ! -d "${node_modules_dir}/.bun" ]]; then
continue
fi
if [[ "${node_modules_dir}" != *"/runfiles/_main/"* ]]; then
selected="${node_modules_dir}"
break
fi
if [[ -z "${selected}" ]]; then
selected="${node_modules_dir}"
fi
done < <(find -L "${runfiles_dir}" -type d -name node_modules 2>/dev/null | sort)
if [[ -n "${selected}" ]]; then
echo "${selected}"
else
echo "${fallback}"
fi
}
link_top_level_entries() {
local source_root="$1"
local destination_root="$2"
local skipped_entry="$3"
local entry=""
local entry_name=""
shopt -s dotglob nullglob
for entry in "${source_root}"/* "${source_root}"/.[!.]* "${source_root}"/..?*; do
entry_name="$(basename "${entry}")"
if [[ "${entry_name}" == "." || "${entry_name}" == ".." ]]; then
continue
fi
if [[ -n "${skipped_entry}" && "${entry_name}" == "${skipped_entry}" ]]; then
continue
fi
ln -s "${entry}" "${destination_root}/${entry_name}"
done
shopt -u dotglob nullglob
}
materialize_package_path() {
local source_root="$1"
local destination_root="$2"
local package_rel_dir
package_rel_dir="$(normalize_rel_dir "$3")"
if [[ "${package_rel_dir}" == "." ]]; then
return 0
fi
local source_cursor="${source_root}"
local destination_cursor="${destination_root}"
local parts=()
local current="${package_rel_dir}"
while [[ -n "${current}" ]]; do
if [[ "${current}" == */* ]]; then
parts+=("${current%%/*}")
current="${current#*/}"
else
parts+=("${current}")
break
fi
done
local index=0
while [[ ${index} -lt $((${#parts[@]} - 1)) ]]; do
local part="${parts[${index}]}"
local next_part="${parts[$((index + 1))]}"
source_cursor="${source_cursor}/${part}"
destination_cursor="${destination_cursor}/${part}"
mkdir -p "${destination_cursor}"
local sibling=""
local sibling_name=""
shopt -s dotglob nullglob
for sibling in "${source_cursor}"/* "${source_cursor}"/.[!.]* "${source_cursor}"/..?*; do
sibling_name="$(basename "${sibling}")"
if [[ "${sibling_name}" == "." || "${sibling_name}" == ".." || "${sibling_name}" == "${next_part}" ]]; then
continue
fi
if [[ ! -e "${destination_cursor}/${sibling_name}" ]]; then
ln -s "${sibling}" "${destination_cursor}/${sibling_name}"
fi
done
shopt -u dotglob nullglob
index=$((index + 1))
done
mkdir -p "${destination_root}/${package_rel_dir}"
}
materialize_directory_entries() {
local source_root="$1"
local destination_root="$2"
local entry=""
local entry_name=""
mkdir -p "${destination_root}"
shopt -s dotglob nullglob
for entry in "${source_root}"/* "${source_root}"/.[!.]* "${source_root}"/..?*; do
entry_name="$(basename "${entry}")"
if [[ "${entry_name}" == "." || "${entry_name}" == ".." ]]; then
continue
fi
rm -rf "${destination_root}/${entry_name}"
ln -s "${entry}" "${destination_root}/${entry_name}"
done
shopt -u dotglob nullglob
}
stage_workspace_view() {
local source_root="$1"
local destination_root="$2"
local package_rel_dir
package_rel_dir="$(normalize_rel_dir "$3")"
local skipped_entry
skipped_entry="$(first_path_component "${package_rel_dir}")"
link_top_level_entries "${source_root}" "${destination_root}" "${skipped_entry}"
if [[ "${package_rel_dir}" == "." ]]; then
return 0
fi
materialize_package_path "${source_root}" "${destination_root}" "${package_rel_dir}"
materialize_directory_entries "${source_root}/${package_rel_dir}" "${destination_root}/${package_rel_dir}"
}
materialize_tree_contents() {
local source_root="$1"
local destination_root="$2"
rm -rf "${destination_root}"
mkdir -p "${destination_root}"
cp -RL "${source_root}/." "${destination_root}"
}
build_workspace_package_map() {
local root="$1"
local out="$2"
python3 - "${root}" >"${out}" <<'PY'
import json
import os
import sys
root = os.path.abspath(sys.argv[1])
for dirpath, dirnames, filenames in os.walk(root):
dirnames[:] = [name for name in dirnames if name != "node_modules"]
if "package.json" not in filenames:
continue
manifest_path = os.path.join(dirpath, "package.json")
try:
with open(manifest_path, "r", encoding="utf-8") as manifest_file:
package_name = json.load(manifest_file).get("name")
except Exception:
continue
if not isinstance(package_name, str):
continue
rel_dir = os.path.relpath(dirpath, root)
if rel_dir == ".":
rel_dir = "."
print(f"{package_name}\t{rel_dir}")
PY
}
workspace_package_rel_dir_for_source() {
local source="$1"
local manifest_path="${source}/package.json"
local package_name=""
if [[ ! -f "${manifest_path}" ]]; then
return 1
fi
package_name="$(python3 - "${manifest_path}" <<'PY'
import json
import sys
try:
with open(sys.argv[1], "r", encoding="utf-8") as manifest_file:
package_name = json.load(manifest_file).get("name", "")
except Exception:
package_name = ""
if isinstance(package_name, str):
print(package_name)
PY
)"
if [[ -z "${package_name}" ]]; then
return 1
fi
awk -F '\t' -v name="${package_name}" '$1 == name { print $2; exit }' "${workspace_package_map}"
}
link_node_modules_entry() {
local source="$1"
local destination="$2"
local workspace_rel_dir=""
rm -rf "${destination}"
workspace_rel_dir="$(workspace_package_rel_dir_for_source "${source}" || true)"
if [[ -n "${workspace_rel_dir}" ]]; then
ln -s "${runtime_workspace}/${workspace_rel_dir}" "${destination}"
return 0
fi
if [[ -L "${source}" ]]; then
ln -s "$(readlink "${source}")" "${destination}"
else
ln -s "${source}" "${destination}"
fi
}
mirror_node_modules_dir() {
local source_dir="$1"
local destination_dir="$2"
local entry=""
local entry_name=""
local scoped_entry=""
local scoped_name=""
rm -rf "${destination_dir}"
mkdir -p "${destination_dir}"
shopt -s dotglob nullglob
for entry in "${source_dir}"/* "${source_dir}"/.[!.]* "${source_dir}"/..?*; do
entry_name="$(basename "${entry}")"
if [[ "${entry_name}" == "." || "${entry_name}" == ".." || "${entry_name}" == ".rules_bun" ]]; then
continue
fi
if [[ -d "${entry}" && ! -L "${entry}" && "${entry_name}" == @* ]]; then
mkdir -p "${destination_dir}/${entry_name}"
for scoped_entry in "${entry}"/* "${entry}"/.[!.]* "${entry}"/..?*; do
scoped_name="$(basename "${scoped_entry}")"
if [[ "${scoped_name}" == "." || "${scoped_name}" == ".." ]]; then
continue
fi
link_node_modules_entry "${scoped_entry}" "${destination_dir}/${entry_name}/${scoped_name}"
done
continue
fi
link_node_modules_entry "${entry}" "${destination_dir}/${entry_name}"
done
shopt -u dotglob nullglob
}
find_install_repo_node_modules() {
local repo_root="$1"
local package_rel_dir
package_rel_dir="$(normalize_rel_dir "$2")"
if [[ "${package_rel_dir}" != "." ]]; then
local candidate="${package_rel_dir}"
while true; do
if [[ -d "${repo_root}/${candidate}/node_modules" ]]; then
echo "${repo_root}/${candidate}/node_modules"
return 0
fi
if [[ "${candidate}" != */* ]]; then
break
fi
candidate="${candidate%/*}"
done
fi
if [[ -d "${repo_root}/node_modules" ]]; then
echo "${repo_root}/node_modules"
return 0
fi
return 1
}
mirror_install_repo_workspace_node_modules() {
local repo_root="$1"
local destination_root="$2"
while IFS= read -r install_node_modules; do
local rel_path="${install_node_modules#${repo_root}/}"
local destination="${destination_root}/${rel_path}"
mkdir -p "$(dirname "${destination}")"
mirror_node_modules_dir "${install_node_modules}" "${destination}"
done < <(find "${repo_root}" \
-path "${repo_root}/node_modules" -prune -o \
-type d -name node_modules -print 2>/dev/null | sort)
}
build_runtime_path() {
local workspace_dir="$1"
local package_dir="$2"
local install_root_dir="$3"
local entries=()
if [[ -d "${install_root_dir}/node_modules/.bin" ]]; then
entries+=("${install_root_dir}/node_modules/.bin")
fi
if [[ -d "${package_dir}/node_modules/.bin" ]]; then
if [[ "${package_dir}/node_modules/.bin" != "${install_root_dir}/node_modules/.bin" ]]; then
entries+=("${package_dir}/node_modules/.bin")
fi
fi
if [[ -d "${workspace_dir}/node_modules/.bin" && "${workspace_dir}/node_modules/.bin" != "${package_dir}/node_modules/.bin" && "${workspace_dir}/node_modules/.bin" != "${install_root_dir}/node_modules/.bin" ]]; then
entries+=("${workspace_dir}/node_modules/.bin")
fi
if [[ -n "${PATH:-}" ]]; then
entries+=("${PATH}")
fi
if [[ ${#entries[@]} -eq 0 ]]; then
echo ""
return 0
fi
local path_value=""
local entry=""
for entry in "${entries[@]}"; do
if [[ -z "${path_value}" ]]; then
path_value="${entry}"
else
path_value="${path_value}:${entry}"
fi
done
echo "${path_value}"
}
resolve_package_rel_dir() {
if [[ -n "${package_rel_dir_hint}" && "${package_rel_dir_hint}" != "." ]]; then
normalize_rel_dir "${package_rel_dir_hint}"
return 0
fi
if [[ -n "${package_json}" ]]; then
find_package_rel_dir_for_path "${package_json}"
return 0
fi
if [[ -n "${primary_source}" ]]; then
find_package_rel_dir_for_path "${primary_source}"
return 0
fi
echo "."
}
resolve_execution_rel_dir() {
local package_rel_dir="$1"
case "${working_dir_mode}" in
workspace)
echo "."
;;
package)
echo "${package_rel_dir}"
;;
entry_point)
if [[ -n "${primary_source}" ]]; then
find_working_rel_dir_for_path "${primary_source}"
else
echo "${package_rel_dir}"
fi
;;
*)
echo "${package_rel_dir}"
;;
esac
}
resolve_install_root_rel_dir() {
if [[ -n "${install_metadata}" && -f "${install_metadata}" ]]; then
local resolved_from_metadata=""
resolved_from_metadata="$(
python3 - "${install_metadata}" "${package_rel_dir}" <<'PY'
import json
import sys
install_metadata_path = sys.argv[1]
package_rel_dir = sys.argv[2]
try:
with open(install_metadata_path, "r", encoding="utf-8") as install_metadata_file:
workspace_package_dirs = json.load(install_metadata_file).get("workspace_package_dirs", [])
except Exception:
workspace_package_dirs = []
normalized_package_rel_dir = package_rel_dir.strip("./") or "."
matches = []
for workspace_package_dir in workspace_package_dirs:
normalized_workspace_package_dir = workspace_package_dir.strip("./")
if not normalized_workspace_package_dir:
continue
if normalized_package_rel_dir == normalized_workspace_package_dir:
matches.append((len(normalized_workspace_package_dir), "."))
continue
suffix = "/" + normalized_workspace_package_dir
if normalized_package_rel_dir.endswith(suffix):
prefix = normalized_package_rel_dir[:-len(suffix)].strip("/") or "."
matches.append((len(normalized_workspace_package_dir), prefix))
if matches:
matches.sort(reverse = True)
print(matches[0][1])
PY
)"
if [[ -n "${resolved_from_metadata}" ]]; then
echo "${resolved_from_metadata}"
return 0
fi
fi
if [[ -n "${install_root_rel_dir_hint}" && "${install_root_rel_dir_hint}" != "." ]]; then
normalize_rel_dir "${install_root_rel_dir_hint}"
return 0
fi
if [[ -n "${package_json}" ]]; then
find_package_rel_dir_for_path "${package_json}"
return 0
fi
if [[ -n "${primary_source}" ]]; then
find_package_rel_dir_for_path "${primary_source}"
return 0
fi
echo "."
}
package_rel_dir="$(resolve_package_rel_dir)"
execution_rel_dir="$(resolve_execution_rel_dir "${package_rel_dir}")"
install_root_rel_dir="$(resolve_install_root_rel_dir)"
package_rel_dir_in_install_root="$(strip_rel_prefix "${package_rel_dir}" "${install_root_rel_dir}")"
runtime_workspace="$(mktemp -d)"
cleanup_runtime_workspace() {
rm -rf "${runtime_workspace}"
}
stage_workspace_view "${workspace_root}" "${runtime_workspace}" "${package_rel_dir}"
runtime_package_dir="${runtime_workspace}"
if [[ "${package_rel_dir}" != "." ]]; then
runtime_package_dir="${runtime_workspace}/${package_rel_dir}"
fi
runtime_install_root="${runtime_workspace}"
if [[ "${install_root_rel_dir}" != "." ]]; then
runtime_install_root="${runtime_workspace}/${install_root_rel_dir}"
fi
runtime_exec_dir="${runtime_workspace}"
if [[ "${execution_rel_dir}" != "." ]]; then
runtime_exec_dir="${runtime_workspace}/${execution_rel_dir}"
fi
if [[ -n "${primary_source}" ]]; then
materialize_tree_contents "${workspace_root}/${package_rel_dir}" "${runtime_package_dir}"
fi
if [[ -n "${package_json}" ]]; then
materialize_tree_contents "${workspace_root}/${install_root_rel_dir}" "${runtime_install_root}"
fi
if [[ -n "${primary_source}" && "${primary_source}" == "${workspace_root}"* ]]; then
primary_source="${runtime_workspace}/$(rel_dir_from_abs_path "${primary_source}")"
fi
if [[ -n "${package_json}" && "${package_json}" == "${workspace_root}"* ]]; then
package_json="${runtime_workspace}/$(rel_dir_from_abs_path "${package_json}")"
fi
workspace_package_map="${runtime_workspace}/.rules_bun_workspace_packages.tsv"
build_workspace_package_map "${runtime_workspace}" "${workspace_package_map}"
primary_node_modules="$(select_primary_node_modules)"
install_repo_root=""
if [[ -n "${primary_node_modules}" ]]; then
install_repo_root="$(dirname "${primary_node_modules}")"
mkdir -p "${runtime_install_root}"
mirror_node_modules_dir "${primary_node_modules}" "${runtime_install_root}/node_modules"
fi
if [[ -n "${install_repo_root}" ]]; then
resolved_install_node_modules="$(find_install_repo_node_modules "${install_repo_root}" "${package_rel_dir_in_install_root}" || true)"
if [[ -n "${resolved_install_node_modules}" && "${resolved_install_node_modules}" != "${install_repo_root}/node_modules" ]]; then
mirror_node_modules_dir "${resolved_install_node_modules}" "${runtime_package_dir}/node_modules"
fi
mirror_install_repo_workspace_node_modules "${install_repo_root}" "${runtime_install_root}"
fi
if [[ ! -e "${runtime_package_dir}/node_modules" && -e "${runtime_install_root}/node_modules" && "${runtime_package_dir}" != "${runtime_install_root}" ]]; then
ln -s "${runtime_install_root}/node_modules" "${runtime_package_dir}/node_modules"
fi
runtime_path="$(build_runtime_path "${runtime_workspace}" "${runtime_package_dir}" "${runtime_install_root}")"
if [[ -n "${runtime_path}" ]]; then
export PATH="${runtime_path}"
fi
"""
def _shell_quote(value):
return "'" + value.replace("'", "'\"'\"'") + "'"
def _dirname(path):
if not path or path == ".":
return "."
index = path.rfind("/")
if index < 0:
return "."
if index == 0:
return "/"
return path[:index]
def find_install_metadata_file(files):
for file in files:
if file.short_path.endswith("node_modules/.rules_bun/install.json"):
return file
return None
def resolve_node_modules_roots(files, workspace_dir = ""):
install_metadata_file = find_install_metadata_file(files)
shared_node_modules_root = None
workspace_node_modules_root = None
if install_metadata_file:
shared_node_modules_root = _dirname(_dirname(install_metadata_file.path))
workspace_marker = ""
if workspace_dir:
workspace_marker = "/%s/node_modules/" % workspace_dir.strip("/")
shortest_path = None
for src in files:
if workspace_marker and workspace_marker in src.path and workspace_node_modules_root == None:
workspace_node_modules_root = src.path[:src.path.find(workspace_marker) + len(workspace_marker) - 1]
if shortest_path == None or len(src.path) < len(shortest_path):
shortest_path = src.path
if shared_node_modules_root == None and shortest_path:
marker = "/node_modules/"
marker_index = shortest_path.find(marker)
if marker_index >= 0:
shared_node_modules_root = shortest_path[:marker_index + len("/node_modules")]
return struct(
install_metadata_file = install_metadata_file,
node_modules_root = workspace_node_modules_root or shared_node_modules_root,
shared_node_modules_root = shared_node_modules_root,
)
def create_bun_workspace_info(ctx, primary_file = None, package_json = None, package_dir_hint = ".", extra_files = None):
direct_runtime_files = []
if primary_file:
direct_runtime_files.append(primary_file)
if package_json and package_json != primary_file:
direct_runtime_files.append(package_json)
direct_runtime_files.extend(extra_files or [])
node_modules_files = depset()
install_metadata_file = None
if getattr(ctx.attr, "node_modules", None):
node_modules_files = ctx.attr.node_modules[DefaultInfo].files
install_metadata_file = find_install_metadata_file(node_modules_files.to_list())
metadata_file = ctx.actions.declare_file(ctx.label.name + ".bun_workspace.json")
ctx.actions.write(
output = metadata_file,
content = json.encode({
"install_metadata": install_metadata_file.short_path if install_metadata_file else "",
"package_dir_hint": package_dir_hint or ".",
"package_json": package_json.short_path if package_json else "",
"primary_file": primary_file.short_path if primary_file else "",
}) + "\n",
)
direct_runtime_files.append(metadata_file)
runtime_files = depset(
direct = direct_runtime_files,
transitive = [node_modules_files],
)
return BunWorkspaceInfo(
install_metadata_file = install_metadata_file,
metadata_file = metadata_file,
node_modules_files = node_modules_files,
package_dir_hint = package_dir_hint or ".",
package_json = package_json,
primary_file = primary_file,
runtime_files = runtime_files,
)
def workspace_runfiles(ctx, workspace_info, direct_files = None, transitive_files = None):
return ctx.runfiles(
files = direct_files or [],
transitive_files = depset(
transitive = [workspace_info.runtime_files] + (transitive_files or []),
),
)
def render_workspace_setup(
bun_short_path,
working_dir_mode,
primary_source_short_path = "",
package_json_short_path = "",
package_dir_hint = ".",
install_root_rel_dir = ".",
install_metadata_short_path = ""):
return _WORKSPACE_SETUP_TEMPLATE.replace("__BUN_SHORT_PATH__", bun_short_path).replace(
"__PRIMARY_SOURCE_SHORT_PATH__",
primary_source_short_path,
).replace(
"__PACKAGE_JSON_SHORT_PATH__",
package_json_short_path,
).replace(
"__PACKAGE_DIR_HINT__",
package_dir_hint or ".",
).replace(
"__INSTALL_ROOT_REL_DIR__",
install_root_rel_dir or ".",
).replace(
"__INSTALL_METADATA_SHORT_PATH__",
install_metadata_short_path,
).replace(
"__WORKING_DIR_MODE__",
working_dir_mode,
)

21
js/BUILD.bazel Normal file
View File

@@ -0,0 +1,21 @@
load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
package(default_visibility = ["//visibility:public"])
exports_files(["defs.bzl"])
filegroup(
name = "repo_runtime_files",
srcs = [
"BUILD.bazel",
"defs.bzl",
],
visibility = ["//visibility:public"],
)
bzl_library(
name = "defs_bzl",
srcs = ["defs.bzl"],
visibility = ["//visibility:public"],
deps = ["//internal:js_compat_bzl"],
)

12
js/defs.bzl Normal file
View File

@@ -0,0 +1,12 @@
"""rules_js-style public API backed by Bun."""
load("//internal:js_compat.bzl", _JsInfo = "JsInfo", _js_binary = "js_binary", _js_library = "js_library", _js_run_devserver = "js_run_devserver", _js_test = "js_test", _ts_library = "ts_library")
visibility("public")
JsInfo = _JsInfo
js_binary = _js_binary
js_test = _js_test
js_run_devserver = _js_run_devserver
js_library = _js_library
ts_library = _ts_library

32
npm/BUILD.bazel Normal file
View File

@@ -0,0 +1,32 @@
load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
package(default_visibility = ["//visibility:public"])
exports_files([
"extensions.bzl",
"repositories.bzl",
])
filegroup(
name = "repo_runtime_files",
srcs = [
"BUILD.bazel",
"extensions.bzl",
"repositories.bzl",
],
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"],
deps = ["//internal:bun_install_bzl"],
)

28
npm/extensions.bzl Normal file
View File

@@ -0,0 +1,28 @@
load("//internal:bun_install.bzl", "bun_install_repository")
_translate = tag_class(
attrs = {
"name": attr.string(mandatory = True),
"package_json": attr.label(mandatory = True),
"lockfile": attr.label(mandatory = True),
"install_inputs": attr.label_list(allow_files = True),
"isolated_home": attr.bool(default = True),
},
)
def _npm_translate_lock_impl(ctx):
for mod in ctx.modules:
for install in mod.tags.translate:
bun_install_repository(
name = install.name,
package_json = install.package_json,
bun_lockfile = install.lockfile,
install_inputs = install.install_inputs,
isolated_home = install.isolated_home,
visible_repo_name = install.name,
)
npm_translate_lock = module_extension(
implementation = _npm_translate_lock_impl,
tag_classes = {"translate": _translate},
)

11
npm/repositories.bzl Normal file
View File

@@ -0,0 +1,11 @@
load("//internal:bun_install.bzl", "bun_install_repository")
def npm_translate_lock(name, package_json, lockfile, install_inputs = [], isolated_home = True):
bun_install_repository(
name = name,
package_json = package_json,
bun_lockfile = lockfile,
install_inputs = install_inputs,
isolated_home = isolated_home,
visible_repo_name = name,
)

1
result
View File

@@ -1 +0,0 @@
/nix/store/742k6q4hns9h1wj61y90glqwfmn2y7pa-release

1
tests/binary_test/.env Normal file
View File

@@ -0,0 +1 @@
BUN_ENV_CWD_TEST=from-dotenv

View File

@@ -71,3 +71,49 @@ sh_test(
args = ["$(location :env_parent_cwd_bin)"],
data = [":env_parent_cwd_bin"],
)
bun_binary(
name = "runtime_flag_bin",
entry_point = "flag_probe.ts",
args = ["one", "two"],
preload = ["preload.ts"],
env_files = ["runtime.env"],
)
sh_test(
name = "bun_binary_runtime_flags_test",
srcs = ["run_flag_binary.sh"],
args = ["$(location :runtime_flag_bin)"],
data = [":runtime_flag_bin"],
)
sh_test(
name = "bun_binary_runtime_flags_shape_test",
srcs = ["verify_runtime_flags_shape.sh"],
args = ["$(location :runtime_flag_bin)"],
data = [":runtime_flag_bin"],
)
bun_binary(
name = "configured_launcher_bin",
entry_point = "hello.ts",
node_modules = "@script_test_vite_node_modules//:node_modules",
smol = True,
conditions = [
"browser",
"development",
],
install_mode = "force",
run_flags = [
"--hot",
"--console-depth",
"4",
],
)
sh_test(
name = "bun_binary_configured_launcher_shape_test",
srcs = ["verify_configured_launcher_shape.sh"],
args = ["$(location :configured_launcher_bin)"],
data = [":configured_launcher_bin"],
)

View File

@@ -0,0 +1 @@
BUN_ENV_PARENT_TEST=from-parent-dotenv

View File

@@ -0,0 +1,7 @@
const state = globalThis as typeof globalThis & { __rules_bun_preloaded?: string };
console.log(JSON.stringify({
preloaded: state.__rules_bun_preloaded ?? null,
env: process.env.RUNTIME_FLAG_TEST ?? null,
argv: process.argv.slice(2),
}));

View File

@@ -0,0 +1 @@
(globalThis as typeof globalThis & { __rules_bun_preloaded?: string }).__rules_bun_preloaded = "yes";

View File

@@ -5,7 +5,7 @@ binary="$1"
expected="$2"
output="$(${binary})"
if [[ "${output}" != "${expected}" ]]; then
if [[ ${output} != "${expected}" ]]; then
echo "Unexpected output from ${binary}: ${output}" >&2
exit 1
fi

View File

@@ -0,0 +1,12 @@
#!/usr/bin/env bash
set -euo pipefail
binary="$1"
output="$(${binary})"
expected='{"preloaded":"yes","env":"from-env-file","argv":["one","two"]}'
if [[ ${output} != "${expected}" ]]; then
echo "Unexpected output from ${binary}: ${output}" >&2
exit 1
fi

View File

@@ -0,0 +1 @@
RUNTIME_FLAG_TEST=from-env-file

View File

@@ -0,0 +1,16 @@
#!/usr/bin/env bash
set -euo pipefail
binary="$1"
grep -Fq -- 'install_metadata="${runfiles_dir}/_main/' "${binary}"
grep -Fq -- 'node_modules/.rules_bun/install.json' "${binary}"
grep -Fq -- "--smol" "${binary}"
grep -Fq -- "--conditions" "${binary}"
grep -Fq -- "'browser'" "${binary}"
grep -Fq -- "'development'" "${binary}"
grep -Fq -- "--install" "${binary}"
grep -Fq -- "'force'" "${binary}"
grep -Fq -- "'--hot'" "${binary}"
grep -Fq -- "'--console-depth'" "${binary}"
grep -Fq -- "'4'" "${binary}"

View File

@@ -4,6 +4,6 @@ set -euo pipefail
rule_file="$1"
build_file="$2"
grep -Eq 'files = \[bun_bin, entry_point\] \+ ctx\.files\.data' "${rule_file}"
grep -Eq 'extra_files = ctx\.files\.data \+ ctx\.files\.preload \+ ctx\.files\.env_files \+ \[bun_bin\]' "${rule_file}"
grep -Eq 'name = "hello_js_with_data_bin"' "${build_file}"
grep -Eq 'data = \["payload\.txt"\]' "${build_file}"

View File

@@ -0,0 +1,8 @@
#!/usr/bin/env bash
set -euo pipefail
binary="$1"
grep -Fq -- '--no-install' "${binary}"
grep -Fq -- '--preload' "${binary}"
grep -Fq -- '--env-file' "${binary}"

View File

@@ -11,6 +11,32 @@ bun_test(
srcs = ["failing.test.ts"],
)
bun_test(
name = "configured_suite",
srcs = ["passing.test.ts"],
preload = ["preload.ts"],
env_files = ["test.env"],
no_env_file = True,
timeout_ms = 250,
update_snapshots = True,
rerun_each = 2,
concurrent = True,
randomize = True,
seed = 7,
bail = 1,
reporter = "junit",
max_concurrency = 4,
coverage = True,
coverage_reporters = ["lcov"],
test_flags = ["--only-failures"],
)
bun_test(
name = "configured_retry_suite",
srcs = ["passing.test.ts"],
retry = 3,
)
sh_test(
name = "bun_test_failing_suite_test",
srcs = ["failing_suite_shape.sh"],
@@ -38,3 +64,16 @@ sh_test(
args = ["$(location //internal:bun_test.bzl)"],
data = ["//internal:bun_test.bzl"],
)
sh_test(
name = "bun_test_configured_suite_shape_test",
srcs = ["configured_suite_shape.sh"],
args = [
"$(location :configured_suite)",
"$(location :configured_retry_suite)",
],
data = [
":configured_suite",
":configured_retry_suite",
],
)

View File

@@ -3,6 +3,5 @@ set -euo pipefail
rule_file="$1"
grep -Fq 'set -euo pipefail' "${rule_file}"
grep -Fq 'src_args = " ".join([_shell_quote(src.short_path) for src in ctx.files.srcs])' "${rule_file}"
grep -Fq 'exec "${{bun_bin}}" --bun test {src_args} "$@"' "${rule_file}"
grep -Fq 'launcher_lines = [render_shell_array("bun_args", ["--bun", "test"])]' "${rule_file}"
grep -Fq 'exec "${bun_bin}" "${bun_args[@]}" "$@"' "${rule_file}"

View File

@@ -3,5 +3,6 @@ set -euo pipefail
rule_file="$1"
grep -Eq 'files = \[bun_bin\] \+ ctx\.files\.srcs \+ ctx\.files\.data' "${rule_file}"
grep -Fq 'extra_files = ctx.files.srcs + ctx.files.data + ctx.files.preload + ctx.files.env_files + [bun_bin]' "${rule_file}"
grep -Eq '"srcs": attr\.label_list\(' "${rule_file}"
grep -Eq '"coverage": attr\.bool\(' "${rule_file}"

View File

@@ -0,0 +1,24 @@
#!/usr/bin/env bash
set -euo pipefail
launcher="$1"
retry_launcher="$2"
grep -Fq -- '--no-install' "${launcher}"
grep -Fq -- '--preload' "${launcher}"
grep -Fq -- '--env-file' "${launcher}"
grep -Fq -- '--no-env-file' "${launcher}"
grep -Fq -- '--timeout' "${launcher}"
grep -Fq -- '--update-snapshots' "${launcher}"
grep -Fq -- '--rerun-each' "${launcher}"
grep -Fq -- '--concurrent' "${launcher}"
grep -Fq -- '--randomize' "${launcher}"
grep -Fq -- '--seed' "${launcher}"
grep -Fq -- '--bail' "${launcher}"
grep -Fq -- '--max-concurrency' "${launcher}"
grep -Fq -- '--reporter' "${launcher}"
grep -Fq -- '--reporter-outfile' "${launcher}"
grep -Fq -- '--coverage' "${launcher}"
grep -Fq -- '--coverage-dir' "${launcher}"
grep -Fq -- '--coverage-reporter' "${launcher}"
grep -Fq -- '--retry' "${retry_launcher}"

View File

@@ -3,5 +3,5 @@ set -euo pipefail
rule_file="$1"
grep -Fq 'exec "${{bun_bin}}" --bun test {src_args} --test-name-pattern "${{TESTBRIDGE_TEST_ONLY}}" "$@"' "${rule_file}"
grep -Fq 'if [[ -n "${{TESTBRIDGE_TEST_ONLY:-}}" ]]' "${rule_file}"
grep -Fq 'reporter_out="${XML_OUTPUT_FILE:-${runtime_workspace}/junit.xml}"' "${rule_file}"
grep -Fq 'bun_args+=("--reporter" "junit" "--reporter-outfile" "${reporter_out}")' "${rule_file}"

View File

@@ -0,0 +1 @@
(globalThis as typeof globalThis & { __rules_bun_test_preloaded?: boolean }).__rules_bun_test_preloaded = true;

View File

@@ -0,0 +1 @@
TEST_FROM_ENV=1

View File

@@ -1,4 +1,4 @@
load("//bun:defs.bzl", "bun_bundle")
load("//bun:defs.bzl", "bun_build", "bun_bundle", "bun_compile")
load("@rules_shell//shell:sh_test.bzl", "sh_test")
bun_bundle(
@@ -18,6 +18,122 @@ bun_bundle(
external = ["left-pad"],
)
bun_build(
name = "site_build",
entry_points = ["site/index.html"],
data = [
"site/main.ts",
"site/styles.css",
],
splitting = True,
)
bun_build(
name = "site_build_with_meta",
entry_points = ["site/index.html"],
data = [
"site/main.ts",
"site/styles.css",
],
metafile = True,
metafile_md = True,
)
bun_build(
name = "advanced_site_build",
tags = ["manual"],
entry_points = ["site/index.html"],
data = [
"site/main.ts",
"site/styles.css",
],
install_mode = "fallback",
target = "node",
format = "cjs",
production = True,
splitting = True,
root = "tests/bundle_test/site",
sourcemap = "linked",
banner = "/* bundle banner */",
footer = "// bundle footer",
public_path = "/static/",
packages = "external",
external = [
"left-pad",
"react",
],
entry_naming = "entries/[name]-[hash].[ext]",
chunk_naming = "chunks/[name]-[hash].[ext]",
asset_naming = "assets/[name]-[hash].[ext]",
minify = True,
minify_syntax = True,
minify_whitespace = True,
minify_identifiers = True,
keep_names = True,
css_chunking = True,
conditions = [
"browser",
"custom",
],
env = "PUBLIC_*",
define = [
"process.env.NODE_ENV:\"production\"",
"__DEV__:false",
],
drop = [
"console",
"debugger",
],
feature = [
"react_fast_refresh",
"server_components",
],
loader = [
".svg:file",
".txt:text",
],
jsx_factory = "h",
jsx_fragment = "Fragment",
jsx_import_source = "preact",
jsx_runtime = "automatic",
jsx_side_effects = True,
react_fast_refresh = True,
emit_dce_annotations = True,
no_bundle = True,
build_flags = [
"--app",
"--server-components",
],
)
bun_compile(
name = "compiled_cli",
entry_point = "cli.ts",
)
bun_compile(
name = "compiled_cli_with_flags",
tags = ["manual"],
entry_point = "cli.ts",
bytecode = True,
compile_exec_argv = [
"--smol",
"--inspect-wait",
],
compile_executable = "fake_cross_bun.bin",
compile_autoload_dotenv = False,
compile_autoload_bunfig = False,
compile_autoload_tsconfig = True,
compile_autoload_package_json = True,
windows_hide_console = True,
windows_icon = "branding/icon.ico",
windows_title = "Rules Bun Test App",
windows_publisher = "rules_bun",
windows_version = "1.2.3.4",
windows_description = "compile flag coverage",
windows_copyright = "(c) rules_bun",
)
sh_test(
name = "bundle_output_test",
srcs = ["verify_bundle.sh"],
@@ -57,3 +173,55 @@ sh_test(
"//tests/bundle_test:BUILD.bazel",
],
)
sh_test(
name = "bundle_sourcemap_shape_test",
srcs = ["verify_sourcemap_shape.sh"],
env_inherit = ["PATH"],
data = [
"//:repo_runtime_files",
"//bun:repo_runtime_files",
"//internal:repo_runtime_files",
"BUILD.bazel",
"//tests/bundle_test/sourcemap_case:BUILD.bazel",
"//tests/bundle_test/sourcemap_case:entry.ts",
],
)
sh_test(
name = "bun_build_site_output_test",
srcs = ["verify_site_build.sh"],
args = ["$(location :site_build)"],
data = [":site_build"],
)
sh_test(
name = "bun_build_site_meta_test",
srcs = ["verify_site_build_meta.sh"],
args = ["$(locations :site_build_with_meta)"],
data = [":site_build_with_meta"],
)
sh_test(
name = "bun_compile_output_test",
srcs = ["run_compiled_binary.sh"],
args = ["$(location :compiled_cli)"],
data = [":compiled_cli"],
)
sh_test(
name = "bun_build_compile_flag_shape_test",
srcs = ["verify_flag_aquery.sh"],
env_inherit = ["PATH"],
data = [
"//:repo_runtime_files",
"//bun:repo_runtime_files",
"//internal:repo_runtime_files",
"BUILD.bazel",
"cli.ts",
"fake_cross_bun.bin",
"site/index.html",
"site/main.ts",
"site/styles.css",
],
)

1
tests/bundle_test/cli.ts Normal file
View File

@@ -0,0 +1 @@
console.log("compiled-cli");

View File

@@ -0,0 +1 @@
placeholder

11
tests/bundle_test/out.js Normal file
View File

@@ -0,0 +1,11 @@
// tests/bundle_test/main.ts
function greet(name) {
return `Hello ${name}`;
}
console.log(greet("bundle"));
export {
greet
};
//# debugId=A86FEBA7FCC390B664756E2164756E21
//# sourceMappingURL=out.js.map

View File

@@ -0,0 +1,10 @@
{
"version": 3,
"sources": ["tests/bundle_test/main.ts"],
"sourcesContent": [
"export function greet(name: string): string {\n return `Hello ${name}`;\n}\n\nconsole.log(greet(\"bundle\"));\n"
],
"mappings": ";AAAO,SAAS,KAAK,CAAC,MAAsB;AAAA,EAC1C,OAAO,SAAS;AAAA;AAGlB,QAAQ,IAAI,MAAM,QAAQ,CAAC;",
"debugId": "A86FEBA7FCC390B664756E2164756E21",
"names": []
}

View File

@@ -0,0 +1,10 @@
#!/usr/bin/env bash
set -euo pipefail
binary="$1"
output="$(${binary})"
if [[ ${output} != "compiled-cli" ]]; then
echo "Unexpected output from compiled binary ${binary}: ${output}" >&2
exit 1
fi

View File

@@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>rules_bun site build</title>
<link rel="stylesheet" href="./styles.css">
</head>
<body>
<div id="app">rules_bun</div>
<script type="module" src="./main.ts"></script>
</body>
</html>

View File

@@ -0,0 +1 @@
document.getElementById("app")?.setAttribute("data-built", "yes");

View File

@@ -0,0 +1,4 @@
body {
background: #f5efe2;
color: #1f1b14;
}

View File

@@ -0,0 +1,11 @@
// ../../../../../../../Projects/rules_bun/tests/bundle_test/main.ts
function greet(name) {
return `Hello ${name}`;
}
console.log(greet("bundle"));
export {
greet
};
//# debugId=D8717FECBBDCEC7764756E2164756E21
//# sourceMappingURL=sourcemap_bundle__main.js.map

View File

@@ -0,0 +1,10 @@
{
"version": 3,
"sources": ["../../../../../../../Projects/rules_bun/tests/bundle_test/main.ts"],
"sourcesContent": [
"export function greet(name: string): string {\n return `Hello ${name}`;\n}\n\nconsole.log(greet(\"bundle\"));\n"
],
"mappings": ";AAAO,SAAS,KAAK,CAAC,MAAsB;AAAA,EAC1C,OAAO,SAAS;AAAA;AAGlB,QAAQ,IAAI,MAAM,QAAQ,CAAC;",
"debugId": "D8717FECBBDCEC7764756E2164756E21",
"names": []
}

View File

@@ -0,0 +1,13 @@
load("//bun:defs.bzl", "bun_bundle")
exports_files([
"BUILD.bazel",
"entry.ts",
])
bun_bundle(
name = "sourcemap_bundle",
tags = ["manual"],
entry_points = ["entry.ts"],
sourcemap = True,
)

View File

@@ -0,0 +1,3 @@
const message: string = "sourcemap coverage";
console.log(message);

View File

@@ -3,12 +3,12 @@ set -euo pipefail
bundle="$1"
if [[ ! -f "${bundle}" ]]; then
if [[ ! -f ${bundle} ]]; then
echo "Bundle output not found: ${bundle}" >&2
exit 1
fi
if [[ ! -s "${bundle}" ]]; then
if [[ ! -s ${bundle} ]]; then
echo "Bundle output is empty: ${bundle}" >&2
exit 1
fi

View File

@@ -4,7 +4,7 @@ set -euo pipefail
rule_file="$1"
build_file="$2"
grep -Eq 'for package in ctx\.attr\.external:' "${rule_file}"
grep -Eq 'args\.add\("--external"\)' "${rule_file}"
grep -Eq 'add_bun_build_common_flags\(args, ctx\.attr\)' "${rule_file}"
grep -Eq '"external": attr\.string_list\(' "${rule_file}"
grep -Eq 'name = "external_bundle"' "${build_file}"
grep -Eq 'external = \["left-pad"\]' "${build_file}"

View File

@@ -0,0 +1,170 @@
#!/usr/bin/env bash
set -euo pipefail
if command -v bazel >/dev/null 2>&1; then
bazel_cmd=(bazel)
elif command -v bazelisk >/dev/null 2>&1; then
bazel_cmd=(bazelisk)
else
echo "bazel or bazelisk is required on PATH" >&2
exit 1
fi
find_workspace_root() {
local candidate
local module_path
local script_dir
for candidate in \
"${TEST_SRCDIR:-}/${TEST_WORKSPACE:-}" \
"${TEST_SRCDIR:-}/_main"; do
if [[ -n ${candidate} && -f "${candidate}/MODULE.bazel" ]]; then
printf '%s\n' "${candidate}"
return 0
fi
done
if [[ -n ${TEST_SRCDIR:-} ]]; then
module_path="$(find "${TEST_SRCDIR}" -maxdepth 3 -name MODULE.bazel -print -quit 2>/dev/null || true)"
if [[ -n ${module_path} ]]; then
dirname "${module_path}"
return 0
fi
fi
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
candidate="$(cd "${script_dir}/../.." && pwd -P)"
if [[ -f "${candidate}/MODULE.bazel" ]]; then
printf '%s\n' "${candidate}"
return 0
fi
echo "Unable to locate rules_bun workspace root" >&2
exit 1
}
rules_bun_root="$(find_workspace_root)"
run_aquery() {
local mnemonic="$1"
local target="$2"
(
cd "${rules_bun_root}" &&
"${bazel_cmd[@]}" aquery "mnemonic(\"${mnemonic}\", ${target})" --output=textproto
)
}
expect_line() {
local output="$1"
local expected="$2"
if ! grep -Fq -- "${expected}" <<<"${output}"; then
echo "Expected aquery output to contain: ${expected}" >&2
exit 1
fi
}
build_output="$(run_aquery "BunBuild" "//tests/bundle_test:advanced_site_build")"
for expected in \
'arguments: "--install"' \
'arguments: "fallback"' \
'arguments: "--target"' \
'arguments: "node"' \
'arguments: "--format"' \
'arguments: "cjs"' \
'arguments: "--production"' \
'arguments: "--splitting"' \
'arguments: "--root"' \
'arguments: "tests/bundle_test/site"' \
'arguments: "--sourcemap"' \
'arguments: "linked"' \
'arguments: "--banner"' \
'arguments: "/* bundle banner */"' \
'arguments: "--footer"' \
'arguments: "// bundle footer"' \
'arguments: "--public-path"' \
'arguments: "/static/"' \
'arguments: "--packages"' \
'arguments: "external"' \
'arguments: "left-pad"' \
'arguments: "react"' \
'arguments: "--entry-naming"' \
'arguments: "entries/[name]-[hash].[ext]"' \
'arguments: "--chunk-naming"' \
'arguments: "chunks/[name]-[hash].[ext]"' \
'arguments: "--asset-naming"' \
'arguments: "assets/[name]-[hash].[ext]"' \
'arguments: "--minify"' \
'arguments: "--minify-syntax"' \
'arguments: "--minify-whitespace"' \
'arguments: "--minify-identifiers"' \
'arguments: "--keep-names"' \
'arguments: "--css-chunking"' \
'arguments: "--conditions"' \
'arguments: "browser"' \
'arguments: "custom"' \
'arguments: "--env"' \
'arguments: "PUBLIC_*"' \
'arguments: "process.env.NODE_ENV:\"production\""' \
'arguments: "__DEV__:false"' \
'arguments: "console"' \
'arguments: "debugger"' \
'arguments: "react_fast_refresh"' \
'arguments: "server_components"' \
'arguments: ".svg:file"' \
'arguments: ".txt:text"' \
'arguments: "--jsx-factory"' \
'arguments: "h"' \
'arguments: "--jsx-fragment"' \
'arguments: "Fragment"' \
'arguments: "--jsx-import-source"' \
'arguments: "preact"' \
'arguments: "--jsx-runtime"' \
'arguments: "automatic"' \
'arguments: "--jsx-side-effects"' \
'arguments: "--react-fast-refresh"' \
'arguments: "--emit-dce-annotations"' \
'arguments: "--no-bundle"' \
'arguments: "--app"' \
'arguments: "--server-components"'; do
expect_line "${build_output}" "${expected}"
done
default_root_output="$(run_aquery "BunBuild" "//tests/bundle_test:site_build_with_meta")"
for expected in \
'arguments: "--root"' \
'arguments: "tests/bundle_test/site"'; do
expect_line "${default_root_output}" "${expected}"
done
compile_output="$(run_aquery "BunCompile" "//tests/bundle_test:compiled_cli_with_flags")"
for expected in \
'arguments: "--bytecode"' \
'arguments: "--compile-exec-argv"' \
'arguments: "--smol"' \
'arguments: "--inspect-wait"' \
'arguments: "--no-compile-autoload-dotenv"' \
'arguments: "--no-compile-autoload-bunfig"' \
'arguments: "--compile-autoload-tsconfig"' \
'arguments: "--compile-autoload-package-json"' \
'arguments: "--compile-executable-path"' \
'arguments: "tests/bundle_test/fake_cross_bun.bin"' \
'arguments: "--windows-hide-console"' \
'arguments: "--windows-icon"' \
'arguments: "branding/icon.ico"' \
'arguments: "--windows-title"' \
'arguments: "Rules Bun Test App"' \
'arguments: "--windows-publisher"' \
'arguments: "rules_bun"' \
'arguments: "--windows-version"' \
'arguments: "1.2.3.4"' \
'arguments: "--windows-description"' \
'arguments: "compile flag coverage"' \
'arguments: "--windows-copyright"' \
'arguments: "(c) rules_bun"'; do
expect_line "${compile_output}" "${expected}"
done

View File

@@ -0,0 +1,14 @@
#!/usr/bin/env bash
set -euo pipefail
output_dir="$1"
if [[ ! -d ${output_dir} ]]; then
echo "Expected output directory: ${output_dir}" >&2
exit 1
fi
if ! find -L "${output_dir}" -type f \( -name '*.js' -o -name '*.css' \) | grep -q .; then
echo "Expected Bun build assets in ${output_dir}" >&2
exit 1
fi

View File

@@ -0,0 +1,29 @@
#!/usr/bin/env bash
set -euo pipefail
output_dir=""
meta_json=""
meta_md=""
for path in "$@"; do
case "${path}" in
*.meta.json) meta_json="${path}" ;;
*.meta.md) meta_md="${path}" ;;
*) output_dir="${path}" ;;
esac
done
if [[ ! -d ${output_dir} ]]; then
echo "Expected directory output, got: ${output_dir}" >&2
exit 1
fi
if [[ ! -f ${meta_json} ]]; then
echo "Expected JSON metafile output" >&2
exit 1
fi
if [[ ! -f ${meta_md} ]]; then
echo "Expected markdown metafile output" >&2
exit 1
fi

View File

@@ -0,0 +1,60 @@
#!/usr/bin/env bash
set -euo pipefail
if command -v bazel >/dev/null 2>&1; then
bazel_cmd=(bazel)
elif command -v bazelisk >/dev/null 2>&1; then
bazel_cmd=(bazelisk)
else
echo "bazel or bazelisk is required on PATH" >&2
exit 1
fi
find_workspace_root() {
local candidate
local module_path
local script_dir
for candidate in \
"${TEST_SRCDIR:-}/${TEST_WORKSPACE:-}" \
"${TEST_SRCDIR:-}/_main"; do
if [[ -n ${candidate} && -f "${candidate}/MODULE.bazel" ]]; then
printf '%s\n' "${candidate}"
return 0
fi
done
if [[ -n ${TEST_SRCDIR:-} ]]; then
module_path="$(find "${TEST_SRCDIR}" -maxdepth 3 -name MODULE.bazel -print -quit 2>/dev/null || true)"
if [[ -n ${module_path} ]]; then
dirname "${module_path}"
return 0
fi
fi
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
candidate="$(cd "${script_dir}/../.." && pwd -P)"
if [[ -f "${candidate}/MODULE.bazel" ]]; then
printf '%s\n' "${candidate}"
return 0
fi
echo "Unable to locate rules_bun workspace root" >&2
exit 1
}
rules_bun_root="$(find_workspace_root)"
bundle_output="$(
cd "${rules_bun_root}" &&
"${bazel_cmd[@]}" aquery 'mnemonic("BunBundle", //tests/bundle_test/sourcemap_case:sourcemap_bundle)' --output=textproto
)"
count="$(grep -Fc 'arguments: "--sourcemap"' <<<"${bundle_output}")"
if [[ ${count} != "1" ]]; then
echo "Expected bun_bundle(sourcemap = True) to emit exactly one --sourcemap flag, got ${count}" >&2
exit 1
fi
grep -Fq 'arguments: "--outfile"' <<<"${bundle_output}"
grep -Fq 'arguments: "tests/bundle_test/sourcemap_case/entry.ts"' <<<"${bundle_output}"

View File

@@ -6,3 +6,10 @@ sh_test(
args = ["$(location //bun:extensions.bzl)"],
data = ["//bun:extensions.bzl"],
)
sh_test(
name = "npm_translate_lock_extension_shape_test",
srcs = ["npm_extension_shape_test.sh"],
args = ["$(location //npm:extensions.bzl)"],
data = ["//npm:extensions.bzl"],
)

View File

@@ -6,5 +6,7 @@ extension_file="$1"
grep -Eq 'bun_install[[:space:]]*=[[:space:]]*module_extension\(' "${extension_file}"
grep -Eq 'tag_classes[[:space:]]*=[[:space:]]*\{"install":[[:space:]]*_install\}' "${extension_file}"
grep -Eq '"name":[[:space:]]*attr\.string\(mandatory[[:space:]]*=[[:space:]]*True\)' "${extension_file}"
grep -Eq '"package_json":[[:space:]]*attr\.string\(mandatory[[:space:]]*=[[:space:]]*True\)' "${extension_file}"
grep -Eq '"bun_lockfile":[[:space:]]*attr\.string\(mandatory[[:space:]]*=[[:space:]]*True\)' "${extension_file}"
grep -Eq '"package_json":[[:space:]]*attr\.label\(mandatory[[:space:]]*=[[:space:]]*True\)' "${extension_file}"
grep -Eq '"bun_lockfile":[[:space:]]*attr\.label\(mandatory[[:space:]]*=[[:space:]]*True\)' "${extension_file}"
grep -Eq '"install_inputs":[[:space:]]*attr\.label_list\(allow_files[[:space:]]*=[[:space:]]*True\)' "${extension_file}"
grep -Eq '"isolated_home":[[:space:]]*attr\.bool\(default[[:space:]]*=[[:space:]]*True\)' "${extension_file}"

View File

@@ -0,0 +1,10 @@
#!/usr/bin/env bash
set -euo pipefail
extension_file="$1"
grep -Eq 'npm_translate_lock[[:space:]]*=[[:space:]]*module_extension\(' "${extension_file}"
grep -Eq 'tag_classes[[:space:]]*=[[:space:]]*\{"translate":[[:space:]]*_translate\}' "${extension_file}"
grep -Eq '"name":[[:space:]]*attr\.string\(mandatory[[:space:]]*=[[:space:]]*True\)' "${extension_file}"
grep -Eq '"package_json":[[:space:]]*attr\.label\(mandatory[[:space:]]*=[[:space:]]*True\)' "${extension_file}"
grep -Eq '"lockfile":[[:space:]]*attr\.label\(mandatory[[:space:]]*=[[:space:]]*True\)' "${extension_file}"

View File

@@ -102,3 +102,53 @@ sh_test(
"//conditions:default": ["@bun_linux_x64//:bun"],
}),
)
sh_test(
name = "bun_install_workspaces_catalog_test",
srcs = ["workspaces_catalog.sh"],
args = select({
":linux_x86_64": ["$(location @bun_linux_x64//:bun)"],
":linux_aarch64": ["$(location @bun_linux_aarch64//:bun)"],
":darwin_x86_64": ["$(location @bun_darwin_x64//:bun)"],
":darwin_aarch64": ["$(location @bun_darwin_aarch64//:bun)"],
"//conditions:default": ["$(location @bun_linux_x64//:bun)"],
}),
data = select({
":linux_x86_64": ["@bun_linux_x64//:bun"],
":linux_aarch64": ["@bun_linux_aarch64//:bun"],
":darwin_x86_64": ["@bun_darwin_x64//:bun"],
":darwin_aarch64": ["@bun_darwin_aarch64//:bun"],
"//conditions:default": ["@bun_linux_x64//:bun"],
}),
)
sh_test(
name = "bun_install_workspace_parity_test",
srcs = ["workspace_parity.sh"],
env_inherit = ["PATH"],
args = select({
":linux_x86_64": ["$(location @bun_linux_x64//:bun)"],
":linux_aarch64": ["$(location @bun_linux_aarch64//:bun)"],
":darwin_x86_64": ["$(location @bun_darwin_x64//:bun)"],
":darwin_aarch64": ["$(location @bun_darwin_aarch64//:bun)"],
"//conditions:default": ["$(location @bun_linux_x64//:bun)"],
}),
data = select({
":linux_x86_64": ["@bun_linux_x64//:bun"],
":linux_aarch64": ["@bun_linux_aarch64//:bun"],
":darwin_x86_64": ["@bun_darwin_x64//:bun"],
":darwin_aarch64": ["@bun_darwin_aarch64//:bun"],
"//conditions:default": ["@bun_linux_x64//:bun"],
}) + [
"//:repo_runtime_files",
"//bun:repo_runtime_files",
"//internal:repo_runtime_files",
],
)
sh_test(
name = "bun_install_install_flags_shape_test",
srcs = ["install_flags_shape.sh"],
args = ["$(location //internal:bun_install.bzl)"],
data = ["//internal:bun_install.bzl"],
)

View File

@@ -4,8 +4,11 @@ set -euo pipefail
rule_file="$1"
grep -Eq 'install", "--frozen-lockfile", "--no-progress"' "${rule_file}"
grep -Eq 'repository_ctx\.file\("package\.json", repository_ctx\.read\(package_json\)\)' "${rule_file}"
grep -Eq 'repository_ctx\.file\("package\.json", _normalized_root_manifest\(repository_ctx, package_json\)\)' "${rule_file}"
grep -Eq 'lockfile_name = bun_lockfile\.basename' "${rule_file}"
grep -Eq 'if lockfile_name not in \["bun\.lock", "bun\.lockb"\]:' "${rule_file}"
grep -Eq 'repository_ctx\.symlink\(bun_lockfile, lockfile_name\)' "${rule_file}"
grep -Eq 'glob\(\["node_modules/\*\*"\]' "${rule_file}"
grep -Eq 'glob\(\["\*\*/node_modules/\*\*"\]' "${rule_file}"
grep -Eq '_DEFAULT_INSTALL_INPUTS = \[' "${rule_file}"
grep -Eq '"install_inputs": attr\.label_list\(allow_files = True\)' "${rule_file}"
grep -Eq '_materialize_install_inputs\(repository_ctx, package_json\)' "${rule_file}"

View File

@@ -3,5 +3,8 @@ set -euo pipefail
rule_file="$1"
grep -Eq '\[str\(bun_bin\), "--bun", "install", "--frozen-lockfile", "--no-progress"\]' "${rule_file}"
grep -Eq 'install_args = \[str\(bun_bin\), "--bun", "install", "--frozen-lockfile", "--no-progress"\]' "${rule_file}"
grep -Eq 'if repository_ctx\.attr\.isolated_home:' "${rule_file}"
grep -Eq 'environment[[:space:]]*=[[:space:]]*\{"HOME":[[:space:]]*str\(repository_ctx\.path\("\."\)\)\}' "${rule_file}"
grep -Eq '"isolated_home": attr\.bool\(default = True\)' "${rule_file}"
grep -Eq '"install_flags": attr\.string_list\(\)' "${rule_file}"

View File

@@ -0,0 +1,16 @@
#!/usr/bin/env bash
set -euo pipefail
rule_file="$1"
grep -Fq 'repository_ctx.attr.production' "${rule_file}"
grep -Fq '"--production"' "${rule_file}"
grep -Fq 'for omit in repository_ctx.attr.omit' "${rule_file}"
grep -Fq '"--omit"' "${rule_file}"
grep -Fq 'repository_ctx.attr.linker' "${rule_file}"
grep -Fq '"--linker"' "${rule_file}"
grep -Fq 'repository_ctx.attr.backend' "${rule_file}"
grep -Fq '"--backend"' "${rule_file}"
grep -Fq 'repository_ctx.attr.ignore_scripts' "${rule_file}"
grep -Fq '"--ignore-scripts"' "${rule_file}"
grep -Fq 'repository_ctx.attr.install_flags' "${rule_file}"

View File

@@ -0,0 +1,517 @@
#!/usr/bin/env bash
set -euo pipefail
bun_path="${1:-bun}"
if command -v bazel >/dev/null 2>&1; then
bazel_cmd=(bazel)
elif command -v bazelisk >/dev/null 2>&1; then
bazel_cmd=(bazelisk)
else
echo "bazel or bazelisk is required on PATH" >&2
exit 1
fi
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
rules_bun_root="$(cd "${script_dir}/../.." && pwd -P)"
workdir="$(mktemp -d)"
trap 'rm -rf "${workdir}"' EXIT
fixture_dir="${workdir}/fixture"
plain_dir="${workdir}/plain"
bazel_dir="${workdir}/bazel"
mkdir -p "${fixture_dir}/packages/pkg-a" "${fixture_dir}/packages/pkg-b" "${fixture_dir}/packages/web"
cat >"${fixture_dir}/package.json" <<'JSON'
{
"name": "workspace-parity-root",
"private": true,
"workspaces": ["packages/*"]
}
JSON
cat >"${fixture_dir}/packages/pkg-a/package.json" <<'JSON'
{
"name": "@workspace/pkg-a",
"version": "1.0.0",
"main": "index.js"
}
JSON
cat >"${fixture_dir}/packages/pkg-a/index.js" <<'JS'
module.exports = { value: 42 };
JS
cat >"${fixture_dir}/packages/pkg-b/package.json" <<'JSON'
{
"name": "@workspace/pkg-b",
"version": "1.0.0",
"dependencies": {
"@workspace/pkg-a": "workspace:*",
"is-number": "7.0.0"
}
}
JSON
cat >"${fixture_dir}/packages/web/package.json" <<'JSON'
{
"name": "@workspace/web",
"private": true,
"type": "module",
"scripts": {
"build": "vite build"
},
"devDependencies": {
"vite": "5.4.14"
}
}
JSON
cat >"${fixture_dir}/packages/web/index.html" <<'HTML'
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Workspace Parity Web</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="./main.js"></script>
</body>
</html>
HTML
cat >"${fixture_dir}/packages/web/main.js" <<'JS'
import { value } from "./value.js";
const app = document.querySelector("#app");
if (app) {
app.textContent = `value=${value}`;
}
JS
cat >"${fixture_dir}/packages/web/value.js" <<'JS'
export const value = 42;
JS
cat >"${fixture_dir}/packages/web/vite.config.js" <<'JS'
export default {
resolve: {
preserveSymlinks: true,
},
optimizeDeps: {
esbuildOptions: {
preserveSymlinks: true,
},
},
};
JS
"${bun_path}" install --cwd "${fixture_dir}" >/dev/null
rm -rf "${fixture_dir}/node_modules" "${fixture_dir}/packages/pkg-b/node_modules"
cp -R "${fixture_dir}" "${plain_dir}"
cp -R "${fixture_dir}" "${bazel_dir}"
"${bun_path}" install --cwd "${plain_dir}" --frozen-lockfile >/dev/null
cat >"${bazel_dir}/MODULE.bazel" <<EOF
module(
name = "workspace_parity_test",
)
bazel_dep(name = "rules_bun", version = "0.2.2")
bazel_dep(name = "rules_shell", version = "0.6.1")
local_path_override(
module_name = "rules_bun",
path = "${rules_bun_root}",
)
bun_ext = use_extension("@rules_bun//bun:extensions.bzl", "bun")
use_repo(
bun_ext,
"bun_darwin_aarch64",
"bun_darwin_x64",
"bun_linux_aarch64",
"bun_linux_x64",
"bun_windows_x64",
)
bun_install_ext = use_extension("@rules_bun//bun:extensions.bzl", "bun_install")
bun_install_ext.install(
name = "node_modules",
package_json = "//:package.json",
bun_lockfile = "//:bun.lock",
)
use_repo(bun_install_ext, "node_modules")
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",
)
EOF
cat >"${bazel_dir}/BUILD.bazel" <<'EOF'
load("@rules_bun//bun:defs.bzl", "bun_script")
load("@rules_shell//shell:sh_test.bzl", "sh_test")
exports_files([
"package.json",
"bun.lock",
"node_modules_smoke_test.sh",
])
bun_script(
name = "web_build",
script = "build",
package_json = "packages/web/package.json",
node_modules = "@node_modules//:node_modules",
data = [
"packages/web/index.html",
"packages/web/main.js",
"packages/web/value.js",
"packages/web/vite.config.js",
],
)
sh_test(
name = "node_modules_smoke_test",
srcs = ["node_modules_smoke_test.sh"],
data = ["@node_modules//:node_modules"],
)
EOF
cat >"${bazel_dir}/node_modules_smoke_test.sh" <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
runfiles_dir="${RUNFILES_DIR:-$0.runfiles}"
if ! find "${runfiles_dir}" -path '*/node_modules/.bin/vite' -print -quit | grep -q .; then
echo "vite binary not found in runfiles node_modules/.bin" >&2
exit 1
fi
EOF
chmod +x "${bazel_dir}/node_modules_smoke_test.sh"
(
cd "${bazel_dir}"
"${bazel_cmd[@]}" build @node_modules//:node_modules >/dev/null
"${bazel_cmd[@]}" test //:node_modules_smoke_test >/dev/null
"${bazel_cmd[@]}" run //:web_build -- --emptyOutDir >/dev/null
)
output_base="$(cd "${bazel_dir}" && "${bazel_cmd[@]}" info output_base)"
bazel_repo_dir="$(find "${output_base}/external" -maxdepth 1 -type d -name '*+node_modules' | head -n 1)"
if [[ -z ${bazel_repo_dir} ]]; then
echo "Could not locate generated Bazel node_modules repository" >&2
exit 1
fi
bazel_node_modules="${bazel_repo_dir}/node_modules"
plain_node_modules="${plain_dir}/node_modules"
if [[ ! -d ${plain_node_modules} ]]; then
echo "Plain Bun install did not produce node_modules" >&2
exit 1
fi
if [[ ! -d ${bazel_node_modules} ]]; then
echo "Bazel bun_install did not produce node_modules" >&2
exit 1
fi
plain_layout_manifest="${workdir}/plain.layout.manifest"
bazel_layout_manifest="${workdir}/bazel.layout.manifest"
python3 - "${plain_dir}" >"${plain_layout_manifest}" <<'PY'
import hashlib
import os
import stat
import sys
root = sys.argv[1]
def include(rel):
if rel == "node_modules" or rel.startswith("node_modules/"):
if rel == "node_modules/.rules_bun" or rel.startswith("node_modules/.rules_bun/"):
return False
return True
if rel.startswith("packages/") and "/node_modules" in rel:
return True
return False
for dirpath, dirnames, filenames in os.walk(root, topdown=True, followlinks=False):
dirnames.sort()
filenames.sort()
rel_dir = os.path.relpath(dirpath, root)
if rel_dir == ".":
rel_dir = ""
for name in dirnames + filenames:
full = os.path.join(dirpath, name)
rel = os.path.join(rel_dir, name) if rel_dir else name
if not include(rel):
continue
st = os.lstat(full)
mode = st.st_mode
if stat.S_ISLNK(mode):
print(f"L {rel} -> {os.readlink(full)}")
elif stat.S_ISDIR(mode):
print(f"D {rel}")
elif stat.S_ISREG(mode):
h = hashlib.sha256()
with open(full, "rb") as f:
while True:
chunk = f.read(1024 * 1024)
if not chunk:
break
h.update(chunk)
print(f"F {rel} {h.hexdigest()}")
else:
print(f"O {rel} {mode}")
PY
python3 - "${bazel_repo_dir}" >"${bazel_layout_manifest}" <<'PY'
import hashlib
import os
import stat
import sys
root = sys.argv[1]
def include(rel):
if rel == "node_modules" or rel.startswith("node_modules/"):
if rel == "node_modules/.rules_bun" or rel.startswith("node_modules/.rules_bun/"):
return False
return True
if rel.startswith("packages/") and "/node_modules" in rel:
return True
return False
for dirpath, dirnames, filenames in os.walk(root, topdown=True, followlinks=False):
dirnames.sort()
filenames.sort()
rel_dir = os.path.relpath(dirpath, root)
if rel_dir == ".":
rel_dir = ""
for name in dirnames + filenames:
full = os.path.join(dirpath, name)
rel = os.path.join(rel_dir, name) if rel_dir else name
if not include(rel):
continue
st = os.lstat(full)
mode = st.st_mode
if stat.S_ISLNK(mode):
print(f"L {rel} -> {os.readlink(full)}")
elif stat.S_ISDIR(mode):
print(f"D {rel}")
elif stat.S_ISREG(mode):
h = hashlib.sha256()
with open(full, "rb") as f:
while True:
chunk = f.read(1024 * 1024)
if not chunk:
break
h.update(chunk)
print(f"F {rel} {h.hexdigest()}")
else:
print(f"O {rel} {mode}")
PY
if ! diff -u "${plain_layout_manifest}" "${bazel_layout_manifest}"; then
echo "Workspace node_modules layout differs between plain bun install and Bazel bun_install" >&2
exit 1
fi
plain_manifest="${workdir}/plain.manifest"
bazel_manifest="${workdir}/bazel.manifest"
python3 - "${plain_node_modules}" >"${plain_manifest}" <<'PY'
import hashlib
import os
import stat
import sys
root = sys.argv[1]
for dirpath, dirnames, filenames in os.walk(root, topdown=True, followlinks=False):
dirnames.sort()
filenames.sort()
rel_dir = os.path.relpath(dirpath, root)
if rel_dir == ".":
rel_dir = ""
for name in dirnames + filenames:
full = os.path.join(dirpath, name)
rel = os.path.join(rel_dir, name) if rel_dir else name
if rel == ".rules_bun" or rel.startswith(".rules_bun/"):
continue
st = os.lstat(full)
mode = st.st_mode
if stat.S_ISLNK(mode):
print(f"L {rel} -> {os.readlink(full)}")
elif stat.S_ISDIR(mode):
print(f"D {rel}")
elif stat.S_ISREG(mode):
h = hashlib.sha256()
with open(full, "rb") as f:
while True:
chunk = f.read(1024 * 1024)
if not chunk:
break
h.update(chunk)
print(f"F {rel} {h.hexdigest()}")
else:
print(f"O {rel} {mode}")
PY
python3 - "${bazel_node_modules}" >"${bazel_manifest}" <<'PY'
import hashlib
import os
import stat
import sys
root = sys.argv[1]
for dirpath, dirnames, filenames in os.walk(root, topdown=True, followlinks=False):
dirnames.sort()
filenames.sort()
rel_dir = os.path.relpath(dirpath, root)
if rel_dir == ".":
rel_dir = ""
for name in dirnames + filenames:
full = os.path.join(dirpath, name)
rel = os.path.join(rel_dir, name) if rel_dir else name
if rel == ".rules_bun" or rel.startswith(".rules_bun/"):
continue
st = os.lstat(full)
mode = st.st_mode
if stat.S_ISLNK(mode):
print(f"L {rel} -> {os.readlink(full)}")
elif stat.S_ISDIR(mode):
print(f"D {rel}")
elif stat.S_ISREG(mode):
h = hashlib.sha256()
with open(full, "rb") as f:
while True:
chunk = f.read(1024 * 1024)
if not chunk:
break
h.update(chunk)
print(f"F {rel} {h.hexdigest()}")
else:
print(f"O {rel} {mode}")
PY
if ! diff -u "${plain_manifest}" "${bazel_manifest}"; then
echo "node_modules trees differ between plain bun install and Bazel bun_install" >&2
exit 1
fi
plain_dist_dir="${workdir}/plain-dist"
bazel_dist_dir="${workdir}/bazel-dist"
rm -rf "${plain_dist_dir}" "${bazel_dist_dir}"
"${bun_path}" run --cwd "${plain_dir}/packages/web" build -- --emptyOutDir --outDir "${plain_dist_dir}" >/dev/null
(
cd "${bazel_dir}"
"${bazel_cmd[@]}" run //:web_build -- --emptyOutDir --outDir "${bazel_dist_dir}" >/dev/null
)
if [[ ! -d ${plain_dist_dir} ]]; then
echo "Plain Bun Vite build did not produce output" >&2
exit 1
fi
if [[ ! -d ${bazel_dist_dir} ]]; then
echo "Bazel Vite build did not produce output" >&2
exit 1
fi
plain_build_manifest="${workdir}/plain.build.manifest"
bazel_build_manifest="${workdir}/bazel.build.manifest"
python3 - "${plain_dist_dir}" >"${plain_build_manifest}" <<'PY'
import hashlib
import os
import stat
import sys
root = sys.argv[1]
for dirpath, dirnames, filenames in os.walk(root, topdown=True, followlinks=False):
dirnames.sort()
filenames.sort()
rel_dir = os.path.relpath(dirpath, root)
if rel_dir == ".":
rel_dir = ""
for name in dirnames + filenames:
full = os.path.join(dirpath, name)
rel = os.path.join(rel_dir, name) if rel_dir else name
st = os.lstat(full)
mode = st.st_mode
if stat.S_ISLNK(mode):
print(f"L {rel} -> {os.readlink(full)}")
elif stat.S_ISDIR(mode):
print(f"D {rel}")
elif stat.S_ISREG(mode):
h = hashlib.sha256()
with open(full, "rb") as f:
while True:
chunk = f.read(1024 * 1024)
if not chunk:
break
h.update(chunk)
print(f"F {rel} {h.hexdigest()}")
else:
print(f"O {rel} {mode}")
PY
python3 - "${bazel_dist_dir}" >"${bazel_build_manifest}" <<'PY'
import hashlib
import os
import stat
import sys
root = sys.argv[1]
for dirpath, dirnames, filenames in os.walk(root, topdown=True, followlinks=False):
dirnames.sort()
filenames.sort()
rel_dir = os.path.relpath(dirpath, root)
if rel_dir == ".":
rel_dir = ""
for name in dirnames + filenames:
full = os.path.join(dirpath, name)
rel = os.path.join(rel_dir, name) if rel_dir else name
st = os.lstat(full)
mode = st.st_mode
if stat.S_ISLNK(mode):
print(f"L {rel} -> {os.readlink(full)}")
elif stat.S_ISDIR(mode):
print(f"D {rel}")
elif stat.S_ISREG(mode):
h = hashlib.sha256()
with open(full, "rb") as f:
while True:
chunk = f.read(1024 * 1024)
if not chunk:
break
h.update(chunk)
print(f"F {rel} {h.hexdigest()}")
else:
print(f"O {rel} {mode}")
PY
if ! diff -u "${plain_build_manifest}" "${bazel_build_manifest}"; then
echo "Vite build outputs differ between plain Bun and Bazel bun_script" >&2
exit 1
fi

View File

@@ -0,0 +1,81 @@
#!/usr/bin/env bash
set -euo pipefail
bun_path="$1"
workdir="$(mktemp -d)"
trap 'rm -rf "${workdir}"' EXIT
mkdir -p "${workdir}/packages/pkg-a" "${workdir}/packages/pkg-b" "${workdir}/packages/web"
cat >"${workdir}/package.json" <<'JSON'
{
"name": "workspace-catalog-root",
"private": true,
"workspaces": {
"packages": ["packages/*"],
"catalog": {
"is-number": "7.0.0",
"vite": "5.4.14"
},
"catalogs": {
"testing": {
"vitest": "3.2.4"
}
}
}
}
JSON
cat >"${workdir}/packages/pkg-a/package.json" <<'JSON'
{
"name": "@workspace/pkg-a",
"version": "1.0.0",
"main": "index.js",
"dependencies": {
"is-number": "catalog:"
},
"scripts": {
"check": "bun -e \"const version = require('is-number/package.json').version; if (version !== '7.0.0') { console.error(version); process.exit(1); }\""
}
}
JSON
cat >"${workdir}/packages/pkg-a/index.js" <<'JS'
module.exports = { value: 42 };
JS
cat >"${workdir}/packages/pkg-b/package.json" <<'JSON'
{
"name": "@workspace/pkg-b",
"version": "1.0.0",
"dependencies": {
"@workspace/pkg-a": "workspace:*",
"is-number": "catalog:"
},
"scripts": {
"check": "bun -e \"const { value } = require('@workspace/pkg-a'); const version = require('is-number/package.json').version; if (value !== 42 || version !== '7.0.0') { console.error({ value, version }); process.exit(1); }\""
}
}
JSON
cat >"${workdir}/packages/web/package.json" <<'JSON'
{
"name": "@workspace/web",
"private": true,
"devDependencies": {
"vite": "catalog:",
"vitest": "catalog:testing"
},
"scripts": {
"check": "bun -e \"const viteVersion = require('vite/package.json').version; const vitestVersion = require('vitest/package.json').version; if (viteVersion !== '5.4.14' || vitestVersion !== '3.2.4') { console.error({ viteVersion, vitestVersion }); process.exit(1); }\""
}
}
JSON
"${bun_path}" install --cwd "${workdir}" >/dev/null
rm -rf "${workdir}/node_modules" "${workdir}/packages/"*/node_modules
"${bun_path}" install --cwd "${workdir}" --frozen-lockfile >/dev/null
"${bun_path}" run --cwd "${workdir}/packages/pkg-a" check >/dev/null
"${bun_path}" run --cwd "${workdir}/packages/pkg-b" check >/dev/null
"${bun_path}" run --cwd "${workdir}/packages/web" check >/dev/null

View File

@@ -4,6 +4,7 @@ test_suite(
name = "examples_test",
tests = [
":examples_basic_run_e2e_test",
":examples_basic_hot_restart_shape_test",
":examples_workspace_bundle_e2e_test",
":examples_workspace_catalog_shape_test",
":examples_vite_monorepo_catalog_shape_test",
@@ -31,6 +32,13 @@ sh_test(
data = ["//examples/basic:web_dev"],
)
sh_test(
name = "examples_basic_hot_restart_shape_test",
srcs = ["examples_basic_hot_restart_shape_test.sh"],
args = ["$(location //examples/basic:web_dev_hot_restart)"],
data = ["//examples/basic:web_dev_hot_restart"],
)
sh_test(
name = "examples_workspace_bundle_e2e_test",
srcs = ["examples_workspace_bundle_e2e_test.sh"],

View File

@@ -4,6 +4,6 @@ set -euo pipefail
build_file="$1"
readme_file="$2"
[[ -f "${build_file}" ]]
[[ -f "${readme_file}" ]]
[[ -f ${build_file} ]]
[[ -f ${readme_file} ]]
grep -Eq '^package\(default_visibility = \["//visibility:public"\]\)$' "${build_file}"

View File

@@ -0,0 +1,11 @@
#!/usr/bin/env bash
set -euo pipefail
binary="$1"
grep -Fq -- 'watch_mode="hot"' "${binary}"
grep -Fq -- 'bun_args+=("--hot")' "${binary}"
grep -Fq -- '--no-clear-screen' "${binary}"
grep -Fq -- 'if [[ 1 -eq 0 ]]; then' "${binary}"
grep -Fq -- 'readarray -t restart_paths' "${binary}"
grep -Fq -- 'examples/basic/README.md' "${binary}"

Some files were not shown because too many files have changed in this diff Show More