Working with LuaRocks#
Nupp libraries are distributed as Lua rocks. LuaRocks owns versions, dependency resolution, servers, and publication; Nupp adds a typed module surface beside the Lua that require loads.
Start a library#
nupp rock init string-tools
cd string-toolsThe generated project has one runtime module and one declaration with the same module path:
string-tools/
├── nupp.lua
├── string-tools-dev-1.rockspec
├── src/
│ └── string_tools.nupp
├── nupp/
│ └── string_tools.d.nupp
└── tests/
└── run.luaThe source builds to build/string_tools.lua, which LuaRocks installs as an ordinary Lua 5.1 module. The rockspec's copy_directories = { "nupp" } carries the declaration inside that rock's versioned installation. Nupp finds it there without putting it on Lua's runtime path.
For a nested module, mirror the runtime name beneath nupp/:
Runtime module Declaration
build/http/client.lua nupp/http/client.d.nupp
build/http/server/init.lua nupp/http/server.d.nuppA declaration is the public contract, not another implementation. It should contain the exported records, types, functions, ownership effects, and return table, but no function bodies. A rock may leave an internal Lua module undeclared; requiring it then has the usual gradual boundary (unknown under strict checking).
Build and test the rock#
nupp test
nupp rock pack
nupp rock testnupp rock pack checks the project and every declaration beneath nupp/, builds the runtime Lua, and runs luarocks make --pack-binary-rock. Name a rockspec when the directory contains more than one:
nupp rock pack string-tools-1.0-1.rockspecnupp rock test packs the same artifact, installs it into a new temporary rock tree, checks that every declaration has a runtime module, and checks a new consumer project against the installed declarations. This catches files that happened to work from the author's checkout but were absent from the rock.
Nupp does not publish or store LuaRocks credentials. Upload the checked rock with the usual LuaRocks tooling:
luarocks upload string-tools-1.0-1.rockspecConsume a typed rock#
Pin the rock in nupp.lua and put it on the target that uses it:
return {
include = { "src" },
dependencies = {
string_tools = {
kind = "luarocks",
rock = "string-tools",
version = "1.0-1",
},
},
build = {
entries = { "app.main" },
dependencies = { "string_tools" },
},
}The build installs it into the project's .rocks tree. At run time, require("string_tools") follows LuaRocks' normal LUA_PATH; during checking and in the language server, the same require reads the installed nupp/string_tools.d.nupp. Completion, hover, definition, and downstream interface invalidation therefore use the published contract.
Project modules take precedence over installed declarations. This makes a local module an intentional override and keeps dependency trees from changing what the project itself names.
For local development, pin a rockspec and source directory instead of a server version:
string_tools = {
kind = "luarocks",
rock = "string-tools",
path = "vendor/string-tools",
rockspec = "vendor/string-tools/string-tools-dev-1.rockspec",
}Nupp reruns luarocks make when that directory changes. A changed declaration invalidates the modules that require it; an unchanged installed interface remains a warm incremental dependency.
Native libraries#
A library implemented partly in C or Rust is still published as a rock. Use Nupp's C or Cargo dependency providers to build the native implementation, expose an ordinary Lua module, and describe that Lua-facing module in nupp/. Consumers depend on the rock, not on Cargo.
Native rocks normally need one artifact per supported platform. Pure Nupp libraries produce platform-independent .all.rock files.
Bundled applications#
Publishing a library and distributing an application are separate operations. A modules build uses the project-local rock tree. A bundle or stamped binary has no tree, so the dependency's bundle globs select the Lua modules carried into package.preload. See carrying a rock into a bundle.