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
.treeignorefor custom exclusions - Saves output to a
.txtfile 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
.treeignorefiles 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.,
file2beforefile10).
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
buildTreeLinesfor each one. - Writes everything into a single
.txtfile.
# 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.jsonfor 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
.treeignoreto exclude environment files, logs, or temporary folders. -
Commit
project-tree.txtinto 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.