Skip to content

mikn/rules_typescript

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

28 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

rules_typescript

An opinionated Bazel ruleset for TypeScript, optimised for the Oxc + Vite toolchain rather than broad compatibility with every JS build tool. If your stack is TypeScript, Vite, and a Vite-based framework — this replaces tsc, your bundler, and your dev server with a single hermetic build. If you need tsc compatibility or non-Vite toolchains, see aspect-build/rules_ts.

Oxc compiles. tsgo type-checks. Vite bundles. Gazelle generates BUILD files. Write .ts, run Gazelle, bazel build //.... No node_modules/. No system Node. Just Bazelisk.

Full documentation: mikn.github.io/rules_typescript

Built for the Vite Ecosystem

This ruleset is designed around Vite as the bundler and dev server. Vite-based frameworks work out of the box:

  • React + Vite — SPA bundling, React Fast Refresh HMR, CSS modules
  • Remix — full client bundle with route-based code splitting via @remix-run/dev Vite plugin
  • TanStack Start — client + SSR server bundles via @tanstack/react-start Vite plugin
  • SvelteKit, Solid Start — any framework that ships a Vite plugin

Frameworks that don't use Vite (e.g., Next.js with webpack/turbopack) are not a priority.

Key Ideas

  • Oxc compiles — Rust-based TypeScript/JSX transformer. .js, .js.map, and .d.ts per file. Hundreds of files in milliseconds.
  • tsgo type-checks — Go port of TypeScript runs as a validation action. Type errors fail bazel build.
  • Vite bundles — production bundles with tree-shaking, code splitting, minification. App mode (HTML + hashed assets) and lib mode.
  • Isolated declarations — explicit return types on exports make .d.ts a per-file syntactic transform. Change implementation without changing API → no downstream recompilation.
  • Gazelle generates BUILD files — auto-infers targets, resolves imports, detects frameworks, generates bundler + dev server targets.
  • Zero prerequisites — only Bazelisk needed. Node.js, pnpm, Go, Rust all fetched hermetically.

Requirements

The only prerequisite is Bazelisk (or Bazel 9+). Everything else — the Rust toolchain, Go toolchain, Node.js runtime, and all npm packages — is fetched hermetically on first build.

Supported platforms: Linux x86_64, Linux ARM64, macOS x86_64, macOS ARM64.

Install

Step 1. Create .bazelversion:

9.0.0

Step 2. Create WORKSPACE.bazel (empty — required by Bazel 9).

Step 3. Add to MODULE.bazel:

module(name = "my_project", version = "0.0.0")

bazel_dep(name = "rules_typescript", version = "0.1.0")
register_toolchains("@rules_typescript//ts/toolchain:all")

bazel_dep(name = "gazelle", version = "0.47.0")

Step 4. Add to .bazelrc:

build --incompatible_strict_action_env
build --nolegacy_external_runfiles
build --output_groups=+_validation

Step 5. Add to BUILD.bazel:

load("@gazelle//:def.bzl", "gazelle")

gazelle(
    name = "gazelle",
    gazelle = "@rules_typescript//gazelle:gazelle_ts",
)

Step 6. Write TypeScript with explicit return types on exports:

export function add(a: number, b: number): number {
  return a + b;
}

Step 7. Generate BUILD files, build, and test:

bazel run //:gazelle
bazel build //...
bazel test //...

Adding npm dependencies

pnpm add zod --lockfile-only   # updates pnpm-lock.yaml, no node_modules created
bazel run //:gazelle           # picks up new package, updates BUILD files
bazel build //...              # downloads package hermetically, builds

Add the npm extension to MODULE.bazel (one-time setup):

npm = use_extension("@rules_typescript//npm:extensions.bzl", "npm")
npm.translate_lock(pnpm_lock = "//:pnpm-lock.yaml")
use_repo(npm, "npm")

No node_modules/ directory ever exists in the source tree. The lockfile is the only npm artifact checked into git. pnpm is only needed to manage the lockfile — Bazel downloads all packages hermetically at build time.

IDE Integration

A tsserver hook resolves modules live from Bazel's build graph — npm types, internal packages, path aliases. No manual tsconfig.json paths to maintain. Works with VS Code, Neovim, Emacs, any editor with tsserver.

bazel run //:refresh_tsconfig  # one-time: generates hook + tsconfig

Then add to VS Code settings: "typescript.tsserver.nodeOptions": "--require .bazel/tsserver-hook.js"

See IDE Setup for all editors.

Feature Highlights

License

MIT

About

TypeScript rules for Bazel using Oxc and tsgo. TypeScript on Bazel should feel like Go on Bazel.

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors