[pull] main from lynx-family:main#595
Merged
pull[bot] merged 4 commits intocolinaaa:mainfrom Apr 22, 2026
Merged
Conversation
<!-- Thank you for submitting a pull request! We appreciate the time and effort you have invested in making these changes. Please ensure that you provide enough information to allow others to review your pull request. Upon submission, your pull request will be automatically assigned with reviewers. If you want to learn more about contributing to this project, please visit: https://github.com/lynx-family/lynx-stack/blob/main/CONTRIBUTING.md. --> <!-- The AI summary below will be auto-generated - feel free to replace it with your own. --> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Fixed `line-height` values in markdown-style formatting to include explicit `px` unit suffix. * **Tests** * Updated test assertions to verify correct `line-height` CSS property rendering. <!-- end of auto-generated comment: release notes by coderabbit.ai --> ## Checklist <!--- Check and mark with an "x" --> - [ ] Tests updated (or not required). - [ ] Documentation updated (or not required). - [ ] Changeset added, and when a BREAKING CHANGE occurs, it needs to be clearly marked (or not required).
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Reduced redundant runtime updates when event handlers and gesture
objects remain stable across re-renders, decreasing unnecessary native
updates
* Improved gesture removal to reliably clear gesture state on affected
elements
* Prevented unnecessary native updates when prop spreading doesn't
semantically change handlers or gestures
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary
This PR reduces redundant native updates for stable main-thread handlers
and gestures by separating cached background-side objects from the
payloads committed to the main thread.
The previous flow mixed commit-time runtime metadata (for example
`_execId`) into objects that were also reused for diffing. As a result,
a handler or gesture could stay semantically unchanged across rerenders
but still look different to the patching layer, which caused unnecessary
native updates. This PR moves those paths to copy-on-commit, and also
tightens the spread-driven gesture removal flow so cleanup only touches
ReactLynx-owned gesture state.
## Key points
### 1. Copy-on-commit for gestures and worklets
The main issue was that commit-time preparation mutated objects that
were later reused for diffing. For stable references, that meant the
diff saw runtime-only churn instead of a real semantic change.
Before, commit-time preparation effectively behaved like this:
```ts
baseGesture.callbacks[name] = onPostWorkletCtx(baseGesture.callbacks[name])!;
```
That mutates the cached gesture object itself.
Now the runtime prepares a committed copy instead:
```ts
const committedCallbacks = { ...baseGesture.callbacks };
committedCallbacks[name] = prepareWorkletForCommit(callback)!;
const committed = {
...baseGesture,
callbacks: committedCallbacks,
};
```
This keeps the background-side cached object stable while still
attaching `_execId` to the payload sent to the main thread.
### 2. Spread props only clone when commit-time rewriting is actually
needed
Stable rerenders through spread props were still expensive because every
changed spread was shallow-cloned and scanned, even when it only
contained primitive attrs.
The old shape was effectively:
```ts
const committed = { ...spread };
for (const key in committed) {
// scan and possibly rewrite every key
}
return committed;
```
Now the spread commit path clones lazily:
```ts
let committed;
for (const key in spread) {
// only clone once we discover a worklet / gesture / timing flag
committed ??= { ...spread };
}
return committed ?? spread;
```
That means ordinary spread updates keep the original object and avoid
unnecessary allocation/scanning, while worklets and gestures still get
rewritten correctly when needed.
### 3. Gesture removal from spread props now cleans up the right state
When a spread removed `main-thread:gesture`, the removal happened late
in `updateSpread()`, after surviving keys had already been processed.
That made cleanup order important.
A problematic transition looked like this:
```ts
{ 'clip-radius': 8, 'main-thread:gesture': g }
-> { 'clip-radius': 8 }
```
`clip-radius` still requires `flatten={false}`, so clearing `flatten`
during gesture cleanup was incorrect. This PR fixes that by only
clearing ReactLynx-owned gesture state and leaving unrelated spread
state alone.
In addition, when `__RemoveGestureDetector` is available, the runtime no
longer forcefully writes `gesture = null` itself. That avoids clobbering
a user-provided `gesture` attr if detector cleanup is already owned by
the native remove API.
### 4. Committed gesture serialization no longer depends on mutating the
original object
The gesture commit path now serializes the committed copy instead of
relying on the original object's `toJSON` behavior. This makes the
runtime more robust when callbacks have been rewritten for commit-time
transport and avoids reintroducing `_execId` churn through
serialization.
Conceptually, the committed payload now comes from the committed object
itself:
```ts
const serialize = () => serializeCommittedGesture(committedGesture);
return {
...committedGesture,
serialize,
toJSON: serialize,
};
```
This keeps serialization aligned with the object that actually contains
the committed callbacks.
### 5. Behavioral boundary of this change
This PR is intentionally scoped to runtime-owned commit and cleanup
behavior:
- Stable main-thread handlers and gestures should no longer trigger
redundant native patches.
- Spread rerenders should avoid unnecessary cloning when no runtime
rewrite is required.
- Removing `main-thread:gesture` from spread props should not clear
unrelated state such as `flatten`.
- When detector removal can already be handled by
`__RemoveGestureDetector`, ReactLynx should not additionally clobber a
user-provided `gesture` attr.
## Checklist
- [x] Tests updated (or not required).
- [x] Documentation updated (or not required).
- [x] Changeset added, and when a BREAKING CHANGE occurs, it needs to be
clearly marked (or not required).
In this commit, @lynx-js/css-serializer generates cssMap with bundle-CSS line and column locations preserved on style rules and declarations. These locations are written into tasm.json as part of the encoder input, so TASM can report CSS diagnostics using coordinates that correspond to the bundled CSS output. Those bundle-space coordinates are then remapped in template-webpack-plugin through main.css.map, allowing emitted webpack warnings to point back to the original source CSS files. The cssMap itself is still grouped by cssId and consumed as the style AST for template encoding; its preserved loc fields are what make later diagnostic remapping accurate. <img width="1320" height="1364" alt="image" src="https://github.com/user-attachments/assets/7e0ae8a3-b291-4eec-803b-905d39becb52" /> <!-- Thank you for submitting a pull request! We appreciate the time and effort you have invested in making these changes. Please ensure that you provide enough information to allow others to review your pull request. Upon submission, your pull request will be automatically assigned with reviewers. If you want to learn more about contributing to this project, please visit: https://github.com/lynx-family/lynx-stack/blob/main/CONTRIBUTING.md. --> <!-- The AI summary below will be auto-generated - feel free to replace it with your own. --> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * CSS source maps enabled by default in build output * CSS diagnostics remapped to original sources and emitted as compilation warnings * CSS extraction accepts raw strings or {content} objects * **Bug Fixes** * Improved precision of CSS diagnostic messages via source-map resolution * Duplicate CSS warnings deduplicated before emission * **Tests** * Added tests and updated snapshots for source-location preservation and diagnostics * **Chores** * Updated peer compatibility for template plugins <!-- end of auto-generated comment: release notes by coderabbit.ai --> ## Checklist <!--- Check and mark with an "x" --> - [ ] Tests updated (or not required). - [ ] Documentation updated (or not required). - [ ] Changeset added, and when a BREAKING CHANGE occurs, it needs to be clearly marked (or not required).
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 subscribe to this conversation on GitHub.
Already have an account?
Sign in.
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.
See Commits and Changes for more details.
Created by
pull[bot] (v2.0.0-alpha.4)
Can you help keep this open source service alive? 💖 Please sponsor : )