diff --git a/CHANGELOG.md b/CHANGELOG.md index 1418fd4..68544c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - 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/` + 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 @@ -36,6 +42,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `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) diff --git a/README.md b/README.md index aefe80d..eef6df6 100644 --- a/README.md +++ b/README.md @@ -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. @@ -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 diff --git a/docs/MIGRATING-0.3.md b/docs/MIGRATING-0.3.md index e38fa53..6dcd494 100644 --- a/docs/MIGRATING-0.3.md +++ b/docs/MIGRATING-0.3.md @@ -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/` 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 | @@ -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: diff --git a/examples/build.mill b/examples/build.mill index a904533..12e20a9 100644 --- a/examples/build.mill +++ b/examples/build.mill @@ -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 diff --git a/millbun/integration/resources/typescript-bundle/build.mill b/millbun/integration/resources/typescript-bundle/build.mill index 02c1ede..3b296da 100644 --- a/millbun/integration/resources/typescript-bundle/build.mill +++ b/millbun/integration/resources/typescript-bundle/build.mill @@ -18,5 +18,5 @@ object app extends BunTypeScriptModule { ) } override def bunBundleTarget = Task { "bun" } - override def bunBundleFormat = Task { "esm" } + override def bunBundleFormat = Task { Some("esm") } } diff --git a/millbun/integration/src/mill/bun/BunScalaJSIntegrationTests.scala b/millbun/integration/src/mill/bun/BunScalaJSIntegrationTests.scala index da59704..0836c27 100644 --- a/millbun/integration/src/mill/bun/BunScalaJSIntegrationTests.scala +++ b/millbun/integration/src/mill/bun/BunScalaJSIntegrationTests.scala @@ -123,10 +123,12 @@ object BunScalaJSIntegrationTests extends TestSuite { assert(runBundledScript(mainJs) == "Hello from transitive scala.js bun") } - test("bunTest") { + test("testForked runs Scala.js tests on Bun") { val tester = this.tester("scalajs-test") - val res = tester.eval("app.test.bunTest") + val res = tester.eval("app.test.testForked") assert(res.isSuccess) + // The deprecated alias must keep resolving until removal. + assert(tester.eval("app.test.bunTest").isSuccess) } test("bunfig propagates to Scala.js workspaces without leaking .npmrc") { @@ -150,7 +152,7 @@ object BunScalaJSIntegrationTests extends TestSuite { assert(os.exists(compileWorkspace / "bunfig.toml")) assert(!os.exists(compileWorkspace / ".npmrc")) - val testRes = tester.eval("app.test.bunTest") + val testRes = tester.eval("app.test.testForked") assert(testRes.isSuccess) val testRoot = tester.workspacePath / "out" / "app" / "test" assert(os.exists(testRoot)) diff --git a/millbun/integration/src/mill/bun/BunTypeScriptIntegrationTests.scala b/millbun/integration/src/mill/bun/BunTypeScriptIntegrationTests.scala index f4854a8..8f4dfef 100644 --- a/millbun/integration/src/mill/bun/BunTypeScriptIntegrationTests.scala +++ b/millbun/integration/src/mill/bun/BunTypeScriptIntegrationTests.scala @@ -86,25 +86,25 @@ object BunTypeScriptIntegrationTests extends TestSuite { test("strict installs require a source lock and bunLock creates it") { val tester = this.tester("typescript-lock") - val missingLock = tester.eval("app.npmInstall") + val missingLock = tester.eval("app.bunInstall") assert(!missingLock.isSuccess) val lockResult = tester.eval("app.bunLock") assert(lockResult.isSuccess) assert(os.exists(tester.workspacePath / "bun.lock")) - val installResult = tester.eval("app.npmInstall") + val installResult = tester.eval("app.bunInstall") assert(installResult.isSuccess) - val args = os.read(tester.workspacePath / "out" / "app" / "npmInstall.dest" / ".bun-args") + val args = os.read(tester.workspacePath / "out" / "app" / "bunInstall.dest" / ".bun-args") assert(args.contains("--frozen-lockfile")) } test("bun target ambient types are pinned") { val tester = this.tester("typescript-simple") - val res = tester.eval("app.npmInstall") + val res = tester.eval("app.bunInstall") assert(res.isSuccess) - val packageJson = ujson.read(os.read(tester.workspacePath / "out" / "app" / "npmInstall.dest" / "package.json")) + val packageJson = ujson.read(os.read(tester.workspacePath / "out" / "app" / "bunInstall.dest" / "package.json")) val devDeps = packageJson("devDependencies").obj // Pinned, and pinned to the Bun we ship: @types/bun is published in lockstep with Bun. @@ -118,7 +118,7 @@ object BunTypeScriptIntegrationTests extends TestSuite { val res = tester.eval("app.compile") assert(res.isSuccess) - val packageJson = ujson.read(os.read(tester.workspacePath / "out" / "app" / "npmInstall.dest" / "package.json")) + val packageJson = ujson.read(os.read(tester.workspacePath / "out" / "app" / "bunInstall.dest" / "package.json")) val devDeps = packageJson("devDependencies").obj assert(devDeps("typescript").str == "5.7.3") @@ -128,8 +128,12 @@ object BunTypeScriptIntegrationTests extends TestSuite { test("bun test module") { val tester = this.tester("typescript-tests") - val res = tester.eval("app.test.test") + val res = tester.eval("app.test.testForked") assert(res.isSuccess) + // Deprecated and inherited aliases must keep resolving until removal. + assert(tester.eval("app.test.test").isSuccess) + assert(tester.eval("app.npmInstall").isSuccess) + assert(outputPath(tester, "app.npmInstall") == tester.workspacePath / "out" / "app" / "bunInstall.dest") } test("bundle workers") { @@ -165,7 +169,7 @@ object BunTypeScriptIntegrationTests extends TestSuite { val res = tester.eval("app.compile") assert(res.isSuccess) - val installDir = tester.workspacePath / "out" / "app" / "npmInstall.dest" + val installDir = tester.workspacePath / "out" / "app" / "bunInstall.dest" val compileDir = tester.workspacePath / "out" / "app" / "compile.dest" // Install workspace keeps both configs. @@ -183,16 +187,16 @@ object BunTypeScriptIntegrationTests extends TestSuite { assert(tester.eval("app.test.bunLock").isSuccess) // Outer module should have is-even in dependencies - val outerRes = tester.eval("app.npmInstall") + val outerRes = tester.eval("app.bunInstall") assert(outerRes.isSuccess) - val outerPkg = ujson.read(os.read(tester.workspacePath / "out" / "app" / "npmInstall.dest" / "package.json")) + val outerPkg = ujson.read(os.read(tester.workspacePath / "out" / "app" / "bunInstall.dest" / "package.json")) assert(outerPkg("dependencies").obj.contains("is-even")) assert(!outerPkg("dependencies").obj.contains("is-odd")) // Test module should have is-odd in devDependencies (not dependencies) - val testRes = tester.eval("app.test.npmInstall") + val testRes = tester.eval("app.test.bunInstall") assert(testRes.isSuccess) - val testPkg = ujson.read(os.read(tester.workspacePath / "out" / "app" / "test" / "npmInstall.dest" / "package.json")) + val testPkg = ujson.read(os.read(tester.workspacePath / "out" / "app" / "test" / "bunInstall.dest" / "package.json")) assert(testPkg("devDependencies").obj.contains("is-odd")) assert(!testPkg("dependencies").obj.contains("is-odd")) assert(!testPkg("devDependencies").obj.contains("is-even")) @@ -200,7 +204,7 @@ object BunTypeScriptIntegrationTests extends TestSuite { assert(testPkg("dependencies").obj.contains("is-even")) // Tests should actually run (both deps available) - val runRes = tester.eval("app.test.test") + val runRes = tester.eval("app.test.testForked") assert(runRes.isSuccess) } @@ -211,7 +215,7 @@ object BunTypeScriptIntegrationTests extends TestSuite { val tester = this.tester("typescript-test-deps") // Without any lock, the install refuses and names the test module's own path. - val unlocked = tester.eval("app.test.npmInstall") + val unlocked = tester.eval("app.test.bunInstall") assert(!unlocked.isSuccess) assert(tester.eval("app.bunLock").isSuccess) @@ -234,8 +238,8 @@ object BunTypeScriptIntegrationTests extends TestSuite { assert(declaredDeps(testLock).contains("is-odd")) // The frozen install now succeeds against the test module's own lock. - assert(tester.eval("app.test.npmInstall").isSuccess) - assert(tester.eval("app.test.test").isSuccess) + assert(tester.eval("app.test.bunInstall").isSuccess) + assert(tester.eval("app.test.testForked").isSuccess) } test("unmanaged local packages install under a frozen lockfile") { @@ -248,8 +252,8 @@ object BunTypeScriptIntegrationTests extends TestSuite { // The lock must not record where this repository happens to be checked out. assert(!lock.contains(tester.workspacePath.toString)) - assert(tester.eval("app.npmInstall").isSuccess) - val installed = outputPath(tester, "app.npmInstall") + assert(tester.eval("app.bunInstall").isSuccess) + val installed = outputPath(tester, "app.bunInstall") assert(os.exists(installed / "node_modules" / "local-lib" / "package.json")) assert(tester.eval("app.bundle").isSuccess) @@ -261,12 +265,12 @@ object BunTypeScriptIntegrationTests extends TestSuite { test("test modules adding nothing reuse the outer install") { // A bare test module must not demand a second lockfile. val tester = this.tester("typescript-tests") - assert(tester.eval("app.test.npmInstall").isSuccess) + assert(tester.eval("app.test.bunInstall").isSuccess) assert(!os.exists(tester.workspacePath / "test" / "bun.lock")) // And must actually reuse the outer install — not run a second one that merely succeeds // because the lockfile requirement happens to be off in this suite. - val installPath = outputPath(tester, "app.test.npmInstall") - assert(installPath == tester.workspacePath / "out" / "app" / "npmInstall.dest") + val installPath = outputPath(tester, "app.test.bunInstall") + assert(installPath == tester.workspacePath / "out" / "app" / "bunInstall.dest") } test("bunEnv") { @@ -274,7 +278,7 @@ object BunTypeScriptIntegrationTests extends TestSuite { val res = tester.eval("app.bundle") assert(res.isSuccess) - val installLog = os.read(tester.workspacePath / "out" / "app" / "npmInstall.dest" / ".bun-env-log") + val installLog = os.read(tester.workspacePath / "out" / "app" / "bunInstall.dest" / ".bun-env-log") val compileLog = os.read(tester.workspacePath / "out" / "app" / "compile.dest" / ".bun-env-log") val bundleLog = os.read(tester.workspacePath / "out" / "app" / "bundle.dest" / ".bun-env-log") diff --git a/millbun/integration/src/mill/bun/BunWorkspaceIntegrationTests.scala b/millbun/integration/src/mill/bun/BunWorkspaceIntegrationTests.scala index 51aee0f..6475eef 100644 --- a/millbun/integration/src/mill/bun/BunWorkspaceIntegrationTests.scala +++ b/millbun/integration/src/mill/bun/BunWorkspaceIntegrationTests.scala @@ -48,8 +48,8 @@ object BunWorkspaceIntegrationTests extends TestSuite: assert(!os.read(workspaceInstall / ".workspace-installed").contains("shared-local")) val scalaResult = tester.eval("scalaApp.bunInstall") - val typescriptResult = tester.eval("typescriptApp.npmInstall") + val typescriptResult = tester.eval("typescriptApp.bunInstall") assert(scalaResult.isSuccess) assert(typescriptResult.isSuccess) assert(os.isLink(outputPath(tester, "scalaApp.bunInstall") / "node_modules")) - assert(os.isLink(outputPath(tester, "typescriptApp.npmInstall") / "node_modules")) + assert(os.isLink(outputPath(tester, "typescriptApp.bunInstall") / "node_modules")) diff --git a/millbun/src/mill/bun/BunToolchainModule.scala b/millbun/src/mill/bun/BunToolchainModule.scala index 5cb1f34..5db38be 100644 --- a/millbun/src/mill/bun/BunToolchainModule.scala +++ b/millbun/src/mill/bun/BunToolchainModule.scala @@ -535,6 +535,9 @@ trait BunToolchainModule extends Module { /** Environment passed to Bun subprocesses. */ def bunEnv: T[Map[String, String]] = Task { Map.empty } + /** Environment for Bun toolchain subprocesses: install, lock, build, and bundle. */ + def bunToolEnv: T[Map[String, String]] = Task { bunEnv() } + /** Explicit resolutions for conflicting transitive npm dependency declarations. */ def npmOverrides: T[Map[String, String]] = Task { Map.empty } diff --git a/millbun/src/mill/bun/BunWorkersModule.scala b/millbun/src/mill/bun/BunWorkersModule.scala index cb50d8a..db095c9 100644 --- a/millbun/src/mill/bun/BunWorkersModule.scala +++ b/millbun/src/mill/bun/BunWorkersModule.scala @@ -33,7 +33,7 @@ trait BunWorkersModule extends BunToolchainModule { this: BunTypeScriptModule => def workerBundleTarget: T[String] = Task { bunBundleTarget() } /** Output format for worker bundles. Defaults to the module format. */ - def workerBundleFormat: T[Option[String]] = Task { Some(bunBundleFormat()) } + def workerBundleFormat: T[Option[String]] = Task { bunBundleFormat() } /** Extra raw flags for worker bundling. */ def workerBundleArgs: T[Seq[String]] = Task { Seq.empty } @@ -61,7 +61,7 @@ trait BunWorkersModule extends BunToolchainModule { this: BunTypeScriptModule => val outDir = Task.dest / "workers" // Declared explicitly: the staged tree carries a node_modules symlink into this install, // and Mill's filesystem checker only permits reading a dest we depend on. - npmInstall() + bunInstall() BunToolchainModule.copyWorkspace(compile().path, workspace) os.makeDir.all(outDir) @@ -94,7 +94,7 @@ trait BunWorkersModule extends BunToolchainModule { this: BunTypeScriptModule => target ) ++ formatArgs ++ extraArgs, cwd = workspace, - env = bunEnv() + env = bunToolEnv() ) } diff --git a/millbun/src/mill/bun/BunWorkspaceModule.scala b/millbun/src/mill/bun/BunWorkspaceModule.scala index fd5b4d5..dfc3892 100644 --- a/millbun/src/mill/bun/BunWorkspaceModule.scala +++ b/millbun/src/mill/bun/BunWorkspaceModule.scala @@ -127,7 +127,7 @@ trait BunWorkspaceModule extends BunToolchainModule: updateLockfile = false ), cwd = Task.dest, - env = bunEnv() + env = bunToolEnv() ) PathRef(Task.dest) } @@ -147,7 +147,7 @@ trait BunWorkspaceModule extends BunToolchainModule: updateLockfile = true ), cwd = Task.dest, - env = bunEnv() + env = bunToolEnv() ) val generated = Task.dest / "bun.lock" diff --git a/millbun/src/mill/javascriptlib/bun/BunTypeScriptModule.scala b/millbun/src/mill/javascriptlib/bun/BunTypeScriptModule.scala index 6fcedde..a0490db 100644 --- a/millbun/src/mill/javascriptlib/bun/BunTypeScriptModule.scala +++ b/millbun/src/mill/javascriptlib/bun/BunTypeScriptModule.scala @@ -24,8 +24,13 @@ trait BunTypeScriptModule extends TypeScriptModule with BunToolchainModule with /** Target used by `bun build`: browser | bun | node. */ def bunBundleTarget: T[String] = Task { "bun" } - /** Output format used by `bun build`. */ - def bunBundleFormat: T[String] = Task { if (enableEsm()) "esm" else "cjs" } + /** Output format passed to `bun build --format`; `None` lets bun infer. + * + * `Option[String]` to match the Scala.js module's member of the same name — a `T[String]` + * here made the two module kinds' shared vocabulary diverge on type, which no alias can + * bridge. 0.3.0 takes the one-time break. + */ + def bunBundleFormat: T[Option[String]] = Task { Some(if (enableEsm()) "esm" else "cjs") } /** Emit a standalone executable instead of a JS bundle. */ @deprecated("Use the compileExecutable task", "0.3.0") @@ -49,11 +54,8 @@ trait BunTypeScriptModule extends TypeScriptModule with BunToolchainModule with /** Bun-only package.json fields not modeled by Mill's typed PackageJson. */ def bunPackageJsonExtras: T[ujson.Obj] = Task { ujson.Obj() } - /** Environment for Bun toolchain subprocesses such as install/build/test. */ - protected def bunToolEnv: T[Map[String, String]] = Task { bunEnv() } - /** Runtime environment for Bun-executed programs and tests. */ - protected def bunRuntimeEnv: T[Map[String, String]] = Task { bunEnv() ++ forkEnv() } + def bunRuntimeEnv: T[Map[String, String]] = Task { bunEnv() ++ forkEnv() } /** TypeScript version used for `bun x tsc`. */ def typeScriptVersion: T[String] = Task { "5.7.3" } @@ -146,8 +148,8 @@ trait BunTypeScriptModule extends TypeScriptModule with BunToolchainModule with } } - /** Replace npm install with bun install. */ - override def npmInstall: T[PathRef] = Task { + /** Install dependencies with Bun. The canonical name; Mill's [[npmInstall]] delegates here. */ + def bunInstall: T[PathRef] = Task { val dest = Task.dest os.makeDir.all(dest) mkBunPackageJson() @@ -183,6 +185,9 @@ trait BunTypeScriptModule extends TypeScriptModule with BunToolchainModule with PathRef(dest) } + /** Mill's inherited install name; delegates to [[bunInstall]]. */ + override def npmInstall: T[PathRef] = Task { bunInstall() } + /** Resolve dependencies and update the source-controlled `bun.lock`. */ def bunLock(): Command[PathRef] = Task.Command { if bunWorkspaceInstall().nonEmpty then @@ -222,7 +227,7 @@ trait BunTypeScriptModule extends TypeScriptModule with BunToolchainModule with tscCopyGenSources() tscLinkResources() BunTypeScriptModule.removeInstallOnlyConfigs(Task.dest) - ensureInstallArtifacts(Task.dest, npmInstall().path, bunLockfiles()) + ensureInstallArtifacts(Task.dest, bunInstall().path, bunLockfiles()) BunTypeScriptModule.copyBunfigsTo(Task.dest, resolvedBunfigs()) mkTsconfig() @@ -237,7 +242,7 @@ trait BunTypeScriptModule extends TypeScriptModule with BunToolchainModule with } override def createNodeModulesSymlink: Task[Unit] = Task.Anon { - ensureInstallArtifacts(Task.dest, npmInstall().path, bunLockfiles()) + ensureInstallArtifacts(Task.dest, bunInstall().path, bunLockfiles()) } /** Run the entrypoint directly with Bun. */ @@ -291,7 +296,7 @@ trait BunTypeScriptModule extends TypeScriptModule with BunToolchainModule with // Declared explicitly: the staged tree carries a node_modules symlink into this // install, and Mill's filesystem checker only permits reading a dest we depend on. - npmInstall() + bunInstall() BunToolchainModule.copyWorkspace(compileDir, buildDir) BunTypeScriptModule.removeInstallOnlyConfigs(buildDir) BunTypeScriptModule.copyBunfigsTo(buildDir, resolvedBunfigs()) @@ -309,10 +314,9 @@ trait BunTypeScriptModule extends TypeScriptModule with BunToolchainModule with "--outfile", outFile.toString, "--target", - bunBundleTarget(), - "--format", - bunBundleFormat() - ) ++ packagesExternal ++ externalArgs ++ compileArgs ++ bunBuildArgs(), + bunBundleTarget() + ) ++ bunBundleFormat().toSeq.flatMap(format => Seq("--format", format)) + ++ packagesExternal ++ externalArgs ++ compileArgs ++ bunBuildArgs(), cwd = buildDir, env = bunToolEnv() ) @@ -329,7 +333,7 @@ trait BunTypeScriptModule extends TypeScriptModule with BunToolchainModule with // Declared explicitly: the staged tree carries a node_modules symlink into this // install, and Mill's filesystem checker only permits reading a dest we depend on. - npmInstall() + bunInstall() BunToolchainModule.copyWorkspace(compileDir, buildDir) BunTypeScriptModule.removeInstallOnlyConfigs(buildDir) BunTypeScriptModule.copyBunfigsTo(buildDir, resolvedBunfigs()) @@ -369,7 +373,7 @@ trait BunTypeScriptModule extends TypeScriptModule with BunToolchainModule with val mainFile = resolvedEntrypoint(mainFilePath(), compileDir).relativeTo(compileDir).toString // Declared explicitly: the staged tree carries a node_modules symlink into this // install, and Mill's filesystem checker only permits reading a dest we depend on. - npmInstall() + bunInstall() BunToolchainModule.copyWorkspace(compileDir, buildDir) BunTypeScriptModule.removeInstallOnlyConfigs(buildDir) BunTypeScriptModule.copyBunfigsTo(buildDir, resolvedBunfigs()) @@ -499,8 +503,9 @@ trait BunTypeScriptModule extends TypeScriptModule with BunToolchainModule with bunTestPackageJson().render() == outer.bunWorkspacePackageJson().render() } - override def npmInstall: T[PathRef] = Task { - if (reusesOuterInstall()) outer.npmInstall() + /** Install this test module's dependencies with Bun; reuses the outer install when equal. */ + def bunInstall: T[PathRef] = Task { + if (reusesOuterInstall()) outer.bunInstall() else { val dest = Task.dest os.makeDir.all(dest) @@ -540,6 +545,9 @@ trait BunTypeScriptModule extends TypeScriptModule with BunToolchainModule with } } + /** Mill's inherited install name; delegates to [[bunInstall]]. */ + override def npmInstall: T[PathRef] = Task { bunInstall() } + /** * Resolve this test module's dependencies and update its own `bun.lock`. * @@ -584,7 +592,7 @@ trait BunTypeScriptModule extends TypeScriptModule with BunToolchainModule with val dest = Task.dest BunToolchainModule.copyWorkspace(this.compile().path, dest) BunTypeScriptModule.removeInstallOnlyConfigs(dest) - outer.ensureInstallArtifacts(dest, npmInstall().path, bunLockfiles()) + outer.ensureInstallArtifacts(dest, bunInstall().path, bunLockfiles()) BunTypeScriptModule.copyBunfigsTo(dest, outer.resolvedBunfigs()) PathRef(dest) } @@ -601,6 +609,18 @@ trait BunTypeScriptModule extends TypeScriptModule with BunToolchainModule with bunTestArgs() ++ timeoutArgs ++ reporterArgs } + /** Run `bun test`. Named after Mill's standard test entrypoint, so both module kinds share it. */ + def testForked(args: mill.api.Args): Command[CommandResult] = Task.Command { + os.call( + Seq(bunExecutable(), "test") ++ resolvedTestFlags() ++ args.value, + cwd = preparedTestWorkspace().path, + env = outer.bunRuntimeEnv(), + stdout = os.Inherit, + stderr = os.Inherit + ) + } + + @deprecated("Use testForked", "0.3.0") def test(args: mill.api.Args): Command[CommandResult] = Task.Command { os.call( Seq(bunExecutable(), "test") ++ resolvedTestFlags() ++ args.value, diff --git a/millbun/src/mill/javascriptlib/bun/BunTypeScriptWebModule.scala b/millbun/src/mill/javascriptlib/bun/BunTypeScriptWebModule.scala index 22c9ac6..98de0c3 100644 --- a/millbun/src/mill/javascriptlib/bun/BunTypeScriptWebModule.scala +++ b/millbun/src/mill/javascriptlib/bun/BunTypeScriptWebModule.scala @@ -65,7 +65,7 @@ trait BunTypeScriptWebModule extends BunTypeScriptModule: sources() ++ generatedSources() ++ resources(), webEntryPoints(), webPublicSources(), - npmInstall().path, + bunInstall().path, bunfigFiles(), webScriptEntryPoint().path ) diff --git a/millbun/src/mill/scalajslib/bun/BunPublishModule.scala b/millbun/src/mill/scalajslib/bun/BunPublishModule.scala index 74025de..0fb3d6f 100644 --- a/millbun/src/mill/scalajslib/bun/BunPublishModule.scala +++ b/millbun/src/mill/scalajslib/bun/BunPublishModule.scala @@ -98,7 +98,7 @@ trait BunPublishModule extends BunScalaJSModule { updateLockfile = false ), cwd = dest, - env = bunEnv() + env = bunToolEnv() ) PathRef(dest) diff --git a/millbun/src/mill/scalajslib/bun/BunScalaJSModule.scala b/millbun/src/mill/scalajslib/bun/BunScalaJSModule.scala index 18316ba..2f29847 100644 --- a/millbun/src/mill/scalajslib/bun/BunScalaJSModule.scala +++ b/millbun/src/mill/scalajslib/bun/BunScalaJSModule.scala @@ -301,7 +301,7 @@ trait BunScalaJSModule extends ScalaJSModule with BunToolchainModule with BunPac updateLockfile = false ), cwd = dest, - env = bunEnv() + env = bunToolEnv() ) mergeVendoredNodeModules(vendoredEntries, dest / "node_modules") @@ -332,7 +332,7 @@ trait BunScalaJSModule extends ScalaJSModule with BunToolchainModule with BunPac updateLockfile = true ), cwd = dest, - env = bunEnv() + env = bunToolEnv() ) val generated = dest / "bun.lock" @@ -449,7 +449,7 @@ trait BunScalaJSModule extends ScalaJSModule with BunToolchainModule with BunPac bytecodeArgs ++ bunBundleArgs(), cwd = linked.dest.path, - env = bunEnv() + env = bunToolEnv() ) PathRef(outDir) @@ -481,7 +481,7 @@ trait BunScalaJSModule extends ScalaJSModule with BunToolchainModule with BunPac splittingArgs ++ bunBundleArgs(), cwd = linked.dest.path, - env = bunEnv() + env = bunToolEnv() ) PathRef(outDir) @@ -518,7 +518,7 @@ trait BunScalaJSModule extends ScalaJSModule with BunToolchainModule with BunPac bytecodeArgs ++ bunBundleArgs(), cwd = buildDir, - env = bunEnv() + env = bunToolEnv() ) PathRef(outFile) @@ -560,7 +560,7 @@ trait BunScalaJSModule extends ScalaJSModule with BunToolchainModule with BunPac bytecodeArgs ++ bunBundleArgs(), cwd = buildDir, - env = bunEnv() + env = bunToolEnv() ) target -> PathRef(outFile) @@ -624,7 +624,12 @@ trait BunScalaJSModule extends ScalaJSModule with BunToolchainModule with BunPac linked } - /** Run Scala.js tests through Mill's test bridge with Bun as the JS runtime. */ + /** Run Scala.js tests through Mill's test bridge with Bun as the JS runtime. + * + * The inherited `testForked` already does exactly this — the overridden [[jsEnvConfig]] and + * [[testLinkTask]] put every test run on Bun — so this alias adds nothing over it. + */ + @deprecated("Use the inherited testForked", "0.3.0") def bunTest(args: mill.api.Args): Command[(msg: String, results: Seq[mill.javalib.testrunner.TestResult])] = Task.Command { testTask( diff --git a/millbun/src/mill/scalajslib/bun/BunScalaJSWebModule.scala b/millbun/src/mill/scalajslib/bun/BunScalaJSWebModule.scala index 68a2f34..e7dfb84 100644 --- a/millbun/src/mill/scalajslib/bun/BunScalaJSWebModule.scala +++ b/millbun/src/mill/scalajslib/bun/BunScalaJSWebModule.scala @@ -94,7 +94,7 @@ trait BunScalaJSWebModule extends BunScalaJSModule: bunExecutable(), Seq("build") ++ entries.map(_.toString) ++ Seq("--minify", "--outdir", destination.toString) ++ bunBundleArgs(), cwd = stage, - env = bunEnv() + env = bunToolEnv() ) PathRef(destination) }