Skip to content

Fix Preact 11 support - #522

Merged
marvinhagemeister merged 6 commits into
mainfrom
fix-context-provider-detection
Aug 21, 2026
Merged

Fix Preact 11 support#522
marvinhagemeister merged 6 commits into
mainfrom
fix-context-provider-detection

Conversation

@JoviDeCroock

@JoviDeCroock JoviDeCroock commented Aug 7, 2026

Copy link
Copy Markdown
Member

Summary

  • use the VNode-based Preact 10 adapter for current Preact 11 releases
  • remove the obsolete Preact 11 adapter built for the abandoned experimental Internal node model
  • detect current context providers and core portals
  • run Signals coverage and the complete E2E suite against both Preact 10 and 11 in CI
  • declare current Preact 11 beta/RC/stable releases in the package peer range while excluding the incompatible 2022 experimental builds

Why

The existing Preact 11 adapter targets the 2022 experimental reconciler. It expects separate internal nodes, node-type flags and _internal lifecycle hooks. Current Preact 11 uses the same VNode shape and options hooks as Preact 10, so those assumptions cause most nodes to be classified as text and prevent profiling and owner tracking from working.

Sharing the working VNode adapter is both smaller and closer to Preact's current implementation than refreshing the obsolete adapter field by field.

Before this change, the Preact 11 E2E run on this branch reported 88 failures, 14 passes and 7 skips. After the change, both supported majors pass the complete suite, including restored Signals coverage and a new core portal regression test.

Validation

  • npm run format:check
  • npm run lint
  • npm run type-check
  • npm run test — 164 passed, 1 skipped
  • npm run test:e2e:10 — 109 passed, 3 skipped
  • npm run test:e2e:11 — 109 passed, 3 skipped
@JoviDeCroock JoviDeCroock changed the title Fix context provider detection without private fields Aug 21, 2026
@JoviDeCroock
JoviDeCroock marked this pull request as ready for review August 21, 2026 04:41

@upupming upupming left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks promising, Thanks!

@marvinhagemeister
marvinhagemeister merged commit 1029c67 into main Aug 21, 2026
1 check passed
@marvinhagemeister
marvinhagemeister deleted the fix-context-provider-detection branch August 21, 2026 07:26
upupming added a commit to lynx-family/lynx-stack that referenced this pull request Aug 21, 2026
Points the @lynx-js/preact-devtools override at the lynx-family/
preact-devtools#20 preview (upstream preactjs/preact-devtools#522
merged: released Preact 11 routes through the shared VNode adapter).
Verified on the Android emulator via lynx-devtool CLI: component tree,
find, and hooks inspection all work against a Preact 11 bundle.
upupming added a commit to lynx-family/preact-devtools that referenced this pull request Aug 21, 2026
* fix: detect context providers without private fields

* fix: support current Preact 11 internals

* fix: isolate Preact 11 portal bindings

* ci: forward retries to Playwright

* fix: exclude experimental Preact 11 renderer

* test: cover Preact 11 Suspense

Co-authored-by: Jovi De Croock <decroockjovi@gmail.com>
upupming added a commit to lynx-family/preact-devtools that referenced this pull request Aug 27, 2026
Brings released-Preact-11 support: the v11 line routes through the
shared VNode adapter and the obsolete experimental adapter is gone.
The fork's ReactLynx id-mapping wiring moves onto bindings.getInstance,
the browser e2e suites keep their fork skips, and peerDependencies
stays off (the fork consumes internal-preact).

Replaces the squash merge that landed the same tree without recording
upstream as a parent, which left the fork permanently 1 commit behind.
upupming added a commit to lynx-family/lynx-stack that referenced this pull request Sep 1, 2026
Migrates ReactLynx to Preact 11, consuming the published fork build:
lynx-family/internal-preact#22.

Ships as a **minor** bump of `@lynx-js/react` with one breaking change:
`useEffect` cleanups of unmounted components move from synchronous
unmount to the after-paint flush, matching React. Page destroy still
drains them synchronously, so cleanups that release native resources
keep running before the runtime goes away.

## What changed

**Dependency** (`pnpm-workspace.yaml` catalog): `preact` resolves to
`@lynx-js/internal-preact@11.0.0-rc.1-20260827072551-a63bfa4` (upstream
11.0.0-rc.1 plus all Lynx fork patches). `@lynx-js/preact-devtools`
moves to `5.0.1-20260827072047-5f77e12`, which carries upstream
preactjs/preact-devtools#522 (released-v11 adapter) alongside the fork's
Lynx wiring. No overrides and no pkg.pr.new URLs remain, so the
single-instance guard is back to its original form.

**Runtime adaptation** (`@lynx-js/react`):
- `Component._dirty`/`_force` booleans became the `_bits` bitfield
(`__g`): `render-constants.ts` now exposes
`BITS`/`COMPONENT_DIRTY`/`COMPONENT_FORCE`; renderToOpcodes,
forceRootRender and the profiling hooks use bit ops.
- Hand-rolled jsx-runtime vnodes drop the removed `__d` slot.
-
`SnapshotInstance`/`BackgroundSnapshotInstance`/`BackgroundElementTemplateInstance`
implement `remove()` (Preact 11's `removeNode` calls `node.remove()`).
- New `withSyncEffectFlush()`: Preact 11 defers passive-effect cleanups
of unmounted components to the after-paint flush (React-aligned,
preactjs/preact#4299). Background destroy is the one path with no later
turn to run them in — `callDestroyLifetimeFun` returns and native tears
the runtime down — so both destroy paths wrap their `render(null)` with
a synchronous scheduler, restoring v10's behavior. Ordinary unmounts
keep the deferred semantics.
- v11 type adjustments: `useRef` requires an initial value; compat
`Children.map` takes a context argument.

**Behavior changes surfaced by tests** (all judged
equivalent-or-better):
- Context consumers no longer double-render on provider updates
(preactjs/preact#4724): fewer `rLynxChange` flushes; patches merge into
the first flush.
- List reorders use v11's LIS diff — equivalent final state via a
different minimal move set.
- None outstanding for `<page />`: its attribute reset now runs as a
layout effect (landed separately in #3622), so it stays inside the
unmount patch as on v10.

## Test status (local)

- root `vitest`: 247 files, 2006 passed, 0 failed (includes
react/runtime 816, element-template 692, testing-library, transform,
signals, webpack, rspeedy projects)
- `rstest`: 202 files, 1847 passed, 0 failed
- `pnpm -r run test:type`: green
- `turbo api-extractor -- --local`: no API drift
- internal-preact fork suite (rc.1): 1152 passed / 186 skipped, lint +
test:ts green

## Known open items

- `useLayoutEffect` remains aliased to `useEffect` in ReactLynx, so user
effect cleanups are deferred on ordinary unmounts. Runtime code that
needs a cleanup inside the unmount commit imports `useLayoutEffect` from
`preact/hooks` directly (see `<page />`). Destroy is synchronous for
everyone.
- Web-platform e2e suites left to CI.
- CodSpeed reports a real regression on the `*-destroyBackground`
benchmarks (-5% to -17%, confirmed in Simulation mode, so not timing
noise). This is the cost of correctness: destroy now actually runs every
unmount cleanup. Before this PR's fix, Preact 11 deferred them to a
flush that never happens during destroy, so those benchmarks were
measuring a destroy that skipped the work entirely. Against the `main`
(Preact 10) baseline — which also runs cleanups synchronously — the
remaining gap is Preact 11's own unmount bookkeeping (the `errorParent`
walk in `options.unmount`, plus the queue round-trip through
`flushAfterPaintEffects`). Batching the flush to once per destroy
instead of once per unmounted component would recover part of it, at the
cost of no longer matching v10's ordering (cleanups would run after the
tree is detached rather than during the unmount walk).





<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
  - Upgraded the rendering engine to Preact 11.
  - Improved list reordering performance and consistency.
  - Added reliable node removal for background and snapshot rendering.

- **Bug Fixes**
- Prevented duplicate context-consumer renders during provider updates.
  - Improved effect cleanup timing during unmount and page destruction.
  - Ensured incomplete renders reset correctly.
- Updated developer tooling and compatibility across supported
integrations.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

3 participants