Elfie

Documentation / Compile targets

Compile targets

A target is one thing your definitions are built into, such as an app, a crate, or a script. You declare targets in your source, assembled from layers that packages provide.

Declaring a target

A target is an ace const whose value is a Target. The const's name is the target's identifier, and the CLI, the maps folder, and the agent tools all use it.

def/targets.lfy
use "elfie/target";
use "cli";
use "platforms";
use "python" as Python;

ace const hello: `A greeting on the command line` = targetOf({
  output = "out/hello",
  language = Python.python,
  runtime = Python.cpython.layer("3.12"),
  platforms = [posix],
  ecosystem = Python.uv,
  frameworks = [Python.argparse],
  interfaces = [commandLine],
  layout = Python.uvScript.layer("hello.py"),
});

global@targets.add(hello);

targetOf gives a record written in braces the type Target. A trait that takes arguments goes in a slot as a layer: Python.cpython.layer("3.12").

Slots

SlotHow manyWhat it saysExamples
output1The directory everything is written under."out/hello"
language1What the code is written in.typescript, python, rust
runtime0–1What runs the code, when the platform doesn't.bun, node, cpython("3.12")
platformsanyWhere the code runs.posix, windows, browser
ecosystem0–1Where dependencies come from.bunPackages, npm, uv, cargo
frameworksanyWhat the code is built on.react, nextjs(root), argparse, clap
interfacesanyWhat it offers people and programs.website, restApi, commandLine
layout1Where each unit's files go.npmLayout, uvScript, cargoWorkspace
layersanyAnything else, such as a house style.Your own layers
dependenciesanyPackages the generated code needs.{ name = "zod", version = "^4" }

A layer in the wrong slot is reported at the const. Dependencies need an ecosystem.

Presets

Packages can offer an ace function that fills in the slots for you. This site is built with the Next.js preset:

def/targets.lfy
use "bun";
use "nextjs" as NextJS;

ace const mainApp: `Elfie.dev main app` = NextJS.app({
  root = `apps/main`,
  runtime = bun,
  ecosystem = bunPackages,
});

global@targets.add(mainApp);

NextJS.app sets the language to TypeScript, the platforms to POSIX and browsers, the frameworks to Next.js and React, and the interfaces to a website and a REST API. It also adds next, react, react-dom, and zod as dependencies.

Choosing what gets built

Each entity is built for the targets in its own @targets. If that list is empty, it uses its file's list, and if that's empty too, it uses global's.

Elfie
global@targets.add(mainApp);
def/tools/cleanup.lfy
@targets.add(tools);
Elfie
cleanup@targets.add(tools);

Traits, ace declarations, and anything declared in a package are never built. A member, a parameter, or an enum member is built with its owner.

Rules for one target

Criteria on a target const are guidance for every unit built for it:

def/targets.lfy
mainApp
  @acceptanceCriteria
  .add({ behavior = `Favicon is [[favicon]]` });

Capabilities

Layers say what they provide and what they require, as plain strings. elfie check reports a requirement that no layer of the target provides.

LayerProvidesRequires
bunjavascript-runtime, node-apijavascript-language
nextjs(root)http-server, html-pages, css-modulesreact, node-api, npm-registry, browser
commandLineprocess, argument-parser
argparseargument-parserpython

That's why the greeter above lists argparse: commandLine needs an argument parser.

Commands

Layers name a shell command for each operation they support: install, add, build, test, lint, format, and run. The compiler runs the build, test, and lint commands, and they must pass with no warnings before the work is accepted. The uv layer, for example, tests with uv run --with pytest pytest and lints with uvx ruff check . && uvx pyright. See Custom compile targets to write your own.

Several targets

A project can declare any number of targets. Each needs its own output directory, and no output may sit inside another's. Pass --target name to elfie compile or elfie verify to work on one.

Bundled packages

PackageLayers and traits
typescripttypescript, npmLayout(name, root)
pythonpython, cpython(version), uv, uvScript(file), uvProject(name, root), argparse
rustrust, cargo, cargoCrate(...), cargoWorkspace(...), clap
lfyelfie, lfyPackage(root), for targets whose output is Elfie
bunbun, bunPackages
nodenode
npmnpm, pnpm, yarn
reactreact, component
nextjsnextjs(root), app(settings)
httpwebsite, restApi, page(path), endpoint(method, path)
clicommandLine, command(name)
platformsposix, windows, browser

Was this guide clear?

Share a suggestion