UNPKG

jirehgrp

Version:

Scaffold frontend apps from Jireh templates (Next.js, React+Vite, Vanilla, etc.)

154 lines (153 loc) 4.99 kB
// src/tree.ts import fs from "fs"; import path from "path"; import kleur from "kleur"; function loadIgnoreList() { return [ "node_modules", ".git", "dist", "build", "out", "coverage", ".cache", ".parcel-cache", "npm-debug.log", "yarn-error.log", "pnpm-debug.log", "package-lock.json", ".DS_Store", "Thumbs.db", ".next", "__pycache__", "*.py[cod]", "*.egg-info", ".pytest_cache", ".mypy_cache", "log", "tmp", "vendor/bundle", ".byebug_history", "vendor", ".env", ".env.*", "storage", "db.sqlite3", "media", "target", "*.class", "*.jar", "*.war", "*.ear", ".vscode", ".idea", "*.iml", "*.log", ]; } function shouldExclude(name, excludeExtensions = []) { return excludeExtensions.some((ext) => name.endsWith(ext)); } function buildTreeObject(dirPath, options = {}) { const { ignore = loadIgnoreList(), excludeExtensions = [], maxDepth, currentDepth = 0 } = options; if (maxDepth !== undefined && currentDepth > maxDepth) { return { name: path.basename(dirPath), path: dirPath, type: "directory" }; } const entries = fs .readdirSync(dirPath, { withFileTypes: true }) .filter((item) => !ignore.includes(item.name)) .filter((item) => !shouldExclude(item.name, excludeExtensions)) .sort((a, b) => { if (a.isDirectory() && !b.isDirectory()) return -1; if (!a.isDirectory() && b.isDirectory()) return 1; return a.name.localeCompare(b.name); }); const children = []; for (const entry of entries) { const fullPath = path.join(dirPath, entry.name); if (entry.isDirectory()) { children.push(buildTreeObject(fullPath, { ...options, currentDepth: currentDepth + 1, })); } else { children.push({ name: entry.name, path: fullPath, type: "file", }); } } return { name: path.basename(dirPath), path: dirPath, type: "directory", children, }; } function buildTreeString(dirPath, prefix = "", options = {}) { const { ignore = loadIgnoreList(), excludeExtensions = [], maxDepth, currentDepth = 0 } = options; const useColor = options.color !== false; if (maxDepth !== undefined && currentDepth > maxDepth) { return ""; } const entries = fs .readdirSync(dirPath, { withFileTypes: true }) .filter((item) => !ignore.includes(item.name)) .filter((item) => !shouldExclude(item.name, excludeExtensions)) .sort((a, b) => { if (a.isDirectory() && !b.isDirectory()) return -1; if (!a.isDirectory() && b.isDirectory()) return 1; return a.name.localeCompare(b.name); }); let treeString = ""; entries.forEach((item, index) => { const isLast = index === entries.length - 1; const connector = isLast ? "└── " : "├── "; let name = item.name + (item.isDirectory() ? "/" : ""); if (useColor) { name = item.isDirectory() ? kleur.cyan(name).toString() : kleur.white(name).toString(); } treeString += prefix + connector + name + "\n"; if (item.isDirectory()) { const newPrefix = prefix + (isLast ? " " : "│ "); treeString += buildTreeString(path.join(dirPath, item.name), newPrefix, { ...options, currentDepth: currentDepth + 1, }); } }); return treeString; } export function writeTreeToFile(dirPath, options = {}) { const { rootName } = options; if (options.json) { const treeObject = buildTreeObject(dirPath, options); const jsonStr = JSON.stringify(treeObject, null, 2); const jsonPath = path.join(dirPath, "structure.json"); fs.writeFileSync(jsonPath, jsonStr, { encoding: "utf-8" }); return jsonPath; } else { const treeString = buildTreeString(dirPath, "", { ...options, color: false }); const root = rootName ?? path.basename(dirPath); const treeWithRoot = `${root}/\n${treeString}`; const structurePath = path.join(dirPath, "structure.txt"); const projectStructurePath = path.join(dirPath, "project_structure.txt"); let filePathToWrite; if (fs.existsSync(structurePath)) { filePathToWrite = projectStructurePath; } else { filePathToWrite = structurePath; } fs.writeFileSync(filePathToWrite, treeWithRoot, { encoding: "utf-8" }); return filePathToWrite; } } export { buildTreeString, buildTreeObject };