Skip to content

gwigz/slua

Repository files navigation

@gwigz/slua-*

LSL type definitions and a TypeScript-to-Lua plugin for Second Life. Full editor support, compile-time safety, minimal runtime overhead.

If TypeScript is where you're productive, you don't need to learn a new language to script for Second Life. SLua has decent tooling, but this lets you stay in the ecosystem you already know.

Packages

Package Description
@gwigz/slua-types Auto-generated TypeScript declarations for all SLua/LSL APIs
@gwigz/slua-tstl-plugin TSTL plugin enforcing SLua constraints

Real-world usage

I use this toolchain for my own projects, it's how I find the rough edges:

Quick Start

Install the packages:

npm install --save-dev typescript typescript-to-lua @gwigz/slua-types @gwigz/slua-tstl-plugin

Create a tsconfig.json in your project:

{
  "compilerOptions": {
    "target": "ESNext",
    "module": "ESNext",
    "strict": true,
    "moduleDetection": "force",
    "types": ["@typescript-to-lua/language-extensions", "@gwigz/slua-types"]
  },
  "tstl": {
    "luaTarget": "Luau",
    "luaLibImport": "inline",
    "luaPlugins": [{ "name": "@gwigz/slua-tstl-plugin" }],
    "extension": "slua"
  }
}

See TypeScriptToLua configuration for more config options.

Editor & GitHub setup (optional)

Map .slua to Lua highlighting in VS Code (and forks):

// .vscode/settings.json
{
  "files.associations": {
    "*.slua": "lua"
  }
}

Tell GitHub to highlight .slua files as Lua:

# .gitattributes
*.slua linguist-language=Lua

Write TypeScript, compile to SLua

const owner = ll.GetOwner()

LLEvents.on("touch_start", (events) => {
  for (const event of events) {
    const key = event.getKey()

    if (key === owner) {
      ll.Say(0, `Hello secondlife:///app/agent/${key}/about!`)

      return
    }
  }
})

Compile with npx tstl (or bunx tstl, pnpm tstl, etc.) to get:

local owner = ll.GetOwner()

LLEvents:on("touch_start", function(events)
    for ____, event in ipairs(events) do
        local key = event:getKey()

        if key == owner then
            ll.Say(0, ("Hello secondlife:///app/agent/" .. tostring(key)) .. "/about!")

            return
        end
    end
end)

Plugin Transforms

The TSTL plugin automatically translates TypeScript patterns to native Luau/LSL equivalents for JSON, base64, string methods, array methods, bitwise operators, and floor division. See the full transform reference for details.

Comments

Due to a TSTL limitation, only valid JSDoc-style comments (/** */) are preserved in the output. Regular comments (//, /* */) are stripped:

/** This comment will appear in the Lua output */
const owner = ll.GetOwner()

// This comment will be stripped
const pos = new Vector(128, 128, 20)
--- This comment will appear in the Lua output
local owner = ll.GetOwner()
local pos = vector.create(128, 128, 20)

Scripts

  • bun run generate - regenerate packages/types/index.d.ts from YAML definitions
  • bun run build - build all workspaces that define a build script
  • bun run build:examples - build all example workspaces only
  • bun test - run all tests
  • bun run lint - lint with oxlint
  • bun run lint:fix - lint and auto-fix
  • bun run fmt - format with oxfmt
  • bun run fmt:check - check formatting without writing

Project Structure

├── packages/
│   ├── types/          # auto-generated .d.ts declarations
│   └── tstl-plugin/    # TSTL plugin for SLua constraints
├── tools/
│   └── gen-types/      # type generation tool (YAML -> .d.ts)
├── examples/
│   ├── getting-started/   # minimal example
│   ├── kitchen-sink/      # feature showcase
│   ├── sim-wide-relay/    # multi-script relay system
│   └── weather-fetcher/   # HTTP request example
└── refs/
    └── lsl-definitions/  # upstream YAML definitions (submodule)