Skip to content

feat(et): support spread attributes and events#2630

Merged
Yradex merged 2 commits into
lynx-family:mainfrom
Yradex:slice/element-template/09
May 15, 2026
Merged

feat(et): support spread attributes and events#2630
Yradex merged 2 commits into
lynx-family:mainfrom
Yradex:slice/element-template/09

Conversation

@Yradex
Copy link
Copy Markdown
Collaborator

@Yradex Yradex commented May 14, 2026

Summary by CodeRabbit

  • New Features

    • Improved support for spreading props (including event handlers) onto elements with correct mapping and consistent behavior across main/background runtimes and hydration.
  • Bug Fixes

    • More reliable hydration and update semantics for spread-derived attributes and event handlers, including correct handler registration, teardown, and slot patching on insert/update.
  • Tests

    • Expanded unit and end-to-end tests covering spread props, event dispatch/routing, background rendering, and compiled fixtures.

Review Change Stack

Overview

This PR adds Element Template support for spread attributes in the experimental ET transform/runtime path. Spread attrs now reserve a dedicated attribute slot with a spread adapter, so the main-thread template create path, serialized hydration payload, and background update path can all preserve the same slot ordering while expanding ordinary attrs and event values through the adapter boundary.

Key Points

  • The transform emits spread attr adapter metadata as part of the ET attr plan instead of lowering spread values into ad hoc static attrs.
  • The runtime prepares spread slots through prop-adapters/spread, keeping event preparation on the existing event adapter path and preserving static/dynamic attr slot ordering.
  • Background hydrate/update and native template-tree mocks now cover spread attrs, nullish spread behavior, and spread-provided event handlers, including inserted subtree updates.

Checklist

  • 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).

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 14, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 21ccd7e7-2d00-475b-a69c-8255826fe5eb

📥 Commits

Reviewing files that changed from the base of the PR and between 4d217ed and bc5e015.

📒 Files selected for processing (3)
  • packages/react/runtime/__test__/element-template/fixtures/background/event/spread-event/index.tsx
  • packages/react/runtime/__test__/element-template/runtime/background/event/compiled-fixtures.test.tsx
  • packages/react/runtime/__test__/element-template/runtime/prop-adapters/event.test.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • packages/react/runtime/test/element-template/fixtures/background/event/spread-event/index.tsx
  • packages/react/runtime/test/element-template/runtime/prop-adapters/event.test.ts
  • packages/react/runtime/test/element-template/runtime/background/event/compiled-fixtures.test.tsx

📝 Walkthrough

Walkthrough

Adds adapter and transform support for spread-style attributes in Element Templates: new spread prop adapter, event-value serialization, attr-slot-plan integration (adaptSpreadAttrSlot), SWC transform emission for spread adapters, runtime/hydration handling, fixtures, and tests covering render, hydration, and compiled cases.

Changes

Spread Event Attribute Handling

Layer / File(s) Summary
Event-value serialization and spread adapter
packages/react/runtime/src/element-template/prop-adapters/event-value.ts, packages/react/runtime/src/element-template/prop-adapters/spread.ts
Adds getEventValue(handleId, attrSlotIndex, eventKey?) and prepareSpreadAttrSlot(...) which validate/coerce spread inputs, filter reserved keys, normalize classNameclass, map event-like keys to serialized event-values (or null), and skip refs/functions.
Attr-slot adapter surface & re-exports
packages/react/runtime/src/element-template/runtime/template/attr-slot-plan.ts, packages/react/runtime/src/element-template/internal.ts
Extends adapter context/type, updates adaptEventAttrSlot to use getEventValue(...), adds adaptSpreadAttrSlot(...) delegating to prepareSpreadAttrSlot, and re-exports the new adapter.
Compiler: emit attr-plan with adapter kinds
packages/react/transform/crates/swc_plugin_element_template/lib.rs, packages/react/transform/crates/swc_plugin_element_template/tests/element_template_contract.rs
Transform now records adapter kind per attr-slot (Event vs Spread), emits __etAttrPlanMap entries using adaptSpreadAttrSlot for spread descriptors, and test expectations updated to assert adapter ordering and spread-plan emission.
Runtime fixtures & compiled-fixture helpers
packages/react/runtime/__test__/element-template/fixtures/background/event/spread-event/index.tsx, packages/react/runtime/__test__/element-template/runtime/background/event/compiled-fixtures.test.tsx
Adds a spread-event fixture and compiled-fixture loader/helpers to exercise spread props and child-subtree spreads across render/hydration boundaries; adds compiled spread-focused tests.
Runtime/hydration/instance tests and test utilities
packages/react/runtime/__test__/element-template/runtime/hydrate.test.ts, .../background/instance.test.ts, .../prop-adapters/event.test.ts, .../prop-adapters/spread.test.ts, .../render/render-opcodes-into-element-template.et.test.tsx, .../test-utils/mock/mockNativePapi/templateTree.test.ts
Updates suites to run background-mode where appropriate, makes event tests adapter-driven, adds unit tests for prepareSpreadAttrSlot, adds hydration/instance tests for prepared spread handler lifecycle and patching behavior, adds render-opcode and mock template-tree tests validating spread-slot expansion and replacement.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • HuJean
  • hzy
  • gaoachao

"🐰
I spread my paws on props and keys,
Mapping bindtap into tidy strings.
className hops to class so neat,
Reserved keys skipped, events meet.
Hooray — the template spreads complete!"

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 19.05% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately captures the main change: adding spread attributes and events support to the Element Template system.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 14, 2026

⚠️ No Changeset found

Latest commit: bc5e015

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@codecov
Copy link
Copy Markdown

codecov Bot commented May 14, 2026

Codecov Report

❌ Patch coverage is 93.02326% with 6 lines in your changes missing coverage. Please review.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
...e/fixtures/background/event/spread-event/index.tsx 0.00% 6 Missing ⚠️

📢 Thoughts on this report? Let us know!

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented May 14, 2026

Merging this PR will degrade performance by 16%

⚠️ Different runtime environments detected

Some benchmarks with significant performance changes were compared across different runtime environments,
which may affect the accuracy of the results.

Open the report in CodSpeed to investigate

❌ 1 regressed benchmark
✅ 80 untouched benchmarks
⏩ 26 skipped benchmarks1

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Benchmark BASE HEAD Efficiency
008-many-use-state-destroyBackground 8 ms 9.5 ms -16%

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing Yradex:slice/element-template/09 (bc5e015) with main (a4692da)

Open in CodSpeed

Footnotes

  1. 26 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@relativeci
Copy link
Copy Markdown

relativeci Bot commented May 14, 2026

React External

#1368 Bundle Size — 695.33KiB (0%).

bc5e015(current) vs a4692da main#1354(baseline)

Bundle metrics  no changes
                 Current
#1368
     Baseline
#1354
No change  Initial JS 0B 0B
No change  Initial CSS 0B 0B
No change  Cache Invalidation 0% 0%
No change  Chunks 0 0
No change  Assets 3 3
No change  Modules 17 17
No change  Duplicate Modules 5 5
No change  Duplicate Code 8.59% 8.59%
No change  Packages 0 0
No change  Duplicate Packages 0 0
Bundle size by type  no changes
                 Current
#1368
     Baseline
#1354
No change  Other 695.33KiB 695.33KiB

Bundle analysis reportBranch Yradex:slice/element-template/09Project dashboard


Generated by RelativeCIDocumentationReport issue

@relativeci
Copy link
Copy Markdown

relativeci Bot commented May 14, 2026

React MTF Example

#1387 Bundle Size — 208.1KiB (0%).

bc5e015(current) vs a4692da main#1373(baseline)

Bundle metrics  no changes
                 Current
#1387
     Baseline
#1373
No change  Initial JS 0B 0B
No change  Initial CSS 0B 0B
No change  Cache Invalidation 0% 0%
No change  Chunks 0 0
No change  Assets 3 3
No change  Modules 192 192
No change  Duplicate Modules 77 77
No change  Duplicate Code 44.4% 44.4%
No change  Packages 2 2
No change  Duplicate Packages 0 0
Bundle size by type  no changes
                 Current
#1387
     Baseline
#1373
No change  IMG 111.23KiB 111.23KiB
No change  Other 96.86KiB 96.86KiB

Bundle analysis reportBranch Yradex:slice/element-template/09Project dashboard


Generated by RelativeCIDocumentationReport issue

@relativeci
Copy link
Copy Markdown

relativeci Bot commented May 14, 2026

Web Explorer

#9829 Bundle Size — 901.35KiB (0%).

bc5e015(current) vs a4692da main#9815(baseline)

Bundle metrics  no changes
                 Current
#9829
     Baseline
#9815
No change  Initial JS 45.06KiB 45.06KiB
No change  Initial CSS 2.22KiB 2.22KiB
No change  Cache Invalidation 0% 0%
No change  Chunks 9 9
No change  Assets 11 11
No change  Modules 230 230
No change  Duplicate Modules 11 11
No change  Duplicate Code 27.22% 27.22%
No change  Packages 10 10
No change  Duplicate Packages 0 0
Bundle size by type  no changes
                 Current
#9829
     Baseline
#9815
No change  JS 497.08KiB 497.08KiB
No change  Other 402.06KiB 402.06KiB
No change  CSS 2.22KiB 2.22KiB

Bundle analysis reportBranch Yradex:slice/element-template/09Project dashboard


Generated by RelativeCIDocumentationReport issue

@relativeci
Copy link
Copy Markdown

relativeci Bot commented May 14, 2026

React Example with Element Template

#522 Bundle Size — 199.95KiB (+0.06%).

bc5e015(current) vs a4692da main#508(baseline)

Bundle metrics  Change 3 changes Regression 1 regression
                 Current
#522
     Baseline
#508
No change  Initial JS 0B 0B
No change  Initial CSS 0B 0B
Change  Cache Invalidation 27.06% 0%
No change  Chunks 0 0
No change  Assets 4 4
No change  Modules 89 89
Regression  Duplicate Modules 27(+3.85%) 26
Change  Duplicate Code 40.06%(+0.12%) 40.01%
No change  Packages 2 2
No change  Duplicate Packages 0 0
Bundle size by type  Change 1 change Regression 1 regression
                 Current
#522
     Baseline
#508
No change  IMG 145.76KiB 145.76KiB
Regression  Other 54.19KiB (+0.22%) 54.08KiB

Bundle analysis reportBranch Yradex:slice/element-template/09Project dashboard


Generated by RelativeCIDocumentationReport issue

@relativeci
Copy link
Copy Markdown

relativeci Bot commented May 14, 2026

React Example

#8254 Bundle Size — 237.15KiB (0%).

bc5e015(current) vs a4692da main#8240(baseline)

Bundle metrics  no changes
                 Current
#8254
     Baseline
#8240
No change  Initial JS 0B 0B
No change  Initial CSS 0B 0B
No change  Cache Invalidation 0% 0%
No change  Chunks 0 0
No change  Assets 4 4
No change  Modules 197 197
No change  Duplicate Modules 80 80
No change  Duplicate Code 44.89% 44.89%
No change  Packages 2 2
No change  Duplicate Packages 0 0
Bundle size by type  no changes
                 Current
#8254
     Baseline
#8240
No change  IMG 145.76KiB 145.76KiB
No change  Other 91.39KiB 91.39KiB

Bundle analysis reportBranch Yradex:slice/element-template/09Project dashboard


Generated by RelativeCIDocumentationReport issue

@Yradex Yradex marked this pull request as ready for review May 14, 2026 06:28
@Yradex Yradex requested review from HuJean, gaoachao and hzy as code owners May 14, 2026 06:28
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
packages/react/runtime/src/element-template/prop-adapters/spread.ts (1)

19-21: 💤 Low value

isSpreadRecord may accept non-plain objects.

The type guard excludes arrays but permits any object type, including Date, RegExp, Error, class instances, etc. Spreading these into attributes could produce unexpected serialization results (e.g., Date objects serialized as ISO strings or empty objects depending on the bridge).

If only plain objects should be spread, consider strengthening the check:

♻️ Stricter plain-object check
 function isSpreadRecord(value: unknown): value is Record<string, unknown> {
-  return value !== null && typeof value === 'object' && !Array.isArray(value);
+  return (
+    value !== null &&
+    typeof value === 'object' &&
+    !Array.isArray(value) &&
+    Object.getPrototypeOf(value) === Object.prototype
+  );
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/react/runtime/src/element-template/prop-adapters/spread.ts` around
lines 19 - 21, The current isSpreadRecord type guard allows any non-array object
(e.g., Date, RegExp, class instances) to be treated as a spreadable record;
change the predicate so it only returns true for plain objects by checking that
value is non-null, typeof value === 'object', not an array, and has a plain
prototype (e.g., Object.getPrototypeOf(value) === Object.prototype or === null)
or by using Object.prototype.toString.call(value) === '[object Object]'; update
the isSpreadRecord function to use one of these stricter checks so only plain
plain-object literals and objects with null prototype are accepted for
spreading.
packages/react/runtime/__test__/element-template/runtime/prop-adapters/spread.test.ts (1)

7-37: ⚡ Quick win

Test relies on object iteration order for class vs className.

Lines 17-18 include both className: 'primary' and class: 'final', and line 32 expects class: 'final'. While modern JavaScript engines preserve string-key insertion order, relying on iteration order when both properties are present makes the test (and the underlying implementation) subtly fragile. Users might reasonably expect class to always take precedence over className, or vice versa.

Consider either:

  1. Documenting the precedence behavior explicitly, or
  2. Updating prepareSpreadAttrSlot to explicitly check for both keys and apply a deterministic precedence rule (e.g., class always wins).
♻️ Example deterministic precedence
+let classValue: unknown = undefined;
+
 for (const key in value) {
   const spreadValue = value[key];
   // ... other checks ...
   
   if (key === 'class' || key === 'className') {
-    prepared['class'] = (spreadValue ?? '') as SerializableValue;
+    if (key === 'class' || classValue === undefined) {
+      classValue = spreadValue;
+    }
     continue;
   }
   // ...
 }
+
+if (classValue !== undefined) {
+  prepared['class'] = (classValue ?? '') as SerializableValue;
+}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/react/runtime/__test__/element-template/runtime/prop-adapters/spread.test.ts`
around lines 7 - 37, The test relies on object iteration order for deciding
between className and class; make the behavior deterministic by updating
prepareSpreadAttrSlot to explicitly check for both "class" and "className" and
resolve precedence (e.g., always prefer "class" over "className" or vice versa),
ensure the returned prepared object sets the chosen key only, and update this
test's expectation to match that explicit rule; reference prepareSpreadAttrSlot
and the test case in spread.test.ts when making the change so the assertion
(expect(prepared).toEqual(...)) reflects the deterministic precedence.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/react/runtime/src/element-template/prop-adapters/spread.ts`:
- Line 64: The unchecked cast "prepared[key] = spreadValue as SerializableValue"
can admit non-serializable values; replace it with a runtime guard that
validates spreadValue before assigning: implement or call an
isSerializable(value) helper (reject functions, Symbols, Promises, DOM nodes,
and objects containing function properties or circular references), or attempt a
safe JSON.stringify in a try/catch to detect unserializable values, and only
assign to prepared[key] when the guard passes; otherwise skip the key or
log/throw a clear error. Ensure the guard is used where spreadValue is checked
(around the existing checks for undefined/functions/special keys) and reference
SerializableValue/prepared/spreadValue in the change.
- Around line 41-44: The current assignment in spread.ts blindly casts
spreadValue for keys 'class'/'className' into prepared['class']; instead
validate the type first: if spreadValue is a string or number, set
prepared['class'] = String(spreadValue); if it's an array, join its string
elements with spaces (e.g., spreadValue.filter(Boolean).map(String).join(' '));
otherwise set prepared['class'] = '' (or omit) to avoid passing objects through.
Update the code at the conditional handling of key === 'class' || key ===
'className' to perform these type checks before assigning prepared['class'].

---

Nitpick comments:
In
`@packages/react/runtime/__test__/element-template/runtime/prop-adapters/spread.test.ts`:
- Around line 7-37: The test relies on object iteration order for deciding
between className and class; make the behavior deterministic by updating
prepareSpreadAttrSlot to explicitly check for both "class" and "className" and
resolve precedence (e.g., always prefer "class" over "className" or vice versa),
ensure the returned prepared object sets the chosen key only, and update this
test's expectation to match that explicit rule; reference prepareSpreadAttrSlot
and the test case in spread.test.ts when making the change so the assertion
(expect(prepared).toEqual(...)) reflects the deterministic precedence.

In `@packages/react/runtime/src/element-template/prop-adapters/spread.ts`:
- Around line 19-21: The current isSpreadRecord type guard allows any non-array
object (e.g., Date, RegExp, class instances) to be treated as a spreadable
record; change the predicate so it only returns true for plain objects by
checking that value is non-null, typeof value === 'object', not an array, and
has a plain prototype (e.g., Object.getPrototypeOf(value) === Object.prototype
or === null) or by using Object.prototype.toString.call(value) === '[object
Object]'; update the isSpreadRecord function to use one of these stricter checks
so only plain plain-object literals and objects with null prototype are accepted
for spreading.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 4504495a-69a6-4b47-955a-c5d08c049365

📥 Commits

Reviewing files that changed from the base of the PR and between e427f43 and 632713c.

⛔ Files ignored due to path filters (2)
  • packages/react/transform/crates/swc_plugin_element_template/tests/__combined_snapshots__/should_handle_spread_attributes.snap is excluded by !**/*.snap
  • packages/react/transform/crates/swc_plugin_element_template/tests/__combined_snapshots__/should_keep_code_and_template_attribute_slots_in_sync_for_spread.snap is excluded by !**/*.snap
📒 Files selected for processing (15)
  • packages/react/runtime/__test__/element-template/fixtures/background/event/spread-event/index.tsx
  • packages/react/runtime/__test__/element-template/fixtures/hydrate/background-hydrate/attrs.nullish-values/output.txt
  • packages/react/runtime/__test__/element-template/runtime/background/event/compiled-fixtures.test.tsx
  • packages/react/runtime/__test__/element-template/runtime/background/hydrate.test.ts
  • packages/react/runtime/__test__/element-template/runtime/background/instance.test.ts
  • packages/react/runtime/__test__/element-template/runtime/prop-adapters/event.test.ts
  • packages/react/runtime/__test__/element-template/runtime/prop-adapters/spread.test.ts
  • packages/react/runtime/__test__/element-template/runtime/render/render-opcodes-into-element-template.et.test.tsx
  • packages/react/runtime/__test__/element-template/test-utils/mock/mockNativePapi/templateTree.test.ts
  • packages/react/runtime/src/element-template/internal.ts
  • packages/react/runtime/src/element-template/prop-adapters/event-value.ts
  • packages/react/runtime/src/element-template/prop-adapters/spread.ts
  • packages/react/runtime/src/element-template/runtime/template/attr-slot-plan.ts
  • packages/react/transform/crates/swc_plugin_element_template/lib.rs
  • packages/react/transform/crates/swc_plugin_element_template/tests/element_template_contract.rs
💤 Files with no reviewable changes (1)
  • packages/react/runtime/test/element-template/fixtures/hydrate/background-hydrate/attrs.nullish-values/output.txt

Comment thread packages/react/runtime/src/element-template/prop-adapters/spread.ts
Comment thread packages/react/runtime/src/element-template/prop-adapters/spread.ts
@Yradex Yradex force-pushed the slice/element-template/09 branch from 632713c to 4d217ed Compare May 14, 2026 12:52
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
packages/react/runtime/src/element-template/prop-adapters/spread.ts (1)

35-36: ⚡ Quick win

Prefer Object.entries() for consistent spread semantics.

The for...in loop at line 35 can include inherited enumerable properties, whereas object spread only uses own properties. While Object.entries(value) would make the intent clearer and match spread behavior, consider that the function already filters special keys (__spread, __self, __source, ref, functions). This pattern is used throughout the codebase; standardizing on Object.entries() would be a beneficial consistency improvement.

Suggested fix
-  for (const key in value) {
-    const spreadValue = value[key];
+  for (const [key, spreadValue] of Object.entries(value)) {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/react/runtime/src/element-template/prop-adapters/spread.ts` around
lines 35 - 36, Replace the for...in iteration in the spread adapter with an
Object.entries() iteration so only own enumerable properties are processed
(matching object spread semantics); in the function that currently uses "for
(const key in value) { const spreadValue = value[key]; }" iterate over
Object.entries(value) and keep the existing special-key filters (__spread,
__self, __source, ref, functions) and handling logic unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@packages/react/runtime/src/element-template/prop-adapters/spread.ts`:
- Around line 35-36: Replace the for...in iteration in the spread adapter with
an Object.entries() iteration so only own enumerable properties are processed
(matching object spread semantics); in the function that currently uses "for
(const key in value) { const spreadValue = value[key]; }" iterate over
Object.entries(value) and keep the existing special-key filters (__spread,
__self, __source, ref, functions) and handling logic unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0d5cc575-d3a6-46ac-a104-b912d11653ee

📥 Commits

Reviewing files that changed from the base of the PR and between 632713c and 4d217ed.

⛔ Files ignored due to path filters (2)
  • packages/react/transform/crates/swc_plugin_element_template/tests/__combined_snapshots__/should_handle_spread_attributes.snap is excluded by !**/*.snap
  • packages/react/transform/crates/swc_plugin_element_template/tests/__combined_snapshots__/should_keep_code_and_template_attribute_slots_in_sync_for_spread.snap is excluded by !**/*.snap
📒 Files selected for processing (15)
  • packages/react/runtime/__test__/element-template/fixtures/background/event/spread-event/index.tsx
  • packages/react/runtime/__test__/element-template/fixtures/hydrate/background-hydrate/attrs.nullish-values/output.txt
  • packages/react/runtime/__test__/element-template/runtime/background/event/compiled-fixtures.test.tsx
  • packages/react/runtime/__test__/element-template/runtime/background/hydrate.test.ts
  • packages/react/runtime/__test__/element-template/runtime/background/instance.test.ts
  • packages/react/runtime/__test__/element-template/runtime/prop-adapters/event.test.ts
  • packages/react/runtime/__test__/element-template/runtime/prop-adapters/spread.test.ts
  • packages/react/runtime/__test__/element-template/runtime/render/render-opcodes-into-element-template.et.test.tsx
  • packages/react/runtime/__test__/element-template/test-utils/mock/mockNativePapi/templateTree.test.ts
  • packages/react/runtime/src/element-template/internal.ts
  • packages/react/runtime/src/element-template/prop-adapters/event-value.ts
  • packages/react/runtime/src/element-template/prop-adapters/spread.ts
  • packages/react/runtime/src/element-template/runtime/template/attr-slot-plan.ts
  • packages/react/transform/crates/swc_plugin_element_template/lib.rs
  • packages/react/transform/crates/swc_plugin_element_template/tests/element_template_contract.rs
💤 Files with no reviewable changes (1)
  • packages/react/runtime/test/element-template/fixtures/hydrate/background-hydrate/attrs.nullish-values/output.txt
✅ Files skipped from review due to trivial changes (2)
  • packages/react/runtime/src/element-template/internal.ts
  • packages/react/runtime/test/element-template/runtime/background/hydrate.test.ts
🚧 Files skipped from review as they are similar to previous changes (9)
  • packages/react/runtime/test/element-template/test-utils/mock/mockNativePapi/templateTree.test.ts
  • packages/react/runtime/test/element-template/runtime/render/render-opcodes-into-element-template.et.test.tsx
  • packages/react/runtime/src/element-template/runtime/template/attr-slot-plan.ts
  • packages/react/runtime/src/element-template/prop-adapters/event-value.ts
  • packages/react/runtime/test/element-template/fixtures/background/event/spread-event/index.tsx
  • packages/react/transform/crates/swc_plugin_element_template/lib.rs
  • packages/react/runtime/test/element-template/runtime/background/instance.test.ts
  • packages/react/transform/crates/swc_plugin_element_template/tests/element_template_contract.rs
  • packages/react/runtime/test/element-template/runtime/prop-adapters/event.test.ts

@Yradex Yradex force-pushed the slice/element-template/09 branch from 4d217ed to bc5e015 Compare May 15, 2026 03:00
@Yradex Yradex merged commit 00a0725 into lynx-family:main May 15, 2026
168 of 178 checks passed
@Yradex Yradex deleted the slice/element-template/09 branch May 15, 2026 08:01
PupilTong added a commit to PupilTong/lynx-stack that referenced this pull request May 15, 2026
PR lynx-family#2643 (refactor: simplify template attribute descriptors) changed
`CompiledAttributeDescriptor` from
  { kind: 'attribute' | 'spread'; binding: 'static' | 'slot'; ... }
to
  { kind: 'static' | 'slot' | 'spread'; ... }

but the spread-slots test added by lynx-family#2630 was left on the old shape. The
new implementation only matches `kind: 'static'` and `kind: 'slot'` and
falls through to the spread branch otherwise, so old-shape descriptors
with `kind: 'attribute', binding: 'slot'` (carrying a non-record value)
silently get skipped, producing wrong results.

Update both cases in templateTree.test.ts to the new descriptor shape.
This is a drive-by fix on top of my web-core merge: every PR rebased on
top of main is currently blocked by this test in the `test-react` job.
upupming added a commit to upupming/lynx-stack that referenced this pull request May 15, 2026
…escriptor shape

`refactor(react): simplify template attribute descriptors (lynx-family#2643)` on
`upstream/main` changed `CompiledAttributeDescriptor` from
`{kind: 'attribute' | 'spread', binding: 'static' | 'slot', ...}` to
`{kind: 'static' | 'slot' | 'spread', ...}`. The fixtures inside
`templateTree.test.ts` (added by lynx-family#2630) still use the old shape and
silently fail the "spread → slot" precedence assertion because the
`kind: 'attribute'` descriptors no longer match any branch in the
refactored `applyCompiledAttributes`.

Neither lynx-family#2630 nor lynx-family#2643 has had a Test workflow run on `upstream/main`
yet, so the regression was not caught upstream — but my branch picks
up both via rebase and the failure blocks this PR's CI. Trivial
fixture-only update; no source / behavior change.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants