A collection of code migrations (also known as codemods or transformations) for the Luau programming language, along with the libraries that support them. Everything here is built on Lute and its syntax libraries.
Warning
The codemod driver in this repository (main.luau, along with its supporting lib/arguments.luau, lib/files.luau
and lib/ignore.luau) is deprecated. Use Lute's built-in lute transform
command to run codemods instead. The codemods in this repository are compatible with lute transform as-is.
| Codemod | Description |
|---|---|
flag-clipper |
Removes references to a fast flag, simplifying the code as if the flag were always true or false |
remove-unused |
Removes unused local variables and functions, e.g. those left behind after a flag cleanup |
convert-instance-requires |
Converts Roblox instance-based requires (require(script.Parent.Module)) to string requires (require("./Module")) |
roundtrip.luau |
Parses and reprints each file unchanged; useful for checking that the parser and printer preserve source exactly |
| Library | Description |
|---|---|
require-resolver |
Detects require calls and resolves them to modules, using a Rojo sourcemap for instance-based requires |
config-resolver |
Reads .luaurc files and resolves the require aliases that apply to a given file |
lib |
Shared utilities (paths, strings) and the deprecated driver's argument parsing, file discovery and ignore handling |
Install Lute, then clone this repository:
$ git clone https://github.com/luau-lang/codemod.gitRun a codemod with lute transform, passing the path to the codemod's init.luau, the files or directories to
transform, and then the codemod's options after a -- separator:
$ lute transform codemod/flag-clipper/init.luau src/ -- --flag MyFlag --value true --sourcemap sourcemap.json- The migration path must point at a file (e.g.
codemod/flag-clipper/init.luau), not a directory. - Directories are searched recursively for
.luauand.luafiles, honouring.gitignorefiles. If no files are given, the current directory is used. - Codemod options are passed as
--name valuepairs after--. Boolean options taketrueorfalse. - Use
--dry-runto run without writing or deleting any files,--interactive(-i) to review a diff of each change before it is written, and--output <path>(-o) to write the result to a separate file when transforming a single file.
See each codemod's README for its options.
A codemod is a Luau module that lute transform loads and applies to each file. In its simplest form, it returns a
function that takes a context and returns the new source code for the file:
local function transform(ctx)
-- transform the provided ctx.source
return ctx.source
end
return transformThe context has the following shape. Its type is available as Context from the @transform alias, which
lute setup --with-luaurc adds to the .luaurc in the current directory.
type Context<Options = { [any]: any }> = {
path: string, -- The path to the file being transformed
source: string, -- The source code of the file
parseresult: syntax.CstParseResult, -- The parsed concrete syntax tree (CST) of the file
options: Options, -- The parsed codemod options
}A transform may return either the new source as a string, or a table of replacements mapping nodes of
ctx.parseresult to their new text. Lute then prints the file with those replacements applied.
The CST in ctx.parseresult is immutable. For most codemods, the simplest approach is to select nodes with
@std/syntax/query and return replacements for them:
local query = require("@std/syntax/query")
local utils = require("@std/syntax/utils")
local function transform(ctx)
return query.findAllFromRoot(ctx.parseresult, utils.isExprConstantNumber):replace(function(number)
return `{number.value * 2}`
end)
end
return transformVisitors from @std/syntax/visitor can also be used to traverse the tree and build up a replacements table.
Codemods that restructure code heavily, like flag-clipper and remove-unused, can instead parse a mutable tree with
parseMutable from @lute/syntax/parser, modify its nodes directly, and return the printed result:
local parser = require("@lute/syntax/parser")
local printer = require("@std/syntax/printer")
local visitor = require("@std/syntax/visitor")
local function transform(ctx)
local parsedResult = parser.parseMutable(ctx.source)
local uppercaseVisitor = visitor.create()
uppercaseVisitor.visitExprLocal = function(node)
node.token.text = node.token.text:upper()
return true
end
visitor.visitBlock(parsedResult.root, uppercaseVisitor)
return printer.printFile(parsedResult)
end
return transformTo delete the file being transformed, return the deletion marker, available as DELETION_MARKER from @transform:
local transformTypes = require("@transform")
local function transform(ctx)
if shouldDeleteFile(ctx.source) then
return transformTypes.DELETION_MARKER
end
-- ...
endDeletions are deferred until all files have been processed, so require resolution still works for files that are about to be deleted.
A codemod can return a table instead of a function. The table must contain a transform function, and may contain an
options list and an initialize function:
type Options = {
name: string,
value: boolean,
}
local function initialize(ctx: { options: Options })
-- runs once, before any file is transformed
-- e.g. parsing a sourcemap for require resolution
end
local function transform(ctx)
local name = ctx.options.name
local value = ctx.options.value
-- ...
end
return {
options = {
{ name = "name", kind = "string" },
{ name = "value", kind = "boolean", default = true },
},
initialize = initialize,
transform = transform,
}Options are either string or boolean. An option with a default is optional; any other option must be passed on
the command line:
$ lute transform transform.luau src/ -- --name STRING --value BOOLEANBefore lute transform existed, codemods were run with this repository's driver:
$ lute codemod/main.luau ./flag-clipper --flag MyFlag --value true --sourcemap sourcemap.json src/The driver accepts the same module shape (transform, options and initialize) and the same deletion marker, but
it does not provide ctx.parseresult and only accepts a string result. It resolves the codemod path relative to
main.luau rather than the working directory. It is no longer recommended, and may be removed in the future.
Tests are plain Lute scripts, run with lute <path>; see .github/workflows/ci.yml for
the full list. Code is formatted with StyLua (stylua --check .).