Contributing to the purescript-lua package set
June 25, 2026 · View on GitHub
This set is a collection of PureScript libraries forked to run on Lua 5.1 through
the pslua compiler. Each fork keeps
the upstream PureScript sources and replaces the JavaScript FFI with Lua. This
document is the practical canon for maintaining a fork; the reasoning behind each
rule lives in docs/adr/.
Toolchain
Pinned through purescript-overlay (ADR 0001,
ADR 0008): purs 0.15.16, spago
1.x (the new PureScript spago — spago.yaml/spago.lock, not spago.dhall), Lua
5.1. Each fork carries its own flake.nix: enter its dev shell with nix develop
and keep flake.lock reasonably current with nix flake update (a long-stale
pslua pin breaks the build). This set repository has no flake of its own; its CI
builds through the pslua dev shell (nix develop github:purescript-lua/purescript-lua).
Commands
- Build:
nix develop -c ./scripts/build - Test:
nix develop -c bash ./scripts/test(forks that ship Lua regression tests) - Lint:
nix develop -c luacheck --quiet --std lua51 --no-unused-args src/ - Format:
nix fmt(ADR 0007)
Lua 5.1 and FFI (ADR 0003)
- No
table.unpack,bit32,utf8, or//.math.powandmath.atan2exist. Array-style tables are 1-indexed. Keep string escapes 5.1-safe. - Parenthesise every FFI export:
return { name = (<value>), ... }. unitis{}, never nil (ADR 0004).
CI (ADR 0002)
All forks share one workflow: substituters pinned in extra_nix_config (no
accept-flake-config), build, optional test via bash, and luacheck with
--std lua51 --no-unused-args.
Agent instructions (ADR 0005)
Each fork has an AGENTS.md (the single source) and a one-line CLAUDE.md that
imports it with @AGENTS.md. Edit AGENTS.md; never duplicate.
Changelogs (ADR 0009)
Every repository keeps a CHANGELOG.md managed with
scriv: fragments in changelog.d/, assembled
into a dated section by scriv collect on release. Forks provide scriv in
their flake.nix dev shell; this set repository has no flake, so its scriv
comes from the pslua dev shell (nix develop github:purescript-lua/purescript-lua).
- A change that touches
src/adds a fragment:scriv createwrites a template underchangelog.d/; fill in the right category (Added/Changed/Fixed/Removed) and commit it with the change. A tooling-, CI-, or flake-only change ships no release, so it needs no fragment. - A fork's scriv section documents the Lua fork's own release line; the inherited upstream changelog stays below it as history.
Releasing (ADR 0006, ADR 0009)
Run scriv collect --version <tag> to fold the pending fragments into
CHANGELOG.md, then commit it with the version bump. After that: annotated git
tag on master → bump the fork's version in src/packages.json → regenerate the
README package table (scripts/gen-readme-table.sh) → refresh
latest-compatible-sets.json → push a psc-* set tag. A tooling-only PR needs no
release.
A psc-* tag publishes one release asset
(ADR 0008): packages.json,
the consumable RemotePackageSet for the new spago (workspace.packageSet.url),
built from src/packages.json by scripts/gen-package-set-json. spago 0.21 /
Dhall is no longer supported.
The README package table is generated from src/packages.json (the single
source of truth): run scripts/gen-readme-table.sh after any version bump and
commit the result. CI (scripts/gen-readme-table.sh --check) fails if it drifts.
Decisions and ADRs
Cross-cutting decisions are recorded as ADRs in docs/adr/.
- Read them before a decision that affects the set (toolchain, CI, FFI conventions, release, formatting). The relevant ADR is usually linked from the section above.
- Add one after a decision. Copy the shape of an existing record (status, context, decision, consequences), give it the next number, and add it to the ADR index. Supersede rather than rewrite an accepted record.
Decisions about the pslua compiler itself live in the
pslua repository under its own
docs/adr/.