feat(react/runtime): delay the timing of runOnMainThread()#1667
feat(react/runtime): delay the timing of runOnMainThread()#1667hzy merged 1 commit intolynx-family:mainfrom
runOnMainThread()#1667Conversation
🦋 Changeset detectedLatest commit: cf82fa7 The changes in this PR will be included in the next version bump. This PR includes changesets to release 3 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
📝 WalkthroughWalkthroughImplements delayed execution for runOnMainThread during initial render/hydration, plumbing delayed tasks through patch payloads and executing them on the main thread via a new runtime API. Updates listeners, bindings, and global interfaces; adds tests reflecting new behavior; updates peer dependency range; adds changesets. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Pre-merge checks (2 passed, 1 warning)❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
Poem
Tip 👮 Agentic pre-merge checks are now available in preview!Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.
Please see the documentation for more information. Example: reviews:
pre_merge_checks:
custom_checks:
- name: "Undocumented Breaking Changes"
mode: "warning"
instructions: |
Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).Please share your feedback with us on this Discord post. ✨ Finishing touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
4a80863 to
2fd3a1f
Compare
React Example#5093 Bundle Size — 238.9KiB (+0.29%).cf82fa7(current) vs 69b3ae0 main#5090(baseline) Bundle metrics
Bundle size by type
Bundle analysis report Branch Yradex:mts/run-on-mt/x Project dashboard Generated by RelativeCI Documentation Report issue |
Web Explorer#5086 Bundle Size — 362.79KiB (0%).cf82fa7(current) vs 69b3ae0 main#5083(baseline) Bundle metrics
Bundle size by type
|
| Current #5086 |
Baseline #5083 |
|
|---|---|---|
236.88KiB |
236.88KiB |
|
94.02KiB |
94.02KiB |
|
31.89KiB |
31.89KiB |
Bundle analysis report Branch Yradex:mts/run-on-mt/x Project dashboard
Generated by RelativeCI Documentation Report issue
2fd3a1f to
af63eb6
Compare
CodSpeed Performance ReportMerging #1667 will not alter performanceComparing Summary
|
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/react/runtime/src/lifecycle/patch/commit.ts (1)
140-145: Don’t drop queued main-thread tasks when there’s no snapshot patch.If runOnMainThread is called during render but diff yields no snapshotPatch, the early return skips sending delayed tasks, potentially stalling them indefinitely.
Apply this diff to gate the early return on an empty delayed queue:
- if (!snapshotPatch && workletRefInitValuePatch.length === 0) { + if (!snapshotPatch && workletRefInitValuePatch.length === 0 && delayedRunOnMainThreadData.length === 0) { // before hydration, skip patch applyQueuedRefs(); originalPreactCommit?.(vnode, commitQueue); return; }Add a follow-up test to cover “no snapshot patch but delayed tasks exist”.
🧹 Nitpick comments (11)
.changeset/lemon-streets-watch.md (1)
5-9: Clarify behavior change and minimum SDK in notes.Add an explicit note that this requires Lynx SDK >= 2.14 and that initial-render calls are deferred (queued and drained post-hydration), plus a brief migration note.
Apply this diff:
fix: Delay execution of `runOnMainThread()` during initial render When called during the initial render, `runOnMainThread()` would execute before the `main-thread:ref` was hydrated, causing it to be incorrectly set to null. -This change delays the function's execution to ensure the ref is available and correctly assigned. +This change delays the function's execution to ensure the ref is available and correctly assigned. + +Notes: +- Requires Lynx SDK >= 2.14. +- Calls made during initial render are queued and executed after hydration completes; ordering is preserved. +- Introduces an internal main-thread runner (_runRunOnMainThreadTask) used by the runtime to drain queued tasks.packages/react/runtime/__test__/worklet/runOnMainThread.test.jsx (3)
79-97: Avoid brittle inline snapshot for event payload; assert structure instead.Inline-snapshot hardcodes resolveId=1 and the full JSON string, which is fragile. Parse and assert shape.
- expect(lynx.getCoreContext().dispatchEvent.mock.calls).toMatchInlineSnapshot(` - [ - [ - { - "data": "{"worklet":{"_wkltId":"835d:450ef:2"},"params":[1,["args"]],"resolveId":1}", - "type": "Lynx.Worklet.runWorkletCtx", - }, - ], - ] - `); + const evt = lynx.getCoreContext().dispatchEvent.mock.calls[0][0]; + expect(evt.type).toBe(WorkletEvents.runWorkletCtx); + const payload = JSON.parse(evt.data); + expect(payload.worklet).toEqual(worklet); + expect(payload.params).toEqual([1, ['args']]); + expect(typeof payload.resolveId).toBe('number');
138-222: Prefer intent-focused assertions over full MTFQueue snapshots.The long inline snapshot couples the test to internal shapes. Assert API order (and select critical fields) to reduce churn.
- { - expect(MTFQueue).toMatchInlineSnapshot(` - [ - { "api": "runWorklet", ... }, - { "api": "setShouldFlush", "value": false }, - { "api": "_runRunOnMainThreadTask", "args": [], "worklet": "MTF_during_render" }, - { "api": "_runRunOnMainThreadTask", "args": [], "worklet": "MTF_after_render" }, - { "api": "setShouldFlush", "value": true }, - { "api": "__FlushElementTree" }, - ] - `); - expect(lynx.getCoreContext().dispatchEvent.mock.calls).toMatchInlineSnapshot(`[]`); - } + { + expect(MTFQueue.map(i => i.api)).toEqual([ + 'runWorklet', + 'setShouldFlush', + '_runRunOnMainThreadTask', + '_runRunOnMainThreadTask', + 'setShouldFlush', + '__FlushElementTree', + ]); + expect(MTFQueue[0].worklet?._wkltId).toBe('MTRef'); // ensure ref hydration happened + expect(lynx.getCoreContext().dispatchEvent).not.toHaveBeenCalled(); + }
224-322: Apply the same snapshot simplification for update-phase test.Mirror the compact order assertions; strengthens the signal while avoiding brittle structure comparisons.
- { - expect(MTFQueue).toMatchInlineSnapshot(` - [ - { "api": "runWorklet", ... }, - { "api": "setShouldFlush", "value": false }, - { "api": "_runRunOnMainThreadTask", "args": [], "worklet": "MTF_during_render" }, - { "api": "setShouldFlush", "value": true }, - { "api": "__FlushElementTree" }, - ] - `); - expect(lynx.getCoreContext().dispatchEvent.mock.calls).toMatchInlineSnapshot(`[]`); - } + { + expect(MTFQueue.map(i => i.api)).toEqual([ + 'runWorklet', + 'setShouldFlush', + '_runRunOnMainThreadTask', + 'setShouldFlush', + '__FlushElementTree', + ]); + expect(MTFQueue[0].worklet?._wkltId).toBe('MTRef'); + expect(lynx.getCoreContext().dispatchEvent).not.toHaveBeenCalled(); + }packages/react/worklet-runtime/src/workletRuntime.ts (1)
15-24: Minor naming nit: consider runMainThreadTask.runRunOnMainThreadTask is a mouthful; runMainThreadTask communicates intent without repetition. Renaming is optional and can be deferred.
packages/react/runtime/src/lynx/tt.ts (1)
118-126: Avoid duplicated “drain delayed data into patch” logic across files.Both tt.ts and commit.ts implement the same drain-and-attach flow. Consider a small helper in ../worklet/runOnMainThread.ts, e.g., drainDelayedMainThreadDataInto(patchList), to centralize behavior and future-proof ordering.
packages/react/worklet-runtime/__test__/runOnMainThread.test.js (2)
22-24: Tighten teardown to avoid cross-test leakage.Also reset globals created by initWorklet/registration to keep tests hermetic.
Apply this diff:
afterEach(() => { delete globalThis.lynxWorkletImpl; + delete globalThis.lynx; + delete globalThis.runWorklet; + delete globalThis.registerWorklet; });
34-45: Consider adding an error-path assertion.runRunOnMainThreadTask always dispatches a FunctionCallRet even on throw. Add a test with a throwing worklet to lock current semantics (until TODO is addressed).
packages/react/runtime/src/lifecycle/patch/commit.ts (1)
186-209: Payload size note.Including delayedRunOnMainThreadData in the JSON payload can grow frames; if needed later, consider sending only worklet ids and minimal params.
packages/react/worklet-runtime/src/bindings/bindings.ts (1)
82-83: Minor naming nit.runRunOnMainThreadTask reads a bit repetitive; runTaskOnMainThread would be shorter. Non-blocking.
packages/react/worklet-runtime/src/runOnMainThread.ts (1)
7-21: Always-dispatch pattern is pragmatic; consider explicit error channel.Currently errors result in returnValue being undefined but still “resolve”. When you tackle the TODO, emit an error field or a separate reject event to allow background to reject the promise.
Example (shape-only, adjust types/consumers before landing):
export function runRunOnMainThreadTask(task: Worklet, params: ClosureValueType[], resolveId: number): void { - let returnValue; + let returnValue, error: unknown = undefined; try { returnValue = runWorklet(task, params); + } catch (e) { + error = e instanceof Error ? { message: e.message, name: e.name } : String(e); } finally { lynx.getJSContext().dispatchEvent({ type: WorkletEvents.FunctionCallRet, data: JSON.stringify({ resolveId, - returnValue, + returnValue, + error, } as RunWorkletCtxRetData), }); } }
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (13)
.changeset/lemon-streets-watch.md(1 hunks)packages/react/runtime/__test__/worklet/runOnMainThread.test.js(0 hunks)packages/react/runtime/__test__/worklet/runOnMainThread.test.jsx(1 hunks)packages/react/runtime/src/lifecycle/patch/commit.ts(4 hunks)packages/react/runtime/src/lifecycle/patch/updateMainThread.ts(4 hunks)packages/react/runtime/src/lynx/tt.ts(2 hunks)packages/react/runtime/src/worklet/runOnMainThread.ts(2 hunks)packages/react/worklet-runtime/__test__/runOnMainThread.test.js(1 hunks)packages/react/worklet-runtime/src/bindings/bindings.ts(1 hunks)packages/react/worklet-runtime/src/global.ts(1 hunks)packages/react/worklet-runtime/src/listeners.ts(2 hunks)packages/react/worklet-runtime/src/runOnMainThread.ts(1 hunks)packages/react/worklet-runtime/src/workletRuntime.ts(2 hunks)
💤 Files with no reviewable changes (1)
- packages/react/runtime/test/worklet/runOnMainThread.test.js
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-12T16:09:32.413Z
Learnt from: colinaaa
PR: lynx-family/lynx-stack#1497
File: packages/react/transform/tests/__swc_snapshots__/src/swc_plugin_snapshot/mod.rs/basic_full_static.js:9-10
Timestamp: 2025-08-12T16:09:32.413Z
Learning: In the Lynx stack, functions prefixed with `__` that are called in transformed code may be injected globally by the Lynx Engine at runtime rather than exported from the React runtime package. For example, `__CreateFrame` is injected globally by the Lynx Engine, not exported from lynx-js/react.
Applied to files:
packages/react/worklet-runtime/src/global.ts
🧬 Code graph analysis (11)
packages/react/worklet-runtime/src/global.ts (1)
packages/react/worklet-runtime/src/bindings/types.ts (2)
Worklet(41-52)ClosureValueType(28-39)
packages/react/worklet-runtime/src/bindings/bindings.ts (2)
packages/react/worklet-runtime/src/runOnMainThread.ts (1)
runRunOnMainThreadTask(7-21)packages/react/worklet-runtime/src/bindings/types.ts (2)
Worklet(41-52)ClosureValueType(28-39)
packages/react/runtime/src/lifecycle/patch/commit.ts (1)
packages/react/runtime/src/worklet/runOnMainThread.ts (2)
delayedRunOnMainThreadData(13-13)takeDelayedRunOnMainThreadData(15-19)
packages/react/worklet-runtime/src/runOnMainThread.ts (1)
packages/react/worklet-runtime/src/bindings/types.ts (2)
Worklet(41-52)ClosureValueType(28-39)
packages/react/runtime/__test__/worklet/runOnMainThread.test.jsx (6)
packages/react/runtime/src/worklet/functionality.ts (1)
clearConfigCacheForTesting(29-29)packages/react/runtime/__test__/utils/envManager.ts (1)
globalEnvManager(86-86)packages/react/runtime/src/lifecycle/patch/commit.ts (1)
replaceCommitHook(246-246)packages/react/runtime/src/worklet/destroy.ts (1)
destroyWorklet(9-16)packages/react/runtime/src/lifecycle/patch/snapshotPatch.ts (1)
initGlobalSnapshotPatch(65-67)packages/react/runtime/src/worklet/runOnMainThread.ts (1)
runOnMainThread(39-66)
packages/react/worklet-runtime/__test__/runOnMainThread.test.js (2)
packages/react/worklet-runtime/src/workletRuntime.ts (1)
initWorklet(171-171)packages/react/worklet-runtime/src/runOnMainThread.ts (1)
runRunOnMainThreadTask(7-21)
packages/react/worklet-runtime/src/workletRuntime.ts (2)
packages/react/worklet-runtime/src/bindings/bindings.ts (1)
runRunOnMainThreadTask(82-82)packages/react/worklet-runtime/src/runOnMainThread.ts (1)
runRunOnMainThreadTask(7-21)
packages/react/runtime/src/lynx/tt.ts (1)
packages/react/runtime/src/worklet/runOnMainThread.ts (2)
delayedRunOnMainThreadData(13-13)takeDelayedRunOnMainThreadData(15-19)
packages/react/runtime/src/worklet/runOnMainThread.ts (5)
packages/react/worklet-runtime/src/bindings/index.ts (2)
RunWorkletCtxData(12-12)WorkletEvents(12-12)packages/react/worklet-runtime/src/bindings/events.ts (2)
RunWorkletCtxData(29-29)WorkletEvents(29-29)packages/react/worklet-runtime/src/bindings/types.ts (1)
Worklet(41-52)packages/react/runtime/src/lifecycle/patch/snapshotPatch.ts (1)
__globalSnapshotPatch(53-53)packages/react/runtime/src/lifecycle/isRendering.ts (1)
isRendering(10-10)
packages/react/worklet-runtime/src/listeners.ts (2)
packages/react/worklet-runtime/src/runOnMainThread.ts (1)
runRunOnMainThreadTask(7-21)packages/react/worklet-runtime/src/bindings/types.ts (1)
ClosureValueType(28-39)
packages/react/runtime/src/lifecycle/patch/updateMainThread.ts (3)
packages/react/runtime/src/worklet/functionality.ts (1)
isMtsEnabled(29-29)packages/react/worklet-runtime/src/runOnMainThread.ts (1)
runRunOnMainThreadTask(7-21)packages/react/worklet-runtime/src/bindings/types.ts (1)
ClosureValueType(28-39)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: benchmark / nodejs-benchmark
🔇 Additional comments (19)
packages/react/runtime/__test__/worklet/runOnMainThread.test.jsx (3)
116-124: LGTM: main-thread guard.The error branch and message are precise and match the runtime checks.
126-136: LGTM: SDK gating.Version check and error path are covered; beforeEach resets the SDK version back to 2.14.
142-146: No import required—renderPageis injected globally by the test harness viainjectCalledByNative.packages/react/worklet-runtime/src/workletRuntime.ts (2)
11-11: Good: explicit import for the main-thread runner.Brings the runner into the runtime module for controlled exposure.
15-24: Type already includes_runRunOnMainThreadTask. TheLynxWorkletImpldefinition inpackages/react/worklet-runtime/src/global.tsalready declares this property, so no additional typing updates are needed.packages/react/runtime/src/lynx/tt.ts (2)
25-25: Import looks correct; live-binding check is safe.Using the live import to peek queue length before draining is fine here.
124-126: Good: piggyback delayed runOnMainThread data onto the hydration patch.This guarantees queued tasks are flushed right after firstScreen commit.
packages/react/worklet-runtime/__test__/runOnMainThread.test.js (1)
9-20: Test bootstrapping is minimal and clear.initWorklet + JSContext dispatch spy is sufficient for this unit.
packages/react/runtime/src/lifecycle/patch/commit.ts (2)
24-25: Type import aligns with new PatchList field.No issues with the public surface expansion here.
66-70: Verify native/Lepus patch parser compatibility
Ensure the native (Lepus) patch payload parser either ignores the newdelayedRunOnMainThreadDatafield or has been updated to consume it to avoid rollout mismatches.packages/react/worklet-runtime/src/bindings/bindings.ts (1)
66-74: Binding is correct and safely optional-chained.The pass-through to global impl is clean.
packages/react/worklet-runtime/src/global.ts (1)
21-21: Global surface addition looks good.Signature matches the binding and runtime implementation.
packages/react/runtime/src/worklet/runOnMainThread.ts (3)
13-19: Queue drain API is clear and side-effect-free.takeDelayedRunOnMainThreadData() resets the queue predictably.
50-59: Delay condition is correct.Queuing when __globalSnapshotPatch is undefined or during render avoids pre-hydration dispatch issues.
60-63: Verify worklet payload and JSON-safe params
- Confirm runRunOnMainThreadTask only relies on worklet._wkltId (not the full object)
- Document or pre-normalize params so JSON.stringify can’t fail (only JSON-serializable values or IDs/handles)
packages/react/runtime/src/lifecycle/patch/updateMainThread.ts (2)
5-10: Imports look correct and scoped to bindings/features added in this PR.No issues.
Also applies to: 20-20
45-45: resolveId is correctly typed as number end-to-end. Verified thatRunWorkletCtxData.resolveIdis declared as anumberin the worklet-runtime bindings and is preserved throughJSON.stringify/JSON.parsein the patch update (no string coercion). No local casting is necessary.packages/react/worklet-runtime/src/listeners.ts (2)
5-7: Imports update aligns with the new delegation path.Looks good.
16-18: resolveId type and payload are correct:RunWorkletCtxData.resolveIdis declared asnumberand thedispatchEventcall sends it viaJSON.stringify(data), so it’s parsed back as a numeric value.
af63eb6 to
8b42f4c
Compare
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (2)
packages/react/worklet-runtime/__test__/runOnMainThread.test.js (2)
36-45: Avoid brittle inline snapshot; assert fields directly.Inline snapshots here are fragile (string escaping of JSON can drift). Prefer explicit assertions on type and decoded payload.
Apply this diff:
- expect(globalThis.lynx.getJSContext().dispatchEvent.mock.calls).toMatchInlineSnapshot(` - [ - [ - { - "data": "{"resolveId":10,"returnValue":"ret"}", - "type": "Lynx.Worklet.FunctionCallRet", - }, - ], - ] - `); + const calls = globalThis.lynx.getJSContext().dispatchEvent.mock.calls; + expect(calls).toHaveLength(1); + expect(calls[0][0].type).toBe('Lynx.Worklet.FunctionCallRet'); + expect(JSON.parse(calls[0][0].data)).toEqual({ resolveId: 10, returnValue: 'ret' });
22-24: Tidy up globals between tests.Clean up
lynxandSystemInfoas well to avoid hidden cross-test coupling.Apply this diff:
afterEach(() => { delete globalThis.lynxWorkletImpl; + delete globalThis.lynx; + delete globalThis.SystemInfo; });
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (16)
.changeset/lemon-streets-watch.md(1 hunks).changeset/smooth-dragons-smash.md(1 hunks)packages/react/runtime/__test__/worklet/runOnMainThread.test.js(0 hunks)packages/react/runtime/__test__/worklet/runOnMainThread.test.jsx(1 hunks)packages/react/runtime/src/lifecycle/patch/commit.ts(4 hunks)packages/react/runtime/src/lifecycle/patch/updateMainThread.ts(4 hunks)packages/react/runtime/src/lynx/tt.ts(2 hunks)packages/react/runtime/src/worklet/runOnMainThread.ts(2 hunks)packages/react/worklet-runtime/__test__/runOnMainThread.test.js(1 hunks)packages/react/worklet-runtime/src/bindings/bindings.ts(1 hunks)packages/react/worklet-runtime/src/global.ts(1 hunks)packages/react/worklet-runtime/src/listeners.ts(2 hunks)packages/react/worklet-runtime/src/runOnMainThread.ts(1 hunks)packages/react/worklet-runtime/src/workletRuntime.ts(2 hunks)packages/rspeedy/plugin-react/package.json(1 hunks)packages/webpack/react-webpack-plugin/package.json(1 hunks)
💤 Files with no reviewable changes (1)
- packages/react/runtime/test/worklet/runOnMainThread.test.js
✅ Files skipped from review due to trivial changes (1)
- .changeset/smooth-dragons-smash.md
🚧 Files skipped from review as they are similar to previous changes (10)
- packages/react/runtime/src/lifecycle/patch/updateMainThread.ts
- .changeset/lemon-streets-watch.md
- packages/react/worklet-runtime/src/bindings/bindings.ts
- packages/react/runtime/src/worklet/runOnMainThread.ts
- packages/react/runtime/src/lynx/tt.ts
- packages/react/worklet-runtime/src/workletRuntime.ts
- packages/react/worklet-runtime/src/listeners.ts
- packages/react/runtime/src/lifecycle/patch/commit.ts
- packages/react/worklet-runtime/src/runOnMainThread.ts
- packages/react/runtime/test/worklet/runOnMainThread.test.jsx
🧰 Additional context used
🧠 Learnings (7)
📚 Learning: 2025-08-07T04:00:59.645Z
Learnt from: colinaaa
PR: lynx-family/lynx-stack#1454
File: pnpm-workspace.yaml:46-46
Timestamp: 2025-08-07T04:00:59.645Z
Learning: In the lynx-family/lynx-stack repository, the webpack patch (patches/webpack5.101.0.patch) was created to fix issues with webpack5.99.9 but only takes effect on webpack5.100.0 and later versions. The patchedDependencies entry should use "webpack@^5.100.0" to ensure the patch applies to the correct version range.
Applied to files:
packages/rspeedy/plugin-react/package.jsonpackages/webpack/react-webpack-plugin/package.json
📚 Learning: 2025-08-19T11:25:36.127Z
Learnt from: colinaaa
PR: lynx-family/lynx-stack#1558
File: .changeset/solid-squids-fall.md:2-2
Timestamp: 2025-08-19T11:25:36.127Z
Learning: In the lynx-family/lynx-stack repository, changesets should use the exact package name from package.json#name, not generic or unscoped names. Each package has its own specific scoped name (e.g., "lynx-js/react-transform" for packages/react/transform).
Applied to files:
packages/rspeedy/plugin-react/package.jsonpackages/webpack/react-webpack-plugin/package.json
📚 Learning: 2025-08-13T11:36:12.075Z
Learnt from: colinaaa
PR: lynx-family/lynx-stack#1523
File: vitest.config.ts:52-72
Timestamp: 2025-08-13T11:36:12.075Z
Learning: The lynx-stack project requires Node.js >=22 as specified in package.json engines, so Node.js compatibility fallbacks for features introduced before v22 are unnecessary.
Applied to files:
packages/rspeedy/plugin-react/package.jsonpackages/webpack/react-webpack-plugin/package.json
📚 Learning: 2025-08-20T04:56:36.011Z
Learnt from: colinaaa
PR: lynx-family/lynx-stack#1566
File: package.json:53-53
Timestamp: 2025-08-20T04:56:36.011Z
Learning: In lynx-stack, Node.js v24 is the preferred/default version for development (established in PR #1557), but Node.js v22 compatibility is maintained specifically for external CI systems like rspack-ecosystem-ci. The engines.node specification uses "^22 || ^24" to support both versions while keeping v24 as the primary target.
Applied to files:
packages/webpack/react-webpack-plugin/package.json
📚 Learning: 2025-08-27T12:42:01.095Z
Learnt from: upupming
PR: lynx-family/lynx-stack#1616
File: packages/webpack/cache-events-webpack-plugin/test/cases/not-cache-events/lazy-bundle/index.js:3-3
Timestamp: 2025-08-27T12:42:01.095Z
Learning: In webpack, properties like __webpack_require__.lynx_ce are injected during compilation/build time when webpack processes modules and generates bundles, not at runtime when dynamic imports execute. Tests for such properties don't need to wait for dynamic imports to complete.
Applied to files:
packages/webpack/react-webpack-plugin/package.json
📚 Learning: 2025-08-14T12:54:51.143Z
Learnt from: upupming
PR: lynx-family/lynx-stack#1370
File: .changeset/brave-melons-add.md:1-7
Timestamp: 2025-08-14T12:54:51.143Z
Learning: In the lynx-family/lynx-stack repository, packages use 0.x.x versioning where minor version bumps indicate breaking changes (not major bumps), following pre-1.0 semantic versioning conventions.
Applied to files:
packages/webpack/react-webpack-plugin/package.json
📚 Learning: 2025-08-11T05:59:28.530Z
Learnt from: upupming
PR: lynx-family/lynx-stack#1305
File: packages/react/testing-library/src/plugins/vitest.ts:4-6
Timestamp: 2025-08-11T05:59:28.530Z
Learning: In the lynx-family/lynx-stack repository, the `packages/react/testing-library` package does not have `vite` as a direct dependency. It relies on `vitest` being available from the monorepo root and accesses Vite types through re-exports from `vitest/node`. Direct imports from `vite` should not be suggested for this package.
Applied to files:
packages/webpack/react-webpack-plugin/package.json
🧬 Code graph analysis (2)
packages/react/worklet-runtime/src/global.ts (1)
packages/react/worklet-runtime/src/bindings/types.ts (2)
Worklet(41-52)ClosureValueType(28-39)
packages/react/worklet-runtime/__test__/runOnMainThread.test.js (1)
packages/react/worklet-runtime/src/runOnMainThread.ts (1)
runRunOnMainThreadTask(7-21)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: build / Build (Windows)
- GitHub Check: build / Build (Ubuntu)
- GitHub Check: test-rust / Test (Ubuntu)
- GitHub Check: code-style-check
- GitHub Check: CodeQL Analyze (javascript-typescript)
- GitHub Check: CodeQL Analyze (actions)
🔇 Additional comments (3)
packages/webpack/react-webpack-plugin/package.json (1)
56-56: Peer range bump to ^0.113.0 looks correct.Matches the repo-wide move to support @lynx-js/react v0.113.x. No further action needed here.
packages/rspeedy/plugin-react/package.json (1)
67-67: Peer range bump to ^0.113.0 is consistent.Aligned with other packages in this PR. Good to go.
packages/react/worklet-runtime/src/global.ts (1)
21-21: LGTM: add _runRunOnMainThreadTask to global API.Signature matches bindings/types and the new runtime wiring. No issues spotted.
8b42f4c to
cf82fa7
Compare
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (1)
packages/react/worklet-runtime/src/listeners.ts (1)
15-18: Guard the listener with try/catch and validate payload before invoking the task.Uncaught errors from JSON.parse or runRunOnMainThreadTask will bubble and can destabilize the JSContext listener. Validate resolveId/worklet/params and report errors instead of throwing.
(event: Event) => { - const data = JSON.parse(event.data as string) as RunWorkletCtxData; - runRunOnMainThreadTask(data.worklet, data.params as ClosureValueType[], data.resolveId); + try { + const raw = JSON.parse(event.data as string); + if (!raw || typeof raw.resolveId !== 'number' || !raw.worklet) { + throw new Error('Invalid RunWorkletCtxData payload'); + } + const data = raw as RunWorkletCtxData; + runRunOnMainThreadTask( + data.worklet, + (data.params ?? []) as ClosureValueType[], + data.resolveId, + ); + } catch (e) { + lynx.reportError(e as Error); + } },
🧹 Nitpick comments (1)
packages/react/worklet-runtime/src/listeners.ts (1)
21-24: Validate ReleaseWorkletRef payload before removing.Defensive check avoids accidental deletions on malformed events.
- (event: Event) => { - removeValueFromWorkletRefMap((event.data as ReleaseWorkletRefData).id); - }, + (event: Event) => { + const id = (event.data as ReleaseWorkletRefData)?.id; + if (typeof id !== 'number') { + lynx.reportError(new Error('Invalid ReleaseWorkletRefData payload')); + return; + } + removeValueFromWorkletRefMap(id); + },
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (15)
.changeset/lemon-streets-watch.md(1 hunks).changeset/smooth-dragons-smash.md(1 hunks)packages/react/runtime/__test__/worklet/runOnMainThread.test.js(0 hunks)packages/react/runtime/__test__/worklet/runOnMainThread.test.jsx(1 hunks)packages/react/runtime/src/lifecycle/patch/commit.ts(4 hunks)packages/react/runtime/src/lifecycle/patch/updateMainThread.ts(4 hunks)packages/react/runtime/src/lynx/tt.ts(2 hunks)packages/react/runtime/src/worklet/runOnMainThread.ts(2 hunks)packages/react/worklet-runtime/__test__/runOnMainThread.test.js(1 hunks)packages/react/worklet-runtime/src/bindings/bindings.ts(1 hunks)packages/react/worklet-runtime/src/global.ts(1 hunks)packages/react/worklet-runtime/src/listeners.ts(2 hunks)packages/react/worklet-runtime/src/runOnMainThread.ts(1 hunks)packages/react/worklet-runtime/src/workletRuntime.ts(2 hunks)packages/rspeedy/plugin-react/package.json(1 hunks)
💤 Files with no reviewable changes (1)
- packages/react/runtime/test/worklet/runOnMainThread.test.js
✅ Files skipped from review due to trivial changes (1)
- .changeset/smooth-dragons-smash.md
🚧 Files skipped from review as they are similar to previous changes (12)
- packages/react/worklet-runtime/src/global.ts
- packages/react/worklet-runtime/src/workletRuntime.ts
- packages/react/worklet-runtime/src/bindings/bindings.ts
- packages/rspeedy/plugin-react/package.json
- packages/react/runtime/src/lynx/tt.ts
- packages/react/runtime/test/worklet/runOnMainThread.test.jsx
- packages/react/runtime/src/worklet/runOnMainThread.ts
- .changeset/lemon-streets-watch.md
- packages/react/runtime/src/lifecycle/patch/commit.ts
- packages/react/runtime/src/lifecycle/patch/updateMainThread.ts
- packages/react/worklet-runtime/test/runOnMainThread.test.js
- packages/react/worklet-runtime/src/runOnMainThread.ts
🧰 Additional context used
🧬 Code graph analysis (1)
packages/react/worklet-runtime/src/listeners.ts (2)
packages/react/worklet-runtime/src/runOnMainThread.ts (1)
runRunOnMainThreadTask(7-21)packages/react/worklet-runtime/src/bindings/types.ts (1)
ClosureValueType(28-39)
🔇 Additional comments (2)
packages/react/worklet-runtime/src/listeners.ts (2)
5-8: Imports update looks correct.Switch to runRunOnMainThreadTask and dropping RunWorkletCtxRetData aligns with delegated return dispatch.
11-19: Sanity-check: only one FunctionCallRet dispatcher remainsFound a single FunctionCallRet dispatch in packages/react/worklet-runtime/src/runOnMainThread.ts; listeners route WorkletEvents.runWorkletCtx → runRunOnMainThreadTask (packages/react/worklet-runtime/src/listeners.ts) and packages/react/runtime/src/worklet/runOnMainThread.ts emits runWorkletCtx — no other FunctionCallRet dispatchers found.
This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated. # Releases ## @lynx-js/react@0.113.0 ### Minor Changes - fix: Delay execution of `runOnMainThread()` during initial render ([#1667](#1667)) When called during the initial render, `runOnMainThread()` would execute before the `main-thread:ref` was hydrated, causing it to be incorrectly set to null. This change delays the function's execution to ensure the ref is available and correctly assigned. ### Patch Changes - Fix "TypeError: cannot read property '0' of undefined" in deferred list-item scenarios. ([#1692](#1692)) Deferred `componentAtIndex` causes nodes that quickly appear/disappear to be enqueued without `__elements`. Update `signMap` before `__FlushElementTree` to resolve the issue. - Keep the same `<page/>` element when calling `rerender` in testing library. ([#1656](#1656)) - Bump `swc_core` to `39.0.3`. ([#1721](#1721)) ## @lynx-js/tailwind-preset@0.3.0 ### Minor Changes - Added `group-*`, `peer-*`, and `parent-*` modifiers (ancestor, sibling, and direct-parent scopes) for `uiVariants` plugin. ([#1741](#1741)) Fixed prefix handling in prefixed projects — `ui-*` state markers are not prefixed, while scope markers (`.group`/`.peer`) honor `config('prefix')`. **BREAKING**: Removed slash-based naming modifiers on self (non-standard); slash modifiers remain supported for scoped markers (e.g. `group/menu`, `peer/tab`). Bumped peer dependency to `tailwindcss@^3.4.0` (required for use of internal features). ## @lynx-js/react-webpack-plugin@0.7.0 ### Minor Changes - Remove `@lynx-js/react` from peerDependencies. ([#1711](#1711)) - Add a new required option `workletRuntimePath`. ([#1711](#1711)) ## @lynx-js/rspeedy@0.11.2 ### Patch Changes - Support `server.proxy`. ([#1745](#1745)) - Support `command` and `env` parameters in the function exported by `lynx.config.js`. ([#1669](#1669)) ```js import { defineConfig } from "@lynx-js/rspeedy"; export default defineConfig(({ command, env }) => { const isBuild = command === "build"; const isTest = env === "test"; return { output: { minify: !isTest, }, performance: { buildCache: isBuild, }, }; }); ``` - Support `resolve.dedupe`. ([#1671](#1671)) This is useful when having multiple duplicated packages in the bundle: ```js import { defineConfig } from "@lynx-js/rspeedy"; export default defineConfig({ resolve: { dedupe: ["tslib"], }, }); ``` - Support `resolve.aliasStrategy` for controlling priority between `tsconfig.json` paths and `resolve.alias` ([#1722](#1722)) ```js import { defineConfig } from "@lynx-js/rspeedy"; export default defineConfig({ resolve: { alias: { "@": "./src", }, // 'prefer-tsconfig' (default): tsconfig.json paths take priority // 'prefer-alias': resolve.alias takes priority aliasStrategy: "prefer-alias", }, }); ``` - Bump Rsbuild v1.5.4 with Rspack v1.5.2. ([#1644](#1644)) - Updated dependencies \[[`d7c5da3`](d7c5da3)]: - @lynx-js/chunk-loading-webpack-plugin@0.3.3 - @lynx-js/cache-events-webpack-plugin@0.0.2 ## @lynx-js/react-rsbuild-plugin@0.10.14 ### Patch Changes - Fix using wrong version of `@lynx-js/react/worklet-runtime`. ([#1711](#1711)) - Be compat with `@lynx-js/react` v0.113.0 ([#1667](#1667)) - Disable `builtin:lightningcss-loader` for `environments.web`. ([#1732](#1732)) - Updated dependencies \[[`5ad38e6`](5ad38e6), [`69b3ae0`](69b3ae0), [`69b3ae0`](69b3ae0), [`c2f90bd`](c2f90bd)]: - @lynx-js/template-webpack-plugin@0.8.6 - @lynx-js/react-webpack-plugin@0.7.0 - @lynx-js/react-alias-rsbuild-plugin@0.10.14 - @lynx-js/css-extract-webpack-plugin@0.6.2 - @lynx-js/use-sync-external-store@1.5.0 - @lynx-js/react-refresh-webpack-plugin@0.3.4 ## @lynx-js/react-alias-rsbuild-plugin@0.10.14 ### Patch Changes - Allow customization of the react$ alias. ([#1653](#1653)) ```js import { defineConfig } from "@lynx-js/rspeedy"; export default defineConfig({ resolve: { alias: { react$: "@lynx-js/react/compat", }, }, }); ``` ## @lynx-js/web-constants@0.16.1 ### Patch Changes - feat: supports lazy bundle. (This feature requires `@lynx-js/lynx-core >= 0.1.3`) ([#1235](#1235)) - Updated dependencies \[]: - @lynx-js/web-worker-rpc@0.16.1 ## @lynx-js/web-core@0.16.1 ### Patch Changes - refactor: improve chunk loading ([#1703](#1703)) - feat: supports lazy bundle. (This feature requires `@lynx-js/lynx-core >= 0.1.3`) ([#1235](#1235)) - Updated dependencies \[[`608f375`](608f375)]: - @lynx-js/web-mainthread-apis@0.16.1 - @lynx-js/web-worker-runtime@0.16.1 - @lynx-js/web-constants@0.16.1 - @lynx-js/web-worker-rpc@0.16.1 ## @lynx-js/web-core-server@0.16.1 ### Patch Changes - refactor: improve chunk loading ([#1703](#1703)) - feat: supports lazy bundle. (This feature requires `@lynx-js/lynx-core >= 0.1.3`) ([#1235](#1235)) ## @lynx-js/web-elements@0.8.6 ### Patch Changes - fix: 1. svg use image tag to render, to differentiate background-image styles ([#1668](#1668)) 1. use blob instead of raw data-uri > Not using data-uri(data:image/svg+xml;utf8,${props.content}) > since it has follow limitations: > > < and > must be encoded to %3C and %3E. > Double quotes must be converted to single quotes. > Colors must use a non-hex format because # will not work inside data-uri. > See: <https://codepen.io/zvuc/pen/BWNLJL> > Instead, we use modern Blob API to create SVG URL that have the same support - Updated dependencies \[[`d618304`](d618304), [`1d97fce`](1d97fce)]: - @lynx-js/web-elements-template@0.8.6 ## @lynx-js/web-elements-template@0.8.6 ### Patch Changes - x-overlay-ng prevent page scroll when visible ([#1499](#1499)) - fix: 1. svg use image tag to render, to differentiate background-image styles ([#1668](#1668)) 1. use blob instead of raw data-uri > Not using data-uri(data:image/svg+xml;utf8,${props.content}) > since it has follow limitations: > > < and > must be encoded to %3C and %3E. > Double quotes must be converted to single quotes. > Colors must use a non-hex format because # will not work inside data-uri. > See: <https://codepen.io/zvuc/pen/BWNLJL> > Instead, we use modern Blob API to create SVG URL that have the same support ## @lynx-js/web-mainthread-apis@0.16.1 ### Patch Changes - feat: supports lazy bundle. (This feature requires `@lynx-js/lynx-core >= 0.1.3`) ([#1235](#1235)) - Updated dependencies \[[`608f375`](608f375)]: - @lynx-js/web-constants@0.16.1 - @lynx-js/web-style-transformer@0.16.1 ## @lynx-js/web-worker-runtime@0.16.1 ### Patch Changes - feat: supports lazy bundle. (This feature requires `@lynx-js/lynx-core >= 0.1.3`) ([#1235](#1235)) - Updated dependencies \[[`608f375`](608f375)]: - @lynx-js/web-mainthread-apis@0.16.1 - @lynx-js/web-constants@0.16.1 - @lynx-js/web-worker-rpc@0.16.1 ## @lynx-js/chunk-loading-webpack-plugin@0.3.3 ### Patch Changes - Fix unmet peer dependency "@rspack/core@'^1.3.10". ([#1660](#1660)) ## @lynx-js/template-webpack-plugin@0.8.6 ### Patch Changes - fix: add appType field for lazy bundle for web ([#1738](#1738)) ## create-rspeedy@0.11.2 ## upgrade-rspeedy@0.11.2 ## @lynx-js/web-style-transformer@0.16.1 ## @lynx-js/web-worker-rpc@0.16.1 Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Summary by CodeRabbit
Checklist