diff --git a/README.md b/README.md --- a/README.md +++ b/README.md @@ -37,7 +37,8 @@ | Standard library | Selected modules and intrinsics | | Whole-project linked output | Supported subset | | Dependency source loading | Selected Hex and path packages | -| Browser, Node.js, and WASI ABIs | Incomplete | +| Browser, bundler, and Node.js | Supported adapter subset | +| WASI ABI | Incomplete | ### Working today @@ -50,12 +51,17 @@ - [x] Lower the supported subset to core IR - [x] Emit deterministic `.wasm` - [x] Render optional WAT -- [x] Run scalar and managed-value exports in Wasmtime tests +- [x] Run scalar and managed-value exports with Wasmtime - [x] Load `gleam.toml` and discover project modules - [x] Compile supported projects into linked Wasm output - [x] Load selected Hex and path dependency sources - [x] Lower supported externals to Wasm imports - [x] Validate target-aware host imports +- [x] Emit deterministic browser, bundler, and Node.js `.mjs` adapters +- [x] Emit optional AST, resolved, typed, IR, runtime, and ABI debug artifacts +- [x] Render source diagnostics with snippets, labels, notes, and stable + project ordering +- [x] Keep normal CLI output concise, with `--no-color` and `NO_COLOR` support ### Supported Gleam surface @@ -80,15 +86,18 @@ values - [x] Branches, comparisons, equality, pattern checks, and failure paths - [x] Selected runtime helpers and stdlib intrinsics +- [x] Checked runtime helper fragments before final module output ### Not yet implemented - [ ] Compile every valid Gleam project shape - [ ] Compile broad dependency source modules without subset limits -- [ ] Compile the full Gleam standard library from source -- [ ] Provide complete browser, bundler, and Node.js host ABIs +- [ ] Compile the full published `gleam_stdlib` package without registry shims +- [ ] Provide complete browser, bundler, and Node.js host APIs beyond the + current adapter subset - [ ] Provide complete WASI adapters -- [ ] Add garbage collection, reference counting, or heap growth checks +- [ ] Add garbage collection or reference counting for long-lived managed + values For more detail, see the case-study book in [docs/book](./docs/book/src/introduction.md) and the user/development docs in diff --git a/docs/internal/README.md b/docs/internal/README.md --- a/docs/internal/README.md +++ b/docs/internal/README.md @@ -13,21 +13,18 @@ Current work is focused on making that Gleam-project-to-Wasm path more usable: -1. Improve CLI artifacts, diagnostics, and host metadata. -2. Fill stdlib and dependency gaps that block realistic projects. -3. Add larger examples as acceptance fixtures after the core gaps are planned. +1. Fill stdlib and dependency gaps that block realistic projects. +2. Add larger examples as acceptance fixtures after the core gaps are planned. ## Specs - [Stdlib and host interop](specs/16_stdlib_and_host_interop.md) -- [CLI and build outputs](specs/17_cli_and_build_outputs.md) - [Example projects](specs/18_example_projects.md) - [WASI host ABI](specs/20_wasi_host_abi.md) ## Task Trackers - [Stdlib and host interop](tasks/16_stdlib_and_host_interop.md) -- [CLI and build outputs](tasks/17_cli_and_build_outputs.md) - [WASI host ABI](tasks/21_wasi_host_abi.md) ### Examples diff --git a/docs/website/changelog.md b/docs/website/changelog.md --- a/docs/website/changelog.md +++ b/docs/website/changelog.md @@ -6,13 +6,27 @@ ### 2026-06-19 +#### Added + +- CLI project and single-file compilation now share output path, target, WAT, + emit, dump, verbose, and JSON reservation flags. +- `run` and `exec` now compile one source file, execute a Wasmtime export, and + render scalar and supported managed return values. +- Build output now includes deterministic Wasm, optional WAT, AST, resolved, + typed, IR, runtime layout, ABI, and import/export metadata artifacts. +- Browser, bundler, and Node.js targets now emit deterministic `.mjs` host + adapter files when Wasm output is requested. +- CLI diagnostics now include stable multi-module ordering, source snippets, + labels, notes, clearer project errors, clearer target and ABI mismatch + messages, and improved type-inference context. + #### Changed -- Promoted the JavaScript host ABI contract to the website development docs. - -#### Removed - -- Removed the completed internal JavaScript host ABI spec and task tracker. +- The CLI now supports a global `--no-color` flag and honors the + [`NO_COLOR`](https://no-color.org/) environment variable for plain + human-readable output. +- WAT is now treated as a rendered debug artifact; structured Wasm byte + emission is the source of truth for final `.wasm` output. ### 2026-06-05 diff --git a/crates/cli/src/args.rs b/crates/cli/src/args.rs --- a/crates/cli/src/args.rs +++ b/crates/cli/src/args.rs @@ -6,6 +6,11 @@ #[derive(Debug, Parser)] #[command(about = "Compile Gleam source to WebAssembly")] pub struct Args { + /// Disable ANSI colors in human-readable output. + /// + /// This is also enabled when the NO_COLOR environment variable is set. + #[arg(long, global = true)] + pub no_color: bool, #[command(subcommand)] pub command: Command, } diff --git a/crates/cli/src/commands.rs b/crates/cli/src/commands.rs --- a/crates/cli/src/commands.rs +++ b/crates/cli/src/commands.rs @@ -46,7 +46,7 @@ } } -pub fn run(command: Command) -> ExitCode { +pub fn run(command: Command, no_color: bool) -> ExitCode { match command { Command::Build { project, output, out_dir, target, emit, wat, dump_dir, verbose, json } => { let mut builder = @@ -61,30 +61,31 @@ Command::Run { input, function, args, target, verbose, json } => { Runner::new(&input, &function, &args, target, verbose, json).run() } - Command::Debug { view, input, tree_sitter, ast, spans, json, no_color } => run_debug( + Command::Debug { view, input, tree_sitter, ast, spans, json, no_color: debug_no_color } => run_debug( view, input.as_deref(), - DebugOptions::new(tree_sitter, ast, spans, json, no_color), + DebugOptions::new(tree_sitter, ast, spans, json, no_color || debug_no_color), + no_color, ), Command::List { project } => list(project.as_deref().unwrap_or_else(|| Path::new("."))), } } -fn run_debug(view: Option, input: Option<&Path>, opts: DebugOptions) -> ExitCode { +fn run_debug(view: Option, input: Option<&Path>, opts: DebugOptions, no_color: bool) -> ExitCode { match view { Some(DebugCommand::Ts(args)) => Debugger::new( &args.input, - DebugOptions::new(true, false, false, args.json, args.no_color), + DebugOptions::new(true, false, false, args.json, no_color || args.no_color), ) .run(), Some(DebugCommand::Spans(args)) => Debugger::new( &args.input, - DebugOptions::new(true, false, true, args.json, args.no_color), + DebugOptions::new(true, false, true, args.json, no_color || args.no_color), ) .run(), Some(DebugCommand::Ast(args)) => Debugger::new( &args.input, - DebugOptions::new(false, true, false, args.json, args.no_color), + DebugOptions::new(false, true, false, args.json, no_color || args.no_color), ) .run(), Some(DebugCommand::Json(args)) => Debugger::new( diff --git a/crates/cli/src/echo.rs b/crates/cli/src/echo.rs --- a/crates/cli/src/echo.rs +++ b/crates/cli/src/echo.rs @@ -1,11 +1,40 @@ +use std::sync::atomic::{AtomicBool, Ordering}; use std::{fmt::Display, process::ExitCode}; use compiler_core::diagnostic::{Diagnostic, Diagnostics}; use compiler_core::source::{SourceFile, Span}; use owo_colors::OwoColorize; +static COLOR_ENABLED: AtomicBool = AtomicBool::new(true); + +pub fn set_color_enabled(enabled: bool) { + COLOR_ENABLED.store(enabled, Ordering::Relaxed); +} + +fn color_enabled() -> bool { + COLOR_ENABLED.load(Ordering::Relaxed) +} + +fn styled_label(label: &str, style: LabelStyle) -> String { + if !color_enabled() { + return label.to_string(); + } + match style { + LabelStyle::Status => label.bright_magenta().bold().to_string(), + LabelStyle::Error => label.bright_red().bold().to_string(), + LabelStyle::Diagnostic => label.bright_yellow().bold().to_string(), + } +} + +#[derive(Clone, Copy)] +enum LabelStyle { + Status, + Error, + Diagnostic, +} + pub fn status(label: &str, message: impl AsRef) { - eprintln!("{} {}", label.bright_magenta().bold(), message.as_ref()); + eprintln!("{} {}", styled_label(label, LabelStyle::Status), message.as_ref()); } pub fn progress(message: impl AsRef) { @@ -13,7 +42,7 @@ } pub fn error(message: impl AsRef) { - eprintln!("{} {}", "error".bright_red().bold(), message.as_ref()); + eprintln!("{} {}", styled_label("error", LabelStyle::Error), message.as_ref()); } pub fn fail(action: &str, subject: impl Display, cause: impl Display) -> ExitCode { @@ -36,7 +65,11 @@ } pub fn diagnostic(message: impl AsRef) { - eprintln!("{} {}", "diagnostic".bright_yellow().bold(), message.as_ref()); + eprintln!( + "{} {}", + styled_label("diagnostic", LabelStyle::Diagnostic), + message.as_ref() + ); } pub fn diagnostics(diagnostics: &Diagnostics) { diff --git a/crates/cli/src/lib.rs b/crates/cli/src/lib.rs --- a/crates/cli/src/lib.rs +++ b/crates/cli/src/lib.rs @@ -6,5 +6,7 @@ pub fn run() -> std::process::ExitCode { let args = args::Args::parse(); - commands::run(args.command) + let no_color = args.no_color || std::env::var_os("NO_COLOR").is_some(); + echo::set_color_enabled(!no_color); + commands::run(args.command, no_color) } diff --git a/crates/cli/tests/build.rs b/crates/cli/tests/build.rs --- a/crates/cli/tests/build.rs +++ b/crates/cli/tests/build.rs @@ -47,6 +47,97 @@ } #[test] +fn global_no_color_disables_status_ansi_output() { + let out_dir = unique_temp_dir("regulus_cli_no_color_build"); + fs::create_dir_all(&out_dir).expect("create output dir"); + + let output = Command::new(env!("CARGO_BIN_EXE_reggie")) + .current_dir(workspace_root()) + .arg("--no-color") + .arg("build") + .arg("examples/scalar_project") + .arg("--out-dir") + .arg(&out_dir) + .output() + .expect("run reggie build"); + + assert!( + output.status.success(), + "build failed\nstdout:\n{}\nstderr:\n{}", + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr) + ); + let stderr = String::from_utf8_lossy(&output.stderr); + assert!(stderr.contains("wasm "), "missing status output:\n{stderr}"); + assert!( + !stderr.contains("\u{1b}["), + "--no-color should disable status ANSI codes: {stderr:?}" + ); + + let _ = fs::remove_dir_all(out_dir); +} + +#[test] +fn global_no_color_is_accepted_after_subcommand() { + let output = Command::new(env!("CARGO_BIN_EXE_reggie")) + .current_dir(workspace_root()) + .arg("list") + .arg("examples/multi_module_project") + .arg("--no-color") + .output() + .expect("run reggie list"); + + assert!( + output.status.success(), + "list failed\nstdout:\n{}\nstderr:\n{}", + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr) + ); + let stderr = String::from_utf8_lossy(&output.stderr); + assert!( + stderr.contains("project multi_module_project"), + "missing project output:\n{stderr}" + ); + assert!( + !stderr.contains("\u{1b}["), + "--no-color should disable list ANSI codes: {stderr:?}" + ); +} + +#[test] +fn no_color_environment_disables_diagnostic_ansi_output() { + let out_dir = unique_temp_dir("regulus_cli_no_color_env"); + fs::create_dir_all(&out_dir).expect("create output dir"); + + let output = Command::new(env!("CARGO_BIN_EXE_reggie")) + .current_dir(workspace_root()) + .env("NO_COLOR", "1") + .arg("build") + .arg("examples/diagnostics/duplicate_modules") + .arg("--out-dir") + .arg(&out_dir) + .output() + .expect("run reggie build"); + + assert!(!output.status.success(), "diagnostic example should fail"); + let stderr = String::from_utf8_lossy(&output.stderr); + assert!( + stderr.contains("error could not load project"), + "missing error output:\n{stderr}" + ); + assert!( + stderr.contains("diagnostic ProjectError"), + "missing diagnostic output:\n{stderr}" + ); + assert!( + !stderr.contains("\u{1b}["), + "NO_COLOR should disable diagnostic ANSI codes: {stderr:?}" + ); + + let _ = fs::remove_dir_all(out_dir); +} + +#[test] fn snapshots_diagnostic_example() { let out_dir = unique_temp_dir("regulus_cli_example_diagnostic"); fs::create_dir_all(&out_dir).expect("create output dir"); diff --git a/crates/core/src/diagnostic.rs b/crates/core/src/diagnostic.rs --- a/crates/core/src/diagnostic.rs +++ b/crates/core/src/diagnostic.rs @@ -47,9 +47,9 @@ } pub fn expected_found( - code: DiagnosticCode, got: impl Display, want: impl Display, span: Span, label: impl Into, + code: DiagnosticCode, expected: impl Display, actual: impl Display, span: Span, label: impl Into, ) -> Self { - Self::spanned(code, format!("expected `{got}` but found `{want}`"), span, label) + Self::spanned(code, format!("expected `{expected}` but found `{actual}`"), span, label) } pub fn duplicate( diff --git a/docs/internal/specs/17_cli_and_build_outputs.md b/docs/internal/specs/17_cli_and_build_outputs.md deleted file mode 100644 --- a/docs/internal/specs/17_cli_and_build_outputs.md +++ /dev/null @@ -1,89 +0,0 @@ -# CLI and build outputs - -Current single-file CLI behavior is documented in [CLI and build outputs][cli]. -This spec tracks the remaining user-facing command and artifact work that is not -specific to project linking. - -Project compilation itself is defined in the website development design record: -[Project compilation and dependencies][projects]. - -[cli]: ../../website/reference/cli-and-build-outputs.md -[projects]: ../../website/development/projects.md - -## Responsibilities - -The CLI should provide predictable commands and stable artifacts for both users -and compiler contributors. - -Remaining responsibilities: - -- keep normal output concise -- make debug output opt-in -- write deterministic artifacts -- avoid partial final artifacts after failed compilation -- expose enough metadata for host adapters and tests - -## Commands - -Single-file compilation remains useful for fixtures and small examples. Project -compilation reuses the same output, target, WAT, emit, dump, verbose, and JSON -reservation flags where possible. - -Current command surface: - -- `build [project]` compiles a Gleam project. -- `compile ` compiles one source file. -- `run ` compiles one source file and executes an export with Wasmtime. -- `exec` is an alias for `run`. -- `debug`/`dbg` inspect compiler-internal views. -- `list [project]` prints discovered project modules. - -`build` and `compile` both support output path selection, optional WAT output, -optional debug dump directories, target selection, and explicit artifact -selection. `run` and `exec` support Wasmtime execution for scalar arguments and -ABI-aware rendering of scalar and managed return values. - -Commands should continue to return clear exit codes for success, diagnostics, -and command misuse. - -## Artifacts - -Suggested outputs: - -- final `.wasm` -- optional `.wat` -- optional AST, resolved, typed, IR, and WAT dumps -- optional runtime layout and ABI metadata -- optional import/export metadata for host adapters -- deterministic JS host adapter files when requested - -Artifact paths should be stable enough for examples, snapshots, and host smoke -tests. - -## Diagnostics - -Diagnostics are rendered for humans by default and remain structured enough for -tests. - -The CLI should support: - -- file paths -- source snippets -- labels -- notes -- stable multi-module ordering -- stage-specific unsupported-feature messages -- clear messages for target and ABI mismatches - -## Backend output model - -The backend now builds a compiler-owned Wasm module and can emit bytes directly. -CLI output should treat WAT as a rendered debug artifact, not as the source of -truth for byte emission. - -Helper-backed modules should continue to assemble through checked helper -fragments before they are included in final output. - -## Active tasks - -See [CLI and build outputs tasks](../tasks/17_cli_and_build_outputs.md). diff --git a/docs/internal/tasks/17_cli_and_build_outputs.md b/docs/internal/tasks/17_cli_and_build_outputs.md deleted file mode 100644 --- a/docs/internal/tasks/17_cli_and_build_outputs.md +++ /dev/null @@ -1,68 +0,0 @@ -# CLI and build outputs tasks - -## Goal - -Make compiler commands predictable and generated artifacts useful. - -## Tasks - -### Commands - -- [x] Keep single-file compilation available for tests and examples. -- [x] Add output path configuration. -- [x] Add target selection for supported runtimes and host profiles: Wasmtime, - browser, bundler, Node.js, and WASI where implemented. -- [x] Return useful exit codes for success, diagnostics, and command misuse. -- [x] Keep project compilation flags consistent with single-file compilation. -- [x] Add CLI `run`/`exec` (aliases for one another) integration tests for Wasmtime execution. -- [x] Add ABI-aware rendering for managed `run` return values such as strings, - tuples, lists, records, `Result`, and `Option`. - -### Artifacts - -- [x] Write `.wasm` artifacts. -- [x] Add optional WAT output. -- [x] Add optional AST, resolved AST, typed output, and IR debug dumps. -- [x] Emit Wasm bytes from a structured module without going through WAT. -- [x] Add deterministic WAT snapshots generated from structured Wasm. -- [x] Emit deterministic `.mjs` adapter files when Wasm output is requested for - browser, bundler, and Node.js targets. -- [x] Expose JS host import and export metadata needed by checked host calls. -- [x] Add optional runtime layout and ABI debug output where helpful. -- [x] Include import and export metadata in explicit debug output where useful. -- [x] Emit or package deterministic browser and Node.js host adapter files when - requested. - -### Backend cleanup - -- [x] Disable silent fallback from structured codegen to the old WAT emitter. -- [x] Keep unsupported IR as source-spanned `WasmError` diagnostics. -- [x] Delete the old `Emitter` implementation and fallback-only tests. -- [x] Give helper-backed modules direct structured byte emission or checked - precompiled fragments. - -### Diagnostics and user output - -- [x] Render diagnostics with source snippets, labels, notes, and file paths. -- [x] Group diagnostics across project modules in a stable order. -- [x] Improve type-inference diagnostics for local generalization, ambiguous - types, recursive types, constructor fields, and branch mismatches. -- [x] Show unsupported-feature diagnostics from AST, resolver, type, lowering, - backend, stdlib, and ABI stages without losing source spans. -- [x] Keep normal compile output concise; make debug output opt-in. -- [x] Add human-readable messages for missing project files, duplicate modules, - unsupported exports, and backend target mismatches. - -### Integration tests - -- [x] Add CLI integration tests for project compilation commands. -- [x] Add CLI integration tests for diagnostics across multiple files. -- [x] Add tests for output path handling and optional WAT/debug artifacts. -- [x] Add tests for target selection and unsupported target combinations. -- [x] Add tests for browser and Node.js JS host profile output. -- [x] Add tests for bundler host adapter artifact paths. - -## Done when - -Users get concise command output, deterministic artifacts, and clear -source-rendered diagnostics for both single-file and project compilation. diff --git a/docs/website/guide/usage/cli.md b/docs/website/guide/usage/cli.md --- a/docs/website/guide/usage/cli.md +++ b/docs/website/guide/usage/cli.md @@ -15,6 +15,19 @@ regulus build ``` +## Global options + +Pass `--no-color` before or after a subcommand to disable ANSI color in +human-readable output: + +```sh +reggie --no-color build examples/scalar_project +reggie build examples/scalar_project --no-color +``` + +Regulus also disables ANSI color when the `NO_COLOR` environment variable is +set[^no-color] + ## Build a project Use `build` for Gleam projects with a `gleam.toml` file. @@ -33,6 +46,17 @@ choose an exact final path or `--out-dir` to write compiler-named artifacts into a directory. +Example: + +```sh +reggie --no-color build examples/scalar_project --out-dir build/docs +``` + +```text +Resolving dependencies +wasm build/docs/scalar_project.wasm (63 bytes) +``` + See [Project compilation and dependencies][project-compilation] for dependency loading, linked output, and current project limits. @@ -47,6 +71,10 @@ By default, single-file compilation writes a `.wasm` file next to the input. `--output` and `--out-dir` work the same way as project builds. + +Single-file compilation exists for fixtures, small examples, and compiler +debugging. Project compilation should use `build` so module discovery, +dependencies, and linked output all follow the project model. ## Run one file @@ -65,10 +93,20 @@ reggie exec path/to/module.gleam ``` -Scalar arguments and return values use the low-level Wasm ABI. `Int` values are -passed as `i64`, `Float` values as `f64`, and `Bool` values as `i32`. Managed -values such as strings, lists, tuples, records, and custom types are pointers -into guest memory at the Wasm boundary. Programs can still print strings through +Example: + +```sh +reggie run examples/scalar_project/src/main.gleam --function add_one 41 +``` + +```text +42 +``` + +Scalar arguments use the low-level Wasm ABI. `Int` values are passed as `i64`, +`Float` values as `f64`, and `Bool` values as `i32`. Return values are rendered +for scalars and supported managed values such as strings, tuples, lists, +records, `Result`, and `Option`. Programs can also print strings through `gleam/io.print` and `gleam/io.println` when targeting Wasmtime. ## Targets @@ -85,11 +123,26 @@ `wasi`. Project builds use the target from `gleam.toml` when `--target` is not provided. -`bundler` emits a deterministic `.mjs` adapter next to the `.wasm` artifact -when Wasm output is requested. That adapter loads the Wasm module, checks -imports, converts scalar and string calls, and reads supported structured -export results. `browser` and `nodejs` are accepted targets, but their complete -host glue and profile-specific APIs are still in progress. +`browser`, `bundler`, and `nodejs` emit deterministic `.mjs` adapters next to +the `.wasm` artifact when Wasm output is requested. The adapters load the Wasm +module, check imports, convert scalar and string calls, and read supported +structured export results. + +Example: + +```sh +reggie --no-color build examples/scalar_project --target nodejs --out-dir build/node +``` + +```text +Resolving dependencies +wasm build/node/scalar_project.wasm (2651 bytes) +js build/node/scalar_project.mjs +``` + +Target-specific externals are checked before Wasm assembly. If a source file +imports a browser or Node.js host module while compiling for Wasmtime, the CLI +reports the target mismatch with a source label and recovery note. ## Artifacts @@ -118,6 +171,18 @@ `--out-dir` when that option is used. Debug emit values write deterministic files beside the selected output path unless `--dump-dir` is set. +Example: + +```sh +reggie --no-color build examples/scalar_project --out-dir build/debug --emit wasm,wat +``` + +```text +Resolving dependencies +wasm build/debug/scalar_project.wasm (63 bytes) +wat build/debug/scalar_project.wat +``` + Use `--dump-dir` to write all compiler debug dumps into a separate directory: ```sh @@ -130,6 +195,45 @@ If compilation fails, Regulus does not write the final Wasm artifact. Debug artifacts are only written when the requested compiler phase completes. + +The backend emits final Wasm bytes from its structured module. WAT is rendered +from that module for debugging and snapshots; it is not the source of truth for +the final `.wasm` artifact. + +## Diagnostics and exit codes + +Successful commands exit with status code `0`. Compilation diagnostics and +project loading errors exit with a non-zero status code. Command misuse, such +as an unknown flag or invalid subcommand, is reported by the CLI argument +parser and also exits non-zero. + +Human diagnostics include file paths, source snippets when a span is available, +labels, and notes. Project diagnostics are grouped in a stable order across +modules. + +Missing project manifests are reported with the path Regulus tried to load: + +```sh +reggie --no-color build /tmp/not-a-regulus-project +``` + +```text +error could not load project /tmp/not-a-regulus-project +diagnostic ProjectError: project manifest not found at /tmp/not-a-regulus-project/gleam.toml + note: pass a project directory or a path to gleam.toml +``` + +Duplicate modules include both conflicting source paths: + +```sh +reggie --no-color build examples/diagnostics/duplicate_modules +``` + +```text +error could not load project examples/diagnostics/duplicate_modules +diagnostic ProjectError: duplicate module `app` in examples/diagnostics/duplicate_modules/src/app.gleam and examples/diagnostics/duplicate_modules/test/app.gleam + note: each module name must be unique across src and test +``` ## Inspect one source file @@ -163,13 +267,21 @@ Use `list` to inspect discovered modules without building artifacts. ```sh -reggie list examples/multi_module_project +reggie --no-color list examples/multi_module_project +``` + +```text +project multi_module_project 1.0.0 (2 modules) +module main -> examples/multi_module_project/src/main.gleam +module math -> examples/multi_module_project/src/math.gleam ``` ## Current limitations -Project compilation is still growing. Broad Hex dependency language coverage, -bodyless externals from dependency source, and richer host ABI adapters are -tracked in `docs/internal`. +Project compilation is still growing. Broad Hex dependency language coverage +and additional host APIs are documented in the development and reference docs +as they stabilize. [project-compilation]: ../../reference/compiling-projects.md + +[^no-color]: https://no-color.org/