You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Tracks the next set of work after PR #359 (issue #348 Phase A — native CJS support for compilePackages) lands. CJS unlocked the React-class blocker; this issue captures the remaining gaps between today (46/67 modules native on the ink sandbox) and interactive ink working end-to-end. Each sub-item below is small enough to be its own issue if anyone wants to pick one up — file as separate issues if scope justifies, or fold into umbrella PRs.
1. Cross-module CJS-call return-value typeof bug
The smallest concrete bug I observed during #348 verification. The repro:
createContext itself is typeof === "function" correctly. The bug is in how Perry handles the return value of a cross-module call into a CJS-wrapped function. Likely candidates: the wrapped export export const createContext = _cjs.createContext resolves at codegen to a function reference rather than a callable that returns a value, so the return value inherits the function's typeof. Or the NaN-box tag on the return value isn't being decoded correctly when the call goes through the IIFE's bound exports.
Acceptance: the three-line repro above prints object matching Node.
2. process import resolution from inside node_modules
Warning: Could not resolve import 'process' from render.js
Warning: Could not resolve import 'process' from ink.js
Warning: Could not resolve import 'process' from reconciler.js
Warning: Could not resolve import 'process' from App.js
... (8 sites in ink alone)
process.argv / process.env / process.stdout work fine when accessed as the implicit global from user code. But ink's modules do import process from "node:process" (or the equivalent bare specifier import process from "process") explicitly. Perry's stdlib already implements the process surface; it just needs the import path to map to it from inside compilePackages targets.
Acceptance: the eight Could not resolve import 'process' warnings disappear from the ink sandbox compile.
3. Add ink's transitive deps to compilePackages
After #348 lands, the ink sandbox compile shows 46/67 modules native, 21 still on V8 fallback. The 21 are ink's transitive deps that aren't in the user's compilePackages list:
chalk (ANSI colors — pure ESM, should be a clean port)
ws (websocket — odd dep for ink; probably for devtools panel)
Two paths:
(a) Document a package.json recipe — "to use ink, add these N packages to compilePackages". Manual but works today.
(b) Auto-include transitive deps of compilePackages entries. More magic, but matches the user expectation that opting one package into native compile pulls its dep tree along. Open question: should this be opt-in (compilePackagesTransitive: true) or default? Default opens up more failure modes; opt-in is safer.
The hardest dep here is yoga-layout — it ships C++ via WASM bindings (yoga-wasm-web) or native bindings. Either path needs separate evaluation:
Compile the JS port of yoga via compilePackages (does one exist that's pure JS?)
Link libyoga natively (cross-compile story for every Perry target)
Implement a Perry-native flexbox layout engine (largest, lowest leverage)
Acceptance: the ink sandbox compile reports Found N module(s): N native, 0 JavaScript for an interactive non-trivial ink program (counter component).
4. Dynamic import() for ink's devtools
Warning: Dynamic import('./devtools.js') not fully supported, returning undefined
ink does runtime import(\"./devtools.js\") to lazy-load its devtools panel. Two options:
(a) Wrap the call in try/catch + treat undefined as "devtools off" (works today, lossy).
(b) Wire dynamic-import support in the native compiler — non-trivial but unblocks any package using lazy-loading.
The (a) workaround is fine for #348's MVP; (b) is its own issue.
Acceptance: the warning disappears, OR ink runs without it firing.
I didn't fully classify these in the #348 scoping pass — possibly downstream symptoms of React not loading (which is now fixed by #359), or independent destructuring issues. Worth re-running the ink compile post-#359-merge and seeing which warnings remain. If they're gone, close. If they persist, file individually.
Acceptance: rerun ink compile post-#359, decide per-warning whether each is a real gap or downstream noise.
6. process.env.NODE_ENV static branch evaluation
#348's CJS wrap hoists both branches of if (process.env.NODE_ENV === 'production') { module.exports = require('./X') } else { module.exports = require('./Y') } as ESM imports. Both files compile, only one runs at module init — wasteful but correct. Cleaning this up would let react.development.js (the heavier branch) skip native compilation entirely when building production binaries.
Conditional source-level constant folding before the wrap, or a lazy import() fallback with compile-time env resolution. Lower priority than 1–4.
Acceptance: in a Perry-compiled production binary, react.development.js is not in the Found N module(s) list.
Hard dependency for interactive ink. Already filed as #347, called out here for traceability. Until #347 ships at least its Phase 1 (line-buffered readline) and Phase 2 (raw-mode + keypress events), useInput / useApp can't function and ink is render-only.
End-to-end verification → counter + todo list ink examples compile, run, match Node.
Phase B definition of done: ink's useInput example compiles to a single-file native binary, runs interactively (keystrokes increment/decrement a counter), and matches Node modulo terminal cursor timing.
Tracks the next set of work after PR #359 (issue #348 Phase A — native CJS support for
compilePackages) lands. CJS unlocked the React-class blocker; this issue captures the remaining gaps between today (46/67 modules native on the ink sandbox) and interactive ink working end-to-end. Each sub-item below is small enough to be its own issue if anyone wants to pick one up — file as separate issues if scope justifies, or fold into umbrella PRs.1. Cross-module CJS-call return-value typeof bug
The smallest concrete bug I observed during #348 verification. The repro:
object(correct —createContextreturns{ Provider, Consumer, $$typeof, ... })function(wrong)createContextitself istypeof === "function"correctly. The bug is in how Perry handles the return value of a cross-module call into a CJS-wrapped function. Likely candidates: the wrapped exportexport const createContext = _cjs.createContextresolves at codegen to a function reference rather than a callable that returns a value, so the return value inherits the function's typeof. Or the NaN-box tag on the return value isn't being decoded correctly when the call goes through the IIFE's bound exports.Acceptance: the three-line repro above prints
objectmatching Node.2.
processimport resolution from inside node_modulesSurfaced repeatedly in #348's scoping pass:
process.argv/process.env/process.stdoutwork fine when accessed as the implicit global from user code. But ink's modules doimport process from "node:process"(or the equivalent bare specifierimport process from "process") explicitly. Perry's stdlib already implements theprocesssurface; it just needs the import path to map to it from insidecompilePackagestargets.Acceptance: the eight
Could not resolve import 'process'warnings disappear from the ink sandbox compile.3. Add ink's transitive deps to
compilePackagesAfter #348 lands, the ink sandbox compile shows 46/67 modules native, 21 still on V8 fallback. The 21 are ink's transitive deps that aren't in the user's
compilePackageslist:chalk(ANSI colors — pure ESM, should be a clean port)scheduler(React's scheduler primitives — CJS, should benefit from Compileink(React-based TUI framework) end-to-end viaperry.compilePackages#348)react-reconciler(the React fiber reconciler that ink uses — CJS, large surface)yoga-layout(flexbox engine —~3.2.1, native-binding-or-WASM)cli-cursor/cli-truncate/cli-boxes/slice-ansi/string-width/wrap-ansi/widest-line/ansi-escapes/ansi-styles/indent-string/auto-bind/signal-exit/stack-utils/code-excerpt/is-in-ci/patch-console/es-toolkit/type-fest/@alcalzone/ansi-tokenizews(websocket — odd dep for ink; probably for devtools panel)Two paths:
package.jsonrecipe — "to use ink, add these N packages tocompilePackages". Manual but works today.compilePackagesentries. More magic, but matches the user expectation that opting one package into native compile pulls its dep tree along. Open question: should this be opt-in (compilePackagesTransitive: true) or default? Default opens up more failure modes; opt-in is safer.The hardest dep here is yoga-layout — it ships C++ via WASM bindings (
yoga-wasm-web) or native bindings. Either path needs separate evaluation:compilePackages(does one exist that's pure JS?)libyoganatively (cross-compile story for every Perry target)Acceptance: the ink sandbox compile reports
Found N module(s): N native, 0 JavaScriptfor an interactive non-trivial ink program (counter component).4. Dynamic
import()for ink's devtoolsink does runtime
import(\"./devtools.js\")to lazy-load its devtools panel. Two options:The (a) workaround is fine for #348's MVP; (b) is its own issue.
Acceptance: the warning disappears, OR ink runs without it firing.
5. Bare-identifier downstream noise
I didn't fully classify these in the #348 scoping pass — possibly downstream symptoms of React not loading (which is now fixed by #359), or independent destructuring issues. Worth re-running the ink compile post-#359-merge and seeing which warnings remain. If they're gone, close. If they persist, file individually.
Acceptance: rerun ink compile post-#359, decide per-warning whether each is a real gap or downstream noise.
6.
process.env.NODE_ENVstatic branch evaluation#348's CJS wrap hoists both branches of
if (process.env.NODE_ENV === 'production') { module.exports = require('./X') } else { module.exports = require('./Y') }as ESM imports. Both files compile, only one runs at module init — wasteful but correct. Cleaning this up would letreact.development.js(the heavier branch) skip native compilation entirely when building production binaries.Conditional source-level constant folding before the wrap, or a lazy
import()fallback with compile-time env resolution. Lower priority than 1–4.Acceptance: in a Perry-compiled production binary,
react.development.jsis not in theFound N module(s)list.7. #347 — TUI primitives (raw stdin, readline, setRawMode)
Hard dependency for interactive ink. Already filed as #347, called out here for traceability. Until #347 ships at least its Phase 1 (line-buffered readline) and Phase 2 (raw-mode + keypress events),
useInput/useAppcan't function and ink is render-only.Order of operations
processimport → disappears 8 warnings, low-risk.ink(React-based TUI framework) end-to-end viaperry.compilePackages#348.Phase B definition of done: ink's
useInputexample compiles to a single-file native binary, runs interactively (keystrokes increment/decrement a counter), and matches Node modulo terminal cursor timing.