Skip to main content

Crate bynk_ts

Crate bynk_ts 

Source
Expand description

The TypeScript tree and printer (phase 7 of the compiler trajectory, design/bynk-compiler-trajectory.md). Emission produces nodes (TsProgram/TsStmt); printer::print is the only code in the compiler that writes a character (R7.3).

Invariant: this crate depends on bynk-syntax only (for bynk_syntax::span::Span, reused unchanged rather than redefined — bynk-emit’s own TypeRef/Ir machinery is not visible here, and never will be: a function taking one would not compile, the dependency isn’t present, by design). cargo tree -p bynk-ts shows bynk-syntax and nothing else. Matches the same load-bearing shape bynk-render’s own module doc states for itself (bynk-render/src/lib.rs).

What exists here today (P7.5, #1307 → P7.8, #1313). The reference’s own §7.1 sketch names four tree enums (TsStmt/TsExpr/TsType/ TsDecl) with real variants (Const, Let, Binary, Named, Class, …), but only as a variant-name list, not a field-level design. P7.5 built only TsStmt’s Verbatim escape hatch (VerbatimOrigin- tagged text, Q2, design/tracks/the-typescript-tree.md §3.2). P7.8 (#1313) adds the rest — TsExpr/TsType/TsDecl, plus real TsStmt kinds — but not the sketch’s full variant list: only what bynk-emit/src/emitter/events_fanout.rs (Arc C’s real next file — P7.8’s own accepted proposal corrected the track doc’s stale schedule, design/tracks/the-typescript-tree.md §6/§9) concretely needs, grounded against that file’s own real shape. bynk-emit still builds no TsProgram beyond Verbatim — Arc C’s own first slice is what starts converting real emission into real nodes, file by file; this crate exists so that conversion has somewhere to land, following R10.3’s own “carve prospectively, at the moment the dependency appears” precedent (bynk-strip’s own carve is the control case cited for this).

Structs§

Printed
The result of printing a TsProgram: the emitted text, and its source map — None when no checkpoint resolved, either because no statement carried a span or every span fell outside source_text (SourceMapBuilder::to_v3’s own “nothing resolves” case).
SourceMapBuilder
Accumulates source-map checkpoints during emission. Lives behind a RefCell on bynk-emit’s LowerCtx so the deep lowering chain and the declaration loop can both record without fighting the borrow checker. A sub-builder (one per spliced body) records against its local buffer, then is merged into the module builder at the splice offset.
TsClassCtor
A class’s own constructor.
TsClassField
One class field.
TsClassMethod
One class method.
TsParam
One function/method/constructor parameter.
TsProgram
A whole generated TypeScript module, as an ordered sequence of top-level statements. Vec<TsStmt>, plain — no richer container yet (P7.6’s own Artefacts { docs: BTreeMap<PathBuf, Document> } is where a project’s documents get keyed; this is one document’s own tree).
TsStmt
One statement — a Verbatim-tagged escape hatch (still constructible only via TsStmt::verbatim, per #1307’s Decision D — the verbatim_sites probe needs exactly one string to line-scan for), or, from this slice, a real structured kind. The real kinds have no such sealing: they’re normal typed constructors, not a “wrap opaque text” escape hatch, so the verbatim_sites concern that motivates verbatim’s own single-constructor discipline doesn’t apply to them.
TsSwitchCase
One case/default arm of a TsStmtKind::Switch. test: None is the default: case — every real default in workers_entry.rs prints its body directly under default: with no { } block, while every real non-default case (regardless of test) always prints a { }-blocked body.
Violation
One construct verbatim_violations found, and the line it was on.

Enums§

TsArrowBody
TsExpr::Arrow’s own body shape (#1435, Arc E slice 1) — see that field’s own doc for why this is a widened field rather than a second TsExpr variant. Expr is every real site before this slice (and the overwhelming majority after it): the arrow’s body is one expression, printed with no surrounding braces. Block is serialisation.rs’s own Float non-finite guard, the first real statement-bodied arrow anywhere in this tree — printed as a real braced block, reusing this crate’s own printer’s existing compact-statement-list renderer (render_compact_stmts, the same one TsStmtKind::InlineBlock already shares with render_branch’s own same-line if/else) rather than a third copy of that “one physical line, semicolon-separated” logic. Every real Block site today is exactly this one-line IIFE shape (an arrow with no other real use of a genuinely multi-line block body has been found); a future multi-line block-bodied arrow is a real, separate gap this variant does not yet cover.
TsBinaryOp
?? (events_fanout.rs’s env ?? {}), plus three more real operators #1321 (workers.rs) grounds: ||/&& (the Bearer-header presence checks, __authz === null || !__authz.startsWith(...) / __authz !== null && __authz.startsWith(...)) and ===/!== (pervasive throughout the file’s own tagged-result and header checks). #1323 (workers_entry.rs) grounds one more: > (the request-body- ceiling guard’s own Number(__contentLength) > <cap> — the one real site anywhere in bynk-emit that needs a relational, not equality, comparison). Not the full JS/TS operator table (Decision B’s own “extend narrowly” posture) — see the printer’s own binary_precedence (bynk-ts/src/printer.rs, private) for why a nested Binary operand’s parenthesisation needed to become precedence-aware once more than one operator existed. Arc C, step (11) (#1388) grounds one more: + (string concatenation) — the ICU-formatting cluster’s own dominant structural pattern, every literal/placeholder segment in a message template joins this way. Real JS/TS precedence (binds tighter than every comparison/logical operator this table already has) and real left-associativity ("a" + "b" + "c" needs no parens, the same way a same-operator ||/&& chain already prints flat) both matter here, not just the operator symbol itself.
TsBindingName
A binding’s own name, in either of the two shapes events_fanout.rs itself uses: a plain identifier (const subs = ...), or an object-destructuring pattern (const { events } = ...) — naming only the destructured properties themselves ({ a, b }), not the renamed ({ a: renamed }) or nested ({ a: { b } }) forms, since nothing in the grounding file needs either.
TsDecl
A top-level declaration. Import, Export, Interface, ConstDecl, and Class were events_fanout.rs’s own grounding (P7.8’s own note: “not the sketch’s Function/TypeAlias — unused in the grounding file”). #1321 (workers.rs) needed both after all — a real gap the accepted proposal’s own Framing didn’t name (its own “no other gap surfaced” checked TsStmt/TsExpr/TsType shapes, not TsDecl ones): compose.ts’s own export function compose(env: Env, …) { … } is a top-level function declaration, and (when the Worker has agents or publishes events) a type DurableObjectNamespace = { … }; fallback alias sits alongside it. Named explicitly as a deviation from the accepted proposal’s own catalogue, not silently added — both are mechanical, direct TsDecl siblings of ConstDecl/Class already here, the same “small, grounded, same class of gap” this track’s own history repeatedly found and closed (P7.8’s Assign/Continue/ TryCatch, Arc C slice 1’s Comment).
TsExpr
An expression. Only the shapes events_fanout.rs concretely uses (Decision B) — not the reference sketch’s full TsExpr list (Arrow, Cond, TemplateLit, Spread are all unused in the grounding file and deliberately not built here). Arc C slice 3 (#1321, workers.rs) adds Arrow, OptionalMember/OptionalIndex (Decision A, gaps 3/4) — this slice’s own real, grounded needs.
TsLit
A literal — the three kinds events_fanout.rs uses (a string, a number, null), plus Bool (#1323’s own real gap: workers_entry.rs’s CorsPolicy.credentials/SecurityPolicy.nosniff object-literal fields are real booleans, e.g. credentials: true, nosniff: false — nothing before this slice’s own grounding ever built one).
TsObjectEntry
One entry of a TsExpr::Object literal. Only Prop existed before #1321 (events_fanout.rs’s own grounding never needed the other three) — workers.rs’s own dominant shape (Decision A, gap 1) is a literal object whose entries are shorthand async methods (every on call/on http/… wrapper attaches this way), its local capability-deps object mixes bare shorthand names with explicit key: value pairs, and three real sites spread another object into one (gap 2, folded in here rather than as its own top-level TsExpr variant — a spread only ever appears as an object- or array-literal entry in this file, never as a standalone expression, so scoping it to entry position is the narrower correct change).
TsType
A type-position node. Named (extended with type arguments — a real gap the reference sketch left unaddressed: Record<string, Array<{...}>>/Promise<Response> both need one, and a bare Named with no type-argument slot cannot represent either), Array (extended with a readonly modifier — P7.9’s own real gap: every List/Query element type bynk-emit’s ts_type_ref*/ts_ty families build is readonly T[], not plain T[]), Object, and Fn (P7.9’s own second real gap — the query-thunk wrapper (() => readonly T[]) and a real parametered function type (a0: T0, …) => Ret both need one) — not the sketch’s Union/Intersection/Literal/TypeParam/Readonly (still unused; readonly here is a modifier on Array, not the sketch’s own separate Readonly wrapper variant).
TsTypeMember
One member of a TsType::Object structural type — a property (Prop) or a method signature (Method, #1323’s own real gap: ack(): void, retry(): void, waitUntil(promise: Promise<unknown>): void — no body, a type-position sibling to TsObjectEntry::Method, which does carry one). Method’s own parameters reuse TsParam directly (not a bare Vec<TsType> the way TsType::Fn’s anonymous, positionally-numbered parameters do) — waitUntil’s own real parameter has a real name (promise) the printed text must show, unlike Fn’s callers, none of which have one to show.
TsUnaryOp
!x (events_fanout.rs’s !Array.isArray(subs), !binding) and typeof x (#1321, workers.rs’s own secret-probe idiom: typeof __secret !== "string") — the two unary operators real content uses, not the full JS/TS table.
VerbatimOrigin
Which family of residual, not-yet-converted emission a TsStmt::verbatim statement came from. A closed enum, deliberately — “makes the ratchet a compile-time construct, not a grep” (Q2’s own settling text). Named file-by-file as Arc C actually needs them (ast_importers’s own five-file floor is the precedent for how this track names residue), not pre-populated for the whole ~19-slice Arc C schedule up front.

Functions§

print
Print program to TypeScript text. source_name/source_text register the .bynk source every statement’s own span is measured against — today always exactly one, since nothing spans two files yet; output_file names the generated file in the source map’s own file field. Only a top-level statement’s own span is recorded as a checkpoint (R7.4’s existing scope, unchanged by P7.8 — see TsStmt::span’s own doc for why a nested statement’s span isn’t recorded yet).
print_class_method
Print a single TsClassMethod on its own, at depth — the class-method sibling of print_object_entry’s own “one fragment, not a whole document” entry point. depth means the class’s own depth (matching print_object_entry’s own convention exactly), so the method itself lands at depth + 1, its own body at depth + 2. #1359’s own real need: emit_provider’s own class wrapper stays hand-written text (each method’s own body needs a per-method source-map sub-builder/ merge, and the wrapper’s own real spacing — no blank line between methods — genuinely differs from TsDecl::Class’s own “one blank line before each method” policy, events_fanout.rs’s real convention, #1317), so each real method prints through here directly into that still-hand-written wrapper. Deliberately no automatic blank-line insertion of its own — the same “caller controls spacing” contract print_object_entry already established.
print_class_method_and_merge
print_class_method’s own sibling (#1477): identical rendering, but merges any of method.body’s own direct-child nested_maps into map as they print, at the real print-time offset — no reverse-engineering the offset from the returned text afterward, the way bynk-emit’s own emit_class_method_and_merge_source_map (emitter/emit.rs) has to today. Kept as a separate function, not an added parameter on print_class_method itself, so every existing caller’s own call sites are untouched — this is a strict addition.
print_expr
Print a single TsExpr on its own — the expression-level sibling of print_type’s own “one fragment, not a whole document” entry point. Arc C, step (11) (#1388) need: emit_icu_placeholder’s own Select arm stays one opaque, hand-built block-bodied IIFE (at the time, TsExpr:: Arrow had no block-body variant at all, and extending it for that one site was rejected as disproportionate) — its own arm VALUES, emit_sub_message’s now-real TsExpr results, still need stringifying back into that opaque host text. #1435 (Arc E slice 1) later added TsArrowBody::Block for a genuinely different real site (serialisation.rs’s Float guard); emit_icu_placeholder’s own Select arm was not reconverted along with it — out of that slice’s own scope — so this call site’s opaque text stays exactly as it was. No source-map/buffer machinery, matching print_type/print_stmt’s own scope exactly.
print_object_entry
Print a single TsObjectEntry on its own, at depth — the object-entry sibling of print_stmt’s own “one fragment, not a whole document” entry point, depth meaning the SAME thing it does for this crate’s own internal multi-line-object renderer: the object’s own depth, so the entry itself lands one level deeper, matching an object built by that renderer exactly. #1337’s own real need: emit_attached_methods (a shared helper spliced into emit_refined_type/emit_record_type/ emit_sum_type’s own still-unconverted &mut String buffers) now returns Vec<TsObjectEntry> instead of writing text directly — each caller renders the returned entries one at a time through this, the same P7.9/#1333 “keep the caller’s own signature, print just the fragment” pattern applied to an object-entry-shaped fragment instead of a whole statement or type.
print_object_entry_and_merge
print_object_entry’s own sibling (#1477), the TsObjectEntry::Method counterpart to print_class_method_and_merge/print_stmt_and_merge: identical rendering, but merges a Method entry’s own body nested_map into map as it prints, at the real print-time offset. Kept separate from print_object_entry itself so every existing caller’s own call sites are untouched.
print_stmt
Print a single TsStmt on its own, at depth — the statement-level sibling of print_type’s own “one fragment, not a whole document” entry point. bynk-emit‘s own #1333 need (emit_doc_block, a shared helper spliced into ~14 still-unconverted callers’ own buffers) wants one statement’s own printed text at a caller-supplied depth, not a whole TsProgram — no source-map/buffer machinery, matching print_type’s own scope exactly, and reusing render_stmt’s own exhaustive per-kind dispatch (this module’s own private renderer) rather than a second copy. Review of #1402: a TsStmtKind::Raw nested inside a Switch case’s own body (emit_stub_rhs’s own ReturnsEach dispatch, Arc C slice 33, tests_emit.rs slice C) carries the identical “no indent of its own, pre-indented at a fixed absolute depth” hazard render_class_method’s and render_multiline_object_entry’s own debug_assert!s already guard — stmt_contains_raw already recurses into Switch cases, so the same check applies here, this fragment entry point’s own first Raw-bearing Switch caller. A bare Raw passed directly as stmt itself is exempt (not a false negative — render_stmt’s own Raw arm never reads depth at all, so calling print_stmt on a bare Raw is safe at any depth, the established print_stmt_renders_raw_text_verbatim_with_no_ added_indent_or_punctuation contract below): only a Raw nested inside a depth-using wrapper (like this Switch case) is the real hazard.
print_stmt_and_merge
print_stmt’s own sibling (#1477), the TsDecl::Function-body counterpart to print_class_method_and_merge: identical rendering, but merges any nested_map stmt carries — on stmt itself, or on anything nested inside it (a function’s body, a class’s constructor/ methods, …) — into map as it prints, at the real print-time offset — the same offset bynk-emit’s own emit_free_fn (emitter/emit.rs) used to recover by exact arithmetic over the returned text, guarded by a debug_assert!, before #1480 converted it to set nested_map directly instead. Kept separate from print_stmt itself so every existing caller’s own call sites are untouched. source_id is map’s own registered source this statement’s content belongs to (see this crate’s own private MergeTarget’s own doc for why this can’t just be hardcoded).
print_type
Print a single TsType on its own — the real callers this closes R7.2 for (bynk-emit’s ts_type_ref*/ts_ty families, P7.9, #1315) each want one type fragment to interpolate into a larger, still-hand-built line (a field’s own type annotation, a parameter list, …), not a whole TsProgram. No source-map/buffer machinery — print() owns that for a whole document; this is the printer’s other, narrower entry point, sharing the same internal recursion rather than a second copy.
verbatim_violations
Scan text (a Verbatim statement’s own wrapped TypeScript) for every line matching one of the six banned constructs. Order of the checks within a line matters only for which construct label a line already matching two patterns gets — real emitted lines don’t do that in practice, so the first match wins and the rest of that line isn’t checked further.