fix(ci): require a default export before treating a file as a route entry - #7028
fix(ci): require a default export before treating a file as a route entry#7028waleedlatif1 wants to merge 2 commits into
Conversation
…ntry Follow-up to #7026, which added `error.tsx` to the entry filenames and with it picked up `[workspaceId]/components/error/error.tsx` — named like a boundary, and not one. It exports `ErrorShell` and `ErrorState` for the thirteen real boundaries to use; Next would reject it as a boundary for having no default export. Counting it inflated the coverage number and would have recorded a shared component in the graph-weight baseline as though it were a route. The filename was never the right test. Every convention-composed entry must default-export the thing Next renders, so that is the discriminator now. Entry count goes 60 → 59, and all thirteen real `error.tsx` boundaries still walk. Also adds `template.tsx` and `default.tsx`. Neither exists under `app/workspace` today, so this changes nothing now — but the enumeration claims to cover what Next composes, and leaving two out makes that claim false the day someone adds one. Both raised in review on #7026 (Cursor and Greptile respectively); I merged before reading them, so this lands separately.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
PR SummaryLow Risk Overview
Also enumerates Reviewed by Cursor Bugbot for commit c01be22. Configure here. |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit c01be22. Configure here.
| * graph-weight baseline as though it were a route. | ||
| */ | ||
| const DEFAULT_EXPORT_RE = | ||
| /(?:^|\n)\s*export\s+default\b|(?:^|\n)\s*export\s*\{[^}]*\bas\s+default\b/ |
There was a problem hiding this comment.
Default-export check misses re-exports
Low Severity
DEFAULT_EXPORT_RE only recognizes export default and export { … as default }, so a valid Next entry written as export { default } from '…' fails hasDefaultExport and is dropped from the walk. That form already appears under app/workspace, and a route using it would silently skip the registry-boundary and graph-weight checks.
Reviewed by Cursor Bugbot for commit c01be22. Configure here.
There was a problem hiding this comment.
Correct — fixed in 376e373.
The regex required as default, so export { default } from './page' fell through and the entry would have dropped out of the walk entirely, skipping both the registry gate and the graph-weight ratchet. Silent omission is the dangerous direction for this discriminator to fail in, since the whole point is deciding what gets checked.
One scoping note on "already appears under app/workspace": the form does appear, but in a barrel (logs/components/dashboard/index.ts), not in a file named page.tsx/layout.tsx/error.tsx/etc. collectEntries only tests entry filenames, so no entry uses it today — the gap was latent, not live. Fixing it anyway.
Four forms now count: export default …, export { default } from, export { default, … } from, and export { X as default }.
export { default as X } deliberately still does not count — it re-exports another module's default under a name and leaves this module without one. Verified all ten variants including that distinction:
ok DEFAULT export default function Page() {}
ok DEFAULT export { default } from './page'
ok DEFAULT export { default, Foo } from './page'
ok DEFAULT export { Foo as default }
ok no-default export { default as Dashboard } from './dashboard'
ok no-default export { ErrorShell, ErrorState }
ok no-default export const defaultValue = 1
Guard still reports 59 entries with all 13 real error.tsx boundaries walked.
Greptile SummaryThis PR narrows workspace route-entry discovery to convention files with detected default exports and expands the recognized Next.js filenames to include
Confidence Score: 4/5The PR appears safe to merge, with a non-blocking gap in recognizing valid default re-export syntax. Current workspace entries use forms recognized by the new filter, but a future route using a bare default re-export would be omitted from both boundary and graph-weight checks. Files Needing Attention: scripts/check-tool-registry-boundary.ts
|
| Filename | Overview |
|---|---|
| scripts/check-tool-registry-boundary.ts | Improves route-entry enumeration, but the new default-export regex silently excludes entries using export { default } from .... |
Reviews (1): Last reviewed commit: "fix(ci): require a default export before..." | Re-trigger Greptile
| const DEFAULT_EXPORT_RE = | ||
| /(?:^|\n)\s*export\s+default\b|(?:^|\n)\s*export\s*\{[^}]*\bas\s+default\b/ |
There was a problem hiding this comment.
Correct — fixed in 376e373.
The regex required as default, so export { default } from './page' fell through and the entry would have dropped out of the walk entirely, skipping both the registry gate and the graph-weight ratchet. Silent omission is the dangerous direction for this discriminator to fail in, since the whole point is deciding what gets checked.
One scoping note on "already appears under app/workspace": the form does appear, but in a barrel (logs/components/dashboard/index.ts), not in a file named page.tsx/layout.tsx/error.tsx/etc. collectEntries only tests entry filenames, so no entry uses it today — the gap was latent, not live. Fixing it anyway.
Four forms now count: export default …, export { default } from, export { default, … } from, and export { X as default }.
export { default as X } deliberately still does not count — it re-exports another module's default under a name and leaves this module without one. Verified all ten variants including that distinction:
ok DEFAULT export default function Page() {}
ok DEFAULT export { default } from './page'
ok DEFAULT export { default, Foo } from './page'
ok DEFAULT export { Foo as default }
ok no-default export { default as Dashboard } from './dashboard'
ok no-default export { ErrorShell, ErrorState }
ok no-default export const defaultValue = 1
Guard still reports 59 entries with all 13 real error.tsx boundaries walked.
`export { default } from './page'` is a valid Next entry and the regex required
`as default`, so such an entry would have dropped out of the walk and skipped
both the registry gate and the graph-weight ratchet — silently, which is the
dangerous direction for a discriminator to fail in.
Latent rather than live: the form appears once under `app/workspace`, in a
barrel, not in an entry filename.
Four forms now count — `export default …`, `export { default } from`,
`export { default, … } from`, and `export { X as default }`.
`export { default as X }` still does not: it re-exports another module's default
under a name and leaves this one without one. Verified all ten variants,
including that last distinction.
Raised by both Cursor and Greptile on #7028.


Follow-up to #7026. Both points were raised in review there and I merged before reading them, so they land here.
The filename was never the right test
#7026 added
error.tsxto the entry filenames, and with it picked up[workspaceId]/components/error/error.tsx— named like a boundary, and not one:Named exports only. It is the shared helper the thirteen real boundaries render, and Next would reject it as a boundary for having no default export.
Counting it inflated the coverage number and — the part that would have bitten — recorded a shared component in the graph-weight baseline as though it were a route.
Every convention-composed entry must default-export the thing Next renders, so that is the discriminator now.
Entry count 60 → 59. All thirteen real
error.tsxboundaries still walk; only the helper drops out.Completing the enumeration
Also adds
template.tsxanddefault.tsx. Neither exists underapp/workspacetoday, so this changes nothing right now — but the list claims to cover what Next composes, and leaving two out makes that claim false the day someone adds one. That is the same failure the surrounding TSDoc already warns about: "A hardcoded list goes stale silently."Verification
Credit
The non-route entry was Cursor's; the missing
template.tsx/default.tsxwas Greptile's. Both were correct.