feat: unify package management through Mill with BunModule traits#27
feat: unify package management through Mill with BunModule traits#27
Conversation
Add BunModule and BunNpmModule traits to build.mill that: - BunModule: Uses Bun as Scala.js runtime, overrides run to call bun directly - BunNpmModule: Declares npm deps in build.mill, generates package.json, runs bun install via Mill tasks (bunInstall, generatePackageJson, bunOutdated) NPM dependencies are now the single source of truth in build.mill. package.json is generated via `./mill agent.generatePackageJson` for IDE compat. Tests use Node.js for the JSEnv test adapter (stdin/com protocol), while all runtime execution uses Bun. These traits will be extracted to a standalone mill-scalajs-bun plugin repo once validated. Closes #26 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Code Review — PR #27: Unify package management through Mill with BunModule traitsOverall this is a solid design direction — making Bugs1. def bunLockFile: T[PathRef] = Task.Source(moduleDir / "bun.lock")
def bunLockFile: T[PathRef] = Task.Source(npmProjectDir() / "bun.lock")(Note: this needs careful handling since 2.
3. val jsFile = linked.dest.path / linked.publicModules.head.jsFileNameThis appears in both linked.publicModules.headOption match {
case Some(m) => linked.dest.path / m.jsFileName
case None => Task.fail("No public modules found in fastLinkJS output")
}Design Concerns4. Triple
5. The slimmed-down
The PR description says "NPM dependencies are now the single source of truth in build.mill" but the committed 6. def generatePackageJson() = Task.Command {
val dir = npmProjectDir()
os.write.over(dir / "package.json", content) // writes to project root!Writing outside Minor7. Generated
def npmPackageName: T[Option[String]] = Task { None }8. def bunOutdated() = Task.Command {
val dest = bunInstall().path // triggers install!Checking for outdated packages shouldn't require installing first. Users may want to run 9. Boilerplate inheritance in Overriding 4 tasks to delegate to override def npmDeps = agent.npmDeps
override def npmDevDeps = agent.npmDevDeps
override def npmPeerDeps = agent.npmPeerDeps
override def npmProjectDir = agent.npmProjectDirFor the plugin extraction, consider a design that supports declaring a "parent npm module" so deps are inherited automatically. For now, a comment like What's good
The bugs in items 1–3 are worth fixing before merge. Items 4–5 are design decisions that should at minimum be explicitly documented. 🤖 Generated with Claude Code |
Summary
BunModuleandBunNpmModuletraits tobuild.millthat unify Scala and npm dependency management through MillBunModule: Uses Bun as Scala.js runtime, overridesrunto call bun directlyBunNpmModule: Declares npm deps inbuild.mill, generatespackage.json, runsbun installvia Mill tasksbuild.mill— no more manualpackage.jsonsyncmill-scalajs-bunplugin repo once validatedNew Mill tasks
./mill agent.bunInstall./mill agent.generatePackageJson./mill agent.bunOutdated./mill agent.runWhy this is better
The root problem: JS library versions in Scala facades drift from npm versions in
package.json. SDK bumps (like #25) require manual sync across both. Anthropic pushes patches nearly daily.With
BunNpmModule, npm deps are declared alongside Maven deps inbuild.mill. Downstream agents (e.g. typst2pptx-scala-agent) can inherit version requirements from scalagent's build graph, eliminating the class of bugs where Scala facades reference types that don't exist in the installed npm version.Design decisions
bunInstallwrites toTask.dest(Mill convention), not project root.bun.lockis copied in/out for reproducible installs.generatePackageJsonis aTask.Commandthat writes to project root for TypeScript LSP / IDE tooling.Test plan
./mill agent.compilepasses./mill agent.testpasses (all tests green)./mill examples.compilepasses./mill agent.bunInstallgenerates correct package.json and installs deps./mill agent.generatePackageJsonwrites IDE-compatible package.json./mill examples.runexecutes example with Bun (requires API key)Closes #26
🤖 Generated with Claude Code