vercel-functions: skip default-export rule for App Router pages (#73)

The rule

    pattern: export\s+default\s+function
    message: 'Use named exports (GET, POST, ...) instead of default
             export for route handlers'

fires on every page.tsx, layout.tsx, loading.tsx, error.tsx, not-found.tsx,
sitemap.ts, and template.tsx in a Next.js App Router project, where a default
export is *required*. Reported in anthropics/claude-code#54989 with concrete
reproduction (a 44-page App Router project firing the error on every page
write/edit).

Add a skipIfFileContains regex that matches the strongest signals of an
App Router file (and not a route handler):

- 'use client' directive
- App Router config exports (metadata / dynamic / revalidate / fetchCache /
  runtime)
- export default function whose name ends in Page / Layout / Loading /
  Error / NotFound / Sitemap / Template / Default (also lowercase
  sitemap / robots / opengraph / manifest for the convention files)
- JSX with a capitalised component tag
- a destructured `{ children }` parameter (layouts / templates)
- a `MetadataRoute` reference (sitemap, robots, manifest helpers)
- imports from next/font, next/image, next/link, next/navigation,
  next/headers, next/cookies

Each of these is overwhelmingly common in App Router pages and overwhelmingly
absent from route.ts handlers. A bug `export default function handler` in
a route.ts still fires the rule because none of the patterns match.

Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
This commit is contained in:
Matt Van Horn
2026-06-21 22:02:59 -07:00
committed by GitHub
parent b2f2bc09dd
commit 5f3f0ad793
2 changed files with 9 additions and 1 deletions
+2 -1
View File
@@ -4660,7 +4660,8 @@
{
"pattern": "export\\s+default\\s+function",
"message": "Use named exports (GET, POST, PUT, DELETE) instead of default export for route handlers",
"severity": "error"
"severity": "error",
"skipIfFileContains": "(?:^|\\\\n)\\\\s*['\\\"]use\\\\s+client['\\\"]|export\\\\s+const\\\\s+(?:metadata|dynamic|revalidate|fetchCache|runtime)\\\\b|export\\\\s+default\\\\s+(?:async\\\\s+)?function\\\\s+\\\\w*(?:Page|Layout|Loading|Error|NotFound|Sitemap|Template|Default|sitemap|robots|opengraph|manifest)\\\\b|<[A-Z][A-Za-z0-9]*|\\\\{\\\\s*children\\\\s*[,}:]|MetadataRoute\\\\.|from\\\\s+['\\\"]next/(?:font|image|link|navigation|headers|cookies)['\\\"]"
},
{
"pattern": "NextApiRequest|NextApiResponse",
+7
View File
@@ -27,6 +27,13 @@ validate:
pattern: export\s+default\s+function
message: 'Use named exports (GET, POST, PUT, DELETE) instead of default export for route handlers'
severity: error
# Skip on App Router page / layout / loading / error / not-found / sitemap / template / default files,
# which require a default export by Next.js convention. Detected via the 'use client' directive,
# an App Router config export (metadata, dynamic, revalidate, fetchCache, runtime), an `export default
# function` whose name matches an App Router file (Page / Layout / Loading / etc.), or any JSX
# element with a capitalised component tag — all signals that the file is a page-style file rather
# than a route handler. See anthropics/claude-code#54989.
skipIfFileContains: "(?:^|\\n)\\s*['\"]use\\s+client['\"]|export\\s+const\\s+(?:metadata|dynamic|revalidate|fetchCache|runtime)\\b|export\\s+default\\s+(?:async\\s+)?function\\s+\\w*(?:Page|Layout|Loading|Error|NotFound|Sitemap|Template|Default|sitemap|robots|opengraph|manifest)\\b|<[A-Z][A-Za-z0-9]*|\\{\\s*children\\s*[,}:]|MetadataRoute\\.|from\\s+['\"]next/(?:font|image|link|navigation|headers|cookies)['\"]"
-
pattern: NextApiRequest|NextApiResponse
message: 'NextApiRequest/NextApiResponse are Pages Router types — use Web API Request/Response'