structure-maker
Advanced tools
Comparing version
233
index.js
@@ -1,44 +0,229 @@ | ||
#!/usr/bin/env node | ||
import fs from 'fs'; | ||
import path from 'path'; | ||
import chalk from 'chalk'; | ||
import minimist from 'minimist'; | ||
const fs = require("fs"); | ||
const path = require("path"); | ||
// const chalk = require("chalk"); | ||
const FILE_TYPE_ICONS = { | ||
'.js': '💻', '.ts': '💻', '.py': '💻', '.java': '💻', // Code files | ||
'.md': '📜', '.txt': '📜', '.json': '📜', // Text files | ||
'.jpg': '🖼️', '.png': '🖼️', '.gif': '🖼️', // Images | ||
'.mp4': '🎥', '.avi': '🎥', // Videos | ||
'.mp3': '🎵', '.wav': '🎵', // Audio | ||
'.pdf': '📚', '.docx': '📚', // Documents | ||
}; | ||
function generateTree(dir, prefix = "") { | ||
function getFileIcon(fileName) { | ||
const ext = path.extname(fileName).toLowerCase(); | ||
return FILE_TYPE_ICONS[ext] || '📄'; | ||
} | ||
function formatDate(date) { | ||
return date.toISOString().split('T')[0]; // YYYY-MM-DD | ||
} | ||
function sortEntries(entries, sortBy) { | ||
return entries.sort((a, b) => { | ||
const aPath = path.join(process.cwd(), a.name); | ||
const bPath = path.join(process.cwd(), b.name); | ||
const aStats = fs.statSync(aPath); | ||
const bStats = fs.statSync(bPath); | ||
if (sortBy === 'size') return bStats.size - aStats.size; | ||
if (sortBy === 'date') return bStats.mtime - aStats.mtime; | ||
return a.name.localeCompare(b.name); // Default: sort by name | ||
}); | ||
} | ||
function generateTree(dir, prefix = "", colorize = false, depth = Infinity, currentDepth = 0, ignorePatterns = [], options = {}) { | ||
const { includeDates = false, sortBy = 'name', verbose = false } = options; | ||
if (currentDepth > depth) return { tree: '', fileCount: 0, folderCount: 0, totalSize: 0, fileTypes: {}, maxDepth: currentDepth - 1, json: [] }; | ||
const entries = fs | ||
.readdirSync(dir, { withFileTypes: true }) | ||
.filter( | ||
(e) => | ||
!["node_modules", ".git", ".expo", ".next", ".DS_Store"].includes( | ||
e.name | ||
) | ||
) | ||
.sort((a, b) => (a.isDirectory() ? -1 : 1)); | ||
.filter((e) => !['node_modules', '.git', '.expo', '.next', '.DS_Store', ...ignorePatterns].includes(e.name)); | ||
sortEntries(entries, sortBy); | ||
let tree = ""; | ||
let tree = ''; | ||
let fileCount = 0; | ||
let folderCount = 0; | ||
let totalSize = 0; | ||
let fileTypes = {}; | ||
let maxDepth = currentDepth; | ||
let json = []; | ||
entries.forEach((entry, index) => { | ||
const isLast = index === entries.length - 1; | ||
const connector = isLast ? "└─" : "├─"; | ||
const connector = isLast ? '└─' : '├─'; | ||
const fullPath = path.join(dir, entry.name); | ||
const stats = fs.statSync(fullPath); | ||
const lastModified = includeDates ? ` [${formatDate(stats.mtime)}]` : ''; | ||
tree += `${prefix}${connector} ${entry.name}\n`; | ||
let displayName, jsonEntry; | ||
if (entry.isDirectory()) { | ||
const icon = '📁'; | ||
displayName = colorize | ||
? `${chalk.blue(icon)} ${chalk.blue.bold(entry.name)}${lastModified}` | ||
: `${icon} ${entry.name}${lastModified}`; | ||
folderCount++; | ||
jsonEntry = { name: entry.name, type: 'directory', lastModified: stats.mtime, children: [] }; | ||
} else { | ||
const icon = getFileIcon(entry.name); | ||
const size = (stats.size / 1024).toFixed(1); | ||
displayName = colorize | ||
? `${chalk.green(icon)} ${chalk.green(entry.name)} (${size} KB)${lastModified}` | ||
: `${icon} ${entry.name} (${size} KB)${lastModified}`; | ||
fileCount++; | ||
totalSize += stats.size; | ||
const ext = path.extname(entry.name).toLowerCase() || 'other'; | ||
fileTypes[ext] = (fileTypes[ext] || 0) + 1; | ||
jsonEntry = { name: entry.name, type: 'file', size: stats.size, lastModified: stats.mtime }; | ||
} | ||
tree += `${prefix}${connector} ${displayName}\n`; | ||
json.push(jsonEntry); | ||
if (entry.isDirectory()) { | ||
const nextPrefix = prefix + (isLast ? " " : "│ "); | ||
tree += generateTree(fullPath, nextPrefix); | ||
const nextPrefix = prefix + (isLast ? ' ' : '│ '); | ||
const subtree = generateTree(fullPath, nextPrefix, colorize, depth, currentDepth + 1, ignorePatterns, options); | ||
tree += subtree.tree || ''; | ||
fileCount += subtree.fileCount || 0; | ||
folderCount += subtree.folderCount || 0; | ||
totalSize += subtree.totalSize || 0; | ||
for (const [ext, count] of Object.entries(subtree.fileTypes || {})) { | ||
fileTypes[ext] = (fileTypes[ext] || 0) + count; | ||
} | ||
maxDepth = Math.max(maxDepth, subtree.maxDepth || currentDepth); | ||
jsonEntry.children = subtree.json || []; | ||
} | ||
if (verbose) console.log(chalk.gray(`Processing: ${fullPath}`)); | ||
}); | ||
return tree; | ||
return { tree, fileCount, folderCount, totalSize, fileTypes, maxDepth, json }; | ||
} | ||
function generateHTML(treeData, rootName) { | ||
let htmlContent = ` | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8"> | ||
<title>Folder Structure - ${rootName}</title> | ||
<style> | ||
.tree { font-family: monospace; } | ||
.folder { cursor: pointer; color: blue; } | ||
.file { color: green; } | ||
.hidden { display: none; } | ||
</style> | ||
</head> | ||
<body> | ||
<h1>Folder Structure: ${rootName}</h1> | ||
<div class="tree"> | ||
<ul>${generateHTMLTree(treeData, rootName)}</ul> | ||
</div> | ||
<script> | ||
document.querySelectorAll('.folder').forEach(folder => { | ||
folder.addEventListener('click', () => { | ||
const children = folder.nextElementSibling; | ||
children.classList.toggle('hidden'); | ||
}); | ||
}); | ||
</script> | ||
</body> | ||
</html> | ||
`; | ||
function generateHTMLTree(data, name) { | ||
let html = `<li><span class="folder">📁 ${name}</span>`; | ||
if (data.length > 0) { | ||
html += '<ul class="hidden">'; | ||
data.forEach(item => { | ||
if (item.type === 'directory') { | ||
html += generateHTMLTree(item.children, item.name); | ||
} else { | ||
html += `<li class="file">${getFileIcon(item.name)} ${item.name} (${(item.size / 1024).toFixed(1)} KB)</li>`; | ||
} | ||
}); | ||
html += '</ul>'; | ||
} | ||
html += '</li>'; | ||
return html; | ||
} | ||
return htmlContent; | ||
} | ||
function main() { | ||
const currentDir = process.cwd(); | ||
const outputFile = "folder-structure.txt"; | ||
const tree = path.basename(currentDir) + "/\n" + generateTree(currentDir); | ||
fs.writeFileSync(outputFile, tree); | ||
console.log(`✔ Folder structure saved to ${outputFile}`); | ||
try { | ||
const args = minimist(process.argv.slice(2), { | ||
string: ['ignore', 'depth', 'sort-by', 'output'], | ||
boolean: ['include-dates', 'verbose'], | ||
default: { ignore: '', depth: Infinity, 'sort-by': 'name', 'include-dates': false, verbose: false, output: 'md' }, | ||
}); | ||
const ignorePatterns = args.ignore ? args.ignore.split(',').map(s => s.trim()) : []; | ||
const depth = parseInt(args.depth) || Infinity; | ||
const options = { | ||
includeDates: args['include-dates'], | ||
sortBy: args['sort-by'] || 'name', | ||
verbose: args.verbose, | ||
}; | ||
const currentDir = process.cwd(); | ||
const rootName = path.basename(currentDir); | ||
const outputFormat = args.output.toLowerCase(); | ||
const { tree, fileCount, folderCount, totalSize, fileTypes, maxDepth, json } = generateTree(currentDir, '', false, depth, 0, ignorePatterns, options); | ||
const fileTypesSummary = Object.entries(fileTypes) | ||
.map(([ext, count]) => `- **${ext || 'other'}**: ${count}`) | ||
.join('\n'); | ||
if (outputFormat === 'md' || outputFormat === 'all') { | ||
const markdownContent = ` | ||
# Folder Structure | ||
## Summary | ||
- **Total Folders**: ${folderCount} | ||
- **Total Files**: ${fileCount} | ||
- **Total Size**: ${(totalSize / 1024 / 1024).toFixed(2)} MB | ||
- **Deepest Folder Level**: ${maxDepth} | ||
- **Root Directory**: ${rootName} | ||
### File Types | ||
${fileTypesSummary} | ||
## Table of Contents | ||
- [Directory Structure](#directory-structure) | ||
## Directory Structure | ||
\`\`\` | ||
${rootName}/ | ||
${tree} | ||
\`\`\` | ||
`; | ||
fs.writeFileSync('folder-structure.md', markdownContent); | ||
console.log(chalk.cyan(`✔ Markdown saved to folder-structure.md`)); | ||
} | ||
if (outputFormat === 'json' || outputFormat === 'all') { | ||
const jsonContent = JSON.stringify({ name: rootName, type: 'directory', children: json }, null, 2); | ||
fs.writeFileSync('folder-structure.json', jsonContent); | ||
console.log(chalk.cyan(`✔ JSON saved to folder-structure.json`)); | ||
} | ||
if (outputFormat === 'html' || outputFormat === 'all') { | ||
const htmlContent = generateHTML(json, rootName); | ||
fs.writeFileSync('folder-structure.html', htmlContent); | ||
console.log(chalk.cyan(`✔ HTML saved to folder-structure.html`)); | ||
} | ||
console.log(chalk.cyan(`Summary: ${folderCount} folders, ${fileCount} files, ${(totalSize / 1024 / 1024).toFixed(2)} MB, deepest level: ${maxDepth}`)); | ||
console.log(`${rootName}/\n${generateTree(currentDir, '', true, depth, 0, ignorePatterns, options).tree}`); | ||
} catch (err) { | ||
console.error(chalk.red('Error:', err.message)); | ||
} | ||
} | ||
main(); | ||
main(); |
{ | ||
"type": "module", | ||
"name": "structure-maker", | ||
"version": "1.0.2", | ||
"version": "1.0.3", | ||
"description": "CLI to create a .txt file showing your folder structure", | ||
"bin": { | ||
"structure-tree": "index.js" | ||
"structure-tree": "index.js" | ||
}, | ||
"keywords": ["cli", "folder", "structure", "tree", "project"], | ||
"author": "Your Name", | ||
"keywords": [ | ||
"cli", | ||
"folder", | ||
"structure", | ||
"tree", | ||
"project" | ||
], | ||
"author": "Chauhan Yuvraj", | ||
"license": "MIT", | ||
"dependencies": { | ||
"chalk": "^5.3.0" | ||
"chalk": "^5.4.1", | ||
"inquirer": "^12.6.0", | ||
"minimist": "^1.2.8" | ||
} | ||
} | ||
} |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
9163
490.78%4
100%196
460%Yes
NaN3
200%3
50%1
Infinity%+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
Updated