`, using `moduleName` or the module holding the entrypoint.
- An entrypoint outside any module builds from the class path.
`moduleName` has no effect without modular JARs.
## Compile to WebAssembly
Declare a `WebImage` artifact or pass `--tool:wasm-gc` / `--tool:wasm-lm`; see [WebImage](https://elide.help/docs/toolchain/jvm/webimage).
To run existing WASM, see [WebAssembly](https://elide.help/docs/wasm). A native image can be the input to a
[container image](https://elide.help/docs/toolchain/jvm/containers).
---
# WebImage: JVM to WebAssembly
URL: https://elide.help/docs/toolchain/jvm/webimage
> Compile Java or Kotlin to a WebAssembly module and JavaScript loader with Elide's WebImage artifact: prerequisites, backends, hosts and compiler options.
A `WebImage` artifact compiles JVM bytecode to a JavaScript loader plus a WASM module. To run an existing WASM or
WASI binary, see [WebAssembly](https://elide.help/docs/wasm).
## Prerequisites
- [Binaryen](https://github.com/WebAssembly/binaryen) 119 or later, with `wasm-as` on `PATH` at build time.
- Code that meets the [Native Image requirements](https://elide.help/docs/toolchain/jvm/native-image): reachability metadata for reflection,
resources and proxies; no native dependencies.
- A host with WASM exception handling (and WASM GC for `wasm-gc`): Node.js 25+ or a current browser.
The deployed bundle needs neither Elide nor Binaryen.
## Build a complete project
```text
hello-wasm/
elide.pkl
src/main/java/Hello.java
```
```java
public class Hello {
public static void main(String[] args) {
System.out.println("Hello from WebImage");
}
}
```
```pkl
amends "elide:project.pkl"
import "elide:Jvm.pkl" as jvm
import "elide:NativeImage.pkl" as nativeImage
name = "hello-wasm"
artifacts {
["app-jar"] = new jvm.Jar {
sources { "main" }
}
["web"] = new nativeImage.WebImage {
from { "app-jar" }
name = "hello"
entrypoint = "Hello"
backend = "wasm-gc"
runtime = "generic"
autoRun = true
sourceMap = false
options {
optimization = "b"
}
}
}
```
```bash
elide build
```
Elide builds `app-jar`, then compiles it to `web`. `name` is the basename without `.js` or `.wasm`;
`optimization = "b"` is quick-build mode. Output goes to `.dev/artifacts/native-image/`:
| File | Purpose |
| --- | --- |
| `hello.js` | JavaScript loader and entrypoint. |
| `hello.js.wasm` | WASM module, loaded by `hello.js`. |
| `hello.js.wat` | WASM text form, for inspection. |
| `hello.js.map` | Source map for the loader, when `sourceMap = true`. |
Deploy `hello.js` and `hello.js.wasm` together. Kotlin and other JVM languages work the same way once packaged in a
JAR. A cache hit restores the whole bundle; turning `sourceMap` off removes the old `.js.map`.
## Choose a backend and host
| Property | Value | Meaning |
| --- | --- | --- |
| `backend` | `"wasm-gc"` (default) | Uses the host's WASM garbage collector. |
| `backend` | `"wasm-lm"` | Uses a garbage collector in linear memory; still needs the loader, not a WASI binary. |
| `runtime` | `"generic"` (default) | Loader supports browsers and Node. |
| `runtime` | `"browser"` | Loader specialized for browsers. |
| `runtime` | `"node"` | Loader specialized for Node-compatible APIs. |
| `autoRun` | `true` (default) | Starts the program when the loader loads; `false` leaves startup to the embedding page. |
| `sourceMap` | `false` (default) | `true` emits `hello.js.map`. |
An entrypoint is required even with `autoRun = false`. No `runtime` targets Cloudflare Workers, Fastly Compute or
WASI. Shared fields (`from`, `entrypoint`, `moduleName`, `options`) are listed under
[Native Image](https://elide.help/docs/toolchain/jvm/native-image#project-artifact). A WebImage cannot be the input to a
[container artifact](https://elide.help/docs/toolchain/jvm/containers).
Target guides: [Browser WASM](https://elide.help/docs/toolchain/web/wasm) and [Edge WASM](https://elide.help/docs/toolchain/edge/wasm).
### Run on a backend host
With `runtime = "generic"` or `"node"`:
```bash
node .dev/artifacts/native-image/hello.js
```
```text
Hello from WebImage
```
### Run in a browser
With `runtime = "browser"` or `"generic"` and `autoRun = true`, put this `index.html` in
`.dev/artifacts/native-image/`:
```html
Hello WebImage
Open the browser console to see the application output.
```
```bash
elide dev --no-tui --port 8000 .dev/artifacts/native-image
```
Open `http://localhost:8000/`; the output appears in the browser console. `file://` URLs do not work.
## Compiler options
The artifact's `options` block takes the [Native Image compiler options](https://elide.help/docs/toolchain/jvm/native-image#compiler-options);
which apply depends on the backend. Two cases specific to WebImage:
- `options.driverMode = "external"` uses `native-image` from `GRAALVM_HOME` or `JAVA_HOME`; that installation must
support the WebAssembly backends.
- `-H:EntryPointsConfig=config/entrypoints.json` in `options.flags` sets entry points; the path is relative to the
project root, and editing the file triggers a rebuild.
## Compile without a project manifest
```bash
elide javac -- -d classes src/main/java/Hello.java
elide native-image -- --tool:wasm-gc -Ob -cp classes -o hello Hello
elide native-image -- --tool:wasm-lm -Ob -cp classes -o hello-lm Hello
```
Bundles are written to the current directory. A `NativeImage` artifact with `--tool:wasm-gc` or `--tool:wasm-lm` in
`options.flags` still builds. To migrate it, change the class to `WebImage`, drop the flag and any `type`, and set
`backend`.
## Troubleshooting
| Symptom | Fix |
| --- | --- |
| `wasm-as` not found | Install Binaryen and put it on the build's `PATH`. |
| External driver rejects the WebAssembly backend | Point `GRAALVM_HOME` or `JAVA_HOME` at an installation whose `native-image` supports it, or use `driverMode = "embedded"`. |
| Conflicting raw options | Remove backend, runtime, startup and source-map flags; use the typed properties. Do not override `Backend` or the launcher property. |
| Host rejects WASM instructions | Use a host with exception handling and, for `wasm-gc`, WASM GC. |
| Browser cannot load the module | Serve loader and module together over HTTP; check the requested URL. |
| `elide run hello.js.wasm` reports missing imports | Run `hello.js` instead; the module needs its loader. |
---
# JVM containers
URL: https://elide.help/docs/toolchain/jvm/containers
> Build a Docker or OCI container image from a JAR or native executable as an Elide project artifact, loaded into the local daemon or written as a tarball.
Container images are project artifacts; there is no `elide` container subcommand.
## Declare an image
```pkl
amends "elide:project.pkl"
import "elide:Jvm.pkl" as Jvm
import "elide:Container.pkl" as Container
artifacts {
["app"] = new Jvm.Jar {
sources { "main" }
}
["image"] = new Container.ContainerImage {
from { "app" }
image = "my-app"
base = "eclipse-temurin:25-jre"
tags { "latest" }
output = "daemon"
}
}
```
```bash
elide build
```
## Fields
| Property | Values | Default |
| --- | --- | --- |
| `from` | Exactly one `Jvm.Jar` (JVM image) or `NativeImage` (executable image). | — |
| `image` | Target image coordinate. | — |
| `base` | Base image coordinate. | — |
| `tags` | Image tags. | none |
| `output` | `"daemon"` (local Docker daemon) or `"tarball"` (importable archive). | `"daemon"` |
| `format` | `"docker"` (manifest v2) or `"oci"`. | `"docker"` |
| `dependsOn` | Artifacts to build first. | none |
An empty `from`, two distinct entries, or any other artifact type (including a `WebImage`) fails before the build.
---
# Python toolchain
URL: https://elide.help/docs/python-toolchain
> What Elide's Python toolchain covers in 1.6.0: the optional Python component, running scripts and modules, pyproject.toml entry points, virtual environments, tests, and what is not available.
Python support is experimental in 1.6.0. Elide runs Python 3.13 with its standard library; it does not install PyPI
packages, format Python, or run Python tests under `elide test`.
| Task | Command | Status |
| --- | --- | --- |
| Add Python to an install | `elide setup python` | Available |
| Run a script, `-c` or `-m` | `elide python ...`, `elide run app.py` | Available |
| Run a `pyproject.toml` entry point | `elide run NAME` | Available |
| Use packages from a virtual environment | `VIRTUAL_ENV=.venv elide python app.py` | Available |
| Run unittest or pytest | `elide python -m unittest`, `elide python -m pytest` | Available |
| Install PyPI packages | `elide install` | Not available; use a virtual environment |
| Discover Python tests | `elide test` | Not available |
| Format, LSP, `elide init` template | | Not available |
## Add Python
Python is an optional component:
```bash
elide setup python
```
Without it, Python commands print `Python support is not installed. Run 'elide setup python' to add it.` and exit `2`.
See [Python with Elide](https://elide.help/docs/python) for channels and package-manager installs.
## Run Python
`elide python` takes the same arguments as `python`; `elide run` and a bare `elide app.py` run `.py` files too:
```bash
elide python app.py --name Elide
elide python -m json.tool input.json
elide run app.py
```
Interpreter flags (`-I`, `-B`, `-u`, `-W`, ...) are listed in [Python with Elide](https://elide.help/docs/python#interpreter-options).
## Run project scripts
`elide run NAME` runs a `[project.scripts]` entry point from the `pyproject.toml` in the current directory. No
`elide.pkl` is needed.
```toml
# pyproject.toml
[project]
name = "greet"
version = "0.1.0"
requires-python = ">=3.13"
[project.scripts]
greet = "greet.cli:main"
```
```python
# greet/cli.py
import sys
def greeting(name: str) -> str:
return f"Hello, {name}!"
def main() -> int:
print(greeting(sys.argv[1] if len(sys.argv) > 1 else "world"))
return 0
```
```bash
elide run greet Elide
```
```terminaloutput
Hello, Elide!
```
`elide greet Elide` does the same. Arguments after the name go to `sys.argv`, and the function's integer return value
is the exit code. A file named `greet`, an `elide.pkl` script or a `package.json` script of the same name wins; see
[Project setup](https://elide.help/docs/projects#add-a-script).
## Packages
`elide install` installs no PyPI packages, and `elide install --ecosystems=pypi` exits `2`. Install packages into a
Python 3.13 virtual environment with another tool and point Elide at it with `VIRTUAL_ENV`; see
[Packages and virtual environments](https://elide.help/docs/python-packages).
## Tests
Run unittest or pytest through `elide python -m`; see [Python tests](https://elide.help/docs/python-testing). `elide test` runs JavaScript,
TypeScript and JUnit tests only.
## Samples
- [Python calling JavaScript](https://elide.help/docs/code-samples#python-calling-javascript)
- [JavaScript importing Python](https://elide.help/docs/code-samples#javascript-importing-python)
- [Python script](https://elide.help/docs/code-samples#python-script)
`elide init` has no Python template.
## Known limitations
- **C extensions**: some C extension modules do not load.
- **pip**: not bundled; `elide python -m pip` fails with `No module named pip`.
- More: [Python with Elide](https://elide.help/docs/python#known-limitations).
---
# Packages and virtual environments
URL: https://elide.help/docs/python-packages
> Use third-party Python packages with Elide: create a Python 3.13 virtual environment, install packages with uv, and set VIRTUAL_ENV so Elide imports them.
Elide does not install PyPI packages in 1.6.0. It imports packages from a Python 3.13 virtual environment named by
`VIRTUAL_ENV`.
| Source | What Elide does |
| --- | --- |
| `pyproject.toml` `[project] dependencies` | Nothing; `[project.scripts]` entry points run with `elide run` ([Python Toolchain](https://elide.help/docs/python-toolchain#run-project-scripts)) |
| `elide.pkl` `dependencies.pypi` | Accepts the declaration; `elide install` skips it ([reference](https://elide.help/docs/elide-pkl-reference#dependencies)) |
| `requirements.txt` | Nothing |
| `$VIRTUAL_ENV/lib/python3.13/site-packages` | Adds it to `sys.path` |
`elide install --ecosystems=pypi` prints `elide install: pypi installs are not available in this build` and exits `2`.
## Create a virtual environment
Create the environment with a Python 3.13 tool. With [uv](https://docs.astral.sh/uv/):
```bash
uv venv --python 3.13 .venv
uv pip install --python .venv/bin/python attrs
```
The environment's own interpreter is only used to install packages; Elide runs the code.
## Use it
```python
# app.py
import attrs
@attrs.define
class Point:
x: int
y: int
print(Point(1, 2))
```
```bash
VIRTUAL_ENV=.venv elide python app.py
```
```terminaloutput
Point(x=1, y=2)
```
Activating the environment sets `VIRTUAL_ENV` too:
```bash
source .venv/bin/activate
elide run app.py
```
Without `VIRTUAL_ENV`, the import fails with `ModuleNotFoundError: No module named 'attrs'`.
## Limits
- The environment must be Python 3.13: Elide reads `lib/python3.13/site-packages`.
- Pure-Python packages work. Some C extension modules do not load.
- pip is not bundled, so `elide python -m pip` fails; install with the environment's own tools.
---
# Python tests
URL: https://elide.help/docs/python-testing
> Run Python tests on Elide with unittest from the standard library or pytest from a virtual environment; `elide test` does not discover Python tests in 1.6.0.
`elide test` does not discover Python tests in 1.6.0; in a Python-only directory it prints `No tests found.` and exits
`0`. Run the test runner as a module instead. The exit code is the runner's, so a failing test fails the command.
The examples test this module:
```python
# greet/cli.py
import sys
def greeting(name: str) -> str:
return f"Hello, {name}!"
def main() -> int:
print(greeting(sys.argv[1] if len(sys.argv) > 1 else "world"))
return 0
```
`greet/` and `tests/` each contain an empty `__init__.py`.
## unittest
unittest ships with the standard library:
```python
# tests/test_cli.py
import unittest
from greet.cli import greeting
class GreetingTest(unittest.TestCase):
def test_names_the_caller(self):
self.assertEqual(greeting("Elide"), "Hello, Elide!")
```
```bash
elide python -m unittest discover -s tests -t .
```
```terminaloutput
.
----------------------------------------------------------------------
Ran 1 test in 0.001s
OK
```
## pytest
Install pytest into a Python 3.13 virtual environment ([Packages and virtual environments](https://elide.help/docs/python-packages)), then run
it with `VIRTUAL_ENV` set:
```bash
uv venv --python 3.13 .venv
uv pip install --python .venv/bin/python pytest
```
```python
# tests/test_greeting.py
from greet.cli import greeting
def test_default_name():
assert greeting("world") == "Hello, world!"
```
```bash
VIRTUAL_ENV=.venv elide python -m pytest -q
```
pytest collects both files and ends with:
```terminaloutput
2 passed in 0.03s
```
## In CI
Add the Python component after installing Elide (`elide setup python`), then run the same commands; see
[Continuous Integration](https://elide.help/docs/installation/ci-cd#example-workflow).
---
# Web toolchain
URL: https://elide.help/docs/toolchain/web
> Run JavaScript and TypeScript with Elide, serve files during development, process web assets, install npm packages and configure web builds in elide.pkl.
Run, serve, build and install dependencies for JavaScript and TypeScript projects.
## Run JavaScript and TypeScript
```bash
elide run app.js
elide run app.ts
```
TypeScript types are stripped. `.mjs`, `.cjs`, `.mts` and `.cts` also work.
## Serve and develop
```bash
elide serve dist # serve a directory on 127.0.0.1:8080
elide dev public # dev server on 127.0.0.1:3000; pages reload when files change
```
Page reload applies to directories, not to a script passed to `elide dev`. `--host` and `--port` set the address,
`--no-watch` turns off watching, and `--no-tui` prints plain logs. More, including `--contexts`:
[Development Server](https://elide.help/docs/dev-server).
## Process assets
No manifest needed:
```bash
elide web build app.ts --out dist # JavaScript
elide web build page.md --out dist # HTML
elide web build style.css --out dist # CSS
```
Several sources and shell globs are accepted.
## Install npm packages
```bash
elide install --ecosystems=npm
```
See [NPM Installer](https://elide.help/docs/toolchain/web/npm).
## Manifest settings
```pkl
web {
minifyHtml = true
css {
minify = true
targets {
new { browser = "chrome"; version = "120" }
new { browser = "safari" }
}
}
}
javascript {
ecma = "stable"
}
```
| Property | Meaning | Default |
| --- | --- | --- |
| `web.minifyHtml` | Minify HTML, including inline scripts and styles. | `true` |
| `web.css.minify` | Minify CSS. | `true` |
| `web.css.targets` | CSS browser targets: `browser` (`chrome`, `firefox`, `safari`, `edge`, `opera` or other) and optional `version`. | none |
| `web.browsers` | Browser targets for all outputs; `css.targets` overrides it for CSS. | none |
| `javascript.ecma` | Declared ECMAScript edition: a year `2015` … `2025`, `"stable"` or `"latest"`. `elide project info` shows it; it does not change which syntax runs or how code is built ([JavaScript language](https://elide.help/docs/javascript-language)). | `"stable"` |
## Project builds
To build a site with `elide build`, declare a [static-site artifact](https://elide.help/docs/toolchain/web/static-site). To compile Java or
Kotlin for the browser, see [Browser WASM](https://elide.help/docs/toolchain/web/wasm).
---
# npm installer
URL: https://elide.help/docs/toolchain/web/npm
> Install npm packages with `elide install --ecosystems=npm`: Elide reads package.json and the npm block in elide.pkl, and links packages into node_modules.
Elide installs npm packages and links them into the project's `node_modules`.
```bash
elide install --ecosystems=npm
```
npm is opt-in; a bare `elide install` installs Maven only. Elide reads `package.json` and the `npm` block in
`elide.pkl`:
```pkl
amends "elide:project.pkl"
dependencies {
npm {
packages {
"react@19.0.0"
}
devPackages {
"typescript@5.6.2"
}
}
}
```
| Property | Meaning |
| --- | --- |
| `packages` | `name@version` or `@scope/name@version`. |
| `devPackages` | Development-time packages. |
Packages go to `.dev/dependencies/npm/node_modules`, linked from `node_modules` at the project root. Resolution is
recorded in `aube-lock.yaml`. Ecosystem selection and shared options: [Dependencies](https://elide.help/docs/toolchain/dependencies).
---
# Static sites
URL: https://elide.help/docs/toolchain/web/static-site
> Build a static site with Elide's StaticSite artifact: render Markdown, process scripts and stylesheets, compress images and copy other files.
A `StaticSite` artifact renders Markdown, processes scripts and stylesheets, compresses images and copies everything
else.
```pkl
amends "elide:project.pkl"
import "elide:Web.pkl" as Web
artifacts {
["site"] = new Web.StaticSite {
srcs = "site"
domain = "example.com"
stylesheets { "site/theme.css" }
hosting = "github-pages"
}
}
```
```bash
elide build
```
## Fields
| Property | Meaning | Default |
| --- | --- | --- |
| `srcs` | Source directory, relative to the project. | — |
| `domain` | Production domain. | — |
| `preview` | Preview domain; informational. | — |
| `prefix` | Mount path for links and assets; must end with `/`. | `"/"` |
| `assets` | Public asset path. | `"_/assets"` |
| `stylesheets` | Added to every page, besides those in its frontmatter. | none |
| `scripts` | Added to every page, besides those in its frontmatter. | none |
| `rewriteLinks` | Rewrite relative Markdown links against `prefix`. | `true` |
| `hosting` | `"workers"` or `"github-pages"`; informational, the build does not publish. | — |
| `dependsOn` | Artifacts to build first. | none |
Minification and browser targets come from the `web` block ([Web Toolchain](https://elide.help/docs/toolchain/web#manifest-settings)).
Without image codecs, the build warns once and copies images unchanged.
## Samples
Start from a template:
```bash
elide init --template web-simple # static site
elide init --template web-demo # static site with TypeScript and Markdown
```
---
# Browser WASM
URL: https://elide.help/docs/toolchain/web/wasm
> Compile Java or Kotlin to WasmGC with Elide: build a WebAssembly module and JavaScript loader, load it from a web page, and check browser support.
A [WebImage](https://elide.help/docs/toolchain/jvm/webimage) artifact with `backend = "wasm-gc"` compiles Java or Kotlin to WasmGC: a
WebAssembly module whose objects live in the browser's garbage-collected heap, plus a JavaScript loader that fetches
and starts it. To run an existing `.wasm` file with Elide, see [WebAssembly](https://elide.help/docs/wasm).
## Prerequisites
- [Binaryen](https://github.com/WebAssembly/binaryen) 119 or later, with `wasm-as` on `PATH` at build time.
- Code that meets the [Native Image requirements](https://elide.help/docs/toolchain/jvm/native-image): reachability metadata for reflection,
resources and proxies; no native dependencies.
## Build
```text
hello-wasm/
elide.pkl
src/main/java/Hello.java
```
```java
public class Hello {
public static void main(String[] args) {
System.out.println("Hello from WasmGC");
}
}
```
```pkl
amends "elide:project.pkl"
import "elide:Jvm.pkl" as jvm
import "elide:NativeImage.pkl" as nativeImage
name = "hello-wasm"
artifacts {
["app-jar"] = new jvm.Jar {
sources { "main" }
}
["web"] = new nativeImage.WebImage {
from { "app-jar" }
name = "hello"
entrypoint = "Hello"
backend = "wasm-gc"
runtime = "browser"
}
}
```
```bash
elide build
```
Output goes to `.dev/artifacts/native-image/`:
| File | Deploy | Hello-world size |
| --- | --- | --- |
| `hello.js` | Yes: the loader. | 110 KB |
| `hello.js.wasm` | Yes: the module. | 1.3 MB (480 KB gzipped) |
| `hello.js.wat` | No: text form, for inspection. | 32 MB |
| `hello.js.map` | Optional: emitted with `sourceMap = true`. | |
| Property | Browser value |
| --- | --- |
| `backend` | `"wasm-gc"` (the default). |
| `runtime` | `"browser"`, or `"generic"` to keep Node support in the same loader. |
| `autoRun` | `true` (default) runs `main` when `hello.js` loads. |
Other properties and compiler options: [WebImage](https://elide.help/docs/toolchain/jvm/webimage#choose-a-backend-and-host).
## Load it in a page
Put `index.html` next to the bundle:
```html
Hello WasmGC
```
```bash
elide dev --no-tui --port 8000 .dev/artifacts/native-image
```
Open `http://localhost:8000/`; `Hello from WasmGC` appears in the browser console.
- The loader requests the module at its own URL plus `.wasm`, so keep `hello.js` and `hello.js.wasm` in the same
directory.
- Load it with a classic `