01 product · compiler

The compiler is the product.

Next.js ships React to the browser and hopes the runtime is fast enough. Svelte hides the runtime but still carries a scheduler. Vesk moves the work to build time — your app is proven good before a byte leaves the server.

01 · preprocess

Source prepass

Statement-mode clauses ( ; key / ; index ), <style> hoisting, island {#client} tagging — offset-preserving, no regex.

02 · parse

Acorn + VeskPlugin

Full TypeScript superset. component , JSX-as-statement, &[track] atoms, generics — all typed, all surfaced to tsc via vskToTsx .

03 · IR

Flat intermediate representation

Validated, dispatch-friendly: StaticNode , MapRegion , TrackDecl , Islands. One IR, two web emitters + Kotlin.

04 · codegen

Platform emit

Server HTML chunks + client DOM + hydration markers; or Kotlin via vesk-native-compiler . Static subtrees get zero hydration runtime.

vs. the bundler mindset compile errors > runtime bugs
Capability Vesk Next.js / Vite + React SvelteKit
Virtual DOM in production None — direct DOM writes Yes (React reconciler) Scheduler + runtime helpers
Zero-JS pages Proven at build, 0 B Always ships runtime Partial, manual
Event delegation AOT — one listener per type Synthetic + per-tree Per-component
Static subtree cost No marker, no JS Hydrated anyway Reduced but present
Cross-platform Web + Kotlin (one IR) Web only Web only
Typecheck vesk typecheck via tsc tsc separate svelte-check

What you write

					
						component Counter() {
  let &[count] = track(0)
  <button onClick={() => count++}>Count: {count}</button>
}
					
				

One model, no hooks, no useState. &[] is sugar — compiler rewrites to get/set and per-cell effects.

What ships

					
						// no VDOM
const $el = document.createElement('button')
effect(() => $el.textContent = `Count: ${get(count)}`)
$el.addEventListener('click', () => set(count, get(count)+1))
					
				

O(1) text update, not O(n) VDOM diff. Static neighbors never re-render.