Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,29 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `BunWorkspaceModule` for one install and lockfile across mixed Scala.js/TypeScript packages.
- Published dependency manifest schema v2 with runtime, optional, and peer requirements.
- `bunDoctor` diagnostics and managed-toolchain CI smoke coverage.
- A committed lockfile written by a newer Bun fails frozen installs with regeneration guidance,
instead of surfacing bun's raw `UnknownLockfileVersion`.

### Changed

- Scala.js linking delegates to Mill's standard linker hooks; applications now choose `scalaJSVersion` explicitly.
- Development dependencies are local tooling inputs and are no longer published transitively.
- `bunPackageJsonExtras` rejects dependency fields now represented by typed settings.
- Missing dependency versions are represented as `latest` instead of an empty package.json value.
- TypeScript `bunBundleFormat` is `Option[String]`, matching Scala.js; `None` lets `bun build` infer.
- `unmanagedDeps` entries are staged into `vendor/` and declared as `file:./vendor/<name>`
dependencies, so local packages install under frozen lockfiles and locks stay portable.
- The TypeScript install task is canonically `bunInstall`; Mill's inherited `npmInstall` delegates to it.
- `bunToolEnv` is defined once on `BunToolchainModule` for all toolchain subprocesses, and the
TypeScript `bunRuntimeEnv` is public.

### Deprecated

- Scala.js `bunBundle*` and `bunCompile*` task names in favor of their canonical aliases.
- `bunOptionalDeps` in favor of `npmOptionalDeps`.
- `managedBunExecutable` in favor of `bunExecutableOverride`.
- The TypeScript `bunCompileExecutable: Boolean` switch in favor of the `compileExecutable` task.
- The TypeScript test command `test` and the Scala.js test command `bunTest`, both in favor of `testForked`.

## [0.2.1] - Overridable test-time JS env (2026-04-17)

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ Scala.js versions are explicit: choose the version your application tests agains
./mill app.run
./mill app.bundle
./mill app.compileExecutable
./mill app.test.test
./mill app.test.testForked
```

`BunScalaJSModule` delegates `fastLinkJS`, `fullLinkJS`, and test linking to Mill's standard `ScalaJSModule` hooks. That keeps the plugin compatible with Mill's linker lifecycle and removes its former private linker-worker coupling.
Expand Down Expand Up @@ -76,7 +76,7 @@ object app extends BunTypeScriptModule {
./mill app.run
./mill app.bundle
./mill app.compileExecutable
./mill app.test.test
./mill app.test.testForked
```

## Web applications
Expand Down
6 changes: 4 additions & 2 deletions build.mill
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,10 @@ object millbun extends ScalaModule, PublishModule {
// pins rather than whatever the developer happens to have on PATH. The shared
// download cache means all fixtures reuse one archive. Export MILL_BUN_USE_SYSTEM=true
// to run against a local Bun instead.
"MILL_BUN_USE_SYSTEM" -> Task.env.getOrElse("MILL_BUN_USE_SYSTEM", "false"),
"MILL_BUN_REQUIRE_LOCKFILE" -> Task.env.getOrElse("MILL_BUN_REQUIRE_LOCKFILE", "false")
"MILL_BUN_USE_SYSTEM" -> Task.env.getOrElse("MILL_BUN_USE_SYSTEM", "false")
// The lockfile requirement runs the SHIPPED default (strict): dependency-bearing
// fixtures commit a bun.lock, regenerated via mill.bun.RegenerateFixtureLocks, and
// fixtures that deliberately test the relaxed path opt out in their own build.mill.
)
}

Expand Down
27 changes: 27 additions & 0 deletions docs/MIGRATING-0.3.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,25 @@ Subsequent installs are frozen. During a staged migration only, set `MILL_BUN_RE

Use `npmDeps`, `npmDevDeps`, `npmOptionalDeps`, `npmPeerDeps`, and `npmOverrides`. `bunPackageJsonExtras` remains available for unmodeled fields such as `scripts`, but now rejects dependency sections so task invalidation and published metadata remain correct.

### `bunBundleFormat` is `Option[String]` on TypeScript modules

Scala.js and TypeScript previously disagreed on this member's type — `Option[String]` versus
`String` — which no deprecation alias can bridge, so 0.3 takes the one-time break. Both are now
`T[Option[String]]`; `None` lets `bun build` infer the format.

```scala
// 0.2
override def bunBundleFormat = Task { "esm" }
// 0.3
override def bunBundleFormat = Task { Some("esm") }
```

### Local packages via `unmanagedDeps` must be directories with a `package.json`

Entries are staged into `vendor/` beside the generated package.json and declared as
`file:./vendor/<name>` dependencies, so they now work under frozen lockfile installs and the
lockfile stays independent of the checkout path. Tarballs are no longer accepted — unpack them.

## Renamed APIs

| 0.2 name | 0.3 name | Status |
Expand All @@ -39,9 +58,17 @@ Use `npmDeps`, `npmDevDeps`, `npmOptionalDeps`, `npmPeerDeps`, and `npmOverrides
| `bunCompileExecutables` | `compileExecutables` | Compatibility alias retained |
| `bunOptionalDeps` | `npmOptionalDeps` | Deprecated compatibility setting |
| `managedBunExecutable` | `bunExecutableOverride` | Deprecated compatibility setting |
| `npmInstall` (TypeScript) | `bunInstall` | Mill's inherited name delegates to `bunInstall` and stays usable |
| `test` (TypeScript test modules) | `testForked` | Deprecated compatibility command |
| `bunTest` (Scala.js test modules) | `testForked` (inherited from Mill) | Deprecated compatibility command |

The old names are planned for removal at 1.0.

Environment hooks share one vocabulary: `bunToolEnv` (toolchain subprocesses — install, lock,
build) is defined on `BunToolchainModule` for every module kind, and the TypeScript
`bunRuntimeEnv` (program and test processes) is now public. Scala.js keeps `bunJsEnv` /
`bunJsEnvArgs` / `bunTestJsEnv` for its Scala.js-test JS environment, unchanged.

## Toolchain behavior

The default is now checksum-verified managed Bun 1.4.0. To preserve the old PATH behavior:
Expand Down
2 changes: 1 addition & 1 deletion examples/build.mill
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ object fullstack extends Module {
// No moduleDir or sources overrides needed.
//
// ./mill tsLib.compile # TypeScript type-check
// ./mill tsLib.test.test # Bun-native tests
// ./mill tsLib.test.testForked # Bun-native tests
// ./mill client.bundle # TS browser bundle (imports tsLib)
// ./mill scalaClient.bundle # Scala.js browser bundle
// ./mill backend.run # JVM Cask server on :8080
Expand Down
15 changes: 15 additions & 0 deletions millbun/integration/resources/scalajs-bundle/bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ trait StubBunModule extends BunScalaJSModule {
def scalaJSVersion = "1.22.0"
override def moduleKind = Task { ModuleKind.CommonJSModule }
override def bunExecutable = Task { "stub-bun" }
// The stub never produces a real lockfile; the strict path is covered by real-bun fixtures.
override def bunRequireLockfile = Task { false }

override protected def runBun(
bunExe: String,
Expand Down
4 changes: 2 additions & 2 deletions millbun/integration/resources/scalajs-transitive/build.mill
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ import mill.scalajslib.*
import mill.scalajslib.api.*
import mill.scalajslib.bun.*

// lib keeps its own moduleDir (lib/): sharing the root with app would make both modules
// resolve the same bun.lock, and only one dependency set can be recorded there.
object lib extends BunScalaJSModule {
override def moduleDir = build.moduleDir
def scalaVersion = "3.8.2"
def scalaJSVersion = "1.22.0"

override def moduleKind = Task { ModuleKind.ESModule }
override def sources = Task.Sources(moduleDir / "lib")
override def npmDeps = Task { Seq("lodash@4.17.21") }
}

Expand Down
15 changes: 15 additions & 0 deletions millbun/integration/resources/scalajs-transitive/bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions millbun/integration/resources/scalajs-web/bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions millbun/integration/resources/typescript-browser/bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion millbun/integration/resources/typescript-bundle/build.mill
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,5 @@ object app extends BunTypeScriptModule {
)
}
override def bunBundleTarget = Task { "bun" }
override def bunBundleFormat = Task { "esm" }
override def bunBundleFormat = Task { Some("esm") }
}
24 changes: 24 additions & 0 deletions millbun/integration/resources/typescript-bundle/bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 24 additions & 0 deletions millbun/integration/resources/typescript-bunfig/app/bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 24 additions & 0 deletions millbun/integration/resources/typescript-compile/bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 24 additions & 0 deletions millbun/integration/resources/typescript-env/bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 24 additions & 0 deletions millbun/integration/resources/typescript-simple/bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 30 additions & 0 deletions millbun/integration/resources/typescript-stale-lock/build.mill
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//| mill-version: 1.1.5
//| mill-jvm-version: system
//| mvnDeps:
//| - com.tjclp::mill-bun_mill1:0.0.0-NIGHTLY

package build

import mill.*
import mill.javascriptlib.bun.*

// Pins a Bun that cannot read the committed lockfileVersion (1.3.14 reads only v1; the lock
// says v2). The install must fail with regeneration guidance before bun ever runs — the stub
// proves no subprocess is reached.
object app extends BunTypeScriptModule {
override def moduleDir = build.moduleDir
override def bunVersion = Task { "1.3.14" }
override def bunVerifyVersion = Task { false }
override def bunExecutable = Task { "stub-bun" }
override def npmDeps = Task { Seq("is-even@1.0.0") }

override protected def runBun(
bunExe: String,
args: Seq[String],
cwd: os.Path,
env: Map[String, String]
): os.CommandResult = {
os.write.over(cwd / ".stub-bun-ran", args.mkString(" "))
os.call(Seq("true"), cwd = cwd, env = env)
}
}
11 changes: 11 additions & 0 deletions millbun/integration/resources/typescript-stale-lock/bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
console.log("never installs");
Loading
Loading