Skip to content

Detect Next.js App Router vs Pages Router with route counting #8

@mvoutov

Description

@mvoutov

What

Upgrade Next.js detection from "this project uses Next.js" to a rich architecture summary: which router, how many routes, how many server vs client components, middleware presence.

Why

The current scanner detects nextjs from package.json but says nothing about the architecture. Knowing "App Router with 23 routes and 6 client components" produces dramatically better skills than just "uses Next.js."

How

Edit src/lib/scanner.js. Add a probeNextjsArchitecture(repoPath) function that runs when nextjs is in the detected frameworks.

What to detect

Router type:

  • app/ directory (or src/app/) with layout.tsx/layout.jsApp Router
  • pages/ directory (or src/pages/) → Pages Router
  • Both present → Both (migration in progress)

Route counting (App Router):

  • Glob for app/**/page.tsx and app/**/page.js — each is a route
  • Glob for app/**/route.ts and app/**/route.js — each is an API endpoint
  • Count dynamic routes: directories matching [param], [...param], [[...param]]
  • Count route groups: directories matching (groupName)

Route counting (Pages Router):

  • Every file directly under pages/ (except _app, _document, _error) is a route
  • Files under pages/api/ are API routes

Server vs Client components (App Router only):

  • Read the first line of every .tsx/.jsx file under app/
  • Files starting with 'use client' or "use client" → client component
  • Everything else → server component (default)

Middleware:

  • middleware.ts or middleware.js at project root → Edge Middleware present

Implementation

function probeNextjsArchitecture(repoPath) {
  const result = { router: null, routes: 0, apiRoutes: 0, serverComponents: 0, clientComponents: 0, hasMiddleware: false };

  // Check both root and src/ for app/ and pages/
  const appDir = findDir(repoPath, ['app', 'src/app']);
  const pagesDir = findDir(repoPath, ['pages', 'src/pages']);

  if (appDir && existsSync(join(appDir, 'layout.tsx')) || existsSync(join(appDir, 'layout.js'))) {
    result.router = pagesDir ? 'both' : 'app';
    // Count routes: walk app/ for page.tsx/page.js files
    // Count API routes: walk app/ for route.ts/route.js files
    // Count server vs client: read first line of .tsx/.jsx files
  } else if (pagesDir) {
    result.router = 'pages';
    // Count files in pages/ (excluding _app, _document, _error)
    // Count files in pages/api/
  }

  // Middleware
  result.hasMiddleware = existsSync(join(repoPath, 'middleware.ts')) || existsSync(join(repoPath, 'middleware.js'));

  return result;
}

// Helper to find a directory from a list of candidates
function findDir(repoPath, candidates) {
  for (const candidate of candidates) {
    const full = join(repoPath, candidate);
    if (existsSync(full) && isDir(full)) return full;
  }
  return null;
}

Call from scanRepo() when nextjs is in frameworks:

if (result.frameworks.includes('nextjs')) {
  result.nextjsArchitecture = probeNextjsArchitecture(repoPath);
}

Display in src/commands/scan.js:

  Next.js: App Router — 23 routes, 4 API endpoints, 47 server / 6 client components, middleware

Performance note

The 'use client' check reads the first line of each .tsx/.jsx file under app/. For a project with 200 components, that's 200 readFileSync calls reading ~100 bytes each. This is fast (<50ms).

Files to change

  • src/lib/scanner.js — add probeNextjsArchitecture(), call from scanRepo()
  • src/commands/scan.js — display architecture details
  • tests/scanner.test.js — add tests for App Router, Pages Router, both, middleware

Acceptance criteria

  • npm test passes
  • Detects App Router vs Pages Router correctly
  • Counts routes and API endpoints
  • Counts server vs client components
  • Detects middleware presence
  • Works with both app/ at root and src/app/

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions