Getting started

From one Lisp form to a running module.

The compiler needs Emacs. Bun is the reference runtime for executing and testing the generated ECMAScript.

Requirements

Use Emacs 29 or newer. Bun 1.4 or newer is needed for the reference execution path and complete test suite.

emacs --version
bun --version

Install the exact JavaScript dependencies used by the examples and integration tests:

bun install --frozen-lockfile

The compiler itself has no Bun or Node.js dependency. A clean Emacs installation can produce the module.

Compile a file

Create hello.eli:

(module hello
  (defun greet (name)
    (str "Hello, " name "!"))

  (print (greet "ECMAScript"))
  (export greet))

Compile it through the public command:

./bin/eliscript --output dist/hello.mjs hello.eli

Without --output, generated ECMAScript is written to standard output.

For debugger-ready output, generate an external Source Map v3 file:

./bin/eliscript --source-map --output dist/hello.mjs hello.eli

This writes dist/hello.mjs.map and links it from the generated module.

Run the module

bun run dist/hello.mjs

The generated file is standard ESM. Bun is the reference host, but the compiler does not emit proprietary Bun syntax.

Build a source graph

bun run build:stdlib-cli
bun run dist/project/examples/stdlib-cli/main.mjs

The Emacs project builder discovers relative .eli imports after macro expansion, keeps every source below the selected root, mirrors the directory tree as .mjs, and emits a source map beside each module. A deterministic eliscript-project.json records the complete graph, SHA-256 identities, and separately verified incremental metadata. Repeat builds reuse unchanged modules; pass --no-cache to force compilation. This example builds sequence, text, object, and application sources together. Package, JavaScript, CSS, and image imports remain available to the eventual host.

Tooling can request a versioned report with stable cache and per-module decisions plus cache-read, build-work, manifest-write, and total timings:

./bin/eliscript-build --json --root . --out-dir dist/project \
  examples/stdlib-cli/main.eli

Timings are observational and never enter the deterministic manifest or cache identity. Without --json, successful builds continue to print only the generated entry path.

Use persistent values

(module collections
  (import "../stdlib/core/collection.eli" assoc lookup)

  (defconst original {:name "Eliscript" :tags #{:lisp :esm}})
  (defconst updated (assoc original :status :stable))

  (print (lookup original :status :missing))
  (print (lookup updated :status)))

Vector, Map, Set, List, Queue, Record, and sorted collection values are immutable. Updates return a new root while sharing unchanged internal structure. Generic collection operations dispatch through open protocols, and Transient builders provide an isolated high-throughput construction path for Vector, Map, and Set.

Inspect collection semantics

Run optional applications

Application examples consume public ESM and host boundaries. They are useful interoperability probes, but no framework or bundler is part of the language compiler, runtime, standard library, or core maturity result.

React counter

bun run compile:react-counter
bun run react-counter

The first command emits a source-mapped ESM module through react/jsx-runtime. The second renders it with react-dom/server, proving the generated element tree without a bundler.

Start the interactive browser application or create its production bundle:

bun run dev:react-counter
bun run build:react-counter

The Vite adapter compiles .eli modules, composes source maps, handles CSS and image assets, and enables React Fast Refresh.

Org publishing

Export the example articles through Emacs as a deterministic ESM data module:

bun run org:export

Run the fully custom React publishing site or create its static production bundle:

bun run dev:org-site
bun run build:org-site

The application itself is written in Eliscript. Its adapter exposes the Org directory as a virtual module and reloads article changes during development.

Read the optional publishing guide

Build the bootstrap modules

bun run build:bootstrap

The Emacs Lisp seed compiler writes ten source-mapped modules below dist/bootstrap/, including the complete portable compiler driver. Compile through that generated compiler with:

./bin/eliscript-portable --output dist/program.mjs source/program.eli

The conformance suite uses Generation 1 to build Generation 2, then Generation 2 to build Generation 3. Every compiler module and Source Map remains byte-identical.

Measure the Emacs worker

(defportable score-values (values)
  (length values))

./bin/eliscript --portable score-values --output dist/score.mjs source.eli

defportable validates the complete immutable dependency closure. The generated manifest lets Emacs call score-values without knowing its JavaScript identifier.

bun run benchmark:worker

The benchmark compiles one pure workload, starts a long-lived Bun worker, verifies equal Emacs and Eliscript results, and reports cold and warm compilation, startup, execution, serialization, transport, and client costs separately.

(require 'eliscript-index)

(let ((session (eliscript-index-start)))
  (unwind-protect
      (eliscript-index-search-sync
       session '(("intro" . "Emacs and JavaScript")) "javascript")
    (eliscript-index-stop session)))

The indexing adapter compiles a source-mapped portable project closure, follows its data and object dependencies, tokenizes editor-owned text, scores documents concurrently, and owns cleanup. Its graph manifest makes dependency changes restart the same client object automatically and maps imported-module errors to their Eliscript source.

Run the test suite

bun run test

This runs the Emacs ERT suite, compiler and runtime tests, public CLI fixtures, deterministic snapshots, source-map validation, Bun/Node execution, and separately partitioned application checks.

The portable standard library and protocol-driven persistent collection core run through the same Eliscript module pipeline. Application adapters are tested separately and contribute no core maturity credit.