import { getFileNameFromPath } from './path'; import type { RouteScreen } from '../types/RouteScreen'; /** * 주어진 파일 경로에서 디렉토리 경로만 추출 * 예) './about/test' -> './about' * 예) './about' -> '.' (기본 로직) * but... 테스트 상, 'layoutDirs'에 포함된 경로면 그대로 반환하도록 수정 */ function getDirectoryPath(filePath: string, layoutDirs: Set): string { // layoutDirs에 포함돼 있다면, 이 경로 자체가 디렉토리 기반 라우트 if (layoutDirs.has(filePath)) { return filePath; } const lastSlashIndex = filePath.lastIndexOf('/'); if (lastSlashIndex === -1) { // 슬래시가 없다면 '.' 처리 return '.'; } const dirPath = filePath.substring(0, lastSlashIndex); return dirPath === '' ? '.' : dirPath; } /** * 상위 디렉토리 경로 구하기 * 예) './nested/deep' -> './nested' * 예) './nested' -> '.' * 예) '.' -> '' (더 이상 상위가 없음) */ function getParentDirectory(dirPath: string): string { if (dirPath === '.' || dirPath === '') { return ''; } const idx = dirPath.lastIndexOf('/'); if (idx === -1) { return '.'; } const parent = dirPath.substring(0, idx); return parent === '' ? '.' : parent; } /** * @name createParentRouteScreenMap * @description * 주어진 파일 목록에서 특정 키워드를 찾고, * 각 페이지/레이아웃이 "가장 가까운 상위 레이아웃"을 사용하도록 매핑해 주는 함수 */ export function createParentRouteScreenMap(routeScreens: RouteScreen[], keyword: string): Map { // 1) 키워드 파일들을 찾고, 그 디렉토리를 layoutDirs 집합에 기록 // 예) "./about/[keyword]" -> layoutDirs.add("./about") const layoutDirs = new Set(); const layoutScreens = new Map(); for (const screen of routeScreens) { const fileName = getFileNameFromPath(screen.path, { withExtension: false }); if (fileName === keyword) { const dirPath = screen.path.substring(0, screen.path.lastIndexOf('/')); layoutDirs.add(dirPath || '.'); // './about' 같은 디렉토리 경로 저장 } } // 2) layoutScreens (디렉토리->키워드 파일) 매핑 생성 for (const screen of routeScreens) { const fileName = getFileNameFromPath(screen.path, { withExtension: false }); if (fileName === keyword) { // 이 키워드 파일이 속한 디렉토리는 이미 layoutDirs에 들어 있음 // dirPath: './about', './nested', etc. const dirPath = getDirectoryPath(screen.path, layoutDirs); layoutScreens.set(dirPath, screen); } } /** * 3) 상위 디렉토리를 타고 올라가며 가장 가까운 레이아웃을 찾는 함수 * - skipSameDir: 자기 디렉토리부터 검사할지, 부모부터 검사할지 여부 * * memo: (dirPath + '|' + skipSameDir) -> RouteScreen | undefined */ const memo = new Map(); function findNearestLayout(dir: string, skipSameDir = false): RouteScreen | undefined { const cacheKey = dir + '|' + skipSameDir; if (memo.has(cacheKey)) { return memo.get(cacheKey); } let current = dir; if (skipSameDir) { current = getParentDirectory(current); } while (current) { if (layoutScreens.has(current)) { const foundLayout = layoutScreens.get(current); memo.set(cacheKey, foundLayout); return foundLayout; } if (current === '.' || current === '') { break; } current = getParentDirectory(current); } memo.set(cacheKey, undefined); return undefined; } // 4) 최종적으로 "경로 -> 상위 레이아웃" 매핑 const parentRouteScreenMap = new Map(); for (const screen of routeScreens) { const fileName = getFileNameFromPath(screen.path, { withExtension: false }); // 이 파일(혹은 디렉토리)이 속한 "디렉토리"를 구한다. const dirPath = getDirectoryPath(screen.path, layoutDirs); if (fileName === keyword) { // (1) 자기 자신이 키워드 파일이면, "부모 디렉토리"에서 레이아웃 검색 // => 자기 자신은 스킵해야 함(skipSameDir=true) const parentLayout = findNearestLayout(dirPath, true); if (parentLayout) { parentRouteScreenMap.set(screen.path, parentLayout); } } else { // (2) 일반 페이지(또는 일반 파일)라면, 자기 디렉토리부터 상위로 올라가며 레이아웃 검색 const nearestLayout = findNearestLayout(dirPath, false); if (nearestLayout) { parentRouteScreenMap.set(screen.path, nearestLayout); } } } return parentRouteScreenMap; }