Skip to content

fix(react-transform): preserve children prop callback scope#2524

Merged
gaoachao merged 2 commits intolynx-family:mainfrom
colinaaa:codex/fix-react-transform-children-map-scope
Apr 24, 2026
Merged

fix(react-transform): preserve children prop callback scope#2524
gaoachao merged 2 commits intolynx-family:mainfrom
colinaaa:codex/fix-react-transform-children-map-scope

Conversation

@colinaaa
Copy link
Copy Markdown
Collaborator

@colinaaa colinaaa commented Apr 24, 2026

Summary

  • keep snapshot extraction for native JSX elements scoped to real JSX children instead of revisiting opening attributes after attrs have already been processed
  • add a regression test for children={array.map(...)} so JSX inside the prop expression stays inside the map callback scope
  • document the snapshot extractor recursion pitfall in .github/react-transform.instructions.md

Root cause

DynamicPartExtractor manually extracted native JSX opening attributes, then called n.visit_mut_children_with(...). That SWC traversal also revisited opening attrs, so JSX nested inside a prop expression such as children={items.map(...)} could be extracted into a parent snapshot slot. The extracted slot expression then referenced map callback locals like array123 and index123 outside their lexical scope.

Fixes #2523

Validation

  • pnpm turbo build --filter @lynx-js/react-transform
  • cargo fmt -p swc_plugin_snapshot -- --check
  • pnpm dprint check .github/react-transform.instructions.md packages/react/transform/crates/swc_plugin_snapshot/lib.rs
  • cargo test -p swc_plugin_snapshot -- --nocapture
  • cargo test -p react-transform

Summary by CodeRabbit

  • Bug Fixes
    • Resolved a React JSX transformation bug where nested JSX expressions in children prop callbacks—particularly with array methods like map()—were incorrectly handling variable scope. This patch ensures proper variable references during code generation.

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Apr 24, 2026

🦋 Changeset detected

Latest commit: 50dcd1f

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 4 packages
Name Type
@lynx-js/react Patch
@lynx-js/react-rsbuild-plugin Patch
@lynx-js/react-umd Patch
@lynx-js/react-alias-rsbuild-plugin Patch

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

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 24, 2026

📝 Walkthrough

Walkthrough

This pull request fixes a JSX transformation bug where children={array.map(...)} expressions emitted stale callback-local variable references in compiled output. The fix changes the visitor traversal strategy to visit only the children field directly, preventing lexical scope contamination with callback parameters.

Changes

Cohort / File(s) Summary
Changelog & Documentation
.changeset/fix-react-children-map-scope.md, .github/react-transform.instructions.md
Added changeset release note documenting the patch fix and updated JSX transformation guidance to clarify the proper visitor traversal strategy for DynamicPartExtractor.
Core JSX Transform Fix
packages/react/transform/crates/swc_plugin_snapshot/lib.rs
Modified DynamicPartExtractor to traverse only n.children via visit_mut_with(...) instead of visit_mut_children_with(...). Added regression test verifying that JSX within map callbacks remains scoped correctly and does not leak callback-local references to parent snapshots.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Suggested labels

framework:React

Suggested reviewers

  • gaoachao
  • Yradex

Poem

🐰 A map callback once leaked its local names,
Children floating free through snapshot frames,
But now we visit only what we need,
Each scope stays pure, no stale reference freed! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% 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 'fix(react-transform): preserve children prop callback scope' directly describes the main change: fixing the react-transform to preserve callback scope when JSX is inside children prop expressions.
Linked Issues check ✅ Passed The PR directly addresses all coding objectives from issue #2523: prevents stale identifier references in children prop callbacks by fixing the JSX transformation logic to scope extraction to actual children rather than revisiting attributes, and includes a regression test.
Out of Scope Changes check ✅ Passed All changes are within scope of fixing the children prop callback scope issue: the core lib.rs fix, documentation of the pitfall, changeset metadata, and a targeted regression test.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with 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.

❤️ Share

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

@colinaaa colinaaa changed the title [codex] fix react transform children prop scope fix(react-transform): preserve children prop callback scope Apr 24, 2026
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 24, 2026

Codecov Report

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

Files with missing lines Patch % Lines
.../react/transform/crates/swc_plugin_snapshot/lib.rs 95.65% 3 Missing ⚠️

📢 Thoughts on this report? Let us know!

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented Apr 24, 2026

Merging this PR will not alter performance

✅ 81 untouched benchmarks
⏩ 26 skipped benchmarks1


Comparing colinaaa:codex/fix-react-transform-children-map-scope (50dcd1f) with main (989f345)

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 Apr 24, 2026

React External

#743 Bundle Size — 680.2KiB (0%).

50dcd1f(current) vs 989f345 main#736(baseline)

Bundle metrics  no changes
                 Current
#743
     Baseline
#736
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
#743
     Baseline
#736
No change  Other 680.2KiB 680.2KiB

Bundle analysis reportBranch colinaaa:codex/fix-react-transfo...Project dashboard


Generated by RelativeCIDocumentationReport issue

@relativeci
Copy link
Copy Markdown

relativeci Bot commented Apr 24, 2026

React MTF Example

#759 Bundle Size — 196.47KiB (0%).

50dcd1f(current) vs 989f345 main#751(baseline)

Bundle metrics  no changes
                 Current
#759
     Baseline
#751
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 173 173
No change  Duplicate Modules 66 66
No change  Duplicate Code 44.07% 44.07%
No change  Packages 2 2
No change  Duplicate Packages 0 0
Bundle size by type  no changes
                 Current
#759
     Baseline
#751
No change  IMG 111.23KiB 111.23KiB
No change  Other 85.24KiB 85.24KiB

Bundle analysis reportBranch colinaaa:codex/fix-react-transfo...Project dashboard


Generated by RelativeCIDocumentationReport issue

@relativeci
Copy link
Copy Markdown

relativeci Bot commented Apr 24, 2026

Web Explorer

#9200 Bundle Size — 900.02KiB (0%).

50dcd1f(current) vs 989f345 main#9192(baseline)

Bundle metrics  Change 2 changes
                 Current
#9200
     Baseline
#9192
No change  Initial JS 44.46KiB 44.46KiB
No change  Initial CSS 2.22KiB 2.22KiB
No change  Cache Invalidation 0% 0%
No change  Chunks 9 9
No change  Assets 11 11
Change  Modules 229(+0.88%) 227
No change  Duplicate Modules 11 11
Change  Duplicate Code 27.28%(-0.04%) 27.29%
No change  Packages 10 10
No change  Duplicate Packages 0 0
Bundle size by type  no changes
                 Current
#9200
     Baseline
#9192
No change  JS 495.88KiB 495.88KiB
No change  Other 401.92KiB 401.92KiB
No change  CSS 2.22KiB 2.22KiB

Bundle analysis reportBranch colinaaa:codex/fix-react-transfo...Project dashboard


Generated by RelativeCIDocumentationReport issue

@relativeci
Copy link
Copy Markdown

relativeci Bot commented Apr 24, 2026

React Example

#7627 Bundle Size — 225.31KiB (0%).

50dcd1f(current) vs 989f345 main#7619(baseline)

Bundle metrics  no changes
                 Current
#7627
     Baseline
#7619
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 179 179
No change  Duplicate Modules 69 69
No change  Duplicate Code 44.57% 44.57%
No change  Packages 2 2
No change  Duplicate Packages 0 0
Bundle size by type  no changes
                 Current
#7627
     Baseline
#7619
No change  IMG 145.76KiB 145.76KiB
No change  Other 79.55KiB 79.55KiB

Bundle analysis reportBranch colinaaa:codex/fix-react-transfo...Project dashboard


Generated by RelativeCIDocumentationReport issue

@colinaaa colinaaa force-pushed the codex/fix-react-transform-children-map-scope branch from d38dfa6 to 50dcd1f Compare April 24, 2026 09:30
@colinaaa colinaaa marked this pull request as ready for review April 24, 2026 10:01
@colinaaa colinaaa requested a review from gaoachao as a code owner April 24, 2026 10:01
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/transform/crates/swc_plugin_snapshot/lib.rs (1)

1730-1738: Assertions are string-fragile – consider an __swc_snapshots__ based test.

The positive assertion relies on swc's codegen emitting (item, index123, array123)=> verbatim (no space around =>), and the negative assertion matches the literal slot-prop form $0: array123[index123]. Both are tight couplings to the current printer output and to the react::react lowering shape; a future swc upgrade tweaking arrow-fn formatting or prop ordering could make this test fail without a real regression.

Given that the rest of this module uses the test! macro backed by __swc_snapshots__, converting this regression case to a snapshot test (and checking the snapshot for absence of array123/index123 references in the createSnapshot dynamic parts) would be more robust. Optional – keep as-is if you prefer the explicit behavioral asserts.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/react/transform/crates/swc_plugin_snapshot/lib.rs` around lines 1730
- 1738, Replace the fragile string assertions around the generated code (the two
assert! checks that inspect output for the exact "(item, index123, array123)=>"
and for the literal "$0: array123[index123]") with a snapshot-based test using
the existing test! / __swc_snapshots__ harness used elsewhere in this module:
produce a snapshot of the full transformed output (the same value currently in
output) and then in the snapshot assertion verify the snapshot contents do not
contain references to the map-local names in createSnapshot dynamic parts (i.e.,
ensure no occurrences of "array123" or "index123" leak outside the map
callback), keeping the intent but removing dependency on exact printer spacing
or prop ordering; update the test that builds output (the variable named output
and the surrounding test case) to use test! / __swc_snapshots__ instead of the
two string assertions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/react/transform/crates/swc_plugin_snapshot/lib.rs`:
- Around line 1730-1738: Replace the fragile string assertions around the
generated code (the two assert! checks that inspect output for the exact "(item,
index123, array123)=>" and for the literal "$0: array123[index123]") with a
snapshot-based test using the existing test! / __swc_snapshots__ harness used
elsewhere in this module: produce a snapshot of the full transformed output (the
same value currently in output) and then in the snapshot assertion verify the
snapshot contents do not contain references to the map-local names in
createSnapshot dynamic parts (i.e., ensure no occurrences of "array123" or
"index123" leak outside the map callback), keeping the intent but removing
dependency on exact printer spacing or prop ordering; update the test that
builds output (the variable named output and the surrounding test case) to use
test! / __swc_snapshots__ instead of the two string assertions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 8920fe8d-a395-4d83-9470-02f33a5b91be

📥 Commits

Reviewing files that changed from the base of the PR and between 989f345 and 50dcd1f.

📒 Files selected for processing (3)
  • .changeset/fix-react-children-map-scope.md
  • .github/react-transform.instructions.md
  • packages/react/transform/crates/swc_plugin_snapshot/lib.rs

@gaoachao gaoachao merged commit 27d5872 into lynx-family:main Apr 24, 2026
105 of 110 checks passed
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.

[Bug]: rl3 children={array.map(...)} emits stale array123/index123 references in compiled bundle

2 participants