A native-speed rewrite of the roblox-ts compiler in golang
README.md

rotor — an all in one roblox toolchain

TypeScript in, Roblox out — at native speed.

latest release ci MIT license

rotor is an all-in-one Roblox toolchain, written in Go. At its core is a native rewrite of the roblox-ts compiler built on typescript-go — a drop-in rbxtsc replacement with byte-identical Luau output — plus a native Luau toolchain and Open Cloud asset + deployment pipelines, all in one binary.

bun add -d @rotor-rbx/rotor    # or: npm i -D @rotor-rbx/rotor — see Install for more ways

📖 Documentation · 🤝 Contributing · 🗺️ Roadmap

Features #

Compile & check — the rbxtsc replacement:

rotor check ./my-game -w     # native full-strictness typecheck: 222 files in 161 ms
rotor build ./my-game -w     # byte-identical Luau, watch mode, incremental rebuilds
rotor doctor                 # diagnose tsconfig, @rbxts packages, plugins, Rojo, cloud setup

Same tsconfig.json, same @rbxts/* packages, same transformer plugins (Flamework etc.), same CLI flags — plus built-in compile-time macros rbxtsc doesn't have (no plugins, no Node sidecar, fully typed):

Macro Inlines
$env("KEY", "fallback") · $env.KEY env vars from .env / .env.<NODE_ENV>
$asset("logo.png") a rbxassetid://… string (cached, auto-uploads on miss)
$keys<T>() a type's string keys (checker-powered)
$nameof(expr) the trailing identifier name as a string
$file("data.json") a file's contents as a Luau value (JSON→table, text→string)
$git("sha"|"branch"|"tag"|"dirty") · $buildTime() build/VCS stamps
$getModuleTree("shared/systems") a folder's module tree (no index.ts required)

Luau toolchain — works on any Rojo project, no rbxts required:

rotor dev                    # watch + incremental compile + rojo serve to Studio
rotor bundle entry.luau -o bundle.luau --minify   # inline a require graph, still runnable
rotor minify file.luau       # strip comments/whitespace, keep --! directives
rotor pack --as luau         # whole project -> one self-reconstructing script (or rbxm/rbxmx)
rotor sourcemap -o sourcemap.json                 # Rojo-compatible, for luau-lsp

Cloud — assets and deployment from one typed config (see below):

rotor asset sync             # upload new/changed assets, lockfile, typed codegen
rotor deploy plan -e prod    # diff config vs live state (terraform-style, no network writes)
rotor deploy apply -e prod   # publish places, settings, badges — only what drifted

Scaffolding — rotor init runs an interactive wizard (template, Biome/oxlint, starter packages, asset/deploy config) or scripts cleanly with --yes/--template.

Configuration — rotor.toml #

One typed TOML config drives the cloud tools. rotor init writes it with a #:schema directive that points at the schema hosted in this repo (served via raw GitHub), so taplo / Even Better TOML give validation + autocomplete with no per-project rotor.schema.json to generate or commit. Need a local copy for offline editing? rotor schema > rotor.schema.json. (Upgrading from a 1.x rotor.config.ts? Run rotor migrate.)

#:schema https://raw.githubusercontent.com/uproot/rotor/master/rotor.schema.json

[assets]
mode = "macro"                  # "module" (assets.luau) | "macro" ($asset transformer)
paths = ["assets/**/*.png", "assets/**/*.ogg"]
[assets.creator]
type = "group"
id = 12345

[deploy.environments.prod]
universeId = 333
[deploy.environments.prod.places.start]
file = "build/game.rbxl"
placeId = 444
maxPlayers = 30
versionType = "published"
[deploy.environments.prod.experience]
name = "My Game"
playability = "public"
[deploy.environments.prod.gamepasses.vip]
name = "VIP"
price = 250
icon = "assets/vip.png"
[deploy.environments.prod.socials.discord]
title = "Join us"
url = "https://discord.gg/x"
type = "discord"
  • rotor asset sync scans the globs, uploads new/changed files via Open Cloud (SHA-256 lockfile rotor-lock.json — unchanged files never re-upload, updates keep asset ids stable). In mode = "module" it generates assets.luau + assets.d.ts; in mode = "macro" you reference assets inline with $asset("assets/logo.png"), which resolves from the cache and auto-uploads any miss when ROBLOX_API_KEY is set.
  • rotor deploy is infrastructure-as-code: it diffs the config against per-environment state (.rotor/deploy/<env>.json), shows a plan, and applies only the drift — place file publishing + place settings, experience settings, badges and game passes (icons upload automatically first, shared icons dedupe), experience icon + thumbnails, developer products, and social links. Deletes require --allow-deletes.
  • Auth is an Open Cloud key in ROBLOX_API_KEY (scopes: Assets R/W, Universe Places W, Universe R/W). rotor doctor checks your config and key setup.

Full config shape, every command flag, and all the macros: docs.md.

Install #

Grab a binary from GitHub Releases, or use a toolchain manager:

# mise
mise use -g github:uproot/rotor@2.0.0

# rokit
rokit add uproot/rotor@2.0.0
# aftman.toml
[tools]
rotor = "uproot/rotor@2.0.0"

# foreman.toml
[tools]
rotor = { github = "uproot/rotor", version = "2.0.0" }

Install via npm / bun #

For rbxts projects that already live in the JS ecosystem, install @rotor-rbx/rotor as a dev dependency — a postinstall step downloads the prebuilt binary for your platform:

bun add -d @rotor-rbx/rotor
npm i -D @rotor-rbx/rotor
pnpm add -D @rotor-rbx/rotor
yarn add -D @rotor-rbx/rotor

Installing straight from GitHub works too: bun add -d github:uproot/rotor (npm/pnpm/yarn equivalents likewise).

bun note: bun skips postinstall scripts by default. Either add "trustedDependencies": ["@rotor-rbx/rotor"] to your project's package.json (then bun install), or do nothing — the rotor shim downloads the binary on first run. pnpm similarly asks you to approve build scripts (pnpm approve-builds), with the same first-run fallback.

Or build from source (Go 1.25+):

git clone https://github.com/uproot/rotor && cd rotor
go build ./cmd/rotor

Benchmarks #

Measured on real production rbxts games, with output byte-identical to rbxtsc 3.0.0:

Workload rotor
Full strict typecheck — 222-file production game 161 ms
Full build — 95-file production game 355 ms
Incremental watch rebuild — same game 180 ms

The JS toolchain spends longer than this booting Node. The ~10× speedup is structural: rotor runs Microsoft's native, parallel TypeScript compiler (typescript-go) instead of the single-threaded JS one.

Contributors #

uproot Coyenn

Contributions welcome — see CONTRIBUTING.md.

License #

MIT. rotor stands on roblox-ts (MIT) and typescript-go (Apache-2.0) — see credits.