Project layout
A @teakit/napi project is a Zig project and a Node.js project sharing one root. This page is a one-stop map: every file in the project, every directory the build creates, and where to look for more on each.
The same layout applies whether you scaffolded with napikit new or wired things up by hand following Manual setup.
Source tree
After napikit build
<name>.js lets both import addon, { namedExport } from "./<name>.js" and the matching CommonJS load work locally without going through the npm package layout. It points at zig-out/lib/<name>.node and follows the root package.json module mode: "type": "module" selects ESM, while "type": "commonjs" or an omitted type selects CommonJS. Zig writes export-aware wrapper sources under zig-out/napikit/loaders/, and the CLI copies the appropriate one to the root.
Both <name>.js and <name>.d.ts at the project root are gitignored derived artifacts. Generated loaders and root declaration copies carry an ownership header; changing package.json.type migrates a loader that still has that header (and the legacy canonical wrapper), while arbitrary user-edited loaders and declarations are preserved. A legacy headerless declaration copy is recognized by matching it to the previous installed declaration, then migrated to the owned form. The CLI verifies that an observed generated/absent loader has not changed before its atomic replacement, so a concurrent user edit is never silently overwritten. Invalid package.json data fails before Zig or output cleanup. If declaration generation is later disabled, the next successful napikit build removes only the stale generated <name>.d.ts files from zig-out/lib/ and the project root.
Before invoking Zig, the CLI snapshots and clears prior native addons and their adjacent declarations. A successful run must produce at least one regular .node; a failed or empty run restores the last usable native outputs, declarations, and wrapper sidecars. This also makes addon renames authoritative: retired build-owned root loaders and declarations are removed after success, while same-named user files remain untouched.
Before each Zig invocation, the CLI clears previous build-owned wrapper sidecars and restores them only if the invocation fails. This ensures a successful build consumes metadata from that same invocation rather than stale export information.
If a newer napikit is paired with an older pinned @teakit/napi Zig dependency that does not emit export-aware wrapper sidecars, the CLI writes a default-export-only compatibility loader and tells you to update the Zig dependency. Default imports continue to work; named imports become available again after the pins are synchronized. A custom root loader is preserved even when those sidecars are absent.
After napikit build --release
This is the publishable tree. napikit publish ships every package discovered under npm/ to npm. The tree is reconciled on every release build: policy fields come from build.zig, your version and user-edited fields are preserved. With .dts = .none, index.d.ts and the manifest's types/managed files entries are absent; switching to .none removes that stale build-owned declaration. See Cross-compiling for the full rules.
If build.zig calls addLib more than once, each addon gets its own npm/<name>/ subtree alongside the others. See Multiple addons in one repo.
Where to put your code
- One file is fine. Keep adding to
src/lib.ziguntil it feels too large. - Splitting into namespaces. A
pub const x = @import("x.zig")inlib.zigbecomes the JS namespaceaddon.x. See Namespaces. - Shared Zig modules outside the root. Pass them through
.importsso they are importable as@import("name")from anywhere in your addon. SeeaddLibImport.
Generated and ignored
Exclude everything derived from version control: node_modules, zig-out, .zig-cache, zig-pkg, and the project-root <name>.js / <name>.d.ts re-exporters. (napikit new writes a .gitignore with all of these; if you set up by hand, see Manual setup, step 5.) Commit npm/ once you start publishing. Every release build keeps it in sync with build.zig automatically.