Naming conventions
UpdatedThe 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.
[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].
