Using Darklua
Process your code with Darklua and sync what it writes
Updated
Darklua rewrites your code into a folder of its own, and Rojo syncs that folder. Rogen never runs Darklua. This page sets a Rogen project up for it by hand, with Darklua reading src and writing dist.
The project gets two configs. default.rogen.json keeps reading src, because luau-lsp builds sourcemap.json from default.project.json, and Darklua's convert_require reads that sourcemap to turn requires by path into requires by instance. dist.rogen.json is a complete copy that adds "compiledPath": "dist", so its project file reads what Darklua writes. Studio syncs that one.
If your Rojo project already mounts the folder Darklua writes and has a .darklua.json, rogen init writes both configs for you, with that folder as the compiledPath. See Coming from Rojo.
Configure Darklua
Add Darklua to your tool manager, such as with rokit add seaofvoices/darklua. Then write .darklua.json beside default.rogen.json:
{
"rules": [
{
"rule": "convert_require",
"current": { "name": "path" },
"target": {
"name": "roblox",
"rojo_sourcemap": "./sourcemap.json",
"indexing_style": "property"
}
}
]
}Add the Processed Config
Copy default.rogen.json to dist.rogen.json. Give it a name, since a project file other than default needs one, and add "compiledPath": "dist". For the project rogen init starts:
{
"name": "my-game",
"sourcePaths": ["src"],
"routes": {
"Server": "ServerScriptService",
"Client": "StarterPlayer/StarterPlayerScripts",
"Shared": "ReplicatedStorage/Shared",
"*": "ReplicatedStorage/Shared"
},
"compiledPath": "dist",
"tree": {
"$className": "DataModel",
"Lighting": { "$path": "place/Lighting" },
"SoundService": { "$path": "place/SoundService" },
"Workspace": { "$path": "place/Workspace" }
}
}Add /dist to .gitignore, so the processed code isn't committed.
Keep the Sourcemap Current
luau-lsp keeps sourcemap.json current while your editor is open. Without it, run this in a terminal of its own:
rogen sourcemap --watch -o sourcemap.jsonProcess and Serve
Run each in its own terminal:
darklua process src dist --watch
rogen serve distrogen serve dist writes dist.project.json and serves it. A bare rogen serve serves default.project.json, which syncs your code before Darklua processed it.
Keep the Configs in Step
A config never reads another, so every change to routes or tree goes into each .rogen.json: default.rogen.json, dist.rogen.json, and release.rogen.json if you have one. Otherwise luau-lsp and Darklua see a different game than Studio does.
Releases and Tests
With a test runner such as Jest or TestEZ, leave the specs out of the place you publish. A release.rogen.json, another complete copy, reads dist and drops them with Rojo's globIgnorePaths:
{
"name": "my-game",
"sourcePaths": ["src"],
"routes": {
"Server": "ServerScriptService",
"Client": "StarterPlayer/StarterPlayerScripts",
"Shared": "ReplicatedStorage/Shared",
"*": "ReplicatedStorage/Shared"
},
"compiledPath": "dist",
"globIgnorePaths": ["**/*.spec.luau"],
"tree": {
"$className": "DataModel",
"Lighting": { "$path": "place/Lighting" },
"SoundService": { "$path": "place/SoundService" },
"Workspace": { "$path": "place/Workspace" }
}
}Process the code once, then build from it:
darklua process src dist
rogen build release -o game.rbxlroblox-ts
With roblox-ts, rbxtsc compiles src to out, the compiledPath of default.rogen.json, and Darklua processes out into dist. In dist.rogen.json, change "compiledPath": "out" to "compiledPath": "dist". Run rbxtsc -w beside darklua process out dist --watch. Compiled code requires by instance already, so .darklua.json needs no convert_require rule and Darklua reads no sourcemap.