Skip to content

What Framework v0.11.4

Choose a tag to compare

@zvndev zvndev released this 05 Jul 01:04
· 174 commits to main since this release

Patch release. All 14 packages move to 0.11.4 together (fixed-group release). Three production-surfaced fixes, plus the devtools/MCP prod-leak hardening carried over from Unreleased. No API changes. 0.11.3 shipped an exports-map packaging regression that broke real consumers, so this patch is warranted for that alone.

Fixed

  • core: a reactive () => cond ? <Component/> : <element> thunk that is the direct child of an element no longer stacks copies of the component branch on repeated toggles. A component realizes to a DocumentFragment bounded by comment markers (<!--c:start--> … <!--c:end-->). The single-node "replace" fast path in reconcileInsert treated that fragment as one node and called replaceChild(fragment, current) — but inserting a fragment moves its children into the DOM and leaves the fragment empty, so the value stored as the new current no longer referenced the real nodes. The next switch away could not find/remove them, and each return appended another copy (a production dashboard stacked four empty-states). DocumentFragment values are now excluded from the fast path and flow through valuesToNodes, which flattens and tracks each child for correct removal. Distinct from the 0.11.3 thunk-in-array-child fix and the 0.11.2 component-swap fix.

  • packaging: every public package export resolves again under require() / createRequire().resolve(), not only import() — fixes a 0.11.3 regression. In 0.11.3, what-react's subpath exports were rewritten from plain strings (which resolve under every condition) to conditional objects listing only types + import, with no require/default fallback. The CJS resolver has no import condition, so createRequire(...).resolve('what-react') threw ERR_PACKAGE_PATH_NOT_EXPORTED, breaking a real consumer's vite.config + Vitest. Every public package's conditional exports entry now ends with a default condition → all 37 public subpaths across the 14 packages resolve under require() as well as import().

  • devtools-mcp: the Vite plugin no longer crashes vite dev when the optional what-devtools peer is not installed. With the peer absent, Vite's dev transform could not resolve the bootstrap's bare import … from 'what-devtools' and the dev server died. what-devtools is now a formally optional peer; the plugin probes for it up front and degrades gracefully — injects nothing + logs one clear console.info — instead of crashing dev.

  • devtools-mcp: the DevTools/MCP client can no longer leak into a production build. (PR #19) The Vite plugin now gates resolveId/load/transformIndexHtml on the resolved Vite command (not just apply: 'serve'), and the injected bootstrap wraps its side effects in if (import.meta.env.DEV) so a prod build contains zero devtools/MCP code however the plugin is wired.

Verified

  • New regression tests: reactive-thunk-branch-switch.test.js (toggle a direct-child thunk between a bare component and an element/component/null; exactly one copy after repeated cycles — fails on 0.11.3, passes now) and vite-plugin-missing-peer.test.js (no injection + one notice + no throw when the peer is unresolvable).
  • New hygiene guard in check-publish-surface.mjs exercises createRequire().resolve() for every public exports subpath across all 14 packages, so an exports map can never silently drop its CJS entry point again.
  • Full suite green (1440 tests) plus the adversarial stress suite. All release gates pass: hygiene:publish, build, check:size (within budget), test:prod, smoke:scaffold, bench:gate. createRequire().resolve('what-react') confirmed working against the published 0.11.4 package.