* feat: add mobile library targets for iOS and Android - three library-mode-only triples join SCRIPTC_TARGET: aarch64-apple-ios, aarch64-apple-ios-simulator (darwin hosts with Xcode's iPhoneOS/iPhoneSimulator SDKs), and aarch64-linux-android (any host with an NDK). The consuming pattern is an embedding app linking profile-configured static archives — Xcode on the Apple side, Gradle/NDK on the Android side — so the executable lane refuses the triples with SC3002 and the pointer to --lib - zig bundles no Apple or bionic libc: resolveCc discovers the sysroot (xcrun for the selected Apple SDK; ANDROID_NDK_ROOT or the newest ndk/<version> under the SDK root) and spells it into targetArgs, where every cache tier already keys it. The canonical LLVM spellings map to zig's own with the minimum-version floors pinned: aarch64-ios.15.0[-simulator] stamps LC_BUILD_VERSION minos 15.0 into every object, aarch64-linux-android.26 pins the bionic stub level an embedder's minSdkVersion must meet. API 26 bionic carries everything the library-lane units call (arc4random_buf included), so no shim TU joins the archive - localization follows the existing object-format rule: both iOS platforms ride the Mach-O host-ld64 arm (ld64 reads iOS objects and preserves platform/minos through the -r merge), Android rides the cross-ELF arm (zig relocatable merge + in-process demotion) via the driver's zig spelling. abi.instance_per_thread needed no per-target work — thread-local statics compile identically on all three - admission is a pure env/host check ahead of toolchain discovery: an iOS triple off a darwin host and near-miss mobile spellings refuse SC3002 with the pairing or the supported set named, in compile() and compileLibrary() both, decorated by the profile teaching machinery - library-multi grows the mobile lanes: M12 admission shape always runs; SCRIPTC_IOS=1 builds localized a/b archives per target and emission (M1 symbol exactness, ambient audit, version floor), links probes with the Xcode clang, and EXECUTES the two-instance and four-thread probes on a booted simulator byte-for-byte against the desktop expectations (device-arch archives are build+link verified); SCRIPTC_ANDROID=1 does the same through the NDK clang and an emulator, reusing a running device or writing and booting a headless arm64 AVD directly - the surface manifest's coverage notes record the mobile posture (the library-admissible surface only; no mobile claim outside it), the profile spec and the platforms page carry the mobile matrix with the version floors, and non-mobile builds are unchanged byte-for-byte * fix: harden mobile target tooling * Harden mobile target test isolation
scriptc
scriptc compiles TypeScript and JavaScript to native executables and WebAssembly modules. It uses the TypeScript compiler for parsing and type checking, then emits LLVM IR for clang to compile. A readable C backend remains available for debugging.
Static builds include a small native runtime, but no Node or JavaScript engine. Code that cannot compile statically is reported as a diagnostic. For npm packages and any-typed code, --dynamic embeds quickjs-ng explicitly.
scriptc is experimental and targets macOS, Linux, Windows, and WebAssembly via WASI Preview 1.
Installation
The compiler requires Node.js 20 or newer and clang. The executables it produces do not require Node.
$ npm install -g scriptc
Build a program
Create hello.ts:
const who = process.argv.length > 2 ? process.argv[2] : "world";
console.log(`hello, ${who}`);
Compile and run it in one step:
$ scriptc run hello.ts
hello, world
Or write a standalone executable:
$ scriptc build hello.ts -o hello
$ ./hello ctate
hello, ctate
Use Node APIs
Supported Node APIs compile to the native runtime. For example, server.ts:
import { createServer } from "node:http";
const server = createServer((req, res) => {
res.setHeader("content-type", "application/json");
res.end(JSON.stringify({ path: req.url }));
});
server.listen(8080, () => {
console.log("listening on http://localhost:8080");
});
$ scriptc build server.ts -o server
$ ./server
listening on http://localhost:8080
Check static coverage
scriptc coverage shows how much of a program can compile statically and gives a coded diagnostic for every dynamic or unsupported site.
$ scriptc coverage hello.ts
statements analyzed 2
compile statically 2 (100%)
fully static — this program has no dynamic remainder.
Build WebAssembly
WASI and other cross-target builds require Zig. Its bundled WASI libc produces a portable WASI Preview 1 module through the production LLVM backend:
$ SCRIPTC_CC=zigcc SCRIPTC_TARGET=wasm32-wasi scriptc build hello.ts --no-keep-c -o hello.wasm >/dev/null
$ file hello.wasm
hello.wasm: WebAssembly (wasm) binary module version 0x1 (MVP)
$ SCRIPTC_CC=zigcc SCRIPTC_TARGET=wasm32-wasi scriptc run hello.ts
hello, world
The WASI target supports the same executable language tiers as the native targets, including async/await, promises, generators, timers, stdin/readline events, callback and promise filesystem APIs, and --dynamic. APIs that require capabilities absent from portable WASI Preview 1—network sockets/fetch, child processes, OS signals, and filesystem watching—fail before linking with SC3002; sanitizer builds, native FFI, and library-mode archive builds are target diagnostics too. See platform support for the precise boundary.
Use npm packages
Pass --dynamic to embed an npm package's JavaScript in the executable. The result does not read node_modules at runtime.
import pc from "picocolors";
console.log(pc.green("hello from scriptc"));
$ npm install picocolors
$ scriptc build cli.ts --dynamic -o cli
$ ./cli
hello from scriptc
Documentation
See the quickstart and CLI reference for the complete workflow. The docs also describe npm dependencies, native FFI, platform support, and the current limitations.
Development
$ pnpm install && pnpm -r build
$ pnpm test:sandbox
The test corpus runs each program under Node and as a compiled native binary, then compares stdout, stderr, and exit codes byte for byte. The full gate also runs the corpus with AddressSanitizer and the runtime reference-count audit.