@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 +27 -0
- package/README.md +155 -0
- package/bin/rotor.js +31 -0
- package/package.json +39 -0
- package/scripts/install.js +113 -0
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
|
+
}
|