bynk_emit/lib.rs
1//! Bynk's TypeScript emission, plus the per-unit build sequencing that drives
2//! it — the layer above `bynk-project` (discovery, the dependency graph),
3//! `bynk-check` (all semantic checking, R3.5), and (since the P7.12 crate
4//! carve) `bynk-ir`/`bynk-lower` (the typed IR and its `&CheckedProgram → Ir`
5//! lowering pass).
6//!
7//! `project` owns `compile_project`/`run_checks`: the two-pass sequence over
8//! a project's units — discover and parse (`bynk-project`), then resolve,
9//! type-check (`bynk-check`) and emit each unit with full visibility of what
10//! it `uses`/`consumes`. `emitter` lowers a checked program to TypeScript.
11//! Input: a project tree (or an in-memory overlay). Output: TypeScript files
12//! plus diagnostics — this crate originates none of its own (P5.5,
13//! `design/tracks/semantics-in-the-checker.md` §3.5, R10.1).
14//!
15//! Extracted from `bynkc` as slice 4 of the crate-decomposition track over
16//! `bynk-syntax` + `bynk-check`. Behaviour is unchanged; `bynkc` depends on this
17//! crate and re-exports its modules so its public API (`compile_project`,
18//! `ProjectOutput`, …) and the binary are untouched.
19
20pub mod emitter;
21pub mod project;
22
23#[cfg(test)]
24pub(crate) mod testkit;
25
26use bynk_check::{checker, resolver};
27use bynk_syntax::{CompileError, lexer, parser};
28
29/// A single-file compile that also returns the non-failing warnings produced on
30/// success — what a CLI prints (v0.89, ADR 0117). [`compile`] is the
31/// warning-discarding convenience over this.
32///
33/// Lives in `bynk-emit` (slice 7 precedent, alongside [`NODE_MAJOR_FLOOR`]) so
34/// both `bynkc` and the `bynk` driver can compile a self-contained single-file
35/// commons in-process without depending on each other; `bynkc` re-exports it so
36/// `bynkc::compile_with_warnings` and `bynkc::Compiled` are unchanged.
37pub struct Compiled {
38 pub ts: String,
39 pub warnings: Vec<CompileError>,
40}
41
42/// Compile a single Bynk source string to a TypeScript string.
43///
44/// Parses the input as a self-contained, single-file commons with no `uses`
45/// against other commons. Use [`project::compile_project`] for multi-file
46/// projects or for any source that declares `uses`. `filename` is used only for
47/// diagnostic rendering.
48pub fn compile(source: &str, filename: &str) -> Result<String, Vec<CompileError>> {
49 compile_with_warnings(source, filename).map(|c| c.ts)
50}
51
52/// The warning-preserving single-file compile behind [`compile`]. See [`Compiled`].
53pub fn compile_with_warnings(source: &str, _filename: &str) -> Result<Compiled, Vec<CompileError>> {
54 let tokens = lexer::tokenize(source).map_err(|e| vec![e])?;
55 // ADR 0117: parse-time warnings (orphan doc blocks) ride alongside the
56 // AST — they surface with the build's warnings instead of failing it.
57 let (commons, mut warnings) = parser::parse_with_warnings(&tokens, source)?;
58 // v0.20a: function types are confined to non-boundary positions — the same
59 // rule the project path applies.
60 let mut boundary_errors = Vec::new();
61 let boundary_types = bynk_check::project_model::collect_type_decls(commons.items.iter());
62 bynk_check::project_model::check_function_type_boundary_items(
63 &commons.items,
64 &boundary_types,
65 &mut boundary_errors,
66 );
67 if !boundary_errors.is_empty() {
68 return Err(boundary_errors);
69 }
70 let resolved = resolver::resolve(commons)?;
71 let typed = checker::check(resolved)?;
72 warnings.extend(typed.warnings.clone());
73 // T3.7 (R3.10): `check` already gated on error-severity diagnostics, so
74 // `typed.warnings` — the only diagnostics left riding along with it — can
75 // never contain one; `certify` re-asserts that structurally rather than
76 // trusting the caller not to skip it.
77 let program = checker::certify(typed, warnings.clone()).unwrap_or_else(|_| {
78 panic!("bynk internal error: check() already gated on error-severity diagnostics")
79 });
80 Ok(Compiled {
81 ts: emitter::emit(&program),
82 warnings,
83 })
84}
85
86/// Minimum supported Node.js **major** version for the `node` platform binding
87/// and for running Bynk's emitted TypeScript.
88///
89/// Single source of truth for the Node floor: the emitted code targets it, the
90/// `bynk` driver's `doctor` command compares a detected `node` against it, and
91/// `bynkc`'s CLI re-exports it rather than restating the number. Lives in
92/// `bynk-emit` (which emits the TS that runs on Node) so both binaries share one
93/// definition (slice 7; was a `bynkc` const before the driver dropped that dep).
94pub const NODE_MAJOR_FLOOR: u32 = 18;
95
96// `write_output`/`write_document` moved to `bynk-driver` (#1047, R2.3/
97// T0.7 residue): every caller was already at driver level, so this crate
98// never needed direct filesystem access for it — the pure move closes it
99// out of this crate's `fs_below_driver` count. See `bynk-driver::output`.