Skip to content

feat: transitive bun dependencies via JAR manifests#6

Merged
arcaputo3 merged 5 commits into
mainfrom
feat/transitive-bun-deps
Apr 7, 2026
Merged

feat: transitive bun dependencies via JAR manifests#6
arcaputo3 merged 5 commits into
mainfrom
feat/transitive-bun-deps

Conversation

@arcaputo3
Copy link
Copy Markdown
Contributor

Summary

  • Introduces transitive JS dependency resolution via META-INF/bun/bun-dependencies.json manifests embedded in published JARs — analogous to how Coursier resolves JVM deps from POMs
  • Adds BunPublishModule opt-in trait for libraries that want to embed manifests
  • Adds bun"pkg@version" compile-time validated string interpolator and bunDeps/bunDevDeps aliases (backward-compatible with existing npmDeps/npmDevDeps)
  • npmDeps and bunDeps are independent (no circular delegation) — both merged at the transitive level

Design decisions

  • Manifests flow through JARs, lockfiles stay in the project — every ecosystem (Cargo, Go, npm) treats lockfiles as application-level artifacts. Library JARs only carry dependency declarations.
  • Manifest embedding is opt-in via BunPublishModule — existing BunScalaJSModule users see no JAR content changes or build overhead.
  • npmDeps/bunDeps are independent — avoids circular delegation footgun if both are overridden. Merged transparently in transitiveNpmDeps.

What's next (Phase 2)

Project-level lockfile management in a follow-up PR:

  • Source-tracked bun.lock in workspace root
  • bunUpdateLock command to regenerate
  • bunInstall seeds from committed lockfile
  • bunFrozenLockfile enforces in CI

Test plan

  • ./mill millbun.compile — compiles cleanly
  • ./mill millbun.test — all 30 unit tests pass (11 BunManifest + 8 BunDep + 7 SplitDep + 4 ExecutableCandidates)
  • Integration test with multi-module project using BunPublishModule

🤖 Generated with Claude Code

arcaputo3 and others added 5 commits April 3, 2026 16:47
When a Scala.js library publishes with BunScalaJSModule, it can embed
a bun dependency manifest (META-INF/bun/bun-dependencies.json) and
lockfile (META-INF/bun/bun.lock) in the JAR. Consumer builds scan
classpath JARs for these manifests and merge them into package.json
automatically — making JS deps transitive, like Coursier does for JVM.

Write side (library authors):
- bunDependencyManifest: generates manifest + embeds lockfile
- bunDependencyManifestResources: wire into JAR resources
- bunOptionalDeps: optional JS packages (won't fail if unavailable)

Read side (consumers):
- classpathBunDeps/classpathBunDevDeps: scan JARs for manifests
- classpathBunLockfiles: extract embedded lockfiles
- transitiveNpmDeps: now includes JAR-discovered deps
- bunInstall: seeds lockfile from JARs for deterministic resolution

BunManifest: data class + JSON serialization + JAR I/O + merge.
11 new unit tests covering round-trip, JAR read/write, merge, edge cases.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…usion

- bun"pkg@version" compile-time validated string interpolator
  validates package name format at compile time, returns plain String
- bunDeps / bunDevDeps as primary API (npmDeps delegates to bunDeps)
- bunOptionalDeps for packages that won't fail if unavailable
- Auto-resource inclusion: resources automatically includes the bun
  dependency manifest when bunDeps is non-empty. Zero manual wiring.
- 8 new interpolator tests, 30 total tests passing

Library authors just declare deps:
  def bunDeps = Task { Seq(bun"react@^19.0.0") }
  // Manifest + lockfile auto-embedded in JAR. Consumers get them free.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- npmDeps and bunDeps are now independent (both default to Seq.empty),
  merged at the transitive level — eliminates circular delegation footgun
- Extract manifest generation into BunPublishModule opt-in trait so
  existing users don't get unexpected JAR content changes

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Lockfiles are application-level artifacts, not library-level. Embedding
them in JARs had fundamental issues: no merge strategy across libraries,
arbitrary winner selection, and bun.lockb never actually extracted.

Strip lockfile embedding from BunPublishModule, classpath lockfile
scanning, and seeding logic from bunInstall. The manifest system
(bun-dependencies.json) remains — it correctly handles transitive dep
discovery. Project-level lockfile management will follow in a separate PR.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add classpathBunOptionalDeps + transitiveBunOptionalDeps so optional
  deps flow through JAR manifests like regular and dev deps
- mkBunPackageJson uses transitive optional deps instead of local-only
- BunPublishModule triggers manifest on any dep type (including devDeps)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@arcaputo3 arcaputo3 merged commit 7ddf243 into main Apr 7, 2026
2 checks passed
@arcaputo3 arcaputo3 deleted the feat/transitive-bun-deps branch April 7, 2026 19:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant