lloyAlloy on GitHub
Naming conventions
Docs

Naming conventions

Updated

The naming_convention lint reads the case of each name the file declares. [lint.naming] in alloy.toml, or lint.naming in .config.aly, sets the case of each kind of name. The lint warns by default, and [lint.rules] naming = "allow" turns it off.

alloy.tomlalloy.toml
[lint.naming]
variable = "snake_case"
const = ["snake_case", "SCREAMING_SNAKE_CASE"]

A value is one style or a list of styles. A name passes when it fits one style of its list, and the fix writes the first style. The styles are snake_case, camelCase, PascalCase, SCREAMING_SNAKE_CASE, and any, which lets every name pass.

The keys, with their defaults, which follow Rust:

variable    snake_case    a local, and a loop variable
const       snake_case or SCREAMING_SNAKE_CASE
function    snake_case    a free function, and a local function
component   PascalCase    an .alx function that returns markup or that a tag names
method      snake_case    a function in an impl or a trait
parameter   snake_case
field       snake_case    a field of a struct or an interface
struct      PascalCase
enum        PascalCase
variant     PascalCase    a variant of an enum
trait       PascalCase
interface   PascalCase
type        PascalCase    a type alias
namespace   PascalCase
attribute   snake_case
macro       snake_case
remote      PascalCase

Alloy marks any binding that the file never assigns again as const, as JavaScript does, so a const takes both cases by default. Set const = "SCREAMING_SNAKE_CASE" to keep the capitals for constants. A local that nothing assigns again takes the const style too, while the prefer_const lint or [fmt] prefer_const is on, since either one makes it a const.

A name of one letter, or of digits after letters, fits each style its letters allow: x is snake_case and camelCase, and T is PascalCase and SCREAMING_SNAKE_CASE. The fix reads an acronym as one word: HTTPServer becomes http_server, httpServer, or HTTP_SERVER.

Some names do not fire. A name that starts with _ marks a value as unused. A local bound to require(...) or :GetService(...) takes the case of its module or service, local Players. A function stored on a table, function M:Destroy(), is a member of the table. A method in the impl of a trait takes its name from the trait. In an .alx file a function that returns markup, or that a tag names, is a component and takes the component style, and so does a local that holds one.

The rename. alloy flux --fix, the editor's quick fix, and alloy fmt rename a name to the first style of its list, at each place the file reads it:

a local or a parameter          its scope; a nested scope that shadows it keeps its own
a file-level function or type   every read in the file; not a field, a key, or a method call

The rename writes nothing when it is not safe. An exported name, a field, a variant, a method, a remote, an attribute, a macro, and a namespace member keep the lint alone, because another file or saved data reads them. So does a declaration under an attribute other than @allow, such as @test, because the runtime may read its name. A rename also stops when the new name is already in the file, or is a keyword or a Luau global. It stops too when the file reads the old name in a place the compiler cannot trace. alloy fmt renames in an .aly file while the lint is on for it, and [fmt] fix_naming = false stops it. In an .alx file the formatter sees the code with its markup held out, so alloy flux --fix does the renames there.

Quieting it. @allow(naming_convention) quiets it on one item, and so do rustc's non_snake_case, non_camel_case_types, and non_upper_case_globals. The three lints it replaced, camel_case_name, type_case, and pascal_case_function, still name it in @allow, --@alloy-lint, and [lint.rules].