Perry(PerryTS)は、TypeScriptのソースをSWCで解析し、LLVMで機械語に変換して単体の実行ファイルを出力するRust製のコンパイラです。標準のビルドではNode.jsやJavaScriptエンジンを同梱せず、macOS・Windows・Linuxに加えてiOS・Android・watchOSのアプリも同じコードから生成します。リポジトリ(PerryTS/perry)は2026年1月19日に作成され、2026年10月8日時点のスター数は4,942(GitHub API)、ライセンスはMITです。
本記事では、最新リリースのv0.5.1520(2026年9月11日公開)をIntel Mac(x86_64)に入れて計測したバイナリサイズと起動時間、公式ドキュメントに書かれた言語機能の制限、ElectronやTauriと比べたときの採用判断をまとめます。
まとめ:Perryの用途と採用前の制約
- Perryは
perry compile main.ts -o appでTypeScriptを機械語の実行ファイルにする事前(AOT)コンパイラ。実行時にJavaScriptエンジンを持たない - インストールは
npm install -g @perryts/perry、Homebrew、wingetのいずれか。リンクのためにXcode Command Line Tools、gcc/clang、WindowsではLLVMが別途必要 - Intel Macでの実測は、hello worldが14.3MB、起動が中央値32.9ms(Node.js v26.5.0は253.2ms)。公称の「約330KB」は、ソースツリーを使ったサイズ最適化ビルドが前提
- 実行時に組み立てた文字列の
evalは、既定ではビルドが通り実行時に例外になる。--strict-evalでコンパイルエラーにできる parallelMapなどで外側の変数に書き込むコードはコンパイルが拒否される。並列処理の結果は戻り値で集める- 版は0.5系で、GitHub Releasesは2026年10月8日時点で累計159件。社内CLIや小さなデスクトップツールで試し、ストア配信アプリの主力にするのは待つのが妥当
Perryの仕組み:SWCで解析しLLVMで機械語を出す流れ
公式ドキュメントのHello Worldの章は、perry file.ts -o output を実行したときの処理を5段階で説明しています。SWCでTypeScriptを構文解析し、HIR(高水準の中間表現)に落とし、インライン化やクロージャ変換などの最適化をかけ、LLVMで機械語を生成し、最後にシステムのCコンパイラ(リンカ)で実行ファイルにまとめる流れです。v0.5.1520ではLLVM 22をコンパイラ本体に組み込んでおり、手元のビルドログにも in-process LLVM backend active (LLVM 22.1.8) と出ました。構文解析を担うSWCそのものの設定や用途はSWCとは?Rust製コンパイラの設定と採用判断を実装目線で解説で扱っています。
Node.jsやBunとの違いは、配布物の中身にあります。READMEの比較表は次のように整理しています。
| 項目 | Perry | Node.js | Bun | Electron |
|---|---|---|---|---|
| 配布するもの | ネイティブバイナリ1本 | コード+Node.js本体 | JSエンジン内蔵のバイナリ1本 | ブラウザエンジン入りのアプリ |
| 実行方式 | 事前コンパイルの機械語 | JIT | JIT | JIT |
| UI | AppKit・UIKit・Win32・GTK4など | なし | なし | Chromium |
| 並列処理 | OSスレッド | worker_threads | Worker | プロセス |
名前が似ている技術との混同にも注意が必要です。TypeScript 7とは?Go製ネイティブコンパイラtsgoで最大10倍高速化|RC公開と移行の要点【2026年最新】の「ネイティブ」は型検査器(tsc)をGoで書き直したという意味で、出力は従来どおりJavaScriptです。PerryはTypeScriptから直接機械語を出す点で別物です。同じく機械語を狙うがC言語を経由する方式はscriptcとは?TypeScriptをC経由でネイティブ化する仕組みと採用条件で比較できます。
インストールから初回コンパイルまでの手順
npm・Homebrew・wingetでの導入と前提ツール
READMEが案内するインストール方法は3通りです。npm版はOSとCPUに合ったビルド済みバイナリを optionalDependencies で取り込む薄いランチャーで、Node.js 16以上が要ります。
# どれか1つ
npm install -g @perryts/perry
brew install perryts/perry/perry
winget install PerryTS.Perry
# 環境の確認(リンカやプラットフォーム用ツールの有無)
perry doctor
Perryは最後のリンクをOSのツールチェーンに任せるため、本体とは別に次の準備が必要です(インストール手順のドキュメントより)。
- macOS:Xcode Command Line Tools(
xcode-select --install) - Linux:gccまたはclang。Debian/Ubuntuなら
build-essential。glibc版の配布バイナリはglibc 2.31以上(Ubuntu 20.04以降、Debian 11以降など)が条件 - Windows:
winget install LLVM.LLVMとperry setup windows(約1.5GB、Visual Studio不要)、またはMSVC Build Tools
npmのパッケージ名は @perryts/perry、GitHubの組織名もPerryTS、公式サイトはperryts.comです。公式の配布物を確認するときは、npmのスコープ、GitHubの組織名、公式サイトのドメインを照合してください。
hello.tsのコンパイルと実行結果
1行のファイルを用意し、perry compile に渡すと実行ファイルができます。以下はIntel Macでの実行結果です。
$ cat hello.ts
console.log("Hello, Perry");
$ perry compile hello.ts -o hello
Collecting modules...
Found 1 module(s): 1 native, 0 JavaScript
Generating code...
perry: in-process LLVM backend active (LLVM 22.1.8)
note: Perry workspace source not found — linking the prebuilt full stdlib (larger binary).
Linking (runtime-only)...
Wrote executable: hello
Binary size: 14.3MB
$ ./hello
Hello, Perry
コンパイルと実行を1度に済ませる perry run、保存のたびに再コンパイルする perry dev、コンパイルせずに互換性だけ調べる perry check、署名からストア提出までをまとめる perry publish も同じCLIに入っています。
Intel Macで実測したバイナリサイズと起動時間
READMEはhello worldを「約330KB」、ドキュメントのサイズ表は「約300KB」としています。ところがGitHub Releasesで配布されているmacOS x86_64版(v0.5.1520)でコンパイルすると、hello worldは14.3MB(14,959,952バイト)でした。上のログにある note が理由で、Perryのソースツリーが手元に無いと、使う機能だけを組み直すサイズ最適化ビルドが働かず、ビルド済みの標準ライブラリを丸ごとリンクします。ソースツリーの場所は環境変数 PERRY_WORKSPACE_ROOT で指定します。
サイズは大きくても依存は少なく、otool -L で確認したhello worldの動的リンク先は libSystem.B.dylib の1本だけでした。Node.jsの追加インストールは不要ですが、配布先のMacが対応するCPUアーキテクチャ・命令セット・macOSの条件を満たす必要があります。計測は2026年10月8日、Intel Core i9-9880H(2.3GHz)を積んだmacOS 26(Darwin 25.6)のMacで、GitHub Releasesの perry-macos-x86_64.tar.gz(v0.5.1520)を既定オプションの perry compile でビルドして行いました。起動時間はPythonの subprocess.run で標準出力を捨てて10回連続実行した中央値で、Node.js側は同じ1行の hello.js を node hello.js で実行しています。fib(35)は各実行ファイル内で Date.now() の差を取り、5回実行しました(Perryは109〜131ms、Node.jsは293〜325ms)。
| 計測項目 | Perry 0.5.1520 | Node.js v26.5.0 |
|---|---|---|
| hello worldのファイルサイズ | 14.3MB | (Node.js本体が必要) |
| UIカウンターアプリのサイズ | 14.5MB | ― |
| hello worldの起動〜終了(10回の中央値) | 32.9ms | 253.2ms |
| 再帰fib(35)の計算時間(5回の中央値) | 114ms | 309ms |
起動時間で約7.7倍、再帰呼び出しの計算で約2.7倍の差がつきました。fib(35)の実行中にはNode.jsのJIT最適化も働くため、この差をウォームアップの有無だけでは説明できません。一方、READMEの公開ベンチマーク一覧には負けている行も載っています。Apple M1 Max上の計測で、クラスのメソッド呼び出し(method_calls)はPerry 35msに対しNode.js 11ms、木構造の割り当て(binary_trees)は13msに対し6msでした。オブジェクト指向の呼び出しが多いコードでは、Node.jsより遅くなる場面があると見込んでおくべきです。
配布時にもう1つ注意があります。perry compile --help によると、ホスト向けビルドの既定は --march native で、ビルドしたマシンのCPU命令(AVX-512など)を使います。古いx86-64 CPUで動かす可能性があるなら、--march generic か x86-64-v2 のように固定します。Linuxで外部ライブラリに依存しない完全静的バイナリが欲しいときは --libc musl を付けます。
TypeScriptの対応範囲と言語機能の制限
Perryの型注釈の扱いと型検査の設定
Perryは型注釈を消してからコードを生成するため、string と宣言した引数に数値が渡っても実行時に例外は出ません。型の推論はPerry独自のエンジンで行い、完全な型検査はしません。tscと同じ精度で型エラーを拾いたい場合は --type-check を付け、MicrosoftのTypeScript型検査器と連携させます(型システムのドキュメントより)。
eval・new Functionの文字列リテラル対応と実行時の制限
eval("1 + 1") のように中身が文字列リテラルで書かれていれば、Perryはそれをネイティブ関数としてコンパイルし、実行すると 2 を返しました。実行時に組み立てた文字列は事前コンパイルできないため、既定ではビルドを止めずに「到達したら例外を投げる値」に置き換えます。ここで紛らわしいのが、const 変数に入れた文字列も実行時扱いになる点です。
const code = "1+1";
console.log(eval(code));
このファイルは perry check が「All checks passed」を返し、コンパイルも末尾の注意書き(notice)だけで成功します。ところが実行すると Error: eval() cannot run in an ahead-of-time compiled binary で止まりました。互換チェックを通ったから本番で動くとは限りません。--strict-eval を付ける(または package.json に "perry": { "strict": true } を書く)と、同じコードは error[U006] でビルドが失敗します。CIでは厳格モードを既定にしておくと、実行時の事故を避けられます。実行時に計算したパスを渡す import(spec) にも同じ扱いが適用されます。
Node.js互換と同梱されるnpmパッケージ
READMEは、Node.js自身のテストスイートで53の node:* モジュールにわたり約97%が通ると公称しています。v0.5.1520のmacOS版アーカイブには、fastify・pg・mysql2・mongodb・ioredis・ws・bcrypt・jsonwebtoken・sharp・axiosなど40本のパッケージをネイティブ実装した静的ライブラリ(libperry_ext_*.a)が入っていました。
制約は「コンパイル時に見えないコードは動かない」ことです。制限事項のドキュメントは、実行時にインストールされたnpmプラグインやローカルのJS/TSプラグインを読み込めないと明記しています。Perryで共有ライブラリにコンパイルしたネイティブプラグインを loadPlugin で読み込む仕組みはありますが、JS/TSのプラグインをそのまま持ち込む設計は移植できません。また、node_modules 内の素のJavaScriptファイルはQuickJSベースの perry-jsruntime で動かす経路があり、これは perry.allowJsRuntime などで明示的に許可したときだけリンクされます(JSランタイムのオプトイン)。プラグイン前提のアプリにはJavaScriptエンジンを内蔵する方式が向いており、その代表であるBunの互換範囲はBun 1.4とは?Rust実装への移行とNode.js互換の実測範囲を実装目線で解説でまとめています。
ネイティブUIと対応プラットフォーム
Perryの perry/ui は、SwiftUIに近い宣言的なAPIを各OSの標準ウィジェットに対応づけます。WebViewやブラウザエンジンは使いません。プラットフォーム概要のドキュメントは10系統を挙げています(READMEはiPadOSを分けて11と数えています)。
| プラットフォーム | –target | UIツールキット | 対応状況 |
|---|---|---|---|
| macOS | (既定) | AppKit | 127/127 |
| iOS | ios | UIKit | 127/127 |
| visionOS | visionos | UIKit | 2Dウィンドウのみ |
| tvOS | tvos | UIKit | フォーカス・ゲームコントローラ対応 |
| watchOS | watchos | SwiftUI | 15ウィジェット |
| Android | android | JNI/Android SDK | 112/112 |
| Wear OS | wearos | JNI/Android SDK | Androidと共通 |
| Windows | windows | Win32 | 112/112 |
| Linux | linux | GTK4 | 112/112 |
| Web | web(wasm) | DOM/CSS | 168ウィジェット |
「127/127」「112/112」はUI関数の実装数です。Apple系の127に対しAndroid・Windows・Linuxは112なので、macOSで書いたUIを他のOSへ持っていくときは、使っている関数が対象OSにあるかを確認します。次は公式サンプルのカウンターアプリで、テンプレートリテラル内の count.value が状態に結びつき、ボタンを押すと表示が更新されます。
import { App, VStack, Text, Button, State } from "perry/ui"
const count = State(0)
App({
title: "Counter",
width: 400,
height: 300,
body: VStack(16, [
Text(`Count: ${count.value}`),
Button("Increment", () => count.set(count.value + 1)),
]),
})
Intel Macでコンパイルすると14.5MBの実行ファイルになり、AppKitやCoreGraphicsなどOS標準のフレームワークを動的にリンクしていました。
parallelMap・spawnによるマルチスレッド処理と書き込み禁止の規則
perry/thread は parallelMap・parallelFilter・spawn の3つを提供します。parallelMap と parallelFilter は配列をCPUコア数に分けてOSスレッドで処理し、小さな配列ではスレッドを作らずその場で処理します。spawn は処理をバックグラウンドのスレッドで実行し、Promiseを返します。Node.jsの worker_threads のようにエンジンのインスタンスを別に立てる方式ではありません。通常のキャプチャ値はコピーされ、外側の変数への書き込みはコンパイル時に拒否されます。ただし SharedArrayBuffer 自体は共有され、各スレッドで型付き配列のビューを作って Atomics で同期できます。モジュール直下に置いた配列やオブジェクトはワーカーから読むこともできず、関数内のローカル変数に代入してから取り込みます(マルチスレッドのドキュメントより)。
import { parallelMap } from "perry/thread";
let counter = 0;
const out = parallelMap([1, 2, 3, 4], (x: number) => {
counter += x; // 外側の変数への書き込み
return x * 2;
});
このコードは closure passed to `parallelMap` writes to outer variable というエラーでリンク前に止まり、「クロージャから値を返してメインスレッドで集計せよ」と案内されます。外側の数値定数を読む今回の例では問題なく、parallelMap([1, 2, 3, 4], (x: number) => x * factor)(factor = 3)は [ 3, 6, 9, 12 ] を返しました。合計値やカウンターが必要な処理は、要素ごとの結果を返して reduce で畳む形に書き換えます。
Electron・Tauri・React Native・Flutterとの違いと採用判断
Perryと既存UIフレームワークの描画方式・ランタイム比較
4つの技術とPerryの差は、UIを誰が描くかと、実行時に何が動くかに集約されます。
- Electron:ChromiumとNode.jsをアプリごとに同梱し、HTML/CSSで描く。仕組みはElectronとは?Web技術でデスクトップアプリを作る入門解説【2026年最新】を参照
- Tauri:OS標準のWebViewでHTML/CSSを描き、バックエンドはRust。比較はTauriとは?Electronとの違い・将来性・始め方まで徹底解説【2026年版】を参照
- React Native:JavaScriptエンジン上でReactを動かし、ネイティブビューを操作する
- Flutter:Dartで書き、独自のレンダリングエンジンが画面を描く
- Perry:TypeScriptを機械語に変換し、AppKitやWin32などOS標準のウィジェットを直接呼ぶ。実行時にJavaScriptエンジンもWebViewも無い
HTMLやCSSの資産をそのまま使いたいならElectronかTauri、TypeScriptのまま実行時のエンジンを無くしたいならPerryという分かれ方です。
Perryを選ぶ場面と見送る場面
2026年10月時点のPerryは、社内向けCLIやサーバーの単一バイナリ化、小規模なデスクトップツールで試すのが適切です。起動が速く、配布先にNode.jsを入れなくてよい利点が、多少の互換性の穴より大きく効くからです。fastify や pg を使うAPIサーバーを、node_modules を持ち込まずに1ファイルで配れる点も運用を軽くします。
逆に、次の条件に1つでも当てはまるなら、現時点での本番採用は見送るべきです。
- 実行時に未コンパイルのJS/TSプラグインを読み込む、またはユーザー入力から
evalや動的importを組み立てる設計になっている - クラスのメソッド呼び出しが多い主要処理を対象環境で比較し、Perryでは必要な処理時間やスループットを満たせない
- ストアで長期運用するモバイルアプリで、ビルドの再現性を数年単位で保つ必要がある(0.5系で、GitHub Releasesは累計159件。2026年10月8日時点の未解決issueはプルリクエストを除いて189件)
導入前の確認には perry check を使いますが、前述のとおり eval の問題はこれでは見つかりません。--strict-eval を付けたビルドと、主要な処理を実際に動かすテストまで回してから判断します。
よくある質問
Perryは無料で使えますか?
使えます。コンパイラ本体はMITライセンスで、GitHubのPerryTS/perryで公開されています。テレメトリは初期状態で無効で、PERRY_NO_TELEMETRY=1 を設定すると常に送信されません。
WindowsでもPerryを使えますか?
使えます。winget install PerryTS.Perry で入り、リンクにはLLVMと perry setup windows を使う方法か、MSVC Build Toolsを使う方法を選べます。ビルド先の指定は --target windows で、ARM版Windows向けの windows-aarch64 も選べます。
既存のNode.jsプロジェクトはそのままコンパイルできますか?
静的に import しているコードの多くはコンパイルできますが、すべてではありません。実行時に読み込むプラグインや、実行時に組み立てた文字列の eval は動きません。まず perry check と --strict-eval 付きのビルドで問題箇所を洗い出してください。
Bunの単一バイナリ化とPerryは何が違いますか?
Bunが作る単一バイナリはJavaScriptエンジンを内蔵し、実行時にJITで動きます。Perryは事前に機械語へ変換し、エンジンを持ちません。今回のhello worldでは起動時間が短くなりました。ただし、未コンパイルのJS/TSを実行時に読み込む方式には制約があります。Perryで事前コンパイルしたネイティブプラグインの読み込みは可能です。
「perryts」とPerryは同じものですか?
同じです。GitHubの組織名とnpmのスコープが perryts、公式サイトがperryts.com、ドキュメントはperryts.github.io/perryにあります。コンパイラのリポジトリはPerryTS/perryです。