1 poin oleh GN⁺ 2 jam lalu | 1 komentar | Bagikan ke WhatsApp
  • scriptc mengompilasi TypeScript biasa menjadi biner native kecil yang berjalan tanpa Node, V8, atau mesin JavaScript, sambil mempertahankan pemeriksaan tipe dari kompiler TypeScript asli dan kompatibilitas perilaku Node
  • Berdasarkan struktur kode, tool ini menentukan apakah kompilasi statis dimungkinkan lalu secara default menghasilkan kode native; hanya saat memilih --dynamic ia menjalankan JavaScript dari paket npm dan kode bertipe any dengan quickjs-ng
  • Mendukung kelas, generic, async/await, exception, regex, API server Node, fetch, hingga dependensi npm; sintaks yang tidak didukung akan ditolak dengan kode error, code frame, dan petunjuk perbaikan
  • Menjalankan lebih dari 800 program di Node dan biner native lalu membandingkan output serta exit code, dan memeriksa error memori dengan AddressSanitizer serta audit reference count
  • Pada pengukuran Apple M series, waktu start sekitar 2.4ms, biner statis 170~200KB, RSS umum 1~4MB, dan biner menjadi sekitar 3MB jika menyertakan mode dinamis serta dependensi tertanam

Model kompilasi statis

  • Menggunakan TypeScript yang sudah ada tanpa dialek atau anotasi terpisah, dan menerapkan tingkat keketatan pemeriksaan tsconfig.json serta library es2025 asli dari TypeScript
    • Jika proyek memiliki @types/node, itu juga ikut diperiksa tipenya
    • Kode yang terjangkau tanpa lowering menghasilkan diagnosis yang akurat dan kompilasi dihentikan
  • scriptc coverage menampilkan jumlah statement yang dianalisis, persentase yang dikompilasi secara statis, serta faktor penghambat dan kode error
    • Pada contoh, 99% atau 4.451 dari 4.481 statement dikompilasi secara statis
  • Cara eksekusi dibedakan secara eksplisit menjadi tiga tahap
    1. Kompilasi statis: mode default dan mengubahnya menjadi kode native tanpa mesin JavaScript
    2. Eksekusi dinamis: jika --dynamic ditentukan, tool ini menyertakan sekitar 620KB quickjs-ng untuk menjalankan JavaScript dari paket npm dan kode bertipe any
      • Semua nilai yang masuk dari kode statis divalidasi saat runtime
      • Jika tipe deklarasi dan nilainya berbeda, ia melempar TypeError yang bisa ditangkap tanpa merusak memori
    3. Penolakan: kode yang tidak bisa ditangani diberi kode error, code frame, dan biasanya petunjuk perbaikan, alih-alih salah dikompilasi secara diam-diam

TypeScript dan library standar yang didukung

  • Fitur bahasa yang didukung mencakup kelas dengan single inheritance dan dynamic dispatch, closure, monomorfisasi generic, discriminated union, destructuring, spread, template literal, getter/setter, dan iterator
    • Jika keamanannya dapat dibuktikan, dynamic dispatch didevirtualisasi
    • Discriminated union ditangani dengan nilai tag yang memanfaatkan narrowing TypeScript
    • async/await diimplementasikan dengan fiber stackful dan scheduling yang disesuaikan dengan JavaScript
    • Mendukung exception dan finally, serta parameter opsional, default, dan rest
  • Regex memakai interpreter bytecode kompatibel ECMAScript yang sama seperti yang digunakan QuickJS, dan hanya ditautkan ke biner yang memakai regex
  • Library standar mencakup string dengan semantik UTF-16, array, Map, dan Set dengan aturan urutan dan identitas yang sama seperti JavaScript
    • Konversi tipe pada JSON melalui validasi runtime
    • Juga menyediakan Math, typed array, Buffer, dan hierarki Error yang mendukung catch bertipe

Node dan Web API

  • API Node mendukung fs, path, process, child_process, os, crypto, url/URL, zlib, timer, dan signal handler
    • fs menyediakan API sinkron dan Promise
    • child_process mendukung pipe stream
    • Event loop tidak memiliki dependensi eksternal
  • Stack server mencakup net, http, https, tls, dgram, dns, fs.watch, readline, dan bisa mengompilasi server proxy nyata
    • TLS menggunakan mbedTLS yang disertakan
  • Sebagian Web API WHATWG seperti fetch, streams, Headers, dan AbortSignal diimplementasikan di atas stack jaringan native dan TLS yang sama
    • Mendukung redirect, gzip, AbortSignal.timeout, dan penyebab error bergaya Node
    • Tidak memakai dependensi HTTP sistem atau libcurl

Dependensi npm dan eksekusi dinamis

  • Dalam --dynamic, tool ini memakai metode resolusi modul Node dan melakukan pemeriksaan tipe berdasarkan .d.ts yang disediakan paket
  • JavaScript dari paket npm disertakan ke dalam biner saat build, sehingga saat eksekusi tidak membaca node_modules
  • scriptc coverage --dynamic menunjukkan apakah setiap statement dijalankan di area statis atau dinamis, serta faktor penghambat yang tersisa
  • Mesin JavaScript hanya disertakan saat mode dinamis dipilih secara eksplisit, sehingga ukuran biner tidak bertambah diam-diam

Akurasi dan keamanan memori

  • Differential testing menjalankan lebih dari 800 program masing-masing di Node dan biner native, lalu membandingkan stdout, stderr, dan exit code per byte
    • Output angka mengikuti representasi round-trip terpendek, dan 1 juta double diverifikasi dengan fuzzing terhadap Node
    • Untuk server, driver klien nyata dihubungkan ke kedua implementasi untuk pengujian
  • Seluruh kumpulan pengujian dijalankan ulang di bawah AddressSanitizer dan audit reference count; jika ada leak atau use-after-free, build gagal
  • Perilaku yang sengaja berbeda dari Node hanya beberapa lusin dan terutama terkait implementasi internal timing serta properti objek error
    • Setiap perbedaan didokumentasikan dan diberi nomor, dan tidak ada perbedaan tersembunyi yang diizinkan

Karakteristik performa

  • Diukur pada Apple M series dengan tugas yang sama dan output yang sama per byte terhadap Node, Go, Rust, dan Zig
  • Waktu start sekitar 2.4ms, lebih cepat daripada Node sekitar 47ms, mirip Zig, dan unggul dari Go serta Rust
  • Ukuran biner statis 170~200KB, dan sekitar 3MB jika menyertakan --dynamic serta dependensi tertanam
    • Sebagai perbandingan, biner Go yang ditampilkan sekitar 2MB, dan Node SEA 60~100MB
  • Penggunaan memori umum adalah RSS 1~4MB, sedangkan Node 67~116MB
  • Runtime tetap mempertahankan semantik f64 yang sesuai JavaScript sambil bersaing dengan bahasa sistem di sebagian besar tugas
    • Inferensi integer dan analisis ownership masuk dalam roadmap

Jalur keluar yang eksplisit

  • comptime(() => ...) menjalankan TypeScript pada waktu build di VM terisolasi di dalam kompiler dan memasukkan hasilnya sebagai literal ke dalam biner
  • --ffi menghubungkan deklarasi TypeScript yang hanya berisi signature langsung ke pemanggilan C ABI, dan menautkan archive, object, serta library sistem yang dideklarasikan di manifest
    • Batasnya eksplisit dan menyertakan informasi panjang
    • Detail caranya bisa dilihat di Native FFI guide
  • Type assertion tervalidasi seperti JSON.parse(...) as Config menyisipkan kode validasi runtime
    • Jika validasi gagal, ia melempar exception yang memuat jalur yang salah serta tipe yang diharapkan dan aktual, seperti expected number at $.port, got string

Struktur kompiler

  • Alur pemrosesan adalah TypeScript → parsing dan type checking tsc → lowering → typed IR → C → clang → executable native
  • packages/compiler mencakup frontend berbasis API tsc, verifikasi dan serialisasi IR, serta backend LLVM dan C
    • Hanya IR yang dipakai sebagai antarmuka antara frontend dan backend
    • LLVM adalah code generator default, dan program di luar cakupan dukungan memakai jalur fallback transparan
    • C dipertahankan sebagai backend referensi permanen, dan --backend c menghasilkan output yang bisa dibaca dengan informasi baris sumber
  • packages/runtime mengimplementasikan value berbasis reference count dan cyclic collector, fiber stackful, event loop kqueue, stack server, dan output angka yang kompatibel dengan JavaScript
    • Menggunakan metode linking per fitur sehingga hanya fitur yang benar-benar dipakai yang masuk ke biner
  • packages/cli menyediakan perintah scriptc build, scriptc run, dan scriptc coverage

Instalasi dan pengembangan

  • Dipasang dengan npm install -g scriptc dan memerlukan clang
  • Platform utama adalah macOS arm64, sedangkan biner Linux dan Windows dikompilasi silang
    • Setiap platform divalidasi melalui jalur differential testing terpisah
  • Pengembangan dimulai dengan pnpm install && pnpm build
    • pnpm test menjalankan kumpulan differential testing dan snapshot diagnosis
    • SCRIPTC_SAN=1 pnpm test menjalankan pengujian yang sama di bawah ASan dan audit reference count
    • pnpm scriptc build x.ts --emit-ir mempertahankan C yang dihasilkan dan x.ir.json
  • Semua fitur ditambahkan bersama differential testing, dan penggabungan hanya bisa dilakukan jika pengujian umum serta pengujian keamanan memori sama-sama lulus

1 komentar

 
GN⁺ 2 jam lalu
Komentar Hacker News
  • Vercel tampaknya merilis proyek sensasional kira-kira sebulan sekali untuk menjaga reliabilitas dan eksistensinya. Rasanya tidak mungkin perusahaan atau proyek yang serius akan memakai scriptc
    Saya tetap menghormati para kontributornya, tetapi kodenya sangat terasa seperti dibuat dengan Claude, dan Claude tidak dicantumkan sebagai kontributor, jadi makin mencurigakan

    • Kontribusi yang benar-benar luar biasa ;-) commit
    • Tokoh utama vibe coding yang sama juga memimpin zerolang, “bahasa pemrograman untuk agen” yang dipublikasikan besar-besaran oleh Vercel pada bulan Mei. Setelah membuat 1.200 commit, pengembangannya berhenti sekitar pertengahan Juni
      proyek / postingan HN terkait
    • Simon Willison tampaknya hanya mengubah README, dan kelihatannya tidak benar-benar terlibat dalam proyek ini
    • Dari riwayat kontribusinya, tampaknya 99% vibe coding dilakukan oleh satu orang, dan orang itu juga tidak terlihat punya latar belakang compiler
    • Banyak produk SaaS bekerja sama dengan Vercel, dan dalam tooling pengembang Next.js dan React diperlakukan sebagai SDK papan atas
  • Porffor sudah lama mengejar tujuan yang sama. Pengembangnya, CanadaHonk, sangat hebat, tetapi proyek ini masih hanya lulus sekitar 68% Test262
    Kecuali saya salah memahami cakupan proyeknya, cara Vercel bisa melaju secepat ini terasa sangat meragukan

  • Ini proyek khas Vercel. Baru dirilis 5 hari, semuanya vibe coding, entah kenapa dapat 1.500 bintang, tidak menyelesaikan masalah siapa pun, dan kemungkinan paling lama dalam beberapa bulan sudah tidak dirawat lagi

  • Daripada hanya mengkritik, saya sudah mencoba menerapkannya langsung ke beberapa proyek lokal, tetapi semuanya menghasilkan ratusan error saat analisis cakupan kode, jadi praktis tidak bisa dipakai
    Kalau menulis semuanya dari nol tanpa library eksternal, mungkin memang bisa dikompilasi jadi biner, tetapi kalau begitu tidak ada alasan untuk tidak memakai bahasa seperti Rust, Go, Zig, D, C, V, Ada, C++, Nim, Swift, Kotlin Native, atau Haskell yang sejak awal memang dirancang untuk kompilasi yang benar

  • Kelebihan TypeScript bukan hanya ekspresivitasnya, tetapi juga kompatibilitas dengan ekosistem npm yang sangat besar. Sebagian besar paket hanya mendefinisikan antarmuka lewat deklarasi tipe dan mendistribusikan kode nyatanya sebagai JavaScript, jadi untuk memakai paket-paket itu pada praktiknya tetap butuh mesin JavaScript
    Kalau memang mau mulai benar-benar dari nol dan sama sekali tidak berencana memakai paket npm, lebih baik pakai AssemblyScript. Node secara eksplisit menyarankan agar tidak mendistribusikan paket dalam TypeScript, karena TypeScript tidak kompatibel mundur bahkan antar minor version, dan pengaturan compilernya juga tidak portabel antar paket

    • scriptc tampaknya menyiasati ini dengan membundel mesin quickjs-ng 620KB secara opsional saat perlu menjalankan dependensi semacam itu
    • Saya lebih ingin memakainya untuk alat command-line dengan kegunaan yang jelas yang perlu berbagi kode dengan proyek TypeScript yang lebih besar, daripada untuk kode yang penuh dependensi
    • Ini bisa menjadi alasan untuk mendistribusikan library tanpa tipe, tetapi sulit memahami kesimpulan bahwa alasan itu lebih penting daripada nilai dari tipe itu sendiri
  • Satu proyek seperti ini saja bisa terus tampil di halaman depan layanan seperti HN. Ini adalah strategi pertumbuhan: menginvestasikan token untuk membuat proyek yang tampak meyakinkan namun tidak diinginkan siapa pun, memperluas jangkauan lewat publikasi, lalu mengulanginya
    Dalam 12 bulan, 90% proyek open source bisa saja menjadi hasil vibe coding yang hanya terlihat menarik tanpa pengguna nyata. Sekarang bahkan compiler penuh pun bisa dibuat dengan mudah, tetapi yang penting adalah pemeliharaan jangka panjang dan komunitas; pengguna tidak akan bertahan hanya karena judul yang mencolok
    Kalau Vercel serius, mereka seharusnya menanggung biaya dan risikonya secara nyata dengan mengadopsinya untuk runtime eksperimental mereka sendiri

  • Ini area masalah yang sangat bagus. Saya menerapkan pekerjaan serupa untuk membuat compiler yang mengoptimalkan kode runtime dengan AI ke Zod: zod-compiler
    Ia mengompilasi skema Zod pada waktu build menjadi rantai operasi boolean sederhana sehingga menjadi 2 hingga 74 kali lebih cepat tanpa perubahan kode, dan pluginnya mengganti pemanggilan Zod dengan parsing yang sudah dikompilasi. Sebagian besar optimisasi ditulis oleh Claude lewat lebih dari 100 iterasi
    Sama seperti scriptc, kita tidak perlu menilai kebenaran secara subjektif karena hasilnya bisa dibandingkan dengan implementasi Zod yang asli. Ini bisa diterapkan ke compiler, alat serialisasi, formatter, query planner, dan sebagainya yang punya implementasi acuan dan benchmark

  • Saya menjalankan benchmark scriptc dan Node dengan Claude. Bahkan pada hasil array byte yang paling menguntungkan, setelah optimisasi khusus pun scriptc masih sekitar 7,5 kali lebih lambat daripada Node 24
    Sebagai gantinya, startup executable 12 kali lebih cepat (1.5ms vs 18.6ms), memakai memori 72 kali lebih sedikit (2.5MiB vs 181MiB), dan menjadi executable tunggal 370KB tanpa dependensi runtime

  • Bagus bahwa mereka mengakui perlunya executable native yang kecil dan cepat, tetapi melihat perjalanan Java selama puluhan tahun, saya ragu soal kepraktisannya. GCJ pada 1990-an secara teknis lumayan, tetapi tidak punya dukungan ekosistem
    Setelah itu GraalVM Native menangani masalahnya dengan lebih menyeluruh dan library serta framework besar mulai berupaya memastikan kompatibilitas, tetapi sampai sekarang pun sangat sulit menjalankan bahkan aplikasi lama yang sederhana secara native dengan sempurna. Upaya seperti scriptc memang disambut baik, tetapi jalan menuju penggunaan praktis tampaknya masih panjang dan berat

    • Setahu saya, tim Graal juga pernah mencoba pendekatan meta-interpreter yang mirip. Dynamic bytecode loading atau reflection yang tidak bisa ditangani eksekusi native hendak mereka interpretasikan lewat implementasi Java Espresso
    • GCJ sejak awal memang selalu lebih dekat ke prototipe. Pengguna yang serius kemungkinan akan membeli JDK komersial dengan alat AOT seperti Excelsior JET atau BEA JRockit
      Salah satu alasan Excelsior menghilang mungkin karena GraalVM dan OpenJ9 tersedia gratis. PTC dan Aicas masih berjalan baik berkat pelanggan embedded dan real-time yang kurang mendapat perhatian
  • Jika pengembangannya berlanjut, ini punya potensi menjadi pencapaian besar setara .NET AOT. Karena baru beberapa hari sejak dirilis, saat ini saya cuma mencobanya secara ringan, tetapi jika tidak ditinggalkan dan terus berkembang, ini bisa sangat membantu ekosistem
    Kode yang dibuat AI, seperti kode buatan manusia, punya rentang kualitas yang lebar. Untuk software penting, ia harus dihasilkan dengan standar yang sama seperti saat menulis langsung, dan semua kodenya harus direview; bila dipakai seperti itu, ini pendekatan yang sangat baik. Proyek yang minim review cenderung punya kualitas lebih rendah dan rasa tanggung jawab pengembang yang lebih lemah, sehingga lebih sulit diadopsi
    Diskusi online sering terjebak di dua kutub: “semuanya dibuat AI” versus “sama sekali tidak memakai AI”, tetapi dalam praktiknya jalan tengah yang mempercepat penilaian yang matang adalah yang paling masuk akal. Software yang tidak seperti itu cenderung dihindari karena risiko kualitas rendah atau ditelantarkan