Externals

use external "Units.ssp" gives a file another project's export, checked by shot check and run by shot run.

Use external

Station.ssp uses Units.ssp, the Generic project beside it:

Units/Types.shot

doc { A temperature in degrees Celsius }
public type Celsius = number

Units/Units.shot

types script.Parent.Types

doc { Converts temperatures and rounds numbers }
module Units = {

    doc { The Celsius temperature for a Fahrenheit one }
    function CelsiusOf(fahrenheit : number) : Celsius
        return (fahrenheit - 32) * 5 / 9
    end

    doc { x rounded to the given number of decimal places }
    function Round(x : number, places : number) : number
        local scale = 10 ^ places
        return math.floor(x * scale + 0.5) / scale
    end

}

Station/Station.shot

types script.Parent.Types

use FileService
use JsonService
use external "Units.ssp"

doc { Temperature readings in Celsius, kept in a JSON file }
class Station = {
    doc { The readings, oldest first }
    public Readings : {Celsius}

    doc { Adds a reading taken in Fahrenheit }
    public AddFahrenheit(fahrenheit : number)
        table.insert(self.Readings, Units.Round(Units.CelsiusOf(fahrenheit), 1))
    end
    ...
}
  • use external "<path>" goes where use goes, above a .shot file's declaration.
  • The local is named after the export, Units; as U names it U.
  • Only the export is reachable (One-file projects, MISTAKES AND THEIR ERRORS), and it can't be a program (THE ERRORS).
  • shot check types every use. Units.CelsiusOf("hot") gave:
Station.ssp/Station/Station.shot:23:59   TypeError: Expected this to be 'number', but got 'string'

At run time

shot run runs used code too: Forecast runs Station, which runs Units (Overview, THE EXAMPLES). With Units.CelsiusOf("hot") in Station, Forecast still checked clean; its run stopped in Units:

error: Units.ssp/Units/Units.shot:8: attempt to perform arithmetic (sub) on string and number
  at Units.ssp/Units/Units.shot:8 function CelsiusOf
  at Station.ssp/Station/Station.shot:23 function AddFahrenheit
  at Main/Main.shot:9
  • A run checks no used project: check each one.
  • Relative paths in used code start at the running project's folder.
  • A run reads used projects' saved files, written a moment after each change: shot save one before running or checking its users at once.
  • A run finds used projects by the header's external lines (THE RECORDED INTERFACE). Forecast without one:
error: Main/Main.shot:3: require("@Station"): Forecast.ssp has no external of that name (a string require is @<Alias> from the project's header, or @shotsystem/<Service>)
  at Main/Main.shot:3

Other run errors: Running a project, WHAT STOPS A RUN.

Checked paths

  • From the folder of the using project's file: "../libs/Units.ssp" is in libs, beside that folder.
  • Quoted, forward slashes, ending .ssp (or an older .shotsystem).

The alias

Each used project gets an alias in the header: its file name, numbered on a clash. Using "Units.ssp" and "../libs/Units.ssp" as LibUnits gave:

recorded external "Units.ssp" as Units interface 62f942db347c in .shotsystem: check reports any later change to that interface
recorded external "../libs/Units.ssp" as Units2 interface 62f942db347c in .shotsystem: check reports any later change to that interface

as only names the local; the header keeps as Units. Plain Luau requires the alias (PLAIN LUAU).

Import external

Station/Types.shot

import external "Units.ssp"

gives every file of Station's folder the public types of Units, like {Celsius} (Types). Only in a Types.shot:

Demo.ssp/Main.shot:1   import external goes in a system's Types.shot: it brings that project's public types into the system's types (use external "Units.ssp" gives a file the export itself)

A name clash with the system's own types is an error:

Station.ssp/Station/Types.shot:1   import external "Units.ssp" brings the type Celsius, and Station/Types.shot:3 declares Celsius in this system: a system's types are one flat set, so rename one of them

So is a name two imports both bring (Units2.ssp copies Units.ssp):

Station.ssp/Station/Types.shot:2import external "Units2.ssp" brings the type Celsius, and import external "Units.ssp" on line 1 brings Celsius too: a system's types are one flat set, so import one of them only
Station.ssp/Station/Types.shot:2import external "Units2.ssp" brings the type Units, and import external "Units.ssp" on line 1 brings Units too: a system's types are one flat set, so import one of them only

The kinds rule

Generic uses Generic; System uses System and Generic; Roblox uses Roblox and Generic. Anything else is an error at its line:

Units.ssp/Units/Units.shot:3   "Station.ssp" is a System project, and a Generic project can only use Generic projects
Units.ssp/Units/Units.shot:4   "Demo.ssp" is a Roblox project, and a Generic project can only use Generic projects
Station.ssp/Station/Station.shot:6   "Demo.ssp" is a Roblox project, and a System project can only use System and Generic projects
Demo.ssp/Demo/Demo.shot:3   "Station.ssp" is a System project, and a Roblox project can only use Roblox and Generic projects

shot external accept doesn't lift the rule. Put shared code in a Generic project.

The recorded interface

The first use external line records the project in the header:

created Station/Station.shot (52 lines)
recorded external "Units.ssp" as Units interface 62f942db347c in .shotsystem: check reports any later change to that interface

Imported from a folder without that line:

Station.ssp/Station/Station.shot:5   use external "Units.ssp": the .shotsystem header has no line for that project; shot external accept "Units.ssp" adds it with its interface
Station.ssp/Station/Types.shot:1   import external "Units.ssp": the .shotsystem header has no line for that project; shot external accept "Units.ssp" adds it with its interface

shot external accept "Units.ssp" --project Station.ssp records it and checks:

accepted "Units.ssp": interface 62f942db347c is recorded in .shotsystem as Units

check:
0 errors (2 files checked, 0.5 s)

The header then holds:

external "Units.ssp" as Units interface 62f942db347c
    .CelsiusOf(fahrenheit : number) -> Celsius
    .Round(x : number, places : number) -> number
    module Units
    type Celsius = number
  • Under it: the export's interface as shot api prints it, sorted, without bodies, docs or internal members.
  • 62f942db347c is the first 12 hex digits of their SHA-256.
  • shot writes these lines; leave them alone.

Unchanged, accept writes nothing:

"Units.ssp": the recorded interface 62f942db347c is the current one; nothing to accept

When a used project changes

A new body or doc keeps the interface. A new signature is an error in each direct user, at each line naming it:

Station.ssp/Station/Station.shot:5   use external "Units.ssp": its interface changed since it was recorded in .shotsystem (interface 62f942db347c, now 5cb27f16afa9):
    - .Round(x : number, places : number) -> number
    + .Round(x : number, places : number?) -> number
    once this project's code fits it, shot external accept "Units.ssp" records the new interface

shot map says so too:

external Units: "Units.ssp", interface 62f942db347c, now 5cb27f16afa9: it changed (shot check shows how; shot external accept "Units.ssp" records it)

Fix the user's code, then accept:

accepted "Units.ssp": interface 5cb27f16afa9 is recorded in .shotsystem (was 62f942db347c)
    - .Round(x : number, places : number) -> number
    + .Round(x : number, places : number?) -> number

check:
0 errors (2 files checked, 0.5 s)

Forecast, using only Station, stayed clean and kept running.

A loop

A project can't use one that uses it back. Each project in or using the loop gets the error:

Station.ssp/Station/Station.shot:6   external loop: Station.ssp -> Demo.ssp -> Station.ssp: a project can't use one that uses it back
Demo.ssp/Demo/Demo.shot:3   external loop: Demo.ssp -> Station.ssp -> Demo.ssp: a project can't use one that uses it back
Outer.ssp/Main.shot:1   external loop: Station.ssp -> Demo.ssp -> Station.ssp: a project can't use one that uses it back

Runs aren't refused: in Outer's, Station was nil. Move shared code into a third project.

The errors

Demo.ssp/Main.shot:1use external "Units": the path names a .ssp project file (.shotsystem works too), like "../Signals.ssp"
Demo.ssp/Main.shot:1use external "..\Units.ssp": write the path with forward slashes, like "../Signals.ssp"
Demo.ssp/Main.shot:1use external needs the project's path in quotes, with forward slashes: use external "Signals.ssp" or use external "Signals.ssp" as Name
Demo.ssp/Main.shot:1use external "Demo.ssp": that is this project itself, and a project can't use itself
Demo.ssp/Main.shot:1"Forecast.ssp" exports the script Main.Main, and a script can't be used from another project: only a module, a class or a func can

A missing project: use external "Missing.ssp": no project there, then the full path it tried.

Plain Luau

A .luau file requires a used project by its alias:

Main.luau

--!strict
local Units = require("@Units")

print(Units.Round(Units.CelsiusOf(212), 1))
  • The alias needs a .shot file naming the project: here Demo's Types.shot, import external "Units.ssp".
  • shot run Demo.ssp printed 100.
  • shot check types it. Units.CelsiusOf("hot") gave:
Demo.ssp/Main.luau:4:35   TypeError: Expected this to be 'number', but got 'string'

With no .shot file naming Units, check reports the require, though it runs:

Demo.ssp/Main.luau:2:15   Unknown require "@Units": a System project's services are EncodingService, FileService, HttpService, JsonService, PageService, ProcessService, ProgramService, RunService and TimeService, and its built-in modules Mop, QuickSignal and QuickValue (local FileService = require("@shotsystem/FileService")); its own modules come by path (require(script.Parent.Mop)), and another project by its alias (require("@Log")) once a .shot file of the project names it with use external "Log.ssp"