lloyAlloy on GitHub
export
Docs

export

Updated
export local x = 1
export function f() end
export struct Vec2 ... end
export { a, b as c }
export type { T }
export default expr
export default function make() end

Adds a name to the table the module returns at the end of its scope. Any declaration takes it: local, const, function, async function, struct, enum, trait, interface, remote, attribute, macro, impl. export { } names bindings after the fact, as renames one on the way out, and export type { } exports types alone. A struct, an enum, an interface, or a type alias in the list sends its type out with it.

The table holds the value each binding has when the module returns, and an import copies it. A write to an exported local inside a function comes later and never reaches an importer, so the stale_export lint reports it. Export a function that returns the value, or keep it in a table and write its fields: export local state = { count = 0 } and state.count += 1. Keep that table in a local, not a const: a const freezes its value by convention, so const_mutation reports each field or index write into it.

A barrel passes names on from another module: export { Shape, Geo } from "./shapes", or an import and an export { } list of it. An importer of the barrel gets what an import of the module gets: the enum's variants for a match, a namespace's types, and a macro.

export default puts one value in the table under default, which is what import Name from reads. It takes an expression or a declaration; a declaration also binds its name in the module. A default struct or enum sends its type out too, and import Name from binds that type under Name. A default sits beside any number of named exports, and a module has at most one. A type is not a value: send one out with export type.

return <expr> at the end of a module is the other way to send one value out. The returned value is the module's default, the way a plain Luau module reads, and import X from "./m" binds it. A module uses one or the other: export and a return together report, in the module and at every import of it. A module of types alone still writes its own return, since it exports no value.

A Roblox service comes in as an import, import Players from "@game/Players" or import { Players } from "@game"; alloy doc import has the forms. A module sends one on the way every other binding goes: export { Players } after the import line.

export namespace Name ... end sends a whole group under one name, and the types the group holds travel with it; alloy doc namespace has the shape.

The word is also free as a name. export opens a declaration before type, {, default, or a declaration word, and is a name anywhere else, so local export = {} and export.spawn = f read the Luau way. Luau treats its own export the same.