Step 2 of 14, updated

A first file

A .shot file is a ModuleScript minus the boilerplate: plain Luau on top, one declaration under it.

You'll learn the project folder, a module, a script, and your first check, build and run.

The project

Watch out Make these folders and files yourself (shot new makes one-file projects; page 1 says why), and run every shot command from inside MyProject.

A module

The table is the declaration; its functions are written inside it:

struck ShotScript writes it for youhighlighted new in ShotScript
Luau todayCoins (a ModuleScript)
--!strictlocal SUFFIX = " coins"local Coins = {}function Coins.Format(amount: number): string	return tostring(amount) .. SUFFIXendreturn Coins
ShotScriptWallet/Coins.shot
types script.Parent.Typeslocal SUFFIX = " coins"module Coins = {	function Format(amount : number) : string		return tostring(amount) .. SUFFIX	end}
What it compiles toWallet/Coins.luau
--!strict
local Types = require(script.Parent.Types)
export type Coins = Types.Coins

local SUFFIX = " coins"

local Coins : Coins = {} :: any
local module = Coins

function Coins.Format(amount : number) : string
	return tostring(amount) .. SUFFIX
end

return Coins
  • types script.Parent.Types points the file at its folder's types (every file but scripts and Types itself).
  • Plain Luau above the declaration is copied as it is: services, requires, constants, local functions.
  • The file name is the declared name. The compiler adds --!strict and the return.
  • The table is typed: Coins's type goes into the folder's Types module, so other files see Format's real signature.
LuauWallet/Types.luau
--!strict
if true then return {} end

--[[$public]] export type Coins = {
	Format : (amount : number) -> string,
}

Tip The Types module only holds types, so it returns at once and never runs.

A script

A file whose first line is @server or @client is a script: it runs by itself when the game starts. @shot is ReplicatedStorage.Shot.

Luau todayServer (a Script)
local ReplicatedStorage =	game:GetService("ReplicatedStorage")local Coins = require(	ReplicatedStorage.Shot.Wallet.Coins)print(Coins.Format(25))
ShotScript53 linesInit/Server.shot
@server enabledlocal Coins = require(@shot.Wallet.Coins)print(Coins.Format(25))
What it compiles toInit/Server.server.luau
--!strict
local Coins = require(script.Parent.Parent.Wallet.Coins)

print(Coins.Format(25))

Init/Client.shot is the same with @client enabled; for now it only prints client started.

Check, build, run

$ shot check
0 errors (4 files checked, 2.8 s)
$ shot build ../build
4 files written to ../build, 0 errors

Tip ../build holds ReplicatedStorage/Shot/... in the Rojo layout, ready for Studio. Every compiled file these pages show is in there.

To run without Studio, add a scenario in MyProject/Scenarios/: it plays the game in a mock Roblox and never ships. world:Boot() starts the scripts, world:AddPlayer joins a player, world:Step runs frames (page 13 lists every call).

LuauScenarios/Hello.luau
-- Hello: start the game with one player and read what it printed.
return function(world)
	world:Boot()
	world:AddPlayer("Ana")
	world:Step(0.1)
end
$ shot run Hello
==== Hello
[server print] 25 coins
[client:Ana print] client started

==== summary
  Hello          OK  (2 lines, 0 errors, 0.1s virtual)
1 of 1 scenario(s) passed
Try it

Make Format return amount instead of the text and run shot check. It answers ./Wallet/Coins.shot:7:3 TypeError: Expected this to be 'string', but got 'number', at the line you wrote.