# How to Compile TypeScript Cores to Native Zig Code at Build Time in the Native SDK

> Compile TypeScript cores to native Zig code at build time using the native-core CLI for the Native SDK. Add the generation step to your build.zig for seamless integration.

- Repository: [Vercel Labs/native](https://github.com/vercel-labs/native)
- Tags: how-to-guide
- Published: 2026-07-18

---

**Use the `native-core` CLI to run `transpileFile()` on your TypeScript core, then add that generation step as a dependency in `build.zig` so the resulting Zig module is compiled with your project at build time.**

The Vercel Labs Native SDK ships a TypeScript-to-Zig transpiler that converts application cores written in a supported TypeScript subset into native Zig modules during compilation. This build-time workflow lets you author business logic in TypeScript while producing efficient native binaries through Zig. Learning how to drive this pipeline from [`packages/core/src/cli.ts`](https://github.com/vercel-labs/native/blob/main/packages/core/src/cli.ts) and integrate it into `build.zig` is the key to shipping hybrid TS/Zig projects.

## How the TypeScript-to-Zig Transpiler Works

The entry point for the transpilation pipeline is `transpileFile(entry: string, options?: TranspileOptions): TranspileResult` in [`packages/core/src/transpile.ts`](https://github.com/vercel-labs/native/blob/main/packages/core/src/transpile.ts) at line 53. This function moves the source code through five discrete stages before emitting a single Zig file.

1. **Parse and type-check** the entry file and all its imports using the TypeScript compiler API via [`typed_ast.ts`](https://github.com/vercel-labs/native/blob/main/typed_ast.ts).
2. **Resolve the module graph** in [`modules.ts`](https://github.com/vercel-labs/native/blob/main/modules.ts), ensuring only files within the app’s `src/` tree are included.
3. **Run the subset checker** from [`checker.ts`](https://github.com/vercel-labs/native/blob/main/checker.ts) to enforce Native SDK rules R1–R18.
4. **Perform integer inference** in [`infer.ts`](https://github.com/vercel-labs/native/blob/main/infer.ts) to map JavaScript numbers to Zig `i64` where required.
5. **Emit a single Zig module** from [`emitter.ts`](https://github.com/vercel-labs/native/blob/main/emitter.ts), writing the runtime prelude, kernel capacity comptime parameters, JavaScript-semantics helpers, and exported core symbols.

## Running the native-core CLI Tool

The command-line wrapper lives in [`packages/core/src/cli.ts`](https://github.com/vercel-labs/native/blob/main/packages/core/src/cli.ts) and exposes the `native-core` binary. When invoked, it calls `transpileFile(entry, { frameCap, heapCap })` and writes the generated Zig code to the path provided by `-o <out.zig>` or prints it to stdout.

You must supply two comptime constants that configure the runtime kernel (`rt.zig`):

- `--frame-cap` sets the frame arena size.
- `--heap-cap` sets the model heap size.

A typical manual invocation looks like this:

```bash
node packages/core/src/cli.ts src/core.ts -o src/core.zig \
    --frame-cap 131072 \
    --heap-cap 524288

```

## Integrating Transpilation into build.zig

Because compilation happens entirely at build time, you can treat the transpiler as a standard system command inside `build.zig`. Add a custom step that runs the CLI before your main executable compilation, and make the executable depend on that step.

```zig
const std = @import("std");

pub fn build(b: *std.Build) void {
    const nativeCore = b.addSystemCommand(&.{
        "node",
        "packages/core/src/cli.ts",
        "examples/system-monitor-ts/src/core.ts",
        "-o", "examples/system-monitor-ts/src/core.zig",
        "--frame-cap", "65536",
        "--heap-cap", "262144",
    });

    const exe = b.addExecutable(.{
        .name = "system-monitor",
        .root_source_file = b.path("examples/system-monitor-ts/src/main.zig"),
        .target = b.standardTargetOptions(.{}),
        .optimize = b.standardOptimizeOption(.{}),
    });
    exe.addCSourceFile(b.path("examples/system-monitor-ts/src/core.zig"), &.{});
    exe.step.dependOn(&nativeCore.step);
    b.installArtifact(exe);
}

```

Calling `exe.step.dependOn(&nativeCore.step)` guarantees that `zig build` regenerates `core.zig` whenever the TypeScript source changes, preventing stale artifacts from being compiled.

## Importing and Using the Generated Zig Module

The output produced by [`packages/core/src/emitter.ts`](https://github.com/vercel-labs/native/blob/main/packages/core/src/emitter.ts) is a self-contained Zig module that includes three major components:

- A **runtime kernel** (`rt`) parameterized by the `frame_cap` and `heap_cap` comptime values.
- **Helper functions** that replicate JavaScript semantics, including `jsAnd`, `jsOr`, `jsShl`, and `numEq`.
- **Exported symbols** matching the public API of the original TypeScript module.

You can import the generated file like any other Zig module and interact with its exports directly:

```zig
const std = @import("std");
const core = @import("core.zig");

pub fn main() void {
    const phase = core.model.phase;
    std.debug.print("App phase: {s}\n", .{ @tagName(phase) });

    const result = core.jsAnd(0x1234, 0x00FF);
    std.debug.print("jsAnd result: {x}\n", .{ result });
}

```

## Troubleshooting Common Build-Time Errors

- **Missing imports (NS1034):** The resolver in [`packages/core/src/modules.ts`](https://github.com/vercel-labs/native/blob/main/packages/core/src/modules.ts) only follows imports inside the entry file’s graph. Absolute or out-of-tree paths trigger diagnostic `NS1034`. Keep all core files under the same `src/` directory or define a recognized path alias.
- **TypeScript subset violations:** The checker enforces rules R1–R18 from [`checker.ts`](https://github.com/vercel-labs/native/blob/main/checker.ts). Unsupported features raise diagnostics defined in [`packages/core/src/diagnostics.ts`](https://github.com/vercel-labs/native/blob/main/packages/core/src/diagnostics.ts). Rewrite the offending code to match the supported subset.
- **Insufficient kernel capacities:** If your model exceeds default limits, the runtime kernel (`rt.zig`) may run out of frame or heap space. Pass explicit `--frame-cap` and `--heap-cap` values to the CLI.
- **Stale generated files:** When the transpilation step is not a declared dependency, Zig can reuse a cached build that skips regeneration. Always link the executable step to the CLI step with `exe.step.dependOn(&nativeCore.step)`.

## Summary

- `transpileFile()` in [`packages/core/src/transpile.ts`](https://github.com/vercel-labs/native/blob/main/packages/core/src/transpile.ts) drives the entire pipeline, from parsing TypeScript to emitting a single Zig module.
- The `native-core` CLI in [`packages/core/src/cli.ts`](https://github.com/vercel-labs/native/blob/main/packages/core/src/cli.ts) exposes this API for build scripts and requires `--frame-cap` and `--heap-cap` to size the runtime kernel.
- In `build.zig`, declare the CLI as a `b.addSystemCommand()` step and make your executable depend on it to enforce correct build ordering.
- The emitted file contains a runtime kernel, JS-semantics helpers such as `jsAnd` and `numEq`, and exported TypeScript symbols ready for use via `@import`.

## Frequently Asked Questions

### What TypeScript features are supported by the Native SDK transpiler?

The transpiler enforces a strict subset defined by rules R1–R18 in [`packages/core/src/checker.ts`](https://github.com/vercel-labs/native/blob/main/packages/core/src/checker.ts). Only features that pass this validation are emitted; everything else raises a diagnostic through [`packages/core/src/diagnostics.ts`](https://github.com/vercel-labs/native/blob/main/packages/core/src/diagnostics.ts) and halts compilation.

### How do I configure memory limits for the generated Zig runtime?

Pass `--frame-cap` and `--heap-cap` flags to the `native-core` CLI. These values become comptime constants in `rt.zig` that size the frame arena and model heap for your transpiled core.

### Why does my build fail with diagnostic NS1034?

Diagnostic `NS1034` indicates that the module resolver in [`packages/core/src/modules.ts`](https://github.com/vercel-labs/native/blob/main/packages/core/src/modules.ts) encountered an import outside the allowed source tree. Move the imported file into the core `src/` directory or register a proper path alias to resolve it.

### Can I transpile TypeScript to Zig without using the Zig build system?

Yes. You can invoke the CLI manually with `node packages/core/src/cli.ts <entry.ts> -o <out.zig>` and then import the resulting file into any Zig project using `@import("out.zig")`. Embedding the call inside `build.zig` is recommended to keep generated artifacts synchronized automatically.