@rotor-rbx/rotor 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,27 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 uproot
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ Third-party components vendored in this repository retain their own licenses:
16
+ - tsgo/ — mirror of microsoft/typescript-go, Apache License 2.0 (see tsgo/LICENSE,
17
+ tsgo/NOTICE, and tsgo/MIRROR.md for the statement of changes)
18
+ - reference/ — roblox-ts and @roblox-ts/luau-ast sources, MIT License (see the
19
+ LICENSE files within each directory)
20
+
21
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,155 @@
1
+ <p align="center">
2
+ <img src="media/logo.png" alt="rotor — an all in one roblox toolchain" width="480">
3
+ </p>
4
+
5
+ <p align="center"><em>TypeScript in, Roblox out — at native speed.</em></p>
6
+
7
+ <p align="center">
8
+ <a href="https://github.com/uproot/rotor/releases/latest"><img src="https://img.shields.io/github/v/release/uproot/rotor" alt="latest release"></a>
9
+ <a href="https://github.com/uproot/rotor/actions/workflows/ci.yml"><img src="https://github.com/uproot/rotor/actions/workflows/ci.yml/badge.svg" alt="ci"></a>
10
+ <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="MIT license"></a>
11
+ </p>
12
+
13
+ rotor is an all-in-one Roblox toolchain, written in Go. At its core is a native rewrite of the [roblox-ts](https://roblox-ts.com) compiler built on [typescript-go](https://github.com/microsoft/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.
14
+
15
+ ```sh
16
+ bun add -d @rotor-rbx/rotor # or: npm i -D @rotor-rbx/rotor — see Install for more ways
17
+ ```
18
+
19
+ 📖 [Documentation](docs.md) · 🤝 [Contributing](CONTRIBUTING.md) · 🗺️ [Roadmap](roadmap.md)
20
+
21
+ ## Features
22
+
23
+ **Compile & check** — the rbxtsc replacement:
24
+
25
+ ```sh
26
+ rotor check ./my-game -w # native full-strictness typecheck: 222 files in 161 ms
27
+ rotor build ./my-game -w # byte-identical Luau, watch mode, incremental rebuilds
28
+ rotor doctor # diagnose tsconfig, @rbxts packages, plugins, Rojo, cloud setup
29
+ ```
30
+
31
+ Same `tsconfig.json`, same `@rbxts/*` packages, same transformer plugins (Flamework etc.), same CLI flags. Plus built-in extensions rbxtsc doesn't have: **`$env`** (compile-time environment variables from `.env` — `$env.GAME_NAME`, `$env("KEY", "fallback")`, no plugin needed) and **`$getModuleTree` on folders** (no `index.ts` required).
32
+
33
+ **Luau toolchain** — works on any Rojo project, no rbxts required:
34
+
35
+ ```sh
36
+ rotor dev # watch + incremental compile + rojo serve to Studio
37
+ rotor bundle entry.luau -o bundle.luau --minify # inline a require graph, still runnable
38
+ rotor minify file.luau # strip comments/whitespace, keep --! directives
39
+ rotor pack --as luau # whole project -> one self-reconstructing script (or rbxm/rbxmx)
40
+ rotor sourcemap -o sourcemap.json # Rojo-compatible, for luau-lsp
41
+ ```
42
+
43
+ **Cloud** — assets and deployment from one typed config (see below):
44
+
45
+ ```sh
46
+ rotor asset sync # upload new/changed assets, lockfile, typed codegen
47
+ rotor deploy plan -e prod # diff config vs live state (terraform-style, no network writes)
48
+ rotor deploy apply -e prod # publish places, settings, badges — only what drifted
49
+ ```
50
+
51
+ **Scaffolding** — `rotor init` runs an interactive wizard (template, Biome/oxlint, starter packages, asset/deploy config) or scripts cleanly with `--yes`/`--template`.
52
+
53
+ ## Configuration — `rotor.config.ts`
54
+
55
+ One typed TypeScript config drives the cloud tools. rotor evaluates it natively (no Node needed), generates `rotor-config.d.ts` for editor typing, and refuses npm imports — it's config, not a program:
56
+
57
+ ```ts
58
+ import { defineConfig } from "@rotor-rbx/rotor";
59
+
60
+ export default defineConfig({
61
+ assets: {
62
+ paths: ["assets/**/*.png", "assets/**/*.ogg"],
63
+ output: { luau: "src/shared/assets.luau", types: "src/shared/assets.d.ts" },
64
+ creator: { type: "group", id: 12345 },
65
+ },
66
+ deploy: {
67
+ environments: {
68
+ dev: {
69
+ universeId: 111,
70
+ places: { start: { file: "build/game.rbxl", placeId: 222 } },
71
+ },
72
+ prod: {
73
+ universeId: 333,
74
+ places: { start: { file: "build/game.rbxl", placeId: 444 } },
75
+ experience: { name: "My Game", playability: "public" },
76
+ badges: { winner: { name: "Winner!", description: "Beat the game", icon: "assets/badge.png" } },
77
+ },
78
+ },
79
+ },
80
+ });
81
+ ```
82
+
83
+ - **`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), and generates `assets.luau` + `assets.d.ts`, so code references `assets.sounds.hit` instead of hardcoded `rbxassetid://` strings.
84
+ - **`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, experience settings, badges (icons upload automatically first). Deletes require `--allow-deletes`.
85
+ - 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.
86
+ - Compile-time env vars come from `.env` / `.env.<NODE_ENV>` next to your tsconfig and are inlined by the `$env` macro; rotor writes `rotor-env.d.ts` so your editor sees the types.
87
+
88
+ Full config shape and every command flag: [docs.md](docs.md).
89
+
90
+ ## Install
91
+
92
+ Grab a binary from [GitHub Releases](https://github.com/uproot/rotor/releases), or use a toolchain manager:
93
+
94
+ ```sh
95
+ # mise
96
+ mise use -g github:uproot/rotor@1.4.0
97
+
98
+ # rokit
99
+ rokit add uproot/rotor@1.4.0
100
+ ```
101
+
102
+ ```toml
103
+ # aftman.toml
104
+ [tools]
105
+ rotor = "uproot/rotor@1.4.0"
106
+
107
+ # foreman.toml
108
+ [tools]
109
+ rotor = { github = "uproot/rotor", version = "1.4.0" }
110
+ ```
111
+
112
+ ### Install via npm / bun
113
+
114
+ For rbxts projects that already live in the JS ecosystem, install [`@rotor-rbx/rotor`](https://www.npmjs.com/package/@rotor-rbx/rotor) as a dev dependency — a postinstall step downloads the prebuilt binary for your platform:
115
+
116
+ ```sh
117
+ bun add -d @rotor-rbx/rotor
118
+ npm i -D @rotor-rbx/rotor
119
+ pnpm add -D @rotor-rbx/rotor
120
+ yarn add -D @rotor-rbx/rotor
121
+ ```
122
+
123
+ Installing straight from GitHub works too: `bun add -d github:uproot/rotor` (npm/pnpm/yarn equivalents likewise).
124
+
125
+ > **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.
126
+
127
+ Or build from source (Go 1.25+):
128
+
129
+ ```sh
130
+ git clone https://github.com/uproot/rotor && cd rotor
131
+ go build ./cmd/rotor
132
+ ```
133
+
134
+ ## Benchmarks
135
+
136
+ Measured on real production rbxts games, with output byte-identical to `rbxtsc` 3.0.0:
137
+
138
+ | Workload | rotor |
139
+ |----------|------:|
140
+ | Full strict typecheck — 222-file production game | **161 ms** |
141
+ | Full build — 95-file production game | **355 ms** |
142
+ | Incremental watch rebuild — same game | **180 ms** |
143
+
144
+ The JS toolchain spends longer than this booting Node. The ~10× speedup is structural: rotor runs Microsoft's native, parallel TypeScript compiler ([typescript-go](https://github.com/microsoft/typescript-go)) instead of the single-threaded JS one.
145
+
146
+ ## Contributors
147
+
148
+ <a href="https://github.com/uproot"><img src="https://github.com/uproot.png" width="56" height="56" alt="uproot"></a>
149
+ <a href="https://github.com/Coyenn"><img src="https://github.com/Coyenn.png" width="56" height="56" alt="Coyenn"></a>
150
+
151
+ Contributions welcome — see [CONTRIBUTING.md](CONTRIBUTING.md).
152
+
153
+ ## License
154
+
155
+ [MIT](LICENSE). rotor stands on [roblox-ts](https://github.com/roblox-ts/roblox-ts) (MIT) and [typescript-go](https://github.com/microsoft/typescript-go) (Apache-2.0) — see [credits](docs.md#credits--licenses).
package/bin/rotor.js ADDED
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env node
2
+ // npm/bun shim for the rotor CLI. Resolves the platform binary downloaded by
3
+ // scripts/install.js (postinstall); if it is missing — e.g. bun skipped the
4
+ // postinstall because "rotor" is not in trustedDependencies — downloads it on
5
+ // first run, then execs it with the original args.
6
+ "use strict";
7
+
8
+ const fs = require("fs");
9
+ const { spawnSync } = require("child_process");
10
+ const { install, binaryPath } = require("../scripts/install.js");
11
+
12
+ async function main() {
13
+ let bin = binaryPath();
14
+ if (!fs.existsSync(bin)) {
15
+ bin = await install();
16
+ }
17
+
18
+ const result = spawnSync(bin, process.argv.slice(2), { stdio: "inherit" });
19
+ if (result.error) throw result.error;
20
+ if (result.signal) {
21
+ // Re-raise the child's fatal signal so callers see the same termination.
22
+ process.kill(process.pid, result.signal);
23
+ return;
24
+ }
25
+ process.exit(result.status === null ? 1 : result.status);
26
+ }
27
+
28
+ main().catch((err) => {
29
+ console.error(err && err.message ? err.message : err);
30
+ process.exit(1);
31
+ });
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@rotor-rbx/rotor",
3
+ "version": "1.4.0",
4
+ "publishConfig": {
5
+ "access": "public",
6
+ "provenance": true
7
+ },
8
+ "description": "All-in-one Roblox toolchain: native roblox-ts compiler, Luau bundler, minifier, dev loop, and packer. This package downloads the prebuilt rotor binary from GitHub Releases.",
9
+ "license": "MIT",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "git+https://github.com/uproot/rotor.git"
13
+ },
14
+ "homepage": "https://github.com/uproot/rotor#readme",
15
+ "bugs": {
16
+ "url": "https://github.com/uproot/rotor/issues"
17
+ },
18
+ "keywords": [
19
+ "roblox",
20
+ "roblox-ts",
21
+ "luau",
22
+ "typescript",
23
+ "compiler",
24
+ "bundler"
25
+ ],
26
+ "bin": {
27
+ "rotor": "bin/rotor.js"
28
+ },
29
+ "scripts": {
30
+ "postinstall": "node scripts/install.js"
31
+ },
32
+ "files": [
33
+ "bin/",
34
+ "scripts/install.js"
35
+ ],
36
+ "engines": {
37
+ "node": ">=18"
38
+ }
39
+ }
@@ -0,0 +1,113 @@
1
+ #!/usr/bin/env node
2
+ // Downloads the prebuilt rotor binary for the current platform from GitHub
3
+ // Releases. Runs as the package's postinstall hook; also required as a module
4
+ // by bin/rotor.js for a first-run download when the postinstall was skipped
5
+ // (e.g. bun without trustedDependencies).
6
+ //
7
+ // Plain Node, no dependencies. Requires Node 18+ (global fetch) or Bun.
8
+ //
9
+ // Env:
10
+ // ROTOR_INSTALL_BASE_URL override the download base URL (testing/proxies);
11
+ // defaults to the GitHub release for pkg.version.
12
+ "use strict";
13
+
14
+ const fs = require("fs");
15
+ const path = require("path");
16
+
17
+ const pkg = require(path.join(__dirname, "..", "package.json"));
18
+
19
+ const PLATFORM_MAP = { win32: "windows", linux: "linux", darwin: "darwin" };
20
+ const ARCH_MAP = { x64: "amd64", arm64: "arm64" };
21
+
22
+ // Anything smaller than this is not a real rotor binary (real ones are >10MB).
23
+ const MIN_BINARY_SIZE = 1024 * 1024;
24
+
25
+ function targetInfo() {
26
+ const os = PLATFORM_MAP[process.platform];
27
+ const arch = ARCH_MAP[process.arch];
28
+ if (!os || !arch) {
29
+ throw new Error(
30
+ `rotor: unsupported platform ${process.platform}-${process.arch} ` +
31
+ `(supported: windows/linux/darwin on amd64/arm64). ` +
32
+ `Build from source instead: go build ./cmd/rotor`
33
+ );
34
+ }
35
+ return { os, arch, ext: os === "windows" ? ".exe" : "" };
36
+ }
37
+
38
+ // Where the platform binary lives inside the installed package.
39
+ function binaryPath() {
40
+ const { os, arch, ext } = targetInfo();
41
+ return path.join(__dirname, "..", "bin", `rotor-${os}-${arch}${ext}`);
42
+ }
43
+
44
+ function assetUrl() {
45
+ const { os, arch, ext } = targetInfo();
46
+ const base = (
47
+ process.env.ROTOR_INSTALL_BASE_URL ||
48
+ `https://github.com/uproot/rotor/releases/download/v${pkg.version}`
49
+ ).replace(/\/+$/, "");
50
+ return `${base}/rotor-v${pkg.version}-${os}-${arch}-bin${ext}`;
51
+ }
52
+
53
+ async function install({ force = false, quiet = false } = {}) {
54
+ const dest = binaryPath();
55
+
56
+ if (!force) {
57
+ try {
58
+ if (fs.statSync(dest).size > MIN_BINARY_SIZE) return dest; // already installed
59
+ } catch {
60
+ /* not downloaded yet */
61
+ }
62
+ }
63
+
64
+ const url = assetUrl();
65
+ if (!quiet) console.error(`rotor: downloading ${url}`);
66
+
67
+ let res;
68
+ try {
69
+ res = await fetch(url, { redirect: "follow" });
70
+ } catch (err) {
71
+ throw new Error(
72
+ `rotor: download failed for ${url}\n ${err && err.message ? err.message : err}`
73
+ );
74
+ }
75
+
76
+ if (res.status === 404) {
77
+ throw new Error(
78
+ `rotor: binary not found (HTTP 404):\n ${url}\n` +
79
+ `The v${pkg.version} release may not be published yet, or the asset for ` +
80
+ `this platform is missing.\n` +
81
+ `Grab a binary from https://github.com/uproot/rotor/releases or build ` +
82
+ `from source: go build ./cmd/rotor`
83
+ );
84
+ }
85
+ if (!res.ok) {
86
+ throw new Error(`rotor: download failed (HTTP ${res.status}) for ${url}`);
87
+ }
88
+
89
+ const buf = Buffer.from(await res.arrayBuffer());
90
+
91
+ // Write to a temp name then rename so a torn download never looks installed.
92
+ const tmp = dest + ".download";
93
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
94
+ fs.writeFileSync(tmp, buf);
95
+ if (process.platform !== "win32") fs.chmodSync(tmp, 0o755);
96
+ fs.renameSync(tmp, dest);
97
+
98
+ if (!quiet) {
99
+ console.error(
100
+ `rotor: installed ${path.basename(dest)} (${(buf.length / 1048576).toFixed(1)} MB)`
101
+ );
102
+ }
103
+ return dest;
104
+ }
105
+
106
+ module.exports = { install, binaryPath, assetUrl, MIN_BINARY_SIZE };
107
+
108
+ if (require.main === module) {
109
+ install().catch((err) => {
110
+ console.error(err && err.message ? err.message : err);
111
+ process.exit(1);
112
+ });
113
+ }