tree-cli-tool
Version:
A powerful command-line tool to display directory tree structure with various configuration options and output formats
171 lines • 6.61 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.TreeBuilder = void 0;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
class TreeBuilder {
constructor(options) {
this.stats = {
totalFiles: 0,
totalDirectories: 0,
totalSize: 0,
};
this.options = options;
}
async buildTree() {
this.resetStats();
const tree = await this.buildNode(this.options.path, 0);
return { tree, stats: this.stats };
}
resetStats() {
this.stats = {
totalFiles: 0,
totalDirectories: 0,
totalSize: 0,
};
}
async buildNode(filePath, depth) {
const stat = await fs.promises.stat(filePath);
const name = path.basename(filePath);
const node = {
name,
path: filePath,
isDirectory: stat.isDirectory(),
depth,
size: this.options.showSize ? stat.size : undefined,
modifiedTime: this.options.showDate ? stat.mtime : undefined,
};
if (stat.isDirectory()) {
this.stats.totalDirectories++;
// 检查是否超过最大深度(depth从0开始,maxDepth表示要显示的层数)
// 如果当前深度已经达到maxDepth,则不再递归子目录
if (this.options.maxDepth !== -1 && depth >= this.options.maxDepth) {
return node;
}
try {
const entries = await fs.promises.readdir(filePath);
const children = [];
for (const entry of entries) {
const entryPath = path.join(filePath, entry);
// 跳过隐藏文件(如果不显示隐藏文件)
if (!this.options.showHidden && entry.startsWith('.')) {
continue;
}
// 检查排除模式
if (this.shouldExclude(entry, entryPath)) {
continue;
}
try {
const entryStat = await fs.promises.stat(entryPath);
// 如果只显示目录,跳过文件
if (this.options.dirsOnly && !entryStat.isDirectory()) {
continue;
}
// 检查文件类型过滤
if (!entryStat.isDirectory() && !this.shouldIncludeFile(entry)) {
continue;
}
const childNode = await this.buildNode(entryPath, depth + 1);
children.push(childNode);
}
catch (error) {
// 跳过无法访问的文件/目录
console.warn(`Warning: Cannot access ${entryPath}`);
}
}
// 排序:目录优先,然后按名称排序
children.sort((a, b) => {
if (a.isDirectory && !b.isDirectory)
return -1;
if (!a.isDirectory && b.isDirectory)
return 1;
return a.name.localeCompare(b.name);
});
node.children = children;
}
catch (error) {
console.warn(`Warning: Cannot read directory ${filePath}`);
}
}
else {
this.stats.totalFiles++;
if (node.size) {
this.stats.totalSize += node.size;
}
}
return node;
}
shouldExclude(name, fullPath) {
// 检查排除模式
for (const pattern of this.options.exclude) {
if (this.matchPattern(name, pattern) ||
this.matchPattern(fullPath, pattern)) {
return true;
}
}
// 检查正则表达式忽略模式
if (this.options.ignorePattern && this.options.ignorePattern.test(name)) {
return true;
}
return false;
}
shouldIncludeFile(fileName) {
const ext = path.extname(fileName).toLowerCase();
// 如果指定了包含类型,只包含指定类型
if (this.options.includeTypes.length > 0) {
return this.options.includeTypes.some(type => ext === (type.startsWith('.') ? type : `.${type}`));
}
// 如果指定了排除类型,排除指定类型
if (this.options.excludeTypes.length > 0) {
return !this.options.excludeTypes.some(type => ext === (type.startsWith('.') ? type : `.${type}`));
}
return true;
}
matchPattern(text, pattern) {
// 简单的 glob 模式匹配
const regexPattern = pattern
.replace(/\./g, '\\.')
.replace(/\*/g, '.*')
.replace(/\?/g, '.');
const regex = new RegExp(`^${regexPattern}$`, 'i');
return regex.test(text);
}
getStats() {
return this.stats;
}
}
exports.TreeBuilder = TreeBuilder;
//# sourceMappingURL=tree-builder.js.map