ur-lang
v1.4.0
Published
UrLang — an Urdu-flavored, statically type-checked language that compiles to JavaScript, with JSX/React support. Works everywhere JS works: Node, browsers, Electron, Tauri.
Maintainers
Readme
UrLang
An Urdu-flavored, statically typed programming language that compiles to JavaScript — built like TypeScript: structural types, inference, narrowing, generics, classes, modules, JSX/React, an LSP, and full interop with the JS ecosystem. Written in strict TypeScript with a zero-dependency compiler core, tested end-to-end against real Vite, React, Tauri, and Electron projects.
Because UrLang transpiles to plain JavaScript (with source maps), it runs everywhere JS runs: Node, browsers, Electron, Tauri, Deno — anything.
qisim Shakhs = { naam: lafz, umar?: adad };
kaam salaam(s: Shakhs): lafz {
agar (s.umar != khaali && s.umar >= 60) {
wapas `janab ${s.naam} sahib`;
}
wapas `salam, ${s.naam}!`;
}
har s [{ naam: "Ali" }, { naam: "Sara", umar: 65 }] mein {
bolo salaam(s);
}Quick start
npm install && npm run build # build the toolchain
npm test # 320+ tests incl. conformance suite
npx tsx src/cli.ts run examples/tour.ur # compile + execute
npx tsx src/cli.ts build src/app.ur -o out # emit .js + .js.map + .d.ts
npx tsx src/cli.ts build src/app.ur --watch # incremental rebuilds
npx tsx src/cli.ts check src/app.ur # type-check only
npx tsx src/cli.ts fmt src/app.ur # format (--check in CI)
npx tsx src/cli.ts lsp # language server (stdio)After npm link, all of the above are just urlang <command>. New project:
npm create urlang my-app -- --template react| Template | What you get |
|---|---|
| vite | Plain web app — HTML/CSS/JS with UrLang logic |
| react | React components written in UrLang (.urx) |
| svelte | Svelte components + typed UrLang logic modules |
| node | Node server, entirely UrLang |
| express | Express API, entirely UrLang |
| bun | Bun server — runs .ur files directly, no build step |
| tauri / tauri-react / tauri-svelte | Desktop app: Rust backend, typed invoke bridge |
| electron | Electron — main process in UrLang too |
The language in 60 seconds
| | UrLang | JS/TS equivalent |
|---|---|---|
| Variables | rakho x = 1; / pakka PI = 3.14; | let / const (with const literal inference) |
| Types | adad lafz bool koi khaali kuchnahi, T[], A \| B, { k?: T }, "literal", Wada<T> | number string boolean any null/undefined void, arrays, unions, objects, literals, Promise<T> |
| Aliases | qisim Shakhs = { naam: lafz }; | type Shakhs = { naam: string }; |
| Print | bolo a, b; | console.log(a, b) |
| Branches | agar (…) / warna agar / warna, ternary ? :, chuno / surat / warna | if / else if / else, switch / case / default |
| Loops | jab tak (…), karo { } jab tak (…), har x list mein, har i 1 se 10 tak, har (rakho i = 0; i < n; i++), bas, agla (labels too) | while, do…while, for…of, for, break, continue |
| Operators | + - * / % **, & \| ^ ~ << >> >>>, ++ --, noeyat, hai, andar, mitao | arithmetic, bitwise, increment, typeof, instanceof, in, delete |
| Nullish | naam ?? "mehmaan", x?.y, x?.(), x?.[0], x! | ??, ?., ?.(), ?.[], ! |
| Destructuring | pakka { naam: shakhs, umar = 0, ...baqi } = s;, [a, b = 1, ...rest], in parameters too | the same |
| Enums | fehrist Rang { Laal, Hara } | enum |
| Casts | v jaisa adad | v as number |
| Functions | kaam f<T>(x: T[], y?: adad, z: adad = 1, ...r: adad[]): T | generics, optional/default/rest params |
| Lambdas | kaam (n: adad): adad { wapas n * 2; } | arrow functions (lexical yeh) |
| Async | intezar (auto-async) | await / async |
| Errors | koshish / pakro / akhir / phenko | try / catch / finally / throw |
| Classes | jamaat<T> / banao / yeh / naya / waris / buzurg, sakit, nijee, hasil, lagao | class / constructor / this / new / extends / super, static, private, get, set |
| Modules | bhejo (+ asal, re-exports), lao { } / asal / sab … se | export (+ default), import { } / default / * as |
| JSX | .urx files: <div a={x}>{y}</div>, <Comp/>, <>…</> | .tsx files |
| Interop | bahar fetch;, .d.ts consumption, typed npm imports | ambient declarations |
Full grammar and typing rules: SPEC.md. Design rationale: docs/DESIGN.md. Every diagnostic has a stable code: docs/errors.md.
Typed like TypeScript
- Structural object types with width subtyping, optional properties (typed
T | khaali), and excess-property checks on fresh literals. - Inference + widening:
pakkakeeps literal types,rakhowidens — sopakka size = "chota"satisfies"chota" | "bara". - Control-flow narrowing:
agar (x != khaali), literal equality,!,&&,||, ternary — the branch sees the narrowed type. - Generics with call-site inference:
kaam pehla<T>(xs: T[]): T. - Typed async: a
kaamcontainingintezaris automaticallyasync; declared returnTmeans callers seeWada<T>;intezarunwraps it. - Cross-module checking:
lao { jama } "./math.ur" se;givesjamaits real exported signature — a bad call in one file is caught when the importer compiles. - Classes compile to native ES classes; instances flow structurally through the type system;
yeh, constructors, andbuzurgcalls are fully checked. - A typed standard library:
xs.map(kaam (n) { wapas n * 2; })isadad[], notkoi[]— the callback's parameter is typed from context, and an unknown method is a compile error.koistays the deliberate escape hatch. - No emit on type errors,
==compiles to===(khaali comparisons compile loose to absorb null/undefined), conditions must bebool— no truthiness bugs.
React, in UrLang
Write React components in .urx files — JSX with Urdu keywords, and props are type-checked exactly like TSX:
npm create urlang my-app -- --template react # or tauri-react// src/Ginti.urx
lao { useState } "react" se;
qisim GintiProps = { shuru: adad };
bhejo kaam Ginti(props: GintiProps): koi {
pakka [ginti, setGinti] = useState(props.shuru);
wapas (
<button onClick={kaam () { setGinti(ginti + 1); }}>
Ginti: {ginti}
</button>
);
}<Ginti/> is a missing-prop error, <Ginti shuru="ek"/> is a type error, <Ginti shuru={0} faltu={1}/> is an unknown-prop error — all at compile time. Everything you'd expect works: fragments, {...spread}, key, optional props, nested components, and .urx importing .ur (and vice versa) with full types across the boundary.
Under the hood it emits the standard automatic JSX runtime (_jsx/_jsxs from react/jsx-runtime) — the same protocol TSX emits, so there's no custom runtime and no Babel. Point it elsewhere with jsxImportSource (urlang({ jsxImportSource: "preact" })); React Compiler composes on top, as it does with TSX.
For Svelte or Vue, the pattern is the one TypeScript users already know: components stay .svelte/.vue, and your typed logic lives in .ur modules they import.
Servers: Node, Express, Bun
// src/main.ur — an Express API, all UrLang
lao asal express "express" se;
lao { banaoUser } "./users.ur" se;
pakka app = express();
app.post("/users", kaam (req: koi, res: koi) {
res.status(201).json(banaoUser(req.body.naam, req.body.umar));
});
app.listen(3000);- Node / Express:
urlang build src/main.ur -o distemits plain ES modules (+ source maps +.d.ts), thennode dist/main.js. Or skip the build in dev withurlang run. - Bun:
.urfiles run directly —bunfig.tomlpreloadsur-lang/bun, and Bun compiles and type-checks them on import, exactly as it does TypeScript. A type error stops the import.
Use with Vite, Tauri, Electron
// vite.config.ts
import urlang from "ur-lang/vite";
export default { plugins: [urlang({ types: ["./src/bridge.d.ts"] })] };import "./app.ur"; // compiled on the fly, source-mapped, type-checked- Tauri:
npm create urlang my-app -- --template tauri— Rust backend with a#[tauri::command], UrLang frontend with typed command wrappers. Proven: this repo'smeri-tauri-app/was scaffolded with that command and builds to a working Windows executable. - Electron:
--template electron— the main process is UrLang too (main.ur), and the renderer's IPC bridge is typed viabridge.d.ts. - Plain web:
--template vite.
Interop with the TypeScript ecosystem
- Use any npm/TS library.
lao { debounce } "lodash-es" se;compiles to a real ES import; Vite bundles it exactly as it would for TypeScript. And it's typed automatically: the compiler resolves the package's own.d.tsfromnode_modules(types/typings/exportsfields,@types/*fallback), solao { invoke } "@tauri-apps/api/core" se;givesinvokeits real signature —invoke(42)is a compile error (UR2016). Packages without declarations degrade gracefully tokoi. - Consume
.d.tsambiently (subset):urlang check app.ur --types api.d.tsor the plugin'stypesoption turns TS declarations into typed UrLang globals — typed preload bridges, typed anything. - Emit
.d.ts:urlang buildwrites declarations for every module, so TypeScript code can import compiled UrLang with full types.
Tooling
- LSP (
urlang lsp): live diagnostics withURxxxxcodes, hover types, completions (member-aware after.), go-to-definition. VS Code extension ineditors/vscode-urlang/. - Watch mode (
urlang build --watch): dependency-aware — changingmath.urrebuildsmath.urand everything that imports it, nothing else. - Formatter (
urlang fmt,--checkfor CI): canonical style, idempotent, preserves comments and blank-line groups. - Playground:
npm run playground:build && npx vite playground— the full compiler running in your browser tab.
Performance
Hand-written lexer/parser, one-pass checker, string-building codegen, hand-rolled VLQ source maps, zero runtime dependencies. A 38,000-line program compiles end-to-end (with source map) in well under a second — run npm run bench for numbers on your machine.
Project layout
src/ compiler: lexer → parser → checker → codegen (+ sourcemaps, dts, fmt, lsp, watch)
tests/ unit + integration tests; tests/conformance/ = spec conformance suite
examples/ language tour and runnable samples
demo/ real Vite app (built + DOM-tested in CI)
packages/create-urlang/ project scaffolder (10 templates: web, react, svelte, node, express, bun, tauri, electron)
editors/vscode-urlang/ VS Code extension (grammar + LSP client)
playground/ in-browser compile+run
SPEC.md language specification docs/errors.md diagnostic codes
CHANGELOG.md semver policy + history docs/DESIGN.md design decisionsVersioning & license
Semantic versioning over the spec, CLI, and public exports — policy in CHANGELOG.md. MIT.
