Rules
Thirty six rules shipped, twenty seven under darklua's names and nine larvae only
updated Aug 16, 20266 min read
Rules are the transforms larvae applies to each output file. They live in the [rules] table of larvae.toml.
There are 36 rules: 9 that only larvae has, and 27 that match darklua name for name, so a .darklua.json rule list ports over unchanged. Every rule is off until the config turns it on. A rule takes true, or a table with the options its row names.
An unknown rule name is an error, never ignored, which is the policy the rest of configuration follows. A rule list that loads is a rule list that ran.
[rules]
remove_types = true
compute_expression = true
const_requires = truelarvae's own rules
Nine rules under larvae's own names. Seven have no darklua equivalent at all; remove_comments and append_text_comment sit here rather than in the parity set because larvae's defaults differ from darklua's.
| Rule | Value | What it does |
|---|---|---|
const_requires |
bool | Turn local X = require(...) into const X = require(...). Skips multi bindings and annotated locals. |
remove_comments |
bool or table | Remove comments. true, or a table with except regexes for comments to keep. Defaults to ["^--!"] so Luau directives survive; set [] to match darklua. |
append_text_comment |
table | Insert a comment at the start or end of every file. Exactly one of text (the literal comment) or file (read the comment from this path), and location = "start" (default) or "end". |
add_luau_directive |
string | Write a Luau directive at the top of every file, ex: "strict" emits --!strict. An existing directive of the same family is left alone. |
remove_calls |
list or table | Remove statement position calls to the named functions, ex: print or debug.profilebegin. Names may be dotted paths; method calls and computed callees are never matched, and a call whose value is used is left alone. |
use_get_service |
bool | Rewrite game.Players into game:GetService("Players") for the known services, which keeps working when a service is renamed or not created yet. A local named game defeats it. |
dedupe_requires |
bool | Top level local X = require(...) statements that resolve to the same module collapse, the later ones become aliases of the first. Skips const, annotated and multi binding locals. |
inject_module_path |
string | Name of a constant defined at the top of every file that references it, holding that file's DataModel path, ex: "@game/ReplicatedStorage/shared/util". Warns when no mount covers the file. |
freeze_module |
bool | Wrap a module's returned table in table.freeze, only when nothing can write to it, so field writes, rebinds and metatable patterns turn the rule off for that file. |
In particular remove_comments keeps --! directives by default, because removing a Luau directive silently changes how the code runs. Set except = [] to match darklua.
[rules]
add_luau_directive = "strict"
remove_calls = ["print", "warn", "debug.profilebegin"]
[rules.append_text_comment]
text = "generated by larvae, do not edit"
location = "start"The darklua parity set
All 27 run under darklua's own names, so a ported config behaves the way the reader expects.
| Rule | Value | What it does |
|---|---|---|
compute_expression |
bool | Fold constant expressions to their value. Only folds when the result prints back exactly, so fractions, values past exact double range and strings holding escapes are left alone. |
convert_function_to_assignment |
bool | Rewrite function foo() into foo = function(), and function o:m() into o.m = function(self). Attributed functions are left alone. |
convert_index_to_field |
bool | Rewrite t["field"] into t.field, and the same for table constructor keys, when the literal spells a valid name that is not a reserved word. |
convert_local_function_to_assign |
bool | Rewrite local function f() into local f = function(), only when the body never mentions f, since the local form is in scope inside itself. |
convert_luau_number |
bool | Rewrite binary literals like 0b1010 as hex and strip digit separators, both being Luau only syntax. |
convert_square_root_call |
bool | Rewrite math.sqrt(x) into (x ^ 0.5). The result is always parenthesised so it survives being the base of another power. |
filter_after_early_return |
bool | Delete the statements after a do ... return ... end block, which can never run. Stops if a type alias is among them, since an exported one is visible outside the block. |
group_local_assignment |
bool | Merge a run of adjacent local declarations into one. Conservative, it stops at a call, a dependency between them, an annotation, a comment or anything but whitespace in between. |
make_assignment_local |
bool | Turn a const declaration back into a local one, the inverse of const_requires, for output that has to stay plain Lua. |
remove_assertions |
bool or table | Remove assert(...) statements. |
remove_attribute |
bool or table | Strip function attributes, ex: @native. true removes every one, or a table with match regexes tested against the attribute name without its @. |
remove_compound_assignment |
bool | Expand x += 1 into x = x + 1 for every compound operator. Targets that would have to run code twice, ex: a computed key holding a call, are left alone. |
remove_continue |
bool | Rewrite a loop containing continue into the repeat ... until true form with continue spelled break. Loops that also break at the same level, and repeat loops whose condition can read body locals, are left alone. |
remove_debug_profiling |
bool or table | Remove debug.profilebegin and debug.profileend statements. |
remove_empty_do |
bool | Drop do end blocks that contain nothing. A lone semicolon counts as content. Nested empties shed one layer per run. |
remove_floor_division |
bool | Rewrite a // b into math.floor(a / b), and a //= b into a = math.floor(a / b). |
remove_function_call_parens |
bool | Rewrite f("x") into f"x" and f({}) into f{} when a call takes exactly one string or table argument. |
remove_if_expression |
bool | Rewrite if c then a else b into c and a or b. Only applied when the then value can never be false or nil, otherwise the and/or form would not be equivalent and the expression is left alone. |
remove_interpolated_string |
bool or table | Rewrite backtick strings into string.format calls. true, or a table with strategy: "string" wraps each value in tostring and formats with %s, "tostring" uses Luau's %*. Multi line and nested backtick strings are left alone. |
remove_method_call |
bool | Rewrite obj:m(x) into obj.m(obj, x). Only for a plain identifier receiver, anything else would be evaluated twice. |
remove_method_definition |
bool | Rewrite function C:m() into function C.m(self), making the implicit self parameter explicit. |
remove_nil_declaration |
bool | Rewrite local x = nil into local x and drop trailing nil values from multi bindings. Skips const, which must keep a value. |
remove_types |
bool | Strip every type annotation: bindings, parameters, return types, generic lists, type aliases and the tail of a :: assertion. |
remove_unused_if_branch |
bool | Prune if branches whose condition is a known constant. A branch that always runs is unwrapped, or becomes a do block when it declares locals so their scope is preserved. |
remove_unused_variable |
bool or table | Remove unused locals and local functions. |
remove_unused_while |
bool | Delete a while loop whose condition is a constant false. |
rename_variables |
bool or table | Rename locals and params to short names. |
remove_unused_variable and rename_variables also accept apply_to and skip globs in their table form, which limit the rule to files:
[rules]
rename_variables = { apply_to = ["src/**"], skip = ["src/vendor/**"] }darklua rules that are not rules here
Three darklua names are deliberately not larvae rules, because larvae already does each job elsewhere. Naming one in [rules] is an error, and the error points at the larvae way to do the same thing.
| darklua rule | where it lives in larvae |
|---|---|
convert_require |
the [requires] table |
inject_global_value |
[defines] |
remove_spaces |
generator = "dense" |
When rules run
[process] run_order (default 1) positions larvae's own rules relative to worms, so a worm saying "before" or "after" has something to be relative to. See configuration.
Rules that come from a worm
A worm can ship rules of its own. They are switched on under [worms.<name>].rules, never in [rules]:
[worms.markup]
repo = "someone/markup-worm"
version = "2.1.0"
rules = { tidy = true }[rules] is larvae's own table and is typed against the names on this page, so a worm's rule cannot shadow a builtin. Namespaced under its worm, a worm rule can share a name with one of larvae's and mean something different. Switching on a rule a worm does not declare is an error naming it. See using worms.