Skip to content

Roadmap

This page covers only features as you encounter them. Internal phases, progress percentages, and implementation details live in the GitHub repository.

Expose the ZNTC AST as a WASM module so it can be consumed outside Node and the browser. The WASM build that today powers the Playground and Metafile Analyze pages will be stabilized and published as a user-facing API. Depends on AST schema stabilization.

Zig plugins currently exist only for built-in transforms (worklet, Fast Refresh). User-authored Zig modules will be loadable as plugins, statically linked at build time and called in-process. No JS↔native serialization overhead, but the Zig compiler must be available at build time.

Rollup plugin context API extension (ESTree AST)

Section titled “Rollup plugin context API extension (ESTree AST)”

The vitePlugin() adapter supports the main Rollup hooks (resolveId · load · transform · renderChunk · generateBundle) and most fields of getModuleInfo(). Remaining items are parts of the plugin context API (this.parse() · this.resolve() · this.emitFile() · ModuleInfo.meta) and ModuleInfo.ast — an ESTree adapter that exposes each module’s ESTree-compatible AST to plugins. It depends on converting ZNTC’s internal AST to the ESTree shape.

Refined dead-code elimination (innerGraph)

Section titled “Refined dead-code elimination (innerGraph)”

Straight-line dead stores on local variables are eliminated. Dead-store analysis across if / for / try control flow is in progress.

Barrel re-exports (export * from, export { X } from, import * as X; export { X }) skip compilation for pure / local / namespace patterns. Wrapper barrels that mutate imported bindings (e.g. lodash-es’s lodash.default.js) currently disable lazy mode entirely as a conservative fallback. Refining this to apply lazy mode to non-mutating parts only is pending. See Tree-shaking for current limits.

Cross-module property mangling, esbuild-style. Not implemented yet.

Scope-hoisting at the level of rspack / rolldown.

Only an in-memory parse cache + resolve cache lives across rebuilds within a watch / serve session. A persistent on-disk cache to speed up cold rebuilds is on the backlog.

On-demand module compilation for faster dev startup. Not implemented yet.

Currently delegated to tsc. Native isolatedDeclarations-based emission is planned.

Two dev servers coexist today:

  • Zig native (zntc serve / zntc dev) — standalone, no Node required.
  • JS (zntc dev <root> app mode) — used to delegate to JS-ecosystem plugins (postcss / sass).

The long-term goal is to vendor BoringSSL, abstract the plugin host, and converge on the Zig server (similar to Bun’s model). This sits behind the stability / CSS / ecosystem milestones.

Reference plugins for common cases: PostCSS, Tailwind, SVG, YAML.

Expand the esbuild → ZNTC and Vite → ZNTC mapping tables. Some material already lives under Migration.

Framework integration (Next.js · Remix · SvelteKit · Expo)

Section titled “Framework integration (Next.js · Remix · SvelteKit · Expo)”

Each of these ships its own bundler tightly coupled to the framework, so an adapter ends up being a partial reimplementation of the framework’s compiler. This is the last milestone.

  • Expo: A meta-framework on top of React Native. Plain React Native apps already build via --platform=react-native, but integrating with Expo Router’s filesystem-routing manifest, the expo prebuild step, and the EAS build pipeline requires a dedicated adapter.

NativeWind, which turns Tailwind classes (className) into styles in React Native, currently works by passing nativewind/babel through as a user Babel plugin (on the --platform=react-native build path). First-class support is planned: ① a reference example plus a React Native build E2E regression guard, ② folding Tailwind CSS compilation behind the plugin API (the @tailwind directives in global.css wired as a React Native entry), and ③ zero-config wiring when nativewind is present in package.json so the React Native preset sets it up automatically. Doing the className → style transform natively in the ZNTC transformer (without Babel) is gated on measuring the actual benefit first.

React Native builds, launches, and debugging are already handled by the ZNTC native core engine (--platform=react-native). What gets added is not a separate bundler but a single react-native.config.js command-plugin entry point — adding that plugin makes the existing react-native start / react-native bundle / react-native run-ios|android go through ZNTC instead of Metro (argument mapping + spinning up the existing RN dev server + wiring up the device launch/debug channels). This entry point lives in @zntc/react-native so the general-purpose zntc CLI stays free of RN dependencies.

React Native bundles are currently always downleveled to ES5. But modern Hermes (the version shipped with current React Native) natively supports many ES2015+ constructs — classes, let/const, arrow functions, destructuring, and more — so blanket ES5 transformation only inflates the bundle with unnecessary helpers and closures. ZNTC’s engine-target feature (Hermes included, per-feature downleveling) already exists, so the React Native preset will switch from a hardcoded ES5 target to downleveling only what the bundled Hermes version actually needs. Constructs Hermes still lacks (certain regular-expression features, etc.) keep being downleveled, and apps with Hermes disabled (JSC) fall back to ES5.

Internal tests already run bundles in a real browser via the Chrome DevTools Protocol to verify source maps and runtime errors. This path is being promoted to a user-facing CLI command (zntc verify --browser): run a build’s output in headless Chrome and report console errors, uncaught exceptions, and source-map resolution as an exit code plus JSON (Playwright stays an optional dependency).

Read vite.config.js directly for zero-cost migration. Long-term goal.

These frameworks bake the bundler into the framework itself — RSC payload serialization, filesystem-routing manifests, loader/action server-client separation. A general-purpose bundler cannot stand in for them. Plain React / Vue / Svelte SPAs and React Native (Metro-compatible output) are supported.

.module.css auto-transform in core --bundle mode

Section titled “.module.css auto-transform in core --bundle mode”

App mode (zntc dev / zntc build) supports .module.css class-name hashing / scoping out of the box — see Plugin Recipes / CSS Modules. Core --bundle mode does not auto-transform .module.css; go through the Vite adapter or a user plugin (PostCSS Modules / Lightning CSS Modules) instead.

Persistent disk cache · Lazy compilation · mangleProps

Section titled “Persistent disk cache · Lazy compilation · mangleProps”

See the corresponding “Planned” items.

Dead assignments inside if / for / try may be preserved. Only straight-line dead stores are eliminated today.

When a barrel module mutates an imported binding (some libraries do this — e.g. lodash-es), the lazy-barrel optimization is disabled wholesale for that module. Correctness is preserved, but the bundle can be larger than necessary.

Large tsconfig paths (hundreds of entries)

Section titled “Large tsconfig paths (hundreds of entries)”

Only the first resolve walks the array linearly; subsequent resolves hit the resolver cache. No measurable impact at normal project sizes.

@zntc/wasm exposes transpile(), build(), buildChunks(), and VirtualFileSystem for user import; see the WASM section of Installation for the usage surface. Direct module-level access to the ESTree-compatible AST from plugins is the remaining gap — see the “Planned / Public WASM AST API” entry above. AST schema stabilization is the prerequisite.