| export interface FileTreeNode { |
| name: string; |
| path: string; |
| isDirectory: boolean; |
| children: FileTreeNode[]; |
| } |
|
|
| function sortTreeInPlace(node: FileTreeNode): void { |
| node.children.sort((a, b) => { |
| if (a.isDirectory !== b.isDirectory) return a.isDirectory ? -1 : 1; |
| return a.name.localeCompare(b.name); |
| }); |
| for (const child of node.children) { |
| if (child.isDirectory) sortTreeInPlace(child); |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| function getOrCreateChild( |
| parent: FileTreeNode, |
| childMap: Map<FileTreeNode, Map<string, FileTreeNode>>, |
| segment: string, |
| prefix: string, |
| isLast: boolean, |
| ): FileTreeNode { |
| let map = childMap.get(parent); |
| if (!map) { |
| map = new Map(); |
| childMap.set(parent, map); |
| } |
| const existing = map.get(segment); |
| if (existing) { |
| |
| if (!isLast && !existing.isDirectory) { |
| existing.isDirectory = true; |
| } |
| return existing; |
| } |
| const node: FileTreeNode = { |
| name: segment, |
| path: prefix, |
| isDirectory: !isLast, |
| children: [], |
| }; |
| parent.children.push(node); |
| map.set(segment, node); |
| return node; |
| } |
|
|
| export function buildFileTree(paths: string[]): FileTreeNode { |
| const root: FileTreeNode = { |
| name: "", |
| path: "", |
| isDirectory: true, |
| children: [], |
| }; |
|
|
| |
| |
| |
| const childMap = new Map<FileTreeNode, Map<string, FileTreeNode>>(); |
|
|
| for (const path of paths) { |
| const segments = path.split("/").filter(Boolean); |
| if (segments.length > 0) { |
| let cursor: FileTreeNode = root; |
| let prefix = ""; |
| for (let i = 0; i < segments.length; i += 1) { |
| const segment = segments[i]; |
| prefix = prefix ? `${prefix}/${segment}` : segment; |
| const isLast = i === segments.length - 1; |
| cursor = getOrCreateChild(cursor, childMap, segment, prefix, isLast); |
| } |
| } |
| } |
|
|
| sortTreeInPlace(root); |
| return root; |
| } |
|
|