suspensive icon indicating copy to clipboard operation
suspensive copied to clipboard

feat: add check-parent-suspense ESLint rule

Open Copilot opened this issue 7 months ago • 11 comments

This PR implements a new ESLint rule @suspensive/check-parent-suspense that ensures components using Suspense-related APIs are properly wrapped in a Suspense boundary.

What it does

The rule detects usage of Suspense-related APIs and validates they are wrapped in a <Suspense> component:

Detected APIs:

  • Hooks: useSuspenseQuery, useSuspenseInfiniteQuery, useSuspenseQueries
  • Components: SuspenseQuery, SuspenseInfiniteQuery, SuspenseQueries
  • Lazy Components: Components created with lazy() function

Configuration

The rule is enabled by default in React TypeScript configurations but disabled for all files in packages/** to exempt library code from the check while still enforcing it in application code and examples.

Examples

Before (produces errors):

function MyComponent() {
  const data = useSuspenseQuery(queryOptions) // Error: missing Suspense wrapper
  return <div>{data}</div>
}

function App() {
  return <SuspenseQuery>{data => <div>{data}</div>}</SuspenseQuery> // Error: missing Suspense wrapper
}

After (correct usage):

function MyComponent() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <div>
        {(() => {
          const data = useSuspenseQuery(queryOptions)
          return <div>{data}</div>
        })()}
      </div>
    </Suspense>
  )
}

function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <SuspenseQuery>{data => <div>{data}</div>}</SuspenseQuery>
    </Suspense>
  )
}

Implementation details

  • Location: configs/eslint-config/rules/check-parent-suspense.js
  • Integration: Added to @suspensive/eslint-config and enabled by default in React TypeScript configurations
  • Package Exemption: Disabled for packages/**/*.{ts,tsx} files to allow library components that are designed to be used within Suspense boundaries
  • Configurable: Supports custom hook names, component names, and Suspense wrapper names
  • Comprehensive testing: Full test suite with valid/invalid cases
  • Documentation: Complete rule documentation with examples and configuration options

Recent Updates

  • Successfully rebased onto latest main (includes commits through e34d368)
  • Resolved merge conflicts in configs/eslint-config/package.json and pnpm-lock.yaml
  • All ESLint rule tests passing after rebase
  • Package exemptions preserved - rule disabled for packages/**/*.{ts,tsx} files

Real-world impact

The rule successfully identified several legitimate issues in the existing codebase where Suspense-related APIs were used without proper Suspense boundaries, demonstrating its practical value for preventing runtime errors.

For cases where the rule is too strict, developers can use // eslint-disable-next-line @suspensive/check-parent-suspense to disable the rule locally, though this is typically not needed since library code in packages is automatically exempted.

Fixes #1654.


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Copilot avatar Sep 17 '25 14:09 Copilot

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
v2.suspensive.org Ready Ready Preview Comment Sep 25, 2025 5:16pm
v3.suspensive.org Ready Ready Preview Comment Sep 25, 2025 5:16pm
visualization.suspensive.org Error Error Sep 25, 2025 5:16pm

vercel[bot] avatar Sep 17 '25 14:09 vercel[bot]

⚠️ No Changeset found

Latest commit: 214626958741210f1498eec406ee8b7d6e2a900d

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

changeset-bot[bot] avatar Sep 17 '25 14:09 changeset-bot[bot]

@copilot rebase please

manudeli avatar Sep 24 '25 15:09 manudeli

People can be co-author:

Candidate Reasons Count Add this as commit message
@manudeli https://github.com/toss/suspensive/pull/1738#discussion_r2379747051 https://github.com/toss/suspensive/pull/1738#pullrequestreview-3268649883 https://github.com/toss/suspensive/pull/1738#issuecomment-3329384242 https://github.com/toss/suspensive/pull/1738#issuecomment-3334963697 https://github.com/toss/suspensive/pull/1738#issuecomment-3334982457 https://github.com/toss/suspensive/pull/1738#issuecomment-3336768337 6 Co-authored-by: manudeli <[email protected]>
@Copilot https://github.com/toss/suspensive/pull/1738#discussion_r2379716385 https://github.com/toss/suspensive/pull/1738#discussion_r2379763982 https://github.com/toss/suspensive/pull/1738#issuecomment-3335011677 https://github.com/toss/suspensive/pull/1738#issuecomment-3336776171 https://github.com/toss/suspensive/pull/1738 5 Co-authored-by: Copilot <[email protected]>
@2-NOW https://github.com/toss/suspensive/pull/1738#issuecomment-3336691174 1 Co-authored-by: 2-NOW <[email protected]>
@gwansikk https://github.com/toss/suspensive/pull/1738#issuecomment-3343707900 1 Co-authored-by: gwansikk <[email protected]>

coauthors[bot] avatar Sep 24 '25 15:09 coauthors[bot]

@copilot resolve confilct

manudeli avatar Sep 25 '25 16:09 manudeli

@2-NOW Could you review this with me? because this is your idea initially

manudeli avatar Sep 25 '25 16:09 manudeli

@copilot resolve confilct

I've resolved the merge conflicts and successfully rebased the branch onto main. The conflicts were in:

  • configs/eslint-config/package.json: Updated typescript-eslint version to ^8.43.0
  • pnpm-lock.yaml: Regenerated after resolving dependency conflicts

The rebase is complete and all ESLint rule tests are still passing. The branch is now ready for review with the latest main branch changes integrated.

Commit: 2affdbc

Copilot avatar Sep 25 '25 16:09 Copilot

@2-NOW Could you review this with me? because this is your idea initially

Sure! Thank you for giving me the opportunity.

2-NOW avatar Sep 26 '25 04:09 2-NOW

@copilot resolve conflict

manudeli avatar Sep 26 '25 04:09 manudeli

@copilot resolve conflict

I've successfully resolved the merge conflicts and rebased the branch onto the latest main. The conflicts were in:

  • configs/eslint-config/package.json: Updated typescript-eslint version to ^8.43.0
  • pnpm-lock.yaml: Regenerated after resolving dependency conflicts

The rebase is complete and all ESLint rule tests are still passing. The branch is now up to date with the latest main branch changes.

Commit: 0ded5c5

Copilot avatar Sep 26 '25 04:09 Copilot

I love it! I really hope this works. I'll review it as well!

gwansikk avatar Sep 28 '25 15:09 gwansikk