1import * as _ from 'lodash-es' 13import path from 'path' 15const serverExportNames = ['getServerSideProps', 'getStaticProps', 'getStaticPaths'] 17const resolveImportPath = async (importPath: string, fromPtPath: string) => { 18 if (!_.startsWith(importPath, '.')) return null 21 const importAbsPath = path.resolve(parentDir, importPath) 27 for (const ext of ptFileExtA) { 28 const pathWithExt = importAbsPath + '.' + ext 36export const getPageExportSources = async (pagePtPath: string) => { 40 const clientSources: string[] = [] 41 const serverSources: string[] = [] 43 // Collect all imports for lookup 44 const importMap: Record<string, string> = {} 45 for (const node of ast.body) { 46 if (node.type === 'ImportDeclaration') { 47 const resolvedPath = await resolveImportPath(node.source.value as string, pagePtPath) 48 if (!resolvedPath) continue 49 for (const spec of node.specifiers) { 50 importMap[spec.local.name] = resolvedPath 55 for (const node of ast.body) { 56 // Handle: export { X as default, getServerSideProps } from './path' 57 if (node.type === 'ExportNamedDeclaration' && node.source) { 58 const resolvedPath = await resolveImportPath(node.source.value as string, pagePtPath) 59 if (!resolvedPath) continue 61 for (const spec of node.specifiers) { 62 const exportedName = (spec.exported as {name: string}).name 63 if (exportedName === 'default') { 64 clientSources.push(resolvedPath) 65 } else if (serverExportNames.includes(exportedName)) { 66 serverSources.push(resolvedPath) 71 // Handle: export default SomeImportedComponent 72 if (node.type === 'ExportDefaultDeclaration') { 73 if (node.declaration.type === 'Identifier') { 74 const importedName = node.declaration.name 75 if (importMap[importedName]) { 76 clientSources.push(importMap[importedName]) 78 clientSources.push(..._.values(importMap)) 81 // Handle: export default function App() {} - the page itself is client code 82 if (node.declaration.type === 'FunctionDeclaration' || 83 node.declaration.type === 'ArrowFunctionExpression' || 84 node.declaration.type === 'ClassDeclaration') { 85 clientSources.push(..._.values(importMap)) 90 return { clientSources: _.uniq(clientSources), serverSources: _.uniq(serverSources) } 93export const getClientServerPathSets = async (pagePathsA: string[]) => { 94 const allClientEntrypoints: string[] = [] 95 const allServerEntrypoints: string[] = [] 99 if (_.includes(pagePath, '/api/')) return 100 if (_.endsWith(pagePath, '/route.js') || _.endsWith(pagePath, '/route.ts')) return 102 const { clientSources, serverSources } = await getPageExportSources(pagePath) 103 allClientEntrypoints.push(...clientSources) 104 allServerEntrypoints.push(...serverSources) 107 const clientImportGraph = await ptMadge(_.uniq(allClientEntrypoints)) 108 const serverImportGraph = await ptMadge(_.uniq(allServerEntrypoints)) 110 const clientPaths = new Set([ 111 ...allClientEntrypoints, 112 ..._.chain(clientImportGraph).values().flatten().value() 115 const serverOnlyPaths = new Set() 116 const serverPaths = [ 117 ...allServerEntrypoints, 118 ..._.chain(serverImportGraph).values().flatten().value() 121 for (const serverPath of serverPaths) { 122 if (!clientPaths.has(serverPath)) { 123 serverOnlyPaths.add(serverPath) 127 return { clientPaths, serverOnlyPaths }