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-tools

The 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.lua

The 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.nupp

A 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 test

nupp 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.rockspec

nupp 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.rockspec

Consume 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.