Menu
Try in Playground

Zero Packages: zero.json, src/, and Targets Explained

How a Zero package is laid out: the zero.json manifest, the src/ directory, and the targets system that lets one package produce executables, libraries, and tests from the same source tree.

Why Packages

A single .0 file is fine when you're learning the language or trying out a snippet. The moment your project grows past one file, you want a package — a directory with a manifest and a known layout that the toolchain understands.

The benefits of moving from loose files to a package:

  • One canonical place for the project's name, version, and metadata.
  • Multiple entry points (executable, library, tests) in one tree.
  • Predictable layout: tools can find your source without configuration.
  • A zero check / zero build against the whole tree, not file by file.

Scaffolding a Package

The fastest way to start is zero new:

zero new cli hello

That creates a hello/ directory laid out like this:

hello/
├── zero.json
└── src/
    └── main.0

cli is the template name — it produces an executable command-line program. Other templates (library, system program) follow the same shape with different defaults.

cd into the new directory, run it, and you're off:

cd hello
zero run

When you invoke zero run from inside a package directory without naming a file, it picks up the default target from zero.json and runs that.

The zero.json Manifest

The manifest from a scaffolded cli package looks like this:

{
    "package": { "name": "hello", "version": "0.1.0" },
    "targets": { "cli": { "kind": "exe", "main": "src/main.0" } }
}

Two top-level keys: package and targets. The first identifies the package; the second tells the compiler what to build.

package

"package": {
    "name": "hello",
    "version": "0.1.0"
}
  • name — a slug identifying the package. Use lowercase letters and dashes.
  • version — semver string. Pre-1.0 packages use 0.x.y.

Other metadata fields (description, author, license, repository) may be supported — defer to the current Zero docs for the authoritative schema since the manifest is still evolving.

targets

"targets": {
    "cli": { "kind": "exe", "main": "src/main.0" }
}

The keys (cli here) are target names you choose. The values describe each target:

  • kind — what the target is. exe for an executable. Other kinds (library, test) follow the same shape.
  • main — the entry source file, relative to the package root.

You can declare more than one target in the same package:

{
    "package": { "name": "image-tools", "version": "0.1.0" },
    "targets": {
        "convert": { "kind": "exe", "main": "src/convert.0" },
        "resize":  { "kind": "exe", "main": "src/resize.0" },
        "lib":     { "kind": "lib", "main": "src/lib.0" }
    }
}

Build a specific target with the CLI by naming it:

zero build convert
zero run resize

The src/ Directory

All source files live under src/. The compiler walks this directory automatically — you don't list every file in the manifest. Each target's main field points to its entry file; the compiler follows imports from there to find everything else it needs.

A package with a few helper modules might look like this:

image-tools/
├── zero.json
└── src/
    ├── convert.0
    ├── resize.0
    ├── lib.0
    └── internal/
        ├── decoder.0
        └── encoder.0

The internal/ subdirectory is just a convention — nothing in the manifest names those files. Imports inside convert.0 reach into internal/decoder.0 directly.

Building and Running

Common workflows once you're inside a package:

zero check        # type-check the whole tree
zero run          # build and run the default target
zero run convert  # build and run a specific named target
zero build        # build the default target
zero build --all  # build every target (when supported)
zero test         # run every test target

The CLI reads zero.json, figures out what to do, and goes. You rarely have to spell out paths once you're working inside a package.

Multiple Source Files: a Quick Example

Suppose src/main.0 calls a helper from src/math.0. The helper file:

pub fun double(value: i32) -> i32 {
    return value * 2
}

The entry file:

pub fun main(world: World) -> Void raises {
    let result = double(21)
    if result == 42 {
        check world.out.write("forty two\n")
    }
}

Run it with zero run. The compiler resolves the reference to double against the rest of the source tree without any explicit import declaration in this simple case. As packages get larger, an explicit imports system handles cross-module visibility — refer to the current Zero docs for the import syntax, which is one of the areas most likely to change before 1.0.

What Not to Check Into Git

A .gitignore for a Zero package usually wants:

# build artifacts and caches
/build/
/target/

# editor cruft
.DS_Store
*.swp

The exact build-output directory name may differ — check the current toolchain docs — but the rule is: source goes in, build artifacts stay out.

Sharing Packages

Zero is pre-1.0 and a package registry isn't part of the stable surface yet. For now, the practical ways to share a package are:

  • Git: clone the repo and run zero check against it.
  • Vendored copy: drop a copy of the source into another project.

When a registry lands, package references will likely move into a dependencies field in zero.json. Treat that as a forward-looking feature, not something to script against today.

Next: Language Basics

You now have everything you need to organize a real Zero project. The next chapter zooms in on the language itself, starting with let bindings — how Zero values get names.

Frequently Asked Questions

What is a Zero package?

A Zero package is a directory containing a zero.json manifest and a src/ folder with .0 source files. The manifest declares the package's name, version, and one or more 'targets' — each target tells the compiler how to build something (an executable, a library, a test binary) from the source.

How do I create a new Zero package?

Run zero new <template> <name>, for example zero new cli hello. The CLI scaffolds a directory with a zero.json, a src/main.0, and any other files the chosen template needs. From there you can zero check, zero run, and zero build inside the package.

What does zero.json contain?

At minimum, a package object with name and version, plus a targets object describing each thing the package builds. A target has a kind (like exe for an executable) and a main pointing to the entry source file. You can declare multiple targets in one manifest.

Can a single Zero package have multiple targets?

Yes. A package can declare any number of targets — for example one exe target for a CLI, one lib target for a reusable library, and one or more test targets. Each target has its own entry point under src/, and you can build or run them individually from the CLI.

Where does the compiler put build output?

Build artifacts land in a build directory inside the package (the exact path is implementation-defined and may change while Zero is pre-1.0). The source tree under src/ is never modified. Treat the build directory as disposable — checking it into git is a bad idea.

Coddy programming languages illustration

Learn to code with Coddy

GET STARTED