Configure a monorepo with workspaces

A workspace is a collection of folders, each with its own ende.json, managed from a single root. Members can import each other by name, share dependencies, and run tools like ende test and ende fmt across the whole repository at once.

Create the workspace

The root ende.json lists the member directories:

ende.json
{
  "workspace": ["./utils", "./app"]
}

Each member gets its own ende.json with a name, a version, and an exports field pointing at its entry point:

utils/ende.json
{
  "exports": "./mod.ts",
  "name": "@acme/utils",
  "version": "0.1.0"
}
utils/mod.ts
export function greet(name: string): string {
  return `Hello, ${name}!`;
}
app/ende.json
{
  "exports": "./main.ts",
  "name": "@acme/app",
  "version": "0.1.0"
}

Other members import @acme/utils by its bare name: no relative paths and no import map entry needed. Ende finds the member through the root workspace list and resolves the import through its exports:

app/main.ts
import { greet } from "@acme/utils";

console.log(greet("workspace"));
$ ende run app/main.ts
Hello, workspace!

Matching members with globs

Listing every member gets tedious in larger repositories. Each /* segment matches one folder depth: packages/* matches packages/foo but not packages/foo/subpackage.

ende.json
{
  "workspace": ["packages/*", "examples/*/*"]
}

Options that must live at the root

Some options control resolution for the whole workspace and are only read from the root ende.json: nodeModulesDir, vendor, minimumDependencyAge, links, lock, and allowScripts. Setting them in a member has no effect and Ende will warn about it.

Note

name, version, and exports go the other way: they belong in members, not in the root.

Sharing dependencies from the root

Members inherit the root imports map, so common dependencies can be declared once:

ende.json
{
  "imports": {
    "@ende/assert": "jsr:@ende/assert@^2026.10.15"
  },
  "workspace": ["./utils", "./app"]
}

A member can declare its own imports too; within that member’s folder, its entries override the root’s.