Skip to content

§ 27 — Standard module catalogue

27. Standard module catalogue

Normative. This chapter defines the catalogue of blessed Cook modules — modules curated by the Cook project and distributed through the official LuaRocks-backed registry (rocks.usecook.com). A module’s presence as a numbered chapter in Part IV is what makes it “blessed”: a conformance-checkable contract that any implementation of the module MUST honour.

The Standard governs the contract, not the reference implementation. The published rock is one implementation that meets the contract; a reimplementation in another language layer MUST produce semantically equivalent behaviour for every public surface specified here.

27.1. Bootstrap

27.1.1. Install

A conforming project obtains a blessed module by listing it under [modules] in cook.toml and running cook modules install. The install populates .cook/modules/share/lua/5.4/<name>/init.lua (and .cook/modules/lib/lua/5.4/<name>.<so-ext> for C extensions) under the calling Cookfile’s module tree, per § 12.2‘s resolution order. [amended CS-0207]

Nothing an author writes belongs in that tree. It is a child of the build-output root (.cook/), it is regenerable from cook.toml and cook.lock, and it is covered wholesale by the ignore rules an implementation’s project-initialisation writes. A module the project itself authors is addressed by path instead (§ 12.5, § 5.1).

A named install wires the use declaration. [added CS-0220] An install that names its modules (cook modules install cook_cc) MUST, after the install succeeds, ensure the Cookfile at the project directory carries a use declaration (§ 5.1) naming each module it installed. A module the author has not used is inert: its makers are unreachable and the chores it registers under § 22.12 cannot be dispatched, so an install that stops at the manifest has delivered a rock and not a capability. The author naming the module IS the request to use it, and no second request adds information.

Three cases, each idempotent:

  1. Where no Cookfile exists, an implementation MUST create one, and its entire content MUST be the use declarations. An implementation MUST NOT seed it with a recipe, a chore, or any other declaration. What a new project’s first recipe should be is not this command’s question, and a starter target written here is a name the next scaffolding step has to collide with or work around.
  2. Where a Cookfile exists and carries no such declaration, an implementation MUST insert one and MUST report the insertion. The insert is subject to § 22.13 in full: every byte outside the inserted range unchanged, and no evaluate-and-re-render.
  3. Where the declaration is already present, an implementation MUST leave the file byte-identical and MUST report nothing. A second install of a module a project already uses is a no-op on the Cookfile, however the author spelled the declaration.

“Already present” is decided on the NAME a declaration binds (§ 12.1), not on the module it resolves to. A use declaration in any of its forms — the name form, and the path form with an explicit or a derived alias — satisfies this rule for the name it binds. The case that makes this normative rather than pedantic is § 27.1.2‘s replacement: an author patching a blessed module writes use cook_cc ./vendor/cc.lua, and an install that added use cook_cc beside it would bind one name twice. A Cookfile binding an alias twice is not a Cookfile with a redundant line in it: the register-phase chunk and an execute-phase body need not resolve the repeated binding the same way, and the build then loads the author’s patched module in one phase and the installed rock in the other, with nothing to see in either file.

Where it creates the Cookfile, an implementation MUST also write the ignore rules its project-initialisation writes, by the merge that initialisation uses. This install has just populated .cook/, so creating the project’s first tracked file without them leaves generated content the author is about to commit.

A name that cannot be spelled is refused, not approximated. A rock name is drawn from the package manager’s character set and a use_name is a LUA_IDENT (§ 5.1), so the two are not the same alphabet: foo-bar and foo.bar are installable and cannot be written as a name-form use. An implementation MUST NOT write a declaration for such a module, MUST NOT substitute a rewritten spelling, and MUST report the module, the reason, and that the declaration was not written. The install itself still succeeds — the rock is on disk and the manifest records it — because refusing to install a module over a spelling defect in a convenience would be the larger failure.

An unparseable Cookfile is not edited. Where the existing Cookfile does not parse, an implementation MUST leave it byte-identical, MUST report that the declaration was not written and name the module it would have written, and the command MUST fail. § 22.13‘s “failure is explicit and total” is the same rule reached from the same place: an implementation that cannot locate where the declaration goes has no honest second choice, and inserting at a guessed offset compounds a syntax error the author is already holding.

An install naming nothing modifies nothing. cook modules install with no arguments realises the manifest and the lockfile, and MUST NOT modify the Cookfile. It names no module, so it carries no request; and the closure it installs contains transitive modules the author never asked for and MUST NOT be made to use.

Removal is deliberately not symmetric. cook modules remove MUST NOT delete a use declaration. The verbs know different things. An install knows the author wants the module usable; a removal knows only that a manifest entry is going away, and not whether recipe bodies still reach through the local that declaration binds. A left-behind use fails at load, naming the module that is missing. A deleted one fails later and deeper, inside a body that calls a maker on a value that is now nil, and it fails after destroying something the author may have annotated. The asymmetry is the refuse-rather-than-clobber posture of § 22.13 applied to the one edit that cannot be an insertion.

27.1.2. Patching a blessed module

Removed by CS-0207. This subsection defined a vendoring escape hatch: a project MAY hand-vendor a copy of a blessed module at the top of cook_modules/, where § 12.2‘s search order gave it precedence over the installed copy, and cook modules install MUST NOT overwrite it.

Both halves are withdrawn, and the capability is subsumed rather than lost. A project that needs a patched module now points a path-form use (§ 5.1) at its own file. That is strictly better on the point the old workflow was weakest: shadowing by precedence left no record in the Cookfile that it had happened, so a reader saw use cook_cc and had to know the search order and inspect the tree to learn which cook_cc ran. A path names the file it means.

The install-time non-overwrite rule went with it because there is nothing left for the install to collide with: the tree it writes contains only what it put there.

27.2. Catalogue index

This Standard’s v0.1 catalogue:

ModuleChapterSummary
cc§ 28C-family (C + C++) native build module

Future Standard revisions extend this catalogue additively.