EasySwitchLua works out of the box on standard Lua 5.1+, LuaJIT, FiveM, Roblox (Luau), and LÖVE2D.
Important
On the GitHub Releases page, download the named release asset (EasySwitchLua-vX.Y.Z.zip), not "Source code (zip)". The source archive does not include the bundled dist — dist/ is gitignored, the dist is only generated by the release CI workflow.
Inside the named zip you'll find :
EasySwitchLua-vX.Y.Z/
├── easyswitch.lua ← readable bundle (with header + matchigo embedded)
├── easyswitch.min.lua ← minified single-line bundle (same code)
├── easyswitch-type/ ← LuaLS / EmmyLua type definitions (.d.lua, ---@meta)
├── README.md
└── LICENSE
easyswitch.lua and easyswitch.min.lua are functionally identical. Use the readable one in development for cleaner stack traces, the minified one when shipping a tight build. They expose the exact same API.
Drop easyswitch.lua (or .min.lua) anywhere on your package.path :
local EasySwitch = require("easyswitch")If package.path is awkward in your context, load directly :
local EasySwitch = dofile("path/to/easyswitch.lua")The bundled file has zero require() calls (everything is inlined including the matchigo runtime), so it runs in any sandbox without package configuration.
The dist is meant to be available to your scripts (loadable on demand) rather than auto-executed. client_scripts / server_scripts / shared_scripts would all run the file at resource start and discard its return value — we want explicit loading instead.
Drop easyswitch.lua (or .min.lua) at the root of your resource folder.
If your client scripts need the lib, declare it as a files entry so it gets sent to clients :
fx_version 'cerulean'
game 'gta5'
files { 'easyswitch.lua' } -- needed for client-side LoadResourceFile
client_scripts { 'client.lua' }
server_scripts { 'server.lua' }For server-only usage, you can skip the files declaration entirely — LoadResourceFile on the server reads the resource's own files regardless of manifest entries.
Note
Don't use shared_scripts / client_scripts / server_scripts for the bundle file itself — they'd auto-execute it at resource start and discard its return value. Declare it as files only (or skip entirely on server) and load it explicitly via the loader below.
In each script that needs the library, paste this 5-line loader once at the top :
local function load_module(name)
local file = LoadResourceFile(GetCurrentResourceName(), name .. '.lua')
if not file then error("Module not found: " .. name, 2) end
return assert(load(file, '@' .. name))()
end
local EasySwitch = load_module('easyswitch')
local P = EasySwitch.P
-- Use it normally
local sw = EasySwitch.new()
:when("hello", function() return "world" end)
print(sw:execute("hello"))That's it — files { 'easyswitch.lua' } for client-side access, nothing in the manifest for server-only, the loader function does the rest.
Each FiveM resource has its own isolated Lua state, so a global stays scoped to that resource. If you don't want to repeat the loader in every script, do it once and stash the result on _G :
-- shared/init.lua (loaded first via shared_scripts in your fxmanifest)
local function load_module(name)
local file = LoadResourceFile(GetCurrentResourceName(), name .. '.lua')
if not file then error("Module not found: " .. name, 2) end
return assert(load(file, '@' .. name))()
end
_G.EasySwitch = load_module('easyswitch')
_G.P = EasySwitch.PThen any other script in the resource can use EasySwitch and P directly with no setup. Globals across resource boundaries don't collide — you can't see another resource's EasySwitch from yours, even if both have one.
- No global pollution — each script holds its own local handle.
- Explicit loading — you control exactly when and where the library is initialised.
- Memoization-friendly — wrap the loader in a cache (
local cache = {}; if cache[name] then return cache[name] end ...) if you load the same module from many scripts.
Note
The dist file itself does NOT define a global EasySwitch. Older READMEs of the v1 EasySwitch claimed it did, but that was inaccurate — the v1 bundle returned a table that nothing was capturing in the FiveM script context. The loader pattern above is the correct fix.
- Download
easyswitch.luafrom the latest release zip. - In Roblox Studio, create a
ModuleScriptnamedEasySwitchunderReplicatedStorage(or wherever you organise your modules). - Paste the full contents of
easyswitch.luainto it. - Use it from any script :
local EasySwitch = require(game.ReplicatedStorage.EasySwitch)
local sw = EasySwitch.new()
:when("attack", function() print("attacking!") end)
:when("defend", function() print("defending!") end)
:default(function(v) print("unknown action:", v) end)
sw:execute("attack")The bundled file has no external require() calls and is fully compatible with Luau's sandbox.
LÖVE runs on LuaJIT (Lua 5.1 compatibility) — no setup needed. Drop easyswitch.lua into your project :
-- main.lua
local EasySwitch = require("easyswitch")
local gameState = EasySwitch.new()
:when("menu", function() -- draw menu
end)
:when("game", function() -- draw game
end)
:when("pause", function() -- draw pause
end)
function love.keypressed(key)
if key == "escape" then gameState:execute("pause") end
endIf you use lua-language-server (the engine behind sumneko.lua in VSCode), drop the easyswitch-type/ folder anywhere in your workspace :
your-project/
├── easyswitch.lua ← runtime
└── easyswitch-type/ ← type defs from the release zip — pick any name
├── easyswitch.d.lua
└── Switch.d.lua
The language server picks up ---@meta files automatically for completion, hover, and type checks. No runtime impact — these files contain definitions only.
For VSCode, place the folder at the workspace root (auto-detected) or add it to your Lua.workspace.library setting.
If you'd rather build the bundle yourself instead of downloading the release zip — useful for local development or applying patches :
git clone https://github.com/Kamionn/EasySwitchLua.git
cd EasySwitchLua
lua build.lua
# → dist/easyswitch.lua + dist/easyswitch.min.luaThen run the test suite to verify your build :
lua tests/test_all.lua # against ./src
lua tests/test_all.lua dist # against the bundled distlocal EasySwitch = require("easyswitch")
print(EasySwitch.new():when("ping", function() return "pong" end):execute("ping"))
-- expected output : pongIf you see pong, you're ready. Continue to Getting started.