Skip to content

Commit 2b58bbd

Browse files
feat(esm): retire --deno-esm; Bun-ESM is the only JS-host ESM target (#759)
## Summary Bun-ESM is the only JS-host ESM target. `--deno-esm` / `-o *.deno.js` is a hard error (E0826) pointing at `--bun-esm` / `.bun.js`. The flag is kept so existing scripts fail loudly instead of becoming an unknown option. `codegen_deno` now emits the Bun host (`codegen_deno` ≡ `codegen_bun`). The historical `tests/codegen-deno/` corpus compiles with `--bun-esm` to `.bun.js`. Aspirational idaptik StartupError fixtures compile with real syntax (`use Console::{log}`, `use Dom::{…}`, tuple-list attrs, `#{ bg: … }` records) — not ReScript `import Dom` / `{IO}` / `{style: bg}` without `#`. Host FFI on this path does not require `/ IO` rows (Canvas-style erasure at emit). `affinescript-vite` remains a scaffold, not in this tree. Remaining Pixi.res (~313 decls) is a follow-up, not this PR. Does **not** implement #486 (native-preview2 / default component; needs ocaml + wasm-tools and must keep `typedwasm.ownership` byte-equal). Closes #56 ## Notes - No local ocaml/dune/bun in the authoring environment; CI has the toolchain (Bun via `scripts/install-bun.sh`). - Docs updated: capability matrix, bun-esm migration, ECOSYSTEM, EFFECTS, CHANGELOG. No production-ready claim (DOC-09).
1 parent 97dc674 commit 2b58bbd

51 files changed

Lines changed: 218 additions & 136 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.github/workflows/ci.yml‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,10 +89,10 @@ jobs:
8989
run: opam exec -- dune runtest
9090
- name: Run codegen WASM tests
9191
run: opam exec -- ./tools/run_codegen_wasm_tests.sh
92-
- name: Run codegen Deno-ESM tests (issue #122)
93-
# Compiles tests/codegen-deno/*.affine with the --deno-esm backend
94-
# and runs the *.harness.mjs under Node (CI has Node 20, not Deno;
95-
# the Phase 1 fixtures are pure logic so Node ESM exercises them).
92+
- name: Run codegen Bun-ESM tests (historical codegen-deno corpus)
93+
# Compiles tests/codegen-deno/*.affine with the --bun-esm backend
94+
# and runs the *.harness.mjs under Node (CI has Node 20; these
95+
# fixtures mock host objects and do not need the Bun binary).
9696
run: opam exec -- ./tools/run_codegen_deno_tests.sh
9797
- name: Run native Bun-ESM tests (issue #734)
9898
run: opam exec -- ./tools/run_codegen_bun_tests.sh

‎.gitignore‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ bisect*.coverage
100100
# issue #122: generated Deno-ESM regression outputs (compiled from the
101101
# committed *.affine fixtures by tools/run_codegen_deno_tests.sh).
102102
/tests/codegen-deno/*.deno.js
103+
/tests/codegen-deno/*.bun.js
103104
# Issue #734: generated native Bun-ESM acceptance outputs.
104105
/tests/codegen-bun/*.bun.js
105106
/tests/codegen-bun/backend-conflict.json

‎CHANGELOG.adoc‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,17 @@ https://semver.org/spec/v2.0.0.html[Semantic Versioning].
1515

1616
=== [Unreleased]
1717

18+
==== Removed
19+
20+
* `--deno-esm` / `-o FILE.deno.js` is a hard error (E0826). Bun-ESM
21+
(`--bun-esm` / `.bun.js`) is the only JS-host ESM target. The flag is
22+
kept so existing scripts fail loudly (Refs #56)
23+
1824
==== Added
1925

26+
* Aspirational idaptik StartupError fixture compiles with real syntax
27+
(`use Console::{log}`, `use Dom::{…}`, tuple-list attrs and
28+
`#{ bg: … }` records) on `--bun-esm` (Refs #56)
2029
* feat(stdlib): Aggregate.affine — SQL group-by + aggregation primitives
2130
— db-theory #3 (7 externs) (PR #527)
2231
* feat(stdlib): Transaction.affine — affine-bounded write-set isolation

‎bin/main.ml‎

Lines changed: 11 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -492,10 +492,8 @@ let repl_cmd_fn () =
492492
let compile_file face json wasm_gc vscode_ext vscode_adapter vscode_no_lc
493493
deno_esm bun_esm target path output =
494494
let face = resolve_face ~quiet:json face path in
495-
let is_deno = deno_esm || Filename.check_suffix output ".deno.js" in
496-
let is_bun = bun_esm || Filename.check_suffix output ".bun.js" in
497-
if is_deno && is_bun then
498-
let message = "--deno-esm and --bun-esm are mutually exclusive" in
495+
if deno_esm || Filename.check_suffix output ".deno.js" then
496+
let message = "Deno-ESM was removed; use --bun-esm (and a .bun.js output path)" in
499497
if json then
500498
json_finish [{ Affinescript.Json_output.severity = Error;
501499
code = "E0826"; message;
@@ -536,10 +534,9 @@ let compile_file face json wasm_gc vscode_ext vscode_adapter vscode_no_lc
536534
use the original [prog] because they handle imports natively
537535
via Codegen.gen_imports / the import section. *)
538536
let flat_prog = Affinescript.Module_loader.flatten_imports loader prog in
539-
let is_deno = deno_esm || Filename.check_suffix output ".deno.js" in
540537
let is_bun = bun_esm || Filename.check_suffix output ".bun.js" in
541538
let is_julia = Filename.check_suffix output ".jl" in
542-
let is_js = (not is_deno) && (not is_bun) && Filename.check_suffix output ".js" in
539+
let is_js = (not is_bun) && Filename.check_suffix output ".js" in
543540
let is_c = Filename.check_suffix output ".c" in
544541
let is_wgsl = Filename.check_suffix output ".wgsl" in
545542
let is_faust = Filename.check_suffix output ".dsp" in
@@ -570,16 +567,6 @@ let compile_file face json wasm_gc vscode_ext vscode_adapter vscode_no_lc
570567
let oc = open_out_bin output in
571568
output_string oc esm_code;
572569
close_out oc
573-
end else if is_deno then begin
574-
match Affinescript.Codegen_deno.codegen_deno flat_prog resolve_ctx.symbols with
575-
| Error msg ->
576-
add { severity = Error; code = "E0824";
577-
message = Printf.sprintf "Deno-ESM codegen error: %s" msg;
578-
span = Affinescript.Span.dummy; help = None; labels = [] }
579-
| Ok esm_code ->
580-
let oc = open_out_bin output in
581-
output_string oc esm_code;
582-
close_out oc
583570
end else if is_julia then begin
584571
match Affinescript.Julia_codegen.codegen_julia flat_prog resolve_ctx.symbols with
585572
| Error msg ->
@@ -779,10 +766,9 @@ let compile_file face json wasm_gc vscode_ext vscode_adapter vscode_no_lc
779766
cross-module imports for backends that don't have native
780767
module-system support. Wasm/Wasm-GC keep the original [prog]. *)
781768
let flat_prog = Affinescript.Module_loader.flatten_imports loader prog in
782-
let is_deno = deno_esm || Filename.check_suffix output ".deno.js" in
783769
let is_bun = bun_esm || Filename.check_suffix output ".bun.js" in
784770
let is_julia = Filename.check_suffix output ".jl" in
785-
let is_js = (not is_deno) && (not is_bun) && Filename.check_suffix output ".js" in
771+
let is_js = (not is_bun) && Filename.check_suffix output ".js" in
786772
let is_c = Filename.check_suffix output ".c" in
787773
let is_wgsl = Filename.check_suffix output ".wgsl" in
788774
let is_faust = Filename.check_suffix output ".dsp" in
@@ -814,17 +800,6 @@ let compile_file face json wasm_gc vscode_ext vscode_adapter vscode_no_lc
814800
close_out oc;
815801
Format.printf "Compiled %s -> %s (Bun-ESM)@." path output;
816802
`Ok ())
817-
else if is_deno then
818-
(match Affinescript.Codegen_deno.codegen_deno flat_prog resolve_ctx.symbols with
819-
| Error e ->
820-
Format.eprintf "@[<v>Deno-ESM codegen error: %s@]@." e;
821-
`Error (false, "Deno-ESM codegen error")
822-
| Ok esm_code ->
823-
let oc = open_out output in
824-
output_string oc esm_code;
825-
close_out oc;
826-
Format.printf "Compiled %s -> %s (Deno-ESM)@." path output;
827-
`Ok ())
828803
else if is_julia then
829804
(match Affinescript.Julia_codegen.codegen_julia flat_prog resolve_ctx.symbols with
830805
| Error e ->
@@ -1280,28 +1255,21 @@ let vscode_no_lc_arg =
12801255
dependency for extensions that ship no language client; the \
12811256
wiring passes null in its place.")
12821257

1283-
(* Issue #122: --deno-esm. Selects the direct AST -> ES-module backend
1284-
({!Affinescript.Codegen_deno}) regardless of output extension, so a
1285-
drop-in `.js` ES module can be produced (e.g. `-o src/storage.js
1286-
--deno-esm`). A `.deno.js` output extension also routes here without
1287-
the flag, as a convenience for the test corpus / ad-hoc use. *)
1258+
(* --deno-esm was issue #122. Retired: the ESM host is Bun (--bun-esm).
1259+
The flag is kept so existing scripts get a clear error instead of
1260+
Cmdliner unknown-option. *)
12881261
let deno_esm_arg =
12891262
Arg.(value & flag & info ["deno-esm"]
1290-
~doc:"Emit a standalone Deno/Node ES module directly from the AST \
1291-
(issue #122): `export class` for struct+impl, `export` for \
1292-
public fns/consts, and `extern fn` lowered to direct host \
1293-
calls (Deno.*Sync / JSON / WebAssembly). No wasm, no require, \
1294-
no handle table — the output is a drop-in importable ESM. A \
1295-
`.deno.js` output extension selects this backend implicitly.")
1263+
~doc:"REMOVED. Deno-ESM is gone; use $(b,--bun-esm) / a `.bun.js` \
1264+
output path. Passing this flag (or `-o *.deno.js`) is an error.")
12961265

12971266
let bun_esm_arg =
12981267
Arg.(value & flag & info ["bun-esm"]
12991268
~doc:"Emit a standalone Bun-native ES module directly from the AST: \
13001269
public declarations are exported and host operations use Bun's \
13011270
Node-compatible synchronous APIs. The emitted module contains no \
1302-
legacy-runtime shim. A `.bun.js` output extension selects this \
1303-
backend implicitly. This option is mutually exclusive with \
1304-
`--deno-esm`.")
1271+
Deno runtime shim. A `.bun.js` output extension selects this \
1272+
backend implicitly.")
13051273

13061274
(** Shared --face flag: select the parser surface-syntax face. *)
13071275
let face_arg =

‎docs/CAPABILITY-MATRIX.adoc‎

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -196,16 +196,19 @@ Formal spec: link:specs/TYPED-WASM-INTERFACE.adoc[specs/TYPED-WASM-INTERFACE.ado
196196
Widening roadmap (L1–6, L14–16, multi-producer ABI): link:specs/TYPED-WASM-ROADMAP.adoc[specs/TYPED-WASM-ROADMAP.adoc].
197197
Producer-side; target spec is the separate `hyperpolymath/typed-wasm` repo.
198198

199-
|Deno-ESM |works |Direct AST→ES-module transpiler (`lib/codegen_deno.ml`),
200-
`--deno-esm` / `.deno.js`. Shipped + consumer-verified (ubicity).
201-
202-
|Bun-ESM |works |Host-profiled direct AST→ES-module transpiler,
203-
`--bun-esm` / `.bun.js`. Public declarations remain importable ESM; filesystem,
204-
argument, and process operations lower through Bun's synchronous
205-
Node-compatibility surface. Host operations are resolved lazily so a generated
206-
module that only uses browser-provided externs remains browser-importable. CI
207-
compiles, checks for legacy-runtime leakage, parses, and executes filesystem,
208-
byte, argument, export, and missing-path controls under Bun.
199+
|Deno-ESM |retired |`--deno-esm` / `-o FILE.deno.js` is a hard error (E0826).
200+
The JS-host ESM target is Bun-ESM. The flag is kept so existing scripts fail
201+
loudly instead of becoming an unknown option.
202+
203+
|Bun-ESM |works |Direct AST→ES-module transpiler (`lib/codegen_deno.ml`,
204+
`codegen_bun`), `--bun-esm` / `.bun.js`. Public declarations remain importable
205+
ESM; filesystem, argument, and process operations lower through Bun's
206+
synchronous Node-compatibility surface. Host operations are resolved lazily so
207+
a generated module that only uses browser-provided externs remains
208+
browser-importable. CI compiles, checks for legacy-runtime leakage, parses,
209+
and executes filesystem, byte, argument, export, and missing-path controls
210+
under Bun. `tools/run_codegen_deno_tests.sh` (historical name) compiles
211+
`tests/codegen-deno/` with `--bun-esm`.
209212
Migration and host-contract details:
210213
link:guides/bun-esm-migration.adoc[guides/bun-esm-migration.adoc].
211214

‎docs/ECOSYSTEM.adoc‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,8 +168,8 @@ surface.
168168

169169
|`affinescript-vite` |scaffold |Build-tool integration shell.
170170

171-
|`affinescript-deno-test` |works |Smoke-test harness used by the Deno-ESM
172-
target.
171+
|`affinescript-deno-test` |historical |Smoke-test harness from the retired
172+
Deno-ESM target. JS-host ESM is Bun-ESM (`--bun-esm`).
173173

174174
|`affinescript-tea` |runtime |INT-07 (#182): real host-side TEA runtime
175175
+ run loop (`TeaApp`: load/init/dispatch/model/setScreen/run,

‎docs/EFFECTS-IMPLEMENTATION.adoc‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,18 @@ type eval_error =
7878
Handler evaluation catches `PerformEffect`, selects a matching arm, and
7979
evaluates the arm body in the handler context.
8080

81+
== JS-host ESM (Bun) — Canvas-style erasure
82+
83+
Host FFI on `--bun-esm` does **not** require `/ IO` (or `{IO}`) effect
84+
rows. `stdlib/Canvas.affine`, `stdlib/Dom.affine`, and
85+
`stdlib/Console.affine` declare ordinary `extern fn` / wrappers; the
86+
direct-ESM emitter lowers them to host calls and erases effect
87+
declarations (`// effect declaration (erased)`). Declared `/ Async` rows
88+
are the exception: they compile to `async function` so `await fetch` is
89+
legal JS. Algebraic `handle { ... }` is a compile error on this backend
90+
(Refs #555). This is the AOT-safe contract for the #56 remainder — not
91+
a second effect system.
92+
8193
== Not Yet Complete
8294

8395
[arabic]

‎docs/STDLIB-EXTERN-AUDIT.adoc‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,8 @@ producer-side type signature here and a verifier-side contract in
7070
runtime adapter all green. `Http.fetch` is the canonical example
7171
(landed via the #225 CPS line, PR1..PR3d).
7272

73-
|*Adapter-side (Deno-ESM)*
74-
|Compiles via the `--deno-esm` backend; the `extern` is realised
73+
|*Adapter-side (JS-host ESM)*
74+
|Compiles via the `--bun-esm` backend (Deno-ESM retired); the `extern` is realised
7575
by the emitted ESM importing a JS module. The "real impl" is the
7676
adapter shim in `packages/<adapter>/mod.js` (or equivalent).
7777
|Adapter package exists, the symbol is exported, and a

‎docs/bindings-roadmap.adoc‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ no further significant → AffineScript work is tractable.
7575
|*WASM-exports calling pattern* — invoke individual `exports.fn_name(args)` from a `WasmExports` value
7676
|`●` usable (Option A landed)
7777
|`stdlib/Deno.affine`
78-
|`wasmCall(exports: WasmExports, name: String, args: [Float]) -> Float` lowers to `Number(exports[name](...args))` on `--deno-esm`. AS-side surface + docstring example landed in `stdlib/Deno.affine`; round-trip exercised by `tests/codegen-deno/wasm_call.{affine,harness.mjs}` against a hand-built 41-byte wasm module exporting `add(i32, i32) -> i32`. *Option A (generic) — typed per-Zig-fn shims can layer on top per-consumer if needed.* Closes #414 via host-side #422 + AS-side this PR. *Generic export-call shipped #455* (`wasm_export_call(exports, name, args: [WasmValue]) -> WasmValue` + `WasmValue` opaque + `wv_i32`/`wv_i64`/`wv_f32`/`wv_f64` + `wv_as_int`/`wv_as_float`/`wv_kind`); end-to-end demo at `examples/wasm-exports-demo.affine` (#500) with smoke-test `tests/codegen-deno/wasm_exports_demo.{affine,harness.mjs}` exercising all four scalar kinds.
78+
|`wasmCall(exports: WasmExports, name: String, args: [Float]) -> Float` lowers to `Number(exports[name](...args))` on `--bun-esm`. AS-side surface + docstring example landed in `stdlib/Deno.affine`; round-trip exercised by `tests/codegen-deno/wasm_call.{affine,harness.mjs}` against a hand-built 41-byte wasm module exporting `add(i32, i32) -> i32`. *Option A (generic) — typed per-Zig-fn shims can layer on top per-consumer if needed.* Closes #414 via host-side #422 + AS-side this PR. *Generic export-call shipped #455* (`wasm_export_call(exports, name, args: [WasmValue]) -> WasmValue` + `WasmValue` opaque + `wv_i32`/`wv_i64`/`wv_f32`/`wv_f64` + `wv_as_int`/`wv_as_float`/`wv_kind`); end-to-end demo at `examples/wasm-exports-demo.affine` (#500) with smoke-test `tests/codegen-deno/wasm_exports_demo.{affine,harness.mjs}` exercising all four scalar kinds.
7979

8080
|6
8181
|*Phoenix Channels / WebSocket* (Socket connect/disconnect, Channel join/leave/push, presence)

‎docs/guides/bun-esm-migration.adoc‎

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,18 @@ The source filename is retained to avoid a high-risk mechanical move in the
5454
same change as the semantic migration. It does not imply that Bun output passes
5555
through or embeds the compatibility runtime.
5656

57-
The compatibility flag remains available for existing external consumers. An
58-
estate migration is complete only when its active workflows, runtime files,
59-
lockfiles, build commands, generated artefact names, examples, and current
60-
documentation have moved to Bun. Historical records may retain their original
61-
runtime names when clearly marked as history; renaming history would make it
62-
less accurate.
57+
`--deno-esm` and `-o *.deno.js` are retired: the compiler errors with E0826
58+
and points at `--bun-esm` / `.bun.js`. The flag is kept so existing scripts
59+
fail loudly instead of becoming an unknown option. An estate migration is
60+
complete only when its active workflows, runtime files, lockfiles, build
61+
commands, generated artefact names, examples, and current documentation have
62+
moved to Bun. Historical records may retain their original runtime names when
63+
clearly marked as history; renaming history would make it less accurate.
64+
65+
`affinescript-vite` remains a scaffold (not in this tree). Effect rows such
66+
as `/ IO` are not required on the JS-host ESM path: host FFI is erased at
67+
emit the same way Canvas/Dom externs are (see
68+
link:../EFFECTS-IMPLEMENTATION.adoc[EFFECTS-IMPLEMENTATION.adoc]).
6369

6470
== Verification contract
6571

0 commit comments

Comments
 (0)