feat: native yoga-layout via taffy + JSON module imports — ink #348 end-to-end#5038
Merged
Conversation
added 2 commits
June 12, 2026 14:49
…led-break-from-nested-switch - perry/yoga native module: taffy-backed flexbox primitives (node tree, style setters, measure callbacks via js_native_call_value, computed layout) so the real 'yoga-layout' npm package runs natively (no WASM). A TS shim ships as the package's index.ts over these primitives. ink lays out + renders text/boxes/colors natively through it. - fix(codegen): a 'break <label>' targeting an outer labeled switch from inside a NESTED switch was resolving (via the LabeledBreak fallback) to the inner switch's exit instead of the outer's — lower_switch never registered its pending label in label_targets (only loops did). This miscompiled react-reconciler's element-type classification switch, so every Context.Provider became fiber tag 29 (invalid) and its children were dropped. Register the pending label on the switch's exit. Refs #348.
Materialize `import x from "./foo.json"` (with or without
`with { type: "json" }`) as a native module exporting the parsed JSON as
its default export, instead of skipping JSON files outright. JSON is a
syntactic subset of a JS expression, so collect_modules synthesizes
`export default <json>;` and feeds it through the normal parse → lower →
codegen path; non-JSON modules take an identical path. Validates as JSON
first for a clear error on malformed input.
Fixes ink's `<Box borderStyle="round">` crash (cli-boxes imports
`boxes.json`, which previously resolved to the empty-module sentinel and
threw `Cannot read properties of undefined (reading 'topLeft')`).
Bundles the version bump (0.5.1162) + changelog for the native yoga/taffy
backend and the labeled-break-from-nested-switch fix already on this branch.
- cargo fmt on yoga.rs / lib.rs (lint job). - Add the 14 `perry/yoga` rows to perry-api-manifest so the manifest-consistency check (#513) sees a counterpart for every NATIVE_MODULE_TABLE dispatch entry (cargo-test job). - Regenerate docs/src/api/reference.md + docs/api/perry.d.ts to match (api-docs-drift job).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Makes ink (React-based TUI) compile and render fully natively — no WASM yoga, no V8. Three independent changes under #348.
(
render(<App/>)→ positioned ANSI from a single native executable.)1. Native
perry/yogabackend over taffyyoga-layout(the WASM flexbox engine ink uses for geometry) is replaced by a native backend ontaffy0.7 (already aperry-runtimedep).crates/perry-runtime/src/yoga.rs— handle-based node store with the full FFI surface ink uses (node new/free, insert/remove/child-count, set number/edge/gap/enum, measure-func register/unregister,calculateLayout, computed box/edge getters).calculateLayoutbuilds a freshTaffyTree, wires per-node measure callbacks back into JS viajs_native_call_value, and runscompute_layout_with_measure. A GC root scanner marks live measure callbacks.crates/perry-codegen/src/lower_call/native_table/yoga.rs— 14NativeModSigrows → runtime entry points (registered innative_table/mod.rs).perry/yogaadded to the perry-namespace module list inperry-api-manifest.node_modules/yoga-layout/src/index.tsis a thin shim re-exporting the native primitives behind the real yogaNode/ConfigAPI.2. JSON module imports → native default export
import data from "./x.json"(with or withoutwith { type: "json" }) was skipped entirely during module collection, leaving the default import bound to the empty-module sentinel. ink'scli-boxes(import cliBoxes from "./boxes.json") thus resolved toundefined, and<Box borderStyle="round">threwCannot read properties of undefined (reading 'topLeft').collect_modules.rsnow materializes a JSON import as a native ESM module: validate as JSON, then compileexport default <json>;through the normal parse → lower → codegen path. Non-JSON modules take an identical code path (the change only removesis_jsonfrom a disjunction that was already false for them). Malformed JSON now fails with a clearFailed to parse JSON module …error.3. Labeled
breakfrom a nestedswitchA labeled
switchdidn't register its label as a break target, sobreak <label>;from a nested switch (react-reconciler'screateFiberFromTypeAndProps) escaped to the wrong exit — droppingContext.Providerchildren, so ink rendered nothing.switch_stmt.rsnow registers the consumed label like loops already did.Testing
root 80x5, child positioning, measure callbacks all correct.import boxes from './data.json'→boxes.round.topLeft === '╭'.borderStyle="round",flexDirectionrow/column,padding/margin/width/justifyContentall lay out via taffy and render positioned ANSI (exit 0).cargo test --bin perrygreen;cargo build --releasegreen.Depends on #5024 (function-prototype own-key tracking), already merged.
Known follow-up (separate, minor)
<Text dimColor>renders as[object Object](boolean style-modifier handling in ink's Text);color=works. Filed separately.Closes #348.