CLI tool
PerryTS/perry avatar
PerryTS/perry

Perry: A TypeScript-to-Native Compiler That Removes the JavaScript Engine

Project brief: A native TypeScript compiler written in Rust. Compiles TypeScript directly to executables using SWC and LLVM.

4,834 stars161 forksRustMIT

At a glance

What is it?
Perry compiles TypeScript to native executables via SWC and LLVM, targeting desktop, mobile, and embedded platforms. It trades JavaScript runtime compatibility for AOT speed and tiny binaries, but its ecosystem compatibility and benchmark results deserve scrutiny.
Who is it for?
Adopt Perry if you need small, fast-starting native binaries from TypeScript for desktop or mobile, and you can tolerate its incomplete Node.js compatibility and ongoing maturity. Skip it if you depend on the full Node.js ecosystem or need proven production stability.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

The Problem: Shipping TypeScript Means Shipping a JavaScript Engine

TypeScript developers have few deployment options. You either install Node.js on every server, bundle a JavaScript runtime like Bun, or ship an entire browser engine with Electron. Each approach carries overhead: engine boot time, memory footprint, and binary size. Perry attacks this problem directly. It compiles TypeScript to machine code ahead of time, producing a standalone executable with no runtime. The README claims a hello-world binary is approximately 330 KB, and a MongoDB GUI built with Perry ships as a roughly 7 MB app. For comparison, Electron apps typically exceed 100 MB. Perry targets developers who want the ergonomics of TypeScript but the deployment characteristics of a systems language. It is not for everyone, but for those who need small, fast-starting native binaries, it addresses a real gap.

How Perry Works: SWC Parses, LLVM Compiles, No JIT

Perry's pipeline is straightforward. SWC, a Rust-based TypeScript parser, handles parsing and transformation. LLVM then compiles the resulting intermediate representation to machine code. This is ahead-of-time compilation, not just-in-time. There is no engine to boot and no warmup phase. The generated binary includes only the runtime components the program uses, which explains the small size. The README mentions a generational garbage collector and compile-time data-race safety. For parallel operations, Perry provides functions like parallelMap and parallelFilter that spawn real OS threads. The compiler rejects shared mutable state at compile time, so data races cannot occur. This is a significant departure from Node.js, where worker_threads require careful manual synchronization. The architecture resembles that of a systems language compiler, but it accepts TypeScript as input.

Getting Started: Installation and Basic Commands

Installation is straightforward. The README lists three package managers: npm install -g @perryts/perry, brew install perryts/perry/perry, and winget install PerryTS.Perry. After installation, you create a project with perry init my-app, then run it with perry run . To compile a standalone binary, you use perry compile src/main.ts -o myapp, which produces an executable you can run directly. The project also supports web and WebAssembly targets via --target web and --target wasm, emitting JavaScript or WASM for browser use. This means the same TypeScript codebase can target native platforms or the web. The README claims npm packages and ES modules work as expected, with an example using Fastify. The commands are simple and familiar to anyone who has used a CLI compiler.

Performance Claims: Fast on Some Workloads, Slower on Others

Perry's README presents benchmark results from open harnesses on an Apple M1 Max. On image convolution, Perry runs at 354 ms versus Node's 1,207 ms and Rust's 392 ms. Fibonacci recursion also matches Rust. However, the full published suite, generated from a JSON artifact, shows losses. Prime sieve: Perry 28 ms, Node 6 ms, Bun 5 ms. Matrix multiplication: Perry 85 ms, Node 33 ms, Bun 33 ms. These are not cherry-picked wins; the README explicitly includes workloads where V8's JIT beats Perry. This honesty is commendable. The takeaway is that Perry can match or beat Rust on certain numeric and allocation-heavy tasks, but it lags on code that benefits from JIT optimizations like loop vectorization or dynamic dispatch. You should run your own benchmarks on your target workloads before adopting Perry for performance-critical applications.

Compatibility: Node.js Modules and npm Packages

Perry claims a 97% pass rate on Node.js's own test suite across 53 node:* modules. It implements fs, http, http2, net, tls, crypto, stream, child_process, worker_threads, fetch, and web globals. It also claims compatibility with about 50 popular npm packages, including Fastify, Express, mysql2, pg, ioredis, ws, bcrypt, and jsonwebtoken. Plain JavaScript compiles too, which is useful for migrating existing codebases. However, 97% is not 100%. The remaining 3% could be edge cases in APIs you rely on. The README does not list which modules fail or which packages are not supported. This is a significant gap for production evaluation. You must check the compatibility list on the project's documentation site or test your own dependencies. If your project depends on an obscure npm package, it may not compile.

Targets and UI: One Codebase, Eleven Platforms

Perry's native targets include macOS, Windows, Linux, iOS, Android, watchOS, and TV. The README mentions a SwiftUI-like API that compiles to real platform widgets, not WebViews. This includes AppKit, UIKit, Android Views, Win32, and GTK4. The same codebase can also target web and WASM. This is a bold promise: write UI once in TypeScript and get native widgets on every major platform. The README also mentions home-screen widgets, which suggests deep platform integration. However, the documentation is likely thin on how this API differs from standard TypeScript web development. If you are a web developer used to HTML and CSS, this API will require learning a new paradigm. The advantage is that you avoid embedding a browser engine, which reduces memory and startup time. But the trade-off is that you are locked into Perry's UI abstractions, which may not cover every platform feature.

Limitations and Failure Modes

Perry is not a drop-in Node.js replacement. The 97% pass rate on Node's test suite means some APIs behave differently or are missing. The README does not specify which modules are incomplete. Another limitation is the compile-time data-race safety: it rejects shared mutable state. This is a constraint that Node.js developers are not used to. If your code relies on shared mutable state across threads, it will not compile. You must refactor to use message passing or immutable data. Also, Perry's runtime includes a generational GC, which may not have the same tuning options as V8. For memory-intensive applications, you may hit GC pauses or unpredictable behavior. The project is still young, with frequent releases (v0.5.1220 as of July 2026). This suggests active development but also potential instability. You should test on your specific target platform, as the README's benchmarks are from one machine (Apple M1 Max).

Alternatives: Node.js, Bun, and Rust

The most direct alternative is Node.js, which is mature, has a massive ecosystem, and supports every platform. The trade-off is the runtime overhead: engine boot, warmup, and memory. Bun is another alternative, offering a single binary with an embedded JavaScript engine. It is faster than Node for many workloads but still uses JIT, so cold starts are not instant. For developers who want native performance, Rust is the obvious choice. The README's own benchmarks show Perry matching or beating Rust on some workloads, but Rust has a more mature toolchain, a richer ecosystem, and no compatibility gaps. The difference in approach is fundamental: Perry translates TypeScript to machine code, while Node and Bun interpret or JIT compile JavaScript. Rust compiles from a language designed for systems programming, not from TypeScript. If you are comfortable writing Rust, you do not need Perry. If you must write TypeScript, Perry offers a unique path.

Maintenance, Licensing, and Upgrade Costs

Perry is licensed under MIT, which is permissive and allows commercial use without restrictions. The project is actively maintained, with releases nearly daily (v0.5.1219 and v0.5.1220 on consecutive days). This high release cadence means you will need to keep up with updates to get bug fixes and new features. The README mentions that the compiler links only the runtime your program uses, which reduces binary size but also means that upgrading Perry may change the runtime behavior. You should track the changelog for breaking changes. The documentation is hosted on GitHub Pages, and there is a Discord community for support. However, the project is relatively new, and the ecosystem of third-party libraries that support Perry's native targets is limited. You may need to write platform-specific code for certain features. The maintenance cost is moderate: you must test your application after each Perry upgrade, especially if you use many npm packages.

Editorial conclusion

Adopt Perry if you need small, fast-starting native binaries from TypeScript for desktop or mobile, and you can tolerate its incomplete Node.js compatibility and ongoing maturity. Skip it if you depend on the full Node.js ecosystem or need proven production stability. Before adopting, verify your critical npm dependencies against Perry's compatibility list, run your own benchmarks on your target hardware, and check the project's issue tracker for unresolved bugs. Perry is a promising AOT compiler, but it is not yet a drop-in Node.js replacement.

Official sources

  1. Official documentation
  2. Official README
  3. Project repository
  4. Release notes
Community notes

Community notes