Skip to content

feat: wasm-buildable leo build pipeline + leo-cli-core + leo-wasm#29476

Draft
mohammadfawaz wants to merge 1 commit into
masterfrom
mohammadfawaz/leo-wasm-build
Draft

feat: wasm-buildable leo build pipeline + leo-cli-core + leo-wasm#29476
mohammadfawaz wants to merge 1 commit into
masterfrom
mohammadfawaz/leo-wasm-build

Conversation

@mohammadfawaz

Copy link
Copy Markdown
Collaborator

No description provided.

@mohammadfawaz mohammadfawaz force-pushed the mohammadfawaz/leo-wasm-build branch from bb6b94f to fed2f06 Compare June 2, 2026 21:36
Brings `leo build` to `wasm32-unknown-unknown` without forking the build logic. A single `handle_build` in the new
`leo-commands` crate drives both the native CLI and the new `leo-wasm` wasm-bindgen layer; the per-target differences
are pushed to two trait seams — `FileSource` (read side, in leo-span) and `ArtifactSink` (write side, in
leo-commands::build). Native passes `DiskFileSource` + `DiskSink`; wasm passes `InMemoryFileSource` + `MemorySink`.

New crates:
- `leo-commands` (renamed from `leo-cli-core`): hosts the per-command `handle_*` cores shared between the CLI and the
  wasm bindings. Today only `build` is hosted; `run` / `test` / `execute` / `deploy` will follow.
- `leo-wasm`: thin wasm-bindgen shim with a single `build(files_json, root, env_json, network_deps_json) -> String`
  entry that materializes the JSON file map into an `InMemoryFileSource`, calls `handle_build`, then JSON-shapes the
  artifacts the `MemorySink` collected.

Read-side abstraction (`FileSource` in `leo-span`):
- `DiskFileSource` / `InMemoryFileSource` / `OverlayFileSource` cover real-disk, JSON-staged in-memory, and editor
  overlay scenarios. LSP gets `RecordingFileSource` + `SingleFileSource`.
- Required methods: `read_file`, `list_leo_files`, `is_file`, `is_dir`, `canonicalize`, `list_files_recursive`.
  `exists` has a default derived from `is_file || is_dir`.
- All entry points take `&dyn FileSource` (not generic) to avoid monomorphization across the compiler / package /
  commands / fmt / lsp call chains.

Write-side abstraction (`ArtifactSink` in `leo-commands::build`):
- `DiskSink` writes through to disk and auto-creates parent dirs; `MemorySink` collects writes in an `IndexMap` using
  `RefCell` (single-threaded by design; `!Send` would be a compile error for a future threaded caller).
- `remove_file` / `remove_dir_all` / `exists` cover the build's pre-clean step for stale interface ABIs.

snarkVM target-gating:
- The full snarkVM umbrella stays native-only across `compiler`, `parser`, `passes`, `package`. The slim
  `snarkvm-console` + `snarkvm-synthesizer-program` subsets are pulled in via leo-ast's `snarkvm_wasm` shim, with
  `extern crate leo_ast as snarkvm` in the three consumer crates so existing `use snarkvm::prelude::*` lines compile
  unchanged on both targets.
- The compiler's `pub mod run` (snarkVM `Process` execution surface — run/test/deploy) stays native-only via the mod
  declaration in `compiler/src/lib.rs`.
- Native-only registry / network fetchers moved into `crates/package/src/native.rs` (mod-gated in lib.rs).
- The LSP-only package/manifest-driven import-stub loader moved into `crates/compiler/src/import_stubs.rs`, similarly
  mod-gated.

Network and workspace dependencies on wasm:
- `Location::Network` deps: callers stage bytecode via `network_deps_json` (`{"name.aleo": "<bytecode>"}`). The wasm
  shim stages each entry under `<root>/__wasm_deps__/<bare>/<name>.aleo` and rewrites matching `Location::Network`
  entries in every manifest to `Location::Local` pointing at the staged path. Unsupplied deps hard-error from the
  build core with a clear message.
- `Location::Workspace` deps: `Workspace::from_directory`, `Workspace::discover`, `WorkspaceManifest::read_from_file`,
  and `resolve_workspace_dependency` all gained `_with_file_source` counterparts. Glob expansion in
  `expand_member_pattern` runs through `FileSource::list_files_recursive` + `glob::Pattern::matches_with`, so the
  filesystem-bound `glob::glob` crate is no longer on the production path. `Workspace::is_member` /
  `auto_register_member` / `initialize_skeleton` stay native-only (real disk reads / writes).

Other refactors:
- Drops the hand-rolled `.aleo` import scanner; `parse_dependencies_from_aleo` runs the real snarkVM parser on both
  targets via the slim wasm shim.
- `max_program_size` switched from a workspace constant to a per-network function that forwards to snarkVM's
  `LATEST_MAX_PROGRAM_SIZE` helper; threaded through every consumer.
- `Package::from_directory[_with_tests]_with_file_source` grew `home_path` / `endpoint` / `network_retries` so
  `load_package` in `leo-commands::build` collapses to one function (no cfg gate).
- `handle_build`'s `validate: bool` parameter dropped; inferred from `cfg!(not(target_arch = "wasm32"))`.
- `ArtifactSink::write` auto-creates parent dirs, removing the per-callsite `ensure_parent_dir` calls.
- `MemorySink` uses `RefCell` instead of `Mutex` (single-threaded build; `.unwrap()` removed).

Tests: 10 unit tests in `leo-wasm::build` (manifest rewrite, key normalization, staged paths, dev-dependency coverage)
and 3 integration tests in `leo-commands::build::tests` (full `handle_build` over `InMemoryFileSource` + `MemorySink`,
fail-closed for unsupplied network deps, workspace glob expansion + member resolution through in-memory file source).
All 13 run under plain `cargo test` on native — no wasm-bindgen-test infra needed.
@mohammadfawaz mohammadfawaz force-pushed the mohammadfawaz/leo-wasm-build branch from fed2f06 to 293cbeb Compare June 2, 2026 21:38
@mohammadfawaz mohammadfawaz self-assigned this Jun 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant