Skip to content

Conversation

@colinaaa
Copy link
Collaborator

@colinaaa colinaaa commented Jul 7, 2025

Summary

Improve the error message when setting circular reference as attributes.

close: m-6732040310

Checklist

  • Tests updated (or not required).
  • Documentation updated (or not required).

Summary by CodeRabbit

  • New Features

    • Introduced enhanced debugging tools that provide component stack traces during rendering in development mode.
    • Improved error messages for circular references in deep equality checks, now including component stack information to aid debugging.
    • Added a new symbolic constant for runtime rendering.
  • Bug Fixes

    • Added error handling for circular references in deep equality checks, preventing crashes and providing clearer error feedback.
  • Tests

    • Added a new test case to verify correct error handling when circular references are encountered in component props.
  • Chores

    • Updated test coverage settings to exclude debugging utilities from coverage reports.
    • Added an empty changeset file for tracking future changes.

@changeset-bot
Copy link

changeset-bot bot commented Jul 7, 2025

🦋 Changeset detected

Latest commit: c2ceb65

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

This PR includes changesets to release 0 packages

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

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

@codspeed-hq
Copy link

codspeed-hq bot commented Jul 7, 2025

CodSpeed Performance Report

Merging #1238 will not alter performance

Comparing colinaaa:colin/0707/circular (c2ceb65) with main (ec7228f)

Summary

✅ 10 untouched benchmarks

@relativeci
Copy link

relativeci bot commented Jul 7, 2025

Web Explorer

#3062 Bundle Size — 304.59KiB (0%).

c2ceb65(current) vs ec7228f main#3054(baseline)

Bundle metrics  Change 1 change
                 Current
#3062
     Baseline
#3054
No change  Initial JS 140.65KiB 140.65KiB
No change  Initial CSS 31.83KiB 31.83KiB
No change  Cache Invalidation 0% 0%
No change  Chunks 4 4
No change  Assets 5 5
Change  Modules 205(+0.49%) 204
No change  Duplicate Modules 17 17
No change  Duplicate Code 4.22% 4.22%
No change  Packages 3 3
No change  Duplicate Packages 0 0
Bundle size by type  no changes
                 Current
#3062
     Baseline
#3054
No change  JS 221.88KiB 221.88KiB
No change  Other 50.89KiB 50.89KiB
No change  CSS 31.83KiB 31.83KiB

Bundle analysis reportBranch colinaaa:colin/0707/circularProject dashboard


Generated by RelativeCIDocumentationReport issue

@relativeci
Copy link

relativeci bot commented Jul 7, 2025

React Example

#3072 Bundle Size — 234.54KiB (+0.04%).

c2ceb65(current) vs ec7228f main#3063(baseline)

Bundle metrics  Change 4 changes Regression 1 regression
                 Current
#3072
     Baseline
#3063
No change  Initial JS 0B 0B
No change  Initial CSS 0B 0B
Change  Cache Invalidation 37.83% 0%
No change  Chunks 0 0
No change  Assets 4 4
Change  Modules 160(+1.91%) 157
Regression  Duplicate Modules 65(+3.17%) 63
Change  Duplicate Code 45.99%(+0.24%) 45.88%
No change  Packages 2 2
No change  Duplicate Packages 0 0
Bundle size by type  Change 1 change Regression 1 regression
                 Current
#3072
     Baseline
#3063
No change  IMG 145.76KiB 145.76KiB
Regression  Other 88.78KiB (+0.11%) 88.68KiB

Bundle analysis reportBranch colinaaa:colin/0707/circularProject dashboard


Generated by RelativeCIDocumentationReport issue

@colinaaa colinaaa marked this pull request as ready for review July 7, 2025 15:34
@colinaaa colinaaa requested review from Yradex and hzy July 8, 2025 03:13
@colinaaa
Copy link
Collaborator Author

colinaaa commented Jul 8, 2025

bugbot run

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Bug: Asymmetric Stack Operations Cause Corruption

The renderStack operations are asymmetric. The DIFF hook conditionally pushes vnodes (only for isPossibleOwner), while the DIFFED hook unconditionally pops. This imbalance causes renderStack corruption, leading to stack underflows, incorrect component stack traces, and potentially runtime errors or inaccurate getCurrentVNode() results.

packages/react/runtime/src/debug/component-stack.ts#L141-L155

options[DIFFED] = vnode => {
if (isPossibleOwner(vnode)) {
ownerStack.pop();
}
renderStack.pop();
if (oldDiffed) oldDiffed(vnode);
};
options[DIFF] = vnode => {
if (isPossibleOwner(vnode)) {
renderStack.push(vnode);
}
if (oldDiff) oldDiff(vnode);
};

Fix in CursorFix in Web


BugBot free trial expires on July 22, 2025
You have used $0.00 of your $0.00 spend limit so far. Manage your spend limit in the Cursor dashboard.

Was this report helpful? Give feedback by reacting with 👍 or 👎

Yradex
Yradex previously approved these changes Jul 8, 2025
hzy
hzy previously approved these changes Jul 18, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jul 18, 2025

Walkthrough

A new debugging utility for tracking component stack traces during rendering was introduced and integrated into the runtime, activated in development mode. Error handling for circular references in object comparison was improved, with enhanced error messages including component stack traces. Related tests and configuration updates were also made.

Changes

File(s) Change Summary
.changeset/tough-drinks-throw.md Added an empty changeset file.
packages/react/runtime/__test__/snapshot/spread.test.jsx Added a test for handling circular references in spread props, asserting error handling and stack state.
packages/react/runtime/src/debug/component-stack.ts Introduced a utility for tracking and reporting component stack traces during rendering; exported related functions.
packages/react/runtime/src/lynx.ts Imported and invoked component stack setup in development mode.
packages/react/runtime/src/renderToOpcodes/constants.ts Added new exported constant ROOT.
packages/react/runtime/src/utils.ts Enhanced deep equality check to handle circular references, augmenting errors with component stack traces in dev mode.
packages/react/runtime/vitest.config.ts Excluded src/debug/component-stack.ts from coverage reporting.

Sequence Diagram(s)

sequenceDiagram
    participant Dev as Developer
    participant App as React App
    participant Lynx as Lynx Runtime
    participant Stack as Component Stack Utility

    Dev->>App: Runs app in development mode
    App->>Lynx: Renders components
    Lynx->>Stack: setupComponentStack() (if __DEV__)
    App->>Lynx: Triggers deep equality check (isDirectOrDeepEqual)
    Lynx->>Stack: getCurrentVNode(), getOwnerStack()
    Lynx->>App: Throws error with component stack if circular reference detected
Loading

Suggested reviewers

  • Yradex

Poem

A bug hopped in, a circle in disguise,
But now our stack trace helps us analyze.
With new debug tools and tests in tow,
We catch the loops that overflow.
🐇✨
In dev, we see the stack so clear—
Bugs beware, the rabbit’s near!

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

 ERROR  Cannot resolve version $@rspack/core in overrides. The direct dependencies don't have dependency "@rspack/core".
For help, run: pnpm help install


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e520b7b and c2ceb65.

📒 Files selected for processing (1)
  • packages/react/runtime/__test__/snapshot/spread.test.jsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/react/runtime/test/snapshot/spread.test.jsx
✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@colinaaa colinaaa requested a review from hzy July 18, 2025 04:20
Copy link
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 (1)
packages/react/runtime/src/debug/component-stack.ts (1)

117-126: Optimize string concatenation in hot path.

The getOwnerStack function uses repeated string concatenation which could impact performance when building stack traces during error scenarios. Consider using an array and joining at the end.

 export function getOwnerStack(vnode: PatchedVNode): string {
   const stack = [vnode];
   let next = vnode;
   while (next._owner != null) {
     stack.push(next._owner);
     next = next._owner;
   }

-  return stack.reduce((acc, owner) => {
-    acc += `  in ${getDisplayName(owner)}`;
-
-    const source = owner.__source;
-    if (source) {
-      acc += ` (at ${source.fileName}:${source.lineNumber})`;
-    }
-
-    return (acc += '\n');
-  }, '');
+  const parts = stack.map(owner => {
+    let line = `  in ${getDisplayName(owner)}`;
+    const source = owner.__source;
+    if (source) {
+      line += ` (at ${source.fileName}:${source.lineNumber})`;
+    }
+    return line;
+  });
+  
+  return parts.join('\n') + '\n';
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6af75c1 and fa5fa88.

📒 Files selected for processing (7)
  • .changeset/tough-drinks-throw.md (1 hunks)
  • packages/react/runtime/__test__/snapshot/spread.test.jsx (1 hunks)
  • packages/react/runtime/src/debug/component-stack.ts (1 hunks)
  • packages/react/runtime/src/lynx.ts (2 hunks)
  • packages/react/runtime/src/renderToOpcodes/constants.ts (1 hunks)
  • packages/react/runtime/src/utils.ts (1 hunks)
  • packages/react/runtime/vitest.config.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
packages/react/runtime/vitest.config.ts (1)
Learnt from: PupilTong
PR: lynx-family/lynx-stack#1029
File: packages/web-platform/web-core/src/uiThread/createRenderAllOnUI.ts:95-99
Timestamp: 2025-07-16T06:28:26.421Z
Learning: In the lynx-stack codebase, CSS selectors in SSR hydration are generated by their own packages, ensuring a predictable format that makes simple string manipulation safe and preferable over regex for performance reasons.
packages/react/runtime/src/lynx.ts (3)
Learnt from: PupilTong
PR: lynx-family/lynx-stack#1029
File: packages/web-platform/web-core-server/src/createLynxView.ts:0-0
Timestamp: 2025-07-16T06:26:22.177Z
Learning: In the lynx-stack SSR implementation, each createLynxView instance is used to render once and then discarded. There's no reuse of the same instance for multiple renders, so event arrays and other state don't need to be cleared between renders.
Learnt from: PupilTong
PR: lynx-family/lynx-stack#1029
File: packages/web-platform/web-core/src/uiThread/createRenderAllOnUI.ts:95-99
Timestamp: 2025-07-16T06:28:26.421Z
Learning: In the lynx-stack codebase, CSS selectors in SSR hydration are generated by their own packages, ensuring a predictable format that makes simple string manipulation safe and preferable over regex for performance reasons.
Learnt from: PupilTong
PR: lynx-family/lynx-stack#1292
File: packages/web-platform/web-core-server/src/createLynxView.ts:144-151
Timestamp: 2025-07-15T10:00:56.154Z
Learning: In the lynx-stack codebase, PupilTong prefers the "let it crash" approach over defensive null safety checks when the condition should never occur in normal operation. This applies to cases like the `element.getAttribute(lynxUniqueIdAttribute)!` call in SSR event capture where the attribute is expected to always be present.
🧬 Code Graph Analysis (1)
packages/react/runtime/src/lynx.ts (1)
packages/react/runtime/src/debug/component-stack.ts (1)
  • setupComponentStack (134-174)
🔇 Additional comments (10)
.changeset/tough-drinks-throw.md (1)

1-4: Empty changeset file needs content.

The changeset file contains only YAML front matter delimiters without any content. For a bug fix that improves error messages, this should include a description of the change and appropriate versioning metadata.

packages/react/runtime/vitest.config.ts (1)

88-88: Appropriate exclusion of debug instrumentation from coverage.

Excluding the component stack debugging module from test coverage is the right approach, as this instrumentation code serves diagnostic purposes and aligns with other debug files already excluded.

packages/react/runtime/src/renderToOpcodes/constants.ts (1)

10-10: Verify the ROOT constant value doesn't conflict with PARENT.

The new ROOT constant has the same value '__' as the existing PARENT constant (line 15). While they may serve different purposes (ROOT as a lifecycle hook, PARENT as a VNode property), having identical values could lead to confusion or potential conflicts.

Please confirm this duplication is intentional and won't cause issues in the runtime.

packages/react/runtime/src/lynx.ts (2)

9-9: Proper integration of component stack debugging.

The import of setupComponentStack correctly brings in the new debugging functionality for tracking component stacks.


39-41: Appropriate conditional setup of component stack tracking.

The component stack setup is correctly placed in a development-only conditional block, ensuring the debugging instrumentation is only active when needed. The placement after the main thread setup ensures it runs in both main and background threads during development.

packages/react/runtime/src/utils.ts (2)

7-7: Proper import of component stack utilities.

The import of debugging utilities from the component stack module enables the enhanced error messaging functionality.


13-33: Excellent enhancement of circular reference error handling.

The implementation effectively addresses the PR objective by:

  1. Robust error handling: Wrapping JSON.stringify in try-catch to handle circular references gracefully
  2. Cross-engine compatibility: The regex pattern /circular|cyclic/i covers error messages from different JavaScript engines (PrimJS, V8, JavaScriptCore) as documented in the comments
  3. Enhanced debugging: In development mode, circular reference errors are augmented with component stack traces, providing valuable context for developers
  4. Proper error propagation: The error is rethrown after enhancement, maintaining the original error semantics

The approach is well-tested across different engines (as mentioned in past review comments) and provides a significant improvement to developer experience when debugging circular reference issues.

packages/react/runtime/__test__/snapshot/spread.test.jsx (1)

639-647: Well-structured test for circular reference error handling.

The test correctly validates that circular references in spread props throw a TypeError with an enhanced error message that includes the component stack trace.

packages/react/runtime/src/debug/component-stack.ts (2)

134-174: Well-implemented component stack tracking with proper lifecycle integration.

The implementation correctly patches Preact's lifecycle hooks to maintain component stacks for debugging. The owner tracking logic properly handles the component hierarchy.


142-173: Consider adding error boundaries for stack operations.

The stack push/pop operations in the lifecycle hooks could throw errors if the stacks get out of sync. Consider adding try-catch blocks to prevent breaking the render cycle.

Example for one of the hooks:

 options[DIFFED] = vnode => {
+  try {
     if (isPossibleOwner(vnode)) {
       ownerStack.pop();
     }
     renderStack.pop();
+  } catch (e) {
+    if (__DEV__) {
+      console.error('Component stack tracking error:', e);
+    }
+  }
   if (oldDiffed) oldDiffed(vnode);
 };
⛔ Skipped due to learnings
Learnt from: PupilTong
PR: lynx-family/lynx-stack#1292
File: packages/web-platform/web-core-server/src/createLynxView.ts:144-151
Timestamp: 2025-07-15T10:00:56.154Z
Learning: In the lynx-stack codebase, PupilTong prefers the "let it crash" approach over defensive null safety checks when the condition should never occur in normal operation. This applies to cases like the `element.getAttribute(lynxUniqueIdAttribute)!` call in SSR event capture where the attribute is expected to always be present.

colinaaa and others added 2 commits July 18, 2025 12:24
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Signed-off-by: Qingyu Wang <[email protected]>
@codecov
Copy link

codecov bot commented Jul 18, 2025

Codecov Report

Attention: Patch coverage is 92.85714% with 6 lines in your changes missing coverage. Please review.

✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
...ackages/react/runtime/src/debug/component-stack.ts 90.76% 6 Missing ⚠️

📢 Thoughts on this report? Let us know!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants