lloyAlloy on GitHub
alloy.toml
Docs

alloy.toml

Updated

The file alloy init writes:

alloy.tomlalloy.toml
#:schema .alloy/alloy.schema.json
[build]
in = "src"
out = "build"
exclude = []
clean = false
artifact = "ship"

[emit]
wait_timeout = 5
# std_require = "@alloy"

[std]
globals = "none"

[fmt]
recommended = true
column_width = 100
indent_type = "spaces"
indent_width = 2
quote_style = "force-single"

[lint]
recommended = true
strict = true

[lint.rules]
# raw_require = "allow"

[flux]
typecheck = true
definitions = []

[test]
out = "tests"
suite = "alloy"
lest = true
shim = true

[project]
name = "game"
sourcemap = true
source_of_truth = true
mount_aliases = true

# [mount]
# alias = [path, mount]: the folder at path lands at mount in the DataModel
# shared = ["src/shared", "@game/ReplicatedStorage/Shared"]
# server = ["src/server", "@game/ServerScriptService/Server"]
# alias = path: a name for a folder another mount already carries
# types = "src/shared/types"

# [ingots]
# an extension that ships as an executable: a path relative to this file,
# or a GitHub release pinned by version
# tailwind = "ingots/tailwind"
# tailwind = { repo = "alloy-luau/tailwind-ingot", version = "0.1.0" }

Every key has a default, and an unknown key is an error. The file writes one value that is not a default: wait_timeout = 5. A new project waits five seconds for a child, so => can return nil and reads as optional (alloy doc =>). Remove the key and the wait has no timeout, which is what a project without the key has always done. Both recommended keys are on: [lint] recommended applies the level each lint declares, and [fmt] recommended applies the layout above; either off leaves that table's own keys as the whole setting (alloy doc lint, alloy doc fmt). [lint.rules] gives one lint, one group, or one markup lint under alx. a level of allow, warn, or deny. alloy doc lint, alloy doc flux, alloy doc fmt, and alloy doc test explain their tables, and alloy doc markup the [alx] table, which the written file leaves out. alloy init writes the file, plus one Luau configuration: a .config.luau with strict mode and the @alloy alias when the folder has neither file, else the mode and the alias added to the .config.luau or .luaurc it already has (alloy doc init). Two alias names belong to the compiler: game, the Roblox services, and alloy, the runtime. A [mount] entry of either name is an error, and so is a game alias in a Luau configuration; alloy doc mount explains. [project] says which project file describes the DataModel tree, and [mount] describes one for a tool that reads no project file. Two keys of [project], both on by default, say how far the table reaches: source_of_truth writes default.project.json and .alloy/build.project.json from it, and mount_aliases serves its names as aliases beside the ones the Luau configuration declares. alloy doc mount explains all of it. [alx] holds the markup settings, the shape luaux.toml has, so a project with .alx files needs no second file. [roblox] rig, R15 by default or R6, picks the character type Player.Character reads as, R15Character? or R6Character?, in the editor and in alloy flux; the editor's alloy-luau.rig setting wins over it (alloy doc R15Character).

The editor checks the file against a JSON Schema. alloy self schema prints it. alloy self code writes it to ~/.alloy/alloy.schema.json and points VS Code (Even Better TOML) and Zed (Tombi, taplo) at it, so every key completes with its type, default, and text, and an unknown key is marked. alloy build writes the project's own schema to .alloy/alloy.schema.json, with the options and the lint names of its ingots; the #:schema .alloy/alloy.schema.json line at the top of the file, which alloy init writes, makes the editor read that one.

.config.aly

A project may write its configuration in Alloy instead: a .config.aly beside the sources, in place of alloy.toml. The keys and the values are the same, and a folder with both files reads alloy.toml. The file is a module, and it runs on load, so code above the export can compute a value:

-- By value: each exported table is one key.
export const build = { out = "dist" }
export const fmt = { indent_width = 4 }
-- A default export, bare or named.
local mode = "release"

export default const config = {
    build = { ["in"] = "src", out = if mode == "release" then "dist" else "build" },
}
-- A return.
return { lint = { strict = false } }

A key that is a Luau keyword stays a bare key, in = "src", as alloy migrate writes it. The file may require a module beside it by a relative path, and it may use the runtime: [ "a", "b" ]:map(f) works. A load that runs longer than two seconds stops.

The editor completes and documents every key and value from the schema alloy.toml validates against, so the two files read the same documentation. A key the schema does not take, a literal of the wrong type, and a load that fails report in the file.