From 4bbe8a345bf0c4f433dc08cf83f89a33407d592e Mon Sep 17 00:00:00 2001 From: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Date: Thu, 30 Apr 2026 05:47:04 -0700 Subject: [PATCH] vercel-functions: skip default-export rule for App Router pages 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. --- generated/build-from-skills.manifest.json | 2 +- generated/skill-manifest.json | 5 +++-- skills/vercel-functions/SKILL.md | 7 +++++++ 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/generated/build-from-skills.manifest.json b/generated/build-from-skills.manifest.json index 80e5f63..7e0facf 100644 --- a/generated/build-from-skills.manifest.json +++ b/generated/build-from-skills.manifest.json @@ -1,6 +1,6 @@ { "version": 1, - "generatedAt": "2026-04-11T21:14:22.852Z", + "generatedAt": "2026-04-30T12:46:51.797Z", "templates": [ { "template": "agents/ai-architect.md.tmpl", diff --git a/generated/skill-manifest.json b/generated/skill-manifest.json index 5c0adb2..3c44f1a 100644 --- a/generated/skill-manifest.json +++ b/generated/skill-manifest.json @@ -1,5 +1,5 @@ { - "generatedAt": "2026-04-28T18:37:08.157Z", + "generatedAt": "2026-04-30T12:46:51.769Z", "version": 2, "skills": { "vercel-agent": { @@ -4316,7 +4316,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", diff --git a/skills/vercel-functions/SKILL.md b/skills/vercel-functions/SKILL.md index 16df88e..b32f58d 100644 --- a/skills/vercel-functions/SKILL.md +++ b/skills/vercel-functions/SKILL.md @@ -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'