Guides menu
Guide

Create & publish a package

A tutorial for library authors: package reusable Serez Code so others import it, expose a runnable command with bin, and publish it so anyone can sz install it.

What you'll learn: the package layout, the serez.json manifest, exporting your API, exposing a command with bin so consumers run it with sz run, and publishing. Requires serez-code ≥ 4.6.2 for bin.

Step 1 — Lay out the package

A package is a folder with an index.sz (the entry that import resolves to) and a serez.json manifest. A runnable command, if any, is a separate .sz file:

greet-kit/
  index.sz       ← what `import "greet-kit"` resolves to
  src/
    greeter.sz   ← your modules
  cli.sz         ← (optional) a runnable command entry
  serez.json     ← the manifest

Step 2 — Write the library and export its API

index.sz wires up the modules consumers will use. Mark everything reachable from outside with export:

// index.sz
import "src/greeter"
// src/greeter.sz
export public class Greeter {
    public Greeter(name) { this.name = name }
    public hello() { return "Hello, " + this.name + "!" }
}
Export everything reachable across files. A function that is not exported is invisible from another module — even internal helpers (including ones starting with _) must be export fn if an exported class or function calls them.

Step 3 — Write the manifest

serez.json describes the package. Only name and version are required:

{
  "name": "greet-kit",
  "version": "1.0.0",
  "description": "A tiny greeting library",
  "author": "you",
  "dependencies": {},
  "permissions": []
}
FieldWhat it is
name / versionPackage identity (version is bumped on each release)
description / authorMetadata shown in the registry
dependenciesOther packages this one needs — installed with it
permissionsCapabilities your code uses: OS, File, Env, etc.
scriptsNamed commands for your own dev workflow (sz run <script>)
binCommands this package exposes to consumers (see below)

Step 4 — Expose a command with bin

If your package ships a tool or CLI (a packer, a generator, a migrator…), declare it in bin. Each entry maps a command name to an entry .sz file. Consumers then run it from their project root with sz run <command> — no path to your file required:

{
  "name": "greet-kit",
  "version": "1.0.0",
  "bin": { "greet": "cli.sz" }
}

The entry receives the consumer's extra arguments through Env.args() (passed as key=value tokens, no dashes):

// cli.sz
fn getOpt(argv, key, def) {
    let prefix = key + "="
    let i = 0
    while (i < argv.length()) {
        if (argv[i].startsWith(prefix)) {
            return argv[i].substring(prefix.length(), argv[i].length())
        }
        i = i + 1
    }
    return def
}

let name = getOpt(Env.args(), "name", "world")
out "Hello, " + name + "!"

A consumer of your package runs:

sz run greet name=Sergio     # → Hello, Sergio!
How sz run <name> resolves: (1) a script in the consumer's serez.json, then (2) a bin command from an installed package (local ./packages, then the global store), then (3) an error listing what is available. Extra arguments are forwarded to your entry, and it runs with the consumer's project root as the working directory. If two packages expose the same command name, consumers disambiguate with sz run <package>:<command>.

Step 5 — Publish

From the package root, publish the current version to the registry:

sz publish

Bump version in serez.json for every release — installs resolve a version, so a new version must be published before consumers can get it.

Step 6 — Install and use

Anyone can now add it to their project:

sz install greet-kit

Import the library…

import "greet-kit"

let g = new Greeter("world")
out g.hello()           // → Hello, world!

…and run the command you exposed with bin:

sz run greet name=world   // → Hello, world!

Next steps