Page Component Mapper
Given a route, produce a depth-limited component tree with file paths, server/client boundaries, props, hooks, state management, data fetching patterns, and styling metadata. Output a ComponentMap artifact consumed by all downstream skills.
Procedure
Step 0: Check Cache
- •Look for
.claude/qa-cache/component-maps/{route-slug}.json. - •If found, follow the caching protocol in
references/caching-protocol.md. - •If cache is valid, return the cached map and print:
Using cached map ({age} old, {N} components, completeness: {level}) | --fresh to rebuild - •If
--freshflag provided, skip cache entirely.
Step 1: Resolve tsconfig Path Aliases (PREREQUISITE)
- •Read
tsconfig.jsonat the project root. If it has anextendsfield, read the parent config too. - •Extract
compilerOptions.pathsandcompilerOptions.baseUrl. - •Build a lookup table mapping each alias pattern to its resolved directory.
Example:
{ "@/*": "./src/*", "~/lib/*": "./lib/*" }. - •Keep this table in working memory -- use it to resolve every aliased import in Steps 2-3.
- •If
tsconfig.jsonis missing, warn the user and proceed with relative-path-only resolution.
Step 2: Identify Route Entry Points
App Router (default -- app/ directory exists):
- •Locate
app/{route}/page.tsx(or.ts,.jsx,.js). - •Also locate if present:
layout.tsx,loading.tsx,error.tsx,not-found.tsxin the same directory and parent directories up toapp/layout.tsx. - •The trace order is: root layout -> nested layouts -> loading -> error -> page.
Pages Router (fallback -- pages/ directory, no app/):
- •Locate
pages/{route}.tsx(orpages/{route}/index.tsx). - •Also check
pages/_app.tsxandpages/_document.tsx. - •Look for
getServerSidePropsorgetStaticPropsexports.
Step 3: Trace Imports (Depth-Limited)
Default depth: 3 levels from route entry. Override with --depth N.
For each file, starting from the entry points found in Step 2:
- •Read the file. Scan the first 5 lines for
'use client'or'use server'directives. - •Extract all import statements. For each import, resolve the target:
- •Relative (
./Button): try.tsx,.ts,.jsx,.js,/index.tsx,/index.ts - •Aliased (
@/components/Button): resolve using the Step 1 lookup table, then try extensions - •External (
react,next/link,@radix-ui/*): record as external, stop tracing - •Dynamic (
next/dynamic(() => import(...)),React.lazy): trace literal paths, flagisDynamic: true - •Barrel (
import { X } from './components'): follow re-export through index files, cap at 5 levels
- •Relative (
- •For detailed resolution rules and edge cases, read
references/import-tracing-protocol.md. - •For each resolved component file, extract:
- •
displayName: the component function/const name - •
boundary:"server"(has'use server'or inapp/without directive),"client"(has'use client'),"shared"(no directive, outsideapp/) - •
props: from TypeScript type annotation on the component parameter - •
hooks: alluseXxx(calls - •
stateManagement: imports fromzustand,jotai,recoil,@tanstack/react-query, or React context usage - •
dataFetching:fetch()calls, server actions, tRPC calls, React Query hooks, SWR hooks - •
styling_approach: based on import type (.module.css-> css-modules, tailwind classes in JSX -> tailwind) - •
has_conditional_classes:clsx(,cn(,classNames(, or ternary in className - •
design_system_component: true if imported from a known UI library - •
accepts_className_prop: true if props type includesclassName - •
layout_role: infer from file location and component name
- •
- •If at the depth limit, record remaining imports with
unresolved: true, unresolvedReason: "depth-limit".
Step 4: Classify Completeness
- •
"full":unresolvedImportsarray is empty - •
"shallow": all unresolved entries have reason"depth-limit" - •
"partial": any unresolved entry has a non-depth reason ("dynamic-computed","barrel-depth-exceeded","not-found")
Step 5: Persist and Present
- •Collect all resolved file paths into
cachedFilesarray. - •Write the ComponentMap to
.claude/qa-cache/component-maps/{route-slug}.json. - •Present a summary to the user:
code
Component Map: /dashboard/settings (depth 3, shallow) Entry: app/dashboard/settings/page.tsx [server] Layout: app/dashboard/layout.tsx [server] 23 components mapped | 4 unresolved (depth-limit) | 2 external (shadcn) Server (8): SettingsPage, SettingsLayout, AccountSection, ... Client (12): ThemeToggle, NotificationForm, ProfileEditor, ... Shared (3): Button, Card, Input Data flow: 2 server actions, 1 React Query hook, 3 prop chains Hooks: useState(5), useEffect(3), useForm(2), useQuery(1)
- •Wait for user confirmation before the coordinator proceeds to diagnosis.
Output Format
The ComponentMap JSON follows the schema in the design document. Key fields:
json
{
"version": "1.0",
"route": "/dashboard/settings",
"depth": 3,
"completeness": "shallow",
"framework": { "name": "next", "routerType": "app", "version": "14.1.0" },
"entryComponent": {
"filePath": "/abs/path/app/dashboard/settings/page.tsx",
"displayName": "SettingsPage",
"boundary": "server",
"imports": [{ "targetPath": "...", "importType": "component" }]
},
"unresolvedImports": [{ "sourcePath": "...", "importSpecifier": "./DeepChild", "reason": "depth-limit" }],
"cachedFiles": ["..."]
}