From 9868933b27de58befbce8a9b800e4dce918b5e4f Mon Sep 17 00:00:00 2001 From: Eric Date: Wed, 4 Mar 2026 07:31:19 +0000 Subject: [PATCH] feat: add phase 6 js_library and ts_library bootstrap --- README.md | 6 +++++ bun/defs.bzl | 3 +++ internal/bun_bundle.bzl | 8 ++++++ internal/bun_test.bzl | 8 ++++++ internal/js_library.bzl | 40 ++++++++++++++++++++++++++++ tests/library_test/BUILD.bazel | 41 +++++++++++++++++++++++++++++ tests/library_test/app.test.ts | 6 +++++ tests/library_test/app.ts | 3 +++ tests/library_test/helper.ts | 3 +++ tests/library_test/verify_bundle.sh | 9 +++++++ 10 files changed, 127 insertions(+) create mode 100644 internal/js_library.bzl create mode 100644 tests/library_test/BUILD.bazel create mode 100644 tests/library_test/app.test.ts create mode 100644 tests/library_test/app.ts create mode 100644 tests/library_test/helper.ts create mode 100755 tests/library_test/verify_bundle.sh diff --git a/README.md b/README.md index 4b178e5..43598c1 100644 --- a/README.md +++ b/README.md @@ -34,3 +34,9 @@ Phase 5 bootstrap is in place: - Bundle rule `bun_bundle` (`/internal/bun_bundle.bzl`) - Public export via `bun/defs.bzl` - Focused output/minify tests (`//tests/bundle_test:all`) + +Phase 6 bootstrap is in place: + +- Source grouping rules `js_library` / `ts_library` (`/internal/js_library.bzl`) +- Transitive `deps` propagation wired into `bun_bundle` and `bun_test` +- Focused dependency-propagation tests (`//tests/library_test:all`) diff --git a/bun/defs.bzl b/bun/defs.bzl index 85fd304..91bfc90 100644 --- a/bun/defs.bzl +++ b/bun/defs.bzl @@ -1,6 +1,7 @@ load("//internal:bun_binary.bzl", _bun_binary = "bun_binary") load("//internal:bun_bundle.bzl", _bun_bundle = "bun_bundle") load("//internal:bun_test.bzl", _bun_test = "bun_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") @@ -8,5 +9,7 @@ visibility("public") bun_binary = _bun_binary bun_bundle = _bun_bundle bun_test = _bun_test +js_library = _js_library +ts_library = _ts_library BunToolchainInfo = _BunToolchainInfo bun_toolchain = _bun_toolchain diff --git a/internal/bun_bundle.bzl b/internal/bun_bundle.bzl index 13af4bc..56b6aee 100644 --- a/internal/bun_bundle.bzl +++ b/internal/bun_bundle.bzl @@ -1,5 +1,7 @@ """Rule for bundling JS/TS sources with Bun.""" +load("//internal:js_library.bzl", "BunSourcesInfo") + def _output_name(target_name, entry): stem = entry.basename.rsplit(".", 1)[0] @@ -13,6 +15,11 @@ def _bun_bundle_impl(ctx): 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) outputs = [] for entry in ctx.files.entry_points: @@ -59,6 +66,7 @@ bun_bundle = rule( allow_files = [".js", ".ts", ".jsx", ".tsx", ".mjs", ".cjs"], ), "node_modules": attr.label(), + "deps": attr.label_list(), "data": attr.label_list(allow_files = True), "target": attr.string( default = "browser", diff --git a/internal/bun_test.bzl b/internal/bun_test.bzl index 63f2c4d..5e46a71 100644 --- a/internal/bun_test.bzl +++ b/internal/bun_test.bzl @@ -1,5 +1,7 @@ """Rule for running test suites with Bun.""" +load("//internal:js_library.bzl", "BunSourcesInfo") + def _shell_quote(value): return "'" + value.replace("'", "'\"'\"'") + "'" @@ -34,6 +36,11 @@ exec "{bun_bin}" test {src_args} "${{extra_args[@]}}" "$@" 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, @@ -56,6 +63,7 @@ bun_test = rule( allow_files = [".js", ".ts", ".jsx", ".tsx", ".mjs", ".cjs"], ), "node_modules": attr.label(), + "deps": attr.label_list(), "data": attr.label_list(allow_files = True), }, test = True, diff --git a/internal/js_library.bzl b/internal/js_library.bzl new file mode 100644 index 0000000..4247824 --- /dev/null +++ b/internal/js_library.bzl @@ -0,0 +1,40 @@ +"""Lightweight JS/TS source grouping rules.""" + +BunSourcesInfo = provider(fields = ["transitive_sources"]) + + +def _bun_library_impl(ctx): + transitive_sources = [ + dep[BunSourcesInfo].transitive_sources + for dep in ctx.attr.deps + if BunSourcesInfo in dep + ] + all_sources = depset( + direct = ctx.files.srcs, + transitive = transitive_sources, + ) + return [ + BunSourcesInfo(transitive_sources = all_sources), + DefaultInfo(files = all_sources), + ] + + +js_library = rule( + implementation = _bun_library_impl, + attrs = { + "srcs": attr.label_list( + allow_files = [".js", ".jsx", ".mjs", ".cjs"], + ), + "deps": attr.label_list(), + }, +) + +ts_library = rule( + implementation = _bun_library_impl, + attrs = { + "srcs": attr.label_list( + allow_files = [".ts", ".tsx"], + ), + "deps": attr.label_list(), + }, +) diff --git a/tests/library_test/BUILD.bazel b/tests/library_test/BUILD.bazel new file mode 100644 index 0000000..2cfacac --- /dev/null +++ b/tests/library_test/BUILD.bazel @@ -0,0 +1,41 @@ +load("//bun:defs.bzl", "bun_bundle", "bun_test", "ts_library") + +ts_library( + name = "helper_lib", + srcs = ["helper.ts"], + target_compatible_with = [ + "@platforms//cpu:x86_64", + "@platforms//os:linux", + ], +) + +bun_bundle( + name = "bundle_with_deps", + entry_points = ["app.ts"], + deps = [":helper_lib"], + target_compatible_with = [ + "@platforms//cpu:x86_64", + "@platforms//os:linux", + ], +) + +sh_test( + name = "bundle_dep_propagation_test", + srcs = ["verify_bundle.sh"], + args = ["$(location :bundle_with_deps)"], + data = [":bundle_with_deps"], + target_compatible_with = [ + "@platforms//cpu:x86_64", + "@platforms//os:linux", + ], +) + +bun_test( + name = "test_with_deps", + srcs = ["app.test.ts"], + deps = [":helper_lib"], + target_compatible_with = [ + "@platforms//cpu:x86_64", + "@platforms//os:linux", + ], +) diff --git a/tests/library_test/app.test.ts b/tests/library_test/app.test.ts new file mode 100644 index 0000000..46535b9 --- /dev/null +++ b/tests/library_test/app.test.ts @@ -0,0 +1,6 @@ +import { expect, test } from "bun:test"; +import { greeting } from "./helper"; + +test("uses helper from ts_library dep", () => { + expect(greeting("test")).toBe("hello-test"); +}); diff --git a/tests/library_test/app.ts b/tests/library_test/app.ts new file mode 100644 index 0000000..d248746 --- /dev/null +++ b/tests/library_test/app.ts @@ -0,0 +1,3 @@ +import { greeting } from "./helper"; + +console.log(greeting("bundle")); diff --git a/tests/library_test/helper.ts b/tests/library_test/helper.ts new file mode 100644 index 0000000..cdf8ab7 --- /dev/null +++ b/tests/library_test/helper.ts @@ -0,0 +1,3 @@ +export function greeting(name: string): string { + return `hello-${name}`; +} diff --git a/tests/library_test/verify_bundle.sh b/tests/library_test/verify_bundle.sh new file mode 100755 index 0000000..006fd97 --- /dev/null +++ b/tests/library_test/verify_bundle.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +set -euo pipefail + +bundle="$1" + +if [[ ! -s "${bundle}" ]]; then + echo "Expected bundled output to exist and be non-empty: ${bundle}" >&2 + exit 1 +fi