Managing a large full-stack project often means dealing with multiple subprojects—API backends, UI applications, automation frameworks, and test generators. Without a clear folder structure, repositories quickly become messy and difficult to navigate.
In this article, we’ll build a JavaScript (Node.js) solution that generates a professional and organized directory tree for your repository. The script outputs everything into a single .txt
file, making it easy to document, share, or review project structure with your team.
-
Problem: Large repositories with multiple subprojects lack a unified, easy-to-understand structure. Manually documenting them is error-prone.
-
Goal: Automatically generate a human-readable directory tree for all subprojects in a monorepo.
-
Deliverable: A Node.js script that:
- Scans multiple subproject folders (
backend-api/
,Ui-Application/
, etc.) - Builds a clean, organized tree view (folders first, files second)
- Ignores junk like
node_modules
,.git
, and build artifacts - Supports
.treeignore
for custom exclusions - Saves output to a
.txt
file for documentation
- Scans multiple subproject folders (
We’ll use Node.js + filesystem APIs (fs
, path
) to recursively walk through directories and print them in a clean ASCII tree format.
The script:
- Accepts CLI options for flexibility (
--out
,--max-depth
,--roots
, etc.) - Supports
.treeignore
files for ignoring unwanted folders/files - Sorts entries with directories first
- Works cross-platform (Linux, macOS, Windows)
Let’s assume you’re managing a full-stack repo with four integrated projects:
backend-api/ Cypress-Auto-Test-Generator/ Template_Cypress_Framwork/ Ui-Application/
Our script will generate a project-tree.txt
like this:
backend-api/ ├── src/ │ ├── controllers/ │ ├── models/ │ └── routes/ ├── tests/ └── package.json Ui-Application/ ├── src/ │ ├── components/ │ ├── pages/ │ └── utils/ └── package.json
Below is the full solution (scripts/generate-tree.js
):
#!/usr/bin/env node /** * Project Tree Generator * Renders directory trees for multiple subprojects and saves to a .txt file. * * Usage: * node scripts/generate-tree.js * node scripts/generate-tree.js --out project-tree.txt --max-depth 10 --hidden false * node scripts/generate-tree.js --roots backend-api Ui-Application */ const fs = require('fs'); const path = require('path');
const args = process.argv.slice(2); const arg = (name, fallback = null) => { const i = args.findIndex(a => a === `--${name}` || a.startsWith(`--${name}=`)); if (i === -1) return fallback; const val = args[i].includes('=') ? args[i].split('=').slice(1).join('=') : args[i + 1]; return val === undefined ? true : val; };
- Extracts command line flags (
--out
,--max-depth
, etc.). - Example:
node scripts/generate-tree.js --out repo.txt --max-depth 5
const OUT_FILE = arg('out', 'project-tree.txt'); const MAX_DEPTH = Number(arg('max-depth', 20)); const SHOW_HIDDEN = String(arg('hidden', 'false')).toLowerCase() === 'true';
- Default output file →
project-tree.txt
- Max recursion depth = 20 (safe for big repos)
- Hidden files/folders skipped unless
--hidden true
const DEFAULT_IGNORE = new Set([ 'node_modules', '.git', 'dist', 'build', '.DS_Store', ]);
- Prevents noise (
node_modules
,dist
, etc.) from cluttering output.
Supports .treeignore
file for custom exclusions.
function sortEntries(a, b) { if (a.isDirectory() && !b.isDirectory()) return -1; if (!a.isDirectory() && b.isDirectory()) return 1; return a.name.localeCompare(b.name, undefined, { numeric: true, sensitivity: 'base' }); }
- Ensures folders appear before files.
- Case-insensitive, numeric sorting (e.g.,
file2
beforefile10
).
function buildTreeLines(root, options) { const { maxDepth = 20, showHidden = false, ignoreNames = DEFAULT_IGNORE } = options; const lines = []; function walk(currentPath, prefix = '', depth = 0) { if (depth > maxDepth) return; let entries = fs.readdirSync(currentPath, { withFileTypes: true }); entries = entries.filter(d => { if (!showHidden && d.name.startsWith('.')) return false; if (ignoreNames.has(d.name)) return false; return true; }); entries.sort(sortEntries); entries.forEach((dirent, index) => { const isLast = index === entries.length - 1; const branch = isLast ? '└── ' : '├── '; const nextPrefix = prefix + (isLast ? ' ' : '│ '); const full = path.join(currentPath, dirent.name); lines.push(`${prefix}${branch}${dirent.name}${dirent.isDirectory() ? '/' : ''}`); if (dirent.isDirectory()) walk(full, nextPrefix, depth + 1); }); } lines.push(`${path.basename(root)}/`); walk(root, '', 1); return lines; }
- Recursively traverses folders.
- Uses ASCII characters (
├──
,└──
,│
) for tree formatting. - Prevents infinite recursion with a depth limit.
(function main() { const allLines = []; ROOTS.forEach(root => { if (!fs.existsSync(root)) { allLines.push(`${root}/`); allLines.push('└── [Not Found]'); return; } const lines = buildTreeLines(root, { maxDepth: MAX_DEPTH, showHidden: SHOW_HIDDEN }); allLines.push(...lines, '', '----------------------------------------', ''); }); fs.writeFileSync(OUT_FILE, allLines.join('\n'), 'utf8'); console.log(`✅ Project tree generated -> ${OUT_FILE}`); })();
- Loops through all subprojects (
backend-api
,Ui-Application
, etc.). - Calls
buildTreeLines
for each one. - Writes everything into a single
.txt
file.
# Default: scans 4 subprojects and writes project-tree.txt node scripts/generate-tree.js # Custom output file node scripts/generate-tree.js --out repo-structure.txt # Limit depth to 6 levels node scripts/generate-tree.js --max-depth 6 # Show hidden files/folders node scripts/generate-tree.js --hidden true # Only scan selected roots node scripts/generate-tree.js --roots backend-api Ui-Application
-
Add to
package.json
for convenience:{ "scripts": { "tree": "node scripts/generate-tree.js", "tree:deep": "node scripts/generate-tree.js --max-depth 50", "tree:full": "node scripts/generate-tree.js --hidden true --max-depth 50" } }
-
Use
.treeignore
to exclude environment files, logs, or temporary folders. -
Commit
project-tree.txt
into documentation for onboarding new developers. -
Regenerate regularly to keep structure documentation up to date.
- Documentation: Makes onboarding easier for new developers.
- Maintainability: Helps quickly detect misplaced files or bloated directories.
- Professionalism: Clients, recruiters, or open-source contributors see a clear, professional structure.
- Automation: Saves hours compared to manually updating README file structures.
We built a professional, accurate, and flexible Node.js project tree generator that works perfectly for multi-project full-stack repositories.
It solves the pain of messy folder navigation and produces clean, SEO-friendly documentation of your project structure.
🔗 Next Step: Integrate this script into your repo and add project-tree.txt
to your documentation. Your teammates—and your future self—will thank you.