Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 28 additions & 13 deletions src/app/api/dev/routes/route.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,36 @@
import { glob } from 'glob'
import { readdir } from 'fs/promises'
import { NextResponse } from 'next/server'
import path from 'path'
import { requireDevToolsAccess } from '@/lib/dev-access'


export const dynamic = 'force-dynamic' // Ensure this route is dynamic

const PAGE_EXTENSIONS = new Set(['.tsx', '.js', '.jsx'])

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing .ts extension - Next.js app router supports page.ts files

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!


async function findPageFiles(rootDir: string, currentDir = rootDir): Promise<string[]> {
const entries = await readdir(currentDir, { withFileTypes: true })
const files = await Promise.all(entries.map(async entry => {
if (entry.name.startsWith('.') || entry.name.startsWith('_')) return []

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

underscore filtering differs from glob behavior - glob ignored **/_*/** (folders starting with underscore), but this skips any file/folder starting with underscore at any level, potentially missing valid pages in non-underscore folders


const fullPath = path.join(currentDir, entry.name)
const relativePath = path.relative(rootDir, fullPath)

if (entry.isDirectory()) {
if (entry.name === 'api') return []
return findPageFiles(rootDir, fullPath)
}

if (!entry.isFile()) return []

const parsed = path.parse(entry.name)
const isPageFile = parsed.name === 'page' && PAGE_EXTENSIONS.has(parsed.ext)
return isPageFile ? [relativePath] : []
}))

return files.flat()
}

// Helper to format route label
function formatRouteLabel(routePath: string) {
if (routePath === '/') return 'Home'
Expand All @@ -28,17 +53,7 @@ export async function GET() {
const cwd = process.cwd()
const appDir = path.join(cwd, 'src/app')

// On Windows, glob returns forward slashes, but we should be careful with path joins
// We use fast-glob indirectly via glob, which supports forward slashes on Windows
const files = await glob('**/page.{tsx,js,jsx}', {
cwd: appDir,
ignore: [
'**/api/**', // skip API routes
'**/_*/**', // skip private folders
'**/.*/**', // skip dotfiles/folders
],
nodir: true
})
const files = await findPageFiles(appDir)

const routes = files.map(file => {
// file is relative to appDir, e.g. "(marketing)/about/page.tsx"
Expand All @@ -47,7 +62,7 @@ export async function GET() {
const dirPath = path.dirname(file)

// Split by separator (handle both / and \ just in case, though glob usually returns /)
const segments = dirPath.split(/[/\\]/)
const segments = dirPath.split(path.sep)

const cleanSegments = segments.filter(segment => {
// Remove route groups like (marketing)
Expand Down