Skip to content

Repository files navigation

⚠️ Warning: This project is not yet stable and may undergo significant changes before reaching version 1.0.0. We strongly advise against using it in production environments.

DashScript

GitHub Contributor Covenant

TypeScript ergonomics, Rust performance, compiled to native. A typed, TypeScript-flavored language (.ts) that compiles to native binaries via idiomatic Rust (wasm / napi outputs planned).

Packages

Package Version Description
dashscript npm The ds toolchain in one package — translates oxc AST → Rust, package.jsonCargo.toml, bindgen, CLI, editor types.

Quick Start

Install

# one package — provides the `ds` command
$ pnpm add -g dashscript

# or as a standalone binary
$ cargo install dashscript

No separate Rust install. DashScript manages its own pinned Rust toolchain — downloaded on first use like an npm dependency — so pnpm add dashscript is all you need.

Write .ts, compile to a native binary

// main.ts — TypeScript-flavored source
function greet(name: string): string {
  return `Hello, ${name}!`;
}

const message: string = greet("DashScript");
$ ds main.ts                      # run a file directly (like `node a.js`)
$ ds build main.ts                # → dist/<name> — a native binary (default)
$ ds build main.ts --target rust  # → dist/<name>/ — the translated Rust crate

ds main.ts runs a .ts file directly — translate → compile (cached) → run, like node a.js. ds build parses .ts with oxc, translates the AST to idiomatic Rust, and ships a native binary in dist/<name>. Pass --target rust to stop at the translated crate instead:

// generated by DashScript (--target rust)
fn greet(name: &str) -> String {
    format!("Hello, {name}!")
}

fn main() {
    let message: String = greet("DashScript");
}

Run

$ ds run <script>    # run a package.json script (like `pnpm run`)

ds run runs a shell command from package.json scripts through the system shell, like pnpm run. (ds run is always explicit — running a file is ds <file.ts>.)

Declare dependencies — package.jsonCargo.toml

DashScript projects use a package.json — the one manifest every JS tool already reads. Standard npm fields map straight to cargo: bin declares executables (one project compiles to several binaries); main[lib]; Rust crate deps under dashscript.cargo.dependencies[dependencies] (npm dependencies stay JS deps, never reaching Cargo.toml). On ds build, the package lowers to a Cargo.toml:

{
  "name": "my-app",
  "bin": {
    "serve": "serve.ts",
    "migrate": "migrate.ts"
  },
  "dashscript": {
    "target": "bin",
    "cargo": {
      "dependencies": {
        "serde": "1.0",
        "tokio": "1.0"
      }
    }
  }
}

Use a Rust crate with full type hints

# fetch the crate — no .d.ts stub; types come straight from the crate source
$ ds add cargo:serde

ds add cargo:<crate> fetches the crate and records it in package.json, with no .d.ts stub — Rust is statically typed, so the crate's own source (in ~/.cargo) is the complete type truth, read directly by the editor the way rust-analyzer reads its deps. For a local Rust file, ds add <file>.rs runs bindgen to emit a .d.ts beside it. No separate ds gen step.

Check & format

ds check is the composite — translatability plus a format check (vp check style); ds lint checks translatability alone; ds fmt formats in place. With no argument each runs over every .ts in the project, and ds check --fix writes the fix. All built in-process on the parsed AST (no external oxlint/oxfmt):

$ ds check          # lint + format check, whole project (like `vp check`)
$ ds check --fix    # ...and write formatting fixes
$ ds lint           # translatability check only
$ ds fmt            # format every .ts in place

The emitted Rust is finally verified with cargo check / cargo clippy on the generated project.

Roadmap

DashScript maps a TypeScript-flavored surface to Rust semantics, growing incrementally as real demand drives each mapping — never speculatively.

  • Language coverage — the full Rust type/memory-safety model (ownership, borrowing, lifetimes, traits), with TypeScript as the presentation only. Today: a safe TS→Rust subset (auto clone/borrow/narrowing bridge the gaps).
  • Standard libraries — ES built-ins (Math/String/Array/Object/Number, … — largely mapped today), then the node: stdlib (node:crypto, node:zlib, node:fs, …). Web APIs are tracked separately under WinterTC below.
  • Compatibility — degrade, don't reject — a construct the static translator can't lower runs under an embedded QuickJS engine at the function granularity (inheriting full ECMAScript semantics) instead of failing, so existing TS/JS keeps working without a rewrite. Reflection (typeof/instanceof/Object.keys) resolves at compile time where the type is known — zero-cost, no dynamic value model — so only the residual dynamic cases degrade.
  • WinterTC Minimum Common Web API — the Ecma TC55 (formerly WinterCG) Minimum Common Web Platform API (shared by Node, Deno, Bun, Cloudflare Workers), on the same static-first + per-function degrade model as the ECMAScript core: each Web API maps to a Rust crate first (static, zero-cost), and an edge the static translator cannot lower takes the per-function engine path with the Web API registered as a builtin (same Rust impl). Synchronous APIs map first (URL, URLSearchParams, TextEncoder, Headers, Blob, FormData, SubtleCrypto, … today); asynchronous ones (fetch, setTimeout, Streams) introduce a tokio runtime and a thread model. Conformance is a WPT subset, run static-first with per-function engine degrade (mirroring test262).
  • More outputswasm and napi targets (Rust compiled to WebAssembly / napi-rs), so .ts ships to the web and Node ecosystems. The static segment (including compile-time reflection) lowers to plain wasm — no engine bundled for the common case; only the degraded minority pulls in QuickJS-wasm (Javy proves rquickjs compiles to wasm32-wasip1).
  • Developer experienceds test, editor/LSP integration, conformance fixtures.
  • Self-hosting (north star) — rewrite the toolchain in .ts itself, reaching oxc (and any Rust crate) through bindgen.

Development

Prerequisites

  • Node.js 18.x or higher
  • pnpm 9.x or higher (recommended package manager)
  • Rust — managed by DashScript (a pinned toolchain is downloaded on demand); no separate install needed to use DashScript
  • Git for version control

Getting Started

  1. Clone the repository:

    git clone https://github.com/DemoMacro/dashscript.git
    cd dashscript
  2. Install dependencies:

    pnpm install
  3. Build all packages:

    pnpm build

Development Commands

pnpm build                       # Build all packages
cd packages/<pkg> && pnpm build  # Build one package
vp check                         # Lint & format

Contributing

We welcome contributions! See CONTRIBUTING.md for the full contribution workflow, coding standards, and PR checklist.

Support & Community

License

This project is licensed under the MIT License - see the LICENSE file for details.


Built with ❤️ by Demo Macro

About

TypeScript ergonomics, Rust performance, compiled to native.

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages