@bobfrankston/tswalk 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +9 -0
- package/.editorconfig +9 -0
- package/.gitattributes +2 -0
- package/.vscode/launch.json +32 -0
- package/.vscode/settings.json +22 -0
- package/.vscode/tasks.json +23 -0
- package/bin/tswalk.js +0 -0
- package/diskwalker.js +240 -0
- package/diskwalker.js.map +1 -0
- package/diskwalker.ts +271 -0
- package/diskwalker2.js +280 -0
- package/diskwalker2.js.map +1 -0
- package/diskwalker2.ts +319 -0
- package/index.js +143 -0
- package/index.js.map +1 -0
- package/index.ts +149 -0
- package/package.json +26 -0
- package/tsconfig.json +14 -0
package/.editorconfig
ADDED
package/.gitattributes
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
// Use IntelliSense to learn about possible attributes.
|
|
3
|
+
// Hover to view descriptions of existing attributes.
|
|
4
|
+
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
|
5
|
+
"version": "0.2.0",
|
|
6
|
+
"configurations": [
|
|
7
|
+
{
|
|
8
|
+
"name": "Launch Program",
|
|
9
|
+
"program": "${workspaceFolder}/app.js",
|
|
10
|
+
"request": "launch",
|
|
11
|
+
"skipFiles": [
|
|
12
|
+
"<node_internals>/**"
|
|
13
|
+
],
|
|
14
|
+
"type": "node"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"type": "node",
|
|
18
|
+
"request": "launch",
|
|
19
|
+
"name": "u:",
|
|
20
|
+
"program": "${workspaceFolder}/index.js",
|
|
21
|
+
"skipFiles": [
|
|
22
|
+
"<node_internals>/**"
|
|
23
|
+
],
|
|
24
|
+
"outFiles": [
|
|
25
|
+
"${workspaceFolder}/**/*.js"
|
|
26
|
+
],
|
|
27
|
+
"args": [
|
|
28
|
+
"u:",
|
|
29
|
+
]
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"workbench.colorCustomizations": {
|
|
3
|
+
"activityBar.activeBackground": "#f4fd05",
|
|
4
|
+
"activityBar.background": "#f4fd05",
|
|
5
|
+
"activityBar.foreground": "#15202b",
|
|
6
|
+
"activityBar.inactiveForeground": "#15202b99",
|
|
7
|
+
"activityBarBadge.background": "#02b7be",
|
|
8
|
+
"activityBarBadge.foreground": "#15202b",
|
|
9
|
+
"commandCenter.border": "#15202b99",
|
|
10
|
+
"sash.hoverBorder": "#f4fd05",
|
|
11
|
+
"statusBar.background": "#c6cd02",
|
|
12
|
+
"statusBar.foreground": "#15202b",
|
|
13
|
+
"statusBarItem.hoverBackground": "#959a02",
|
|
14
|
+
"statusBarItem.remoteBackground": "#c6cd02",
|
|
15
|
+
"statusBarItem.remoteForeground": "#15202b",
|
|
16
|
+
"titleBar.activeBackground": "#c6cd02",
|
|
17
|
+
"titleBar.activeForeground": "#15202b",
|
|
18
|
+
"titleBar.inactiveBackground": "#c6cd0299",
|
|
19
|
+
"titleBar.inactiveForeground": "#15202b99"
|
|
20
|
+
},
|
|
21
|
+
"peacock.color": "#c6cd02"
|
|
22
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
// Template 1.0 y:\x\bin\MakeCodeTemplates\vscode\
|
|
3
|
+
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
|
4
|
+
// for the documentation about the tasks.json format
|
|
5
|
+
"version": "2.0.0",
|
|
6
|
+
"tasks": [
|
|
7
|
+
{
|
|
8
|
+
"type": "typescript",
|
|
9
|
+
"tsconfig": "tsconfig.json",
|
|
10
|
+
"option": "watch",
|
|
11
|
+
"runOptions": {
|
|
12
|
+
"runOn": "folderOpen"
|
|
13
|
+
},
|
|
14
|
+
"problemMatcher": [
|
|
15
|
+
"$tsc-watch"
|
|
16
|
+
],
|
|
17
|
+
"group": {
|
|
18
|
+
"kind": "build",
|
|
19
|
+
"isDefault": true
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
}
|
package/bin/tswalk.js
ADDED
|
File without changes
|
package/diskwalker.js
ADDED
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import * as fp from 'fs/promises';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { spawn } from 'child_process';
|
|
4
|
+
import { styleText } from 'node:util';
|
|
5
|
+
// Error types to suppress unless verbose
|
|
6
|
+
const suppressedErrors = ['ENOENT', 'EPERM', 'EACCES'];
|
|
7
|
+
export class DiskWalker {
|
|
8
|
+
topPath;
|
|
9
|
+
static defaultOptions = {
|
|
10
|
+
depth: 1,
|
|
11
|
+
threshold: 0,
|
|
12
|
+
cmd: '',
|
|
13
|
+
verbose: false,
|
|
14
|
+
showCompressed: false,
|
|
15
|
+
finding: null,
|
|
16
|
+
forceAcl: false,
|
|
17
|
+
quiet: false,
|
|
18
|
+
sizeUnit: 'MB'
|
|
19
|
+
};
|
|
20
|
+
options;
|
|
21
|
+
walker;
|
|
22
|
+
lastProgressTime = 0;
|
|
23
|
+
constructor(topPath = '.', options = {}) {
|
|
24
|
+
this.topPath = topPath;
|
|
25
|
+
this.options = { ...DiskWalker.defaultOptions, ...options };
|
|
26
|
+
this.topPath = path.resolve(topPath);
|
|
27
|
+
this.walker = new WalkTree(this.topPath, this.options, this.showProgress.bind(this));
|
|
28
|
+
}
|
|
29
|
+
truncatePath(fullPath, maxLength = 64) {
|
|
30
|
+
if (fullPath.length <= maxLength)
|
|
31
|
+
return fullPath;
|
|
32
|
+
const half = Math.floor((maxLength - 3) / 2);
|
|
33
|
+
const start = fullPath.slice(0, half);
|
|
34
|
+
const end = fullPath.slice(-half);
|
|
35
|
+
return `${start}...${end}`;
|
|
36
|
+
}
|
|
37
|
+
showProgress(walker, currentPath) {
|
|
38
|
+
const now = Date.now();
|
|
39
|
+
if (now - this.lastProgressTime > 1000 && !this.options.quiet) {
|
|
40
|
+
const truncatedPath = this.truncatePath(currentPath);
|
|
41
|
+
process.stdout.write('\x1b[2K\r'); // Clear entire line and return to start
|
|
42
|
+
const line = styleText(['blue'], `${walker.formatSize().padStart(13)} ${this.options.sizeUnit} ${truncatedPath}`);
|
|
43
|
+
process.stdout.write(line);
|
|
44
|
+
this.lastProgressTime = now;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
async walk() {
|
|
48
|
+
try {
|
|
49
|
+
await this.walker.walk();
|
|
50
|
+
if (!this.options.quiet) {
|
|
51
|
+
process.stdout.write('\x1b[2K\r'); // Clear progress line
|
|
52
|
+
}
|
|
53
|
+
console.log(`${this.walker.formatSize()} ${this.options.sizeUnit} ${this.topPath}`);
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
56
|
+
console.error('Failed:', err.message);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
class WalkTree {
|
|
61
|
+
dirPath;
|
|
62
|
+
options;
|
|
63
|
+
progressCallback;
|
|
64
|
+
atDepth;
|
|
65
|
+
length = 0;
|
|
66
|
+
compressedLength = 0;
|
|
67
|
+
fileCount = 0;
|
|
68
|
+
dirCount = 0;
|
|
69
|
+
totalFileCount = 0;
|
|
70
|
+
totalDirCount = 0;
|
|
71
|
+
otherCount = 0;
|
|
72
|
+
prefix;
|
|
73
|
+
nextTitleUpdate = new Date(0);
|
|
74
|
+
seenErrors = new Set();
|
|
75
|
+
constructor(dirPath, options, progressCallback, atDepth = 0) {
|
|
76
|
+
this.dirPath = dirPath;
|
|
77
|
+
this.options = options;
|
|
78
|
+
this.progressCallback = progressCallback;
|
|
79
|
+
this.atDepth = atDepth;
|
|
80
|
+
this.prefix = ' '.repeat(5 + atDepth * 5);
|
|
81
|
+
if (options.verbose) {
|
|
82
|
+
this.prefix = atDepth.toString().padStart(2, '0') + ' ' + this.prefix;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
formatSize() {
|
|
86
|
+
const divisor = this.options.sizeUnit === 'GB' ? (1024 * 1024 * 1024) : (1024 * 1024);
|
|
87
|
+
const size = this.length / divisor;
|
|
88
|
+
const formatted = size.toLocaleString(undefined, { minimumFractionDigits: 2 });
|
|
89
|
+
if (this.options.showCompressed && this.length !== this.compressedLength) {
|
|
90
|
+
const compressedSize = this.compressedLength / divisor;
|
|
91
|
+
return `${formatted}/${compressedSize.toLocaleString(undefined, { minimumFractionDigits: 2 })}`;
|
|
92
|
+
}
|
|
93
|
+
return formatted;
|
|
94
|
+
}
|
|
95
|
+
logError(context, err) {
|
|
96
|
+
if (!this.options.verbose &&
|
|
97
|
+
(this.options.quiet || suppressedErrors.some(e => err.message.includes(e)))) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const errorKey = `${context}:${err.message}:${this.dirPath}`;
|
|
101
|
+
if (!this.seenErrors.has(errorKey)) {
|
|
102
|
+
console.error(styleText(['red'], `Error processing ${context}: ${err.message}`));
|
|
103
|
+
this.seenErrors.add(errorKey);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
async isSymlink(testPath) {
|
|
107
|
+
try {
|
|
108
|
+
const stats = await fp.lstat(testPath);
|
|
109
|
+
return stats.isSymbolicLink();
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
async processDirectory() {
|
|
116
|
+
try {
|
|
117
|
+
if (Date.now() > this.nextTitleUpdate.getTime()) {
|
|
118
|
+
process.title = this.dirPath;
|
|
119
|
+
this.nextTitleUpdate = new Date(Date.now() + 500);
|
|
120
|
+
}
|
|
121
|
+
const files = await fp.readdir(this.dirPath, { withFileTypes: true });
|
|
122
|
+
for (const file of files) {
|
|
123
|
+
if (!file.isDirectory()) {
|
|
124
|
+
this.fileCount++;
|
|
125
|
+
this.totalFileCount++;
|
|
126
|
+
try {
|
|
127
|
+
const stats = await fp.stat(path.join(this.dirPath, file.name));
|
|
128
|
+
this.length += stats.size;
|
|
129
|
+
this.compressedLength += stats.size;
|
|
130
|
+
}
|
|
131
|
+
catch (err) {
|
|
132
|
+
this.logError(file.name, err);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
catch (err) {
|
|
138
|
+
this.logError('directory', err);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
async executeCommand() {
|
|
142
|
+
if (!this.options.cmd)
|
|
143
|
+
return;
|
|
144
|
+
const currentDir = process.cwd();
|
|
145
|
+
try {
|
|
146
|
+
process.chdir(this.dirPath);
|
|
147
|
+
await new Promise((resolve, reject) => {
|
|
148
|
+
const proc = spawn(this.options.cmd, [], { shell: true });
|
|
149
|
+
proc.on('close', resolve);
|
|
150
|
+
proc.on('error', reject);
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
catch (err) {
|
|
154
|
+
this.logError('command execution', err);
|
|
155
|
+
}
|
|
156
|
+
finally {
|
|
157
|
+
process.chdir(currentDir);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
async findFiles() {
|
|
161
|
+
if (!this.options.finding)
|
|
162
|
+
return;
|
|
163
|
+
try {
|
|
164
|
+
for (const pattern of this.options.finding) {
|
|
165
|
+
const files = await fp.readdir(this.dirPath);
|
|
166
|
+
for (const file of files) {
|
|
167
|
+
if (file.match(pattern)) {
|
|
168
|
+
try {
|
|
169
|
+
const stats = await fp.stat(path.join(this.dirPath, file));
|
|
170
|
+
const size = stats.size / 1024;
|
|
171
|
+
process.stdout.write('\x1b[2K\r'); // Clear progress line
|
|
172
|
+
console.log(`${stats.mtime.toISOString().slice(0, 19).replace('T', ' ')} ` +
|
|
173
|
+
`${size.toFixed(1).padStart(8)}KB ${this.prefix} ${path.join(this.dirPath, file)}`);
|
|
174
|
+
}
|
|
175
|
+
catch (err) {
|
|
176
|
+
this.logError(file, err);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
catch (err) {
|
|
183
|
+
this.logError('file search', err);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
async walk() {
|
|
187
|
+
try {
|
|
188
|
+
const isSymlink = await this.isSymlink(this.dirPath);
|
|
189
|
+
if (isSymlink)
|
|
190
|
+
return;
|
|
191
|
+
this.progressCallback(this, this.dirPath);
|
|
192
|
+
if (this.options.cmd || this.options.finding || this.options.forceAcl) {
|
|
193
|
+
if (this.options.forceAcl) {
|
|
194
|
+
// ACL operations not implemented
|
|
195
|
+
}
|
|
196
|
+
if (this.options.cmd) {
|
|
197
|
+
await this.executeCommand();
|
|
198
|
+
}
|
|
199
|
+
if (this.options.finding) {
|
|
200
|
+
await this.findFiles();
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
await this.processDirectory();
|
|
205
|
+
}
|
|
206
|
+
const entries = await fp.readdir(this.dirPath, { withFileTypes: true });
|
|
207
|
+
for (const entry of entries) {
|
|
208
|
+
if (entry.isDirectory()) {
|
|
209
|
+
const subPath = path.join(this.dirPath, entry.name);
|
|
210
|
+
const walker = new WalkTree(subPath, this.options, this.progressCallback, this.atDepth + 1);
|
|
211
|
+
try {
|
|
212
|
+
await walker.walk();
|
|
213
|
+
this.dirCount++;
|
|
214
|
+
this.length += walker.length;
|
|
215
|
+
this.compressedLength += walker.compressedLength;
|
|
216
|
+
this.totalFileCount += walker.totalFileCount;
|
|
217
|
+
this.totalDirCount += walker.totalDirCount + 1;
|
|
218
|
+
const divisor = this.options.sizeUnit === 'GB' ? (1024 * 1024 * 1024) : (1024 * 1024);
|
|
219
|
+
const size = walker.length / divisor;
|
|
220
|
+
if (size >= this.options.threshold && this.atDepth < this.options.depth) {
|
|
221
|
+
if (!this.options.quiet) {
|
|
222
|
+
process.stdout.write('\x1b[2K\r'); // Clear progress line
|
|
223
|
+
}
|
|
224
|
+
console.log(`${this.prefix} ${walker.formatSize().padStart(13)} ${this.options.sizeUnit} ` +
|
|
225
|
+
`${walker.totalDirCount.toString().padStart(5)} ` +
|
|
226
|
+
`${walker.totalFileCount.toString().padStart(6)} ${entry.name}`);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
catch (err) {
|
|
230
|
+
this.logError(entry.name, err);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
catch (err) {
|
|
236
|
+
this.logError('directory walk', err);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
//# sourceMappingURL=diskwalker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diskwalker.js","sourceRoot":"","sources":["diskwalker.ts"],"names":[],"mappings":"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"}
|
package/diskwalker.ts
ADDED
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as fp from 'fs/promises'
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import { spawn } from 'child_process';
|
|
5
|
+
import { styleText } from 'node:util';
|
|
6
|
+
|
|
7
|
+
interface WalkOptions {
|
|
8
|
+
depth: number;
|
|
9
|
+
threshold: number;
|
|
10
|
+
cmd: string;
|
|
11
|
+
verbose: boolean;
|
|
12
|
+
showCompressed: boolean;
|
|
13
|
+
finding: string[] | null;
|
|
14
|
+
forceAcl: boolean;
|
|
15
|
+
quiet: boolean;
|
|
16
|
+
sizeUnit: 'MB' | 'GB';
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
type ProgressCallback = (walker: WalkTree, currentPath: string) => void;
|
|
20
|
+
|
|
21
|
+
// Error types to suppress unless verbose
|
|
22
|
+
const suppressedErrors = ['ENOENT', 'EPERM', 'EACCES'];
|
|
23
|
+
|
|
24
|
+
export class DiskWalker {
|
|
25
|
+
private static defaultOptions: WalkOptions = {
|
|
26
|
+
depth: 1,
|
|
27
|
+
threshold: 0,
|
|
28
|
+
cmd: '',
|
|
29
|
+
verbose: false,
|
|
30
|
+
showCompressed: false,
|
|
31
|
+
finding: null,
|
|
32
|
+
forceAcl: false,
|
|
33
|
+
quiet: false,
|
|
34
|
+
sizeUnit: 'MB'
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
private options: WalkOptions;
|
|
38
|
+
private walker: WalkTree;
|
|
39
|
+
private lastProgressTime: number = 0;
|
|
40
|
+
|
|
41
|
+
constructor(private topPath: string = '.', options: Partial<WalkOptions> = {}) {
|
|
42
|
+
this.options = { ...DiskWalker.defaultOptions, ...options };
|
|
43
|
+
this.topPath = path.resolve(topPath);
|
|
44
|
+
this.walker = new WalkTree(this.topPath, this.options, this.showProgress.bind(this));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
private truncatePath(fullPath: string, maxLength: number = 64): string {
|
|
48
|
+
if (fullPath.length <= maxLength) return fullPath;
|
|
49
|
+
const half = Math.floor((maxLength - 3) / 2);
|
|
50
|
+
const start = fullPath.slice(0, half);
|
|
51
|
+
const end = fullPath.slice(-half);
|
|
52
|
+
return `${start}...${end}`;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
private showProgress(walker: WalkTree, currentPath: string): void {
|
|
56
|
+
const now = Date.now();
|
|
57
|
+
if (now - this.lastProgressTime > 1000 && !this.options.quiet) {
|
|
58
|
+
const truncatedPath = this.truncatePath(currentPath);
|
|
59
|
+
process.stdout.write('\x1b[2K\r'); // Clear entire line and return to start
|
|
60
|
+
const line = styleText(['blue'], `${walker.formatSize().padStart(13)} ${this.options.sizeUnit} ${truncatedPath}`);
|
|
61
|
+
process.stdout.write(line);
|
|
62
|
+
|
|
63
|
+
this.lastProgressTime = now;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async walk(): Promise<void> {
|
|
68
|
+
try {
|
|
69
|
+
await this.walker.walk();
|
|
70
|
+
if (!this.options.quiet) {
|
|
71
|
+
process.stdout.write('\x1b[2K\r'); // Clear progress line
|
|
72
|
+
}
|
|
73
|
+
console.log(`${this.walker.formatSize()} ${this.options.sizeUnit} ${this.topPath}`);
|
|
74
|
+
} catch (err) {
|
|
75
|
+
console.error('Failed:', (err as Error).message);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
class WalkTree {
|
|
81
|
+
private length: number = 0;
|
|
82
|
+
private compressedLength: number = 0;
|
|
83
|
+
private fileCount: number = 0;
|
|
84
|
+
private dirCount: number = 0;
|
|
85
|
+
private totalFileCount: number = 0;
|
|
86
|
+
private totalDirCount: number = 0;
|
|
87
|
+
private otherCount: number = 0;
|
|
88
|
+
private prefix: string;
|
|
89
|
+
private nextTitleUpdate: Date = new Date(0);
|
|
90
|
+
private seenErrors = new Set<string>();
|
|
91
|
+
|
|
92
|
+
constructor(
|
|
93
|
+
private dirPath: string,
|
|
94
|
+
private options: WalkOptions,
|
|
95
|
+
private progressCallback: ProgressCallback,
|
|
96
|
+
private atDepth: number = 0
|
|
97
|
+
) {
|
|
98
|
+
this.prefix = ' '.repeat(5 + atDepth * 5);
|
|
99
|
+
if (options.verbose) {
|
|
100
|
+
this.prefix = atDepth.toString().padStart(2, '0') + ' ' + this.prefix;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
public formatSize(): string {
|
|
105
|
+
const divisor = this.options.sizeUnit === 'GB' ? (1024 * 1024 * 1024) : (1024 * 1024);
|
|
106
|
+
const size = this.length / divisor;
|
|
107
|
+
const formatted = size.toLocaleString(undefined, { minimumFractionDigits: 2 });
|
|
108
|
+
|
|
109
|
+
if (this.options.showCompressed && this.length !== this.compressedLength) {
|
|
110
|
+
const compressedSize = this.compressedLength / divisor;
|
|
111
|
+
return `${formatted}/${compressedSize.toLocaleString(undefined, { minimumFractionDigits: 2 })}`;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return formatted;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
private logError(context: string, err: Error): void {
|
|
118
|
+
if (!this.options.verbose &&
|
|
119
|
+
(this.options.quiet || suppressedErrors.some(e => err.message.includes(e)))) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const errorKey = `${context}:${err.message}:${this.dirPath}`;
|
|
124
|
+
if (!this.seenErrors.has(errorKey)) {
|
|
125
|
+
console.error(styleText(['red'], `Error processing ${context}: ${err.message}`));
|
|
126
|
+
this.seenErrors.add(errorKey);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
private async isSymlink(testPath: string): Promise<boolean> {
|
|
131
|
+
try {
|
|
132
|
+
const stats = await fp.lstat(testPath);
|
|
133
|
+
return stats.isSymbolicLink();
|
|
134
|
+
} catch {
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
private async processDirectory(): Promise<void> {
|
|
140
|
+
try {
|
|
141
|
+
if (Date.now() > this.nextTitleUpdate.getTime()) {
|
|
142
|
+
process.title = this.dirPath;
|
|
143
|
+
this.nextTitleUpdate = new Date(Date.now() + 500);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const files = await fp.readdir(this.dirPath, { withFileTypes: true });
|
|
147
|
+
|
|
148
|
+
for (const file of files) {
|
|
149
|
+
if (!file.isDirectory()) {
|
|
150
|
+
this.fileCount++;
|
|
151
|
+
this.totalFileCount++;
|
|
152
|
+
|
|
153
|
+
try {
|
|
154
|
+
const stats = await fp.stat(path.join(this.dirPath, file.name));
|
|
155
|
+
this.length += stats.size;
|
|
156
|
+
this.compressedLength += stats.size;
|
|
157
|
+
} catch (err) {
|
|
158
|
+
this.logError(file.name, err as Error);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
} catch (err) {
|
|
163
|
+
this.logError('directory', err as Error);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
private async executeCommand(): Promise<void> {
|
|
168
|
+
if (!this.options.cmd) return;
|
|
169
|
+
|
|
170
|
+
const currentDir = process.cwd();
|
|
171
|
+
try {
|
|
172
|
+
process.chdir(this.dirPath);
|
|
173
|
+
await new Promise((resolve, reject) => {
|
|
174
|
+
const proc = spawn(this.options.cmd, [], { shell: true });
|
|
175
|
+
proc.on('close', resolve);
|
|
176
|
+
proc.on('error', reject);
|
|
177
|
+
});
|
|
178
|
+
} catch (err) {
|
|
179
|
+
this.logError('command execution', err as Error);
|
|
180
|
+
} finally {
|
|
181
|
+
process.chdir(currentDir);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
private async findFiles(): Promise<void> {
|
|
186
|
+
if (!this.options.finding) return;
|
|
187
|
+
|
|
188
|
+
try {
|
|
189
|
+
for (const pattern of this.options.finding) {
|
|
190
|
+
const files = await fp.readdir(this.dirPath);
|
|
191
|
+
for (const file of files) {
|
|
192
|
+
if (file.match(pattern)) {
|
|
193
|
+
try {
|
|
194
|
+
const stats = await fp.stat(path.join(this.dirPath, file));
|
|
195
|
+
const size = stats.size / 1024;
|
|
196
|
+
process.stdout.write('\x1b[2K\r'); // Clear progress line
|
|
197
|
+
console.log(
|
|
198
|
+
`${stats.mtime.toISOString().slice(0, 19).replace('T', ' ')} ` +
|
|
199
|
+
`${size.toFixed(1).padStart(8)}KB ${this.prefix} ${path.join(this.dirPath, file)}`
|
|
200
|
+
);
|
|
201
|
+
} catch (err) {
|
|
202
|
+
this.logError(file, err as Error);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
} catch (err) {
|
|
208
|
+
this.logError('file search', err as Error);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
public async walk(): Promise<void> {
|
|
213
|
+
try {
|
|
214
|
+
const isSymlink = await this.isSymlink(this.dirPath);
|
|
215
|
+
if (isSymlink) return;
|
|
216
|
+
|
|
217
|
+
this.progressCallback(this, this.dirPath);
|
|
218
|
+
|
|
219
|
+
if (this.options.cmd || this.options.finding || this.options.forceAcl) {
|
|
220
|
+
if (this.options.forceAcl) {
|
|
221
|
+
// ACL operations not implemented
|
|
222
|
+
}
|
|
223
|
+
if (this.options.cmd) {
|
|
224
|
+
await this.executeCommand();
|
|
225
|
+
}
|
|
226
|
+
if (this.options.finding) {
|
|
227
|
+
await this.findFiles();
|
|
228
|
+
}
|
|
229
|
+
} else {
|
|
230
|
+
await this.processDirectory();
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const entries = await fp.readdir(this.dirPath, { withFileTypes: true });
|
|
234
|
+
|
|
235
|
+
for (const entry of entries) {
|
|
236
|
+
if (entry.isDirectory()) {
|
|
237
|
+
const subPath = path.join(this.dirPath, entry.name);
|
|
238
|
+
const walker = new WalkTree(subPath, this.options, this.progressCallback, this.atDepth + 1);
|
|
239
|
+
|
|
240
|
+
try {
|
|
241
|
+
await walker.walk();
|
|
242
|
+
this.dirCount++;
|
|
243
|
+
this.length += walker.length;
|
|
244
|
+
this.compressedLength += walker.compressedLength;
|
|
245
|
+
this.totalFileCount += walker.totalFileCount;
|
|
246
|
+
this.totalDirCount += walker.totalDirCount + 1;
|
|
247
|
+
|
|
248
|
+
const divisor = this.options.sizeUnit === 'GB' ? (1024 * 1024 * 1024) : (1024 * 1024);
|
|
249
|
+
const size = walker.length / divisor;
|
|
250
|
+
if (size >= this.options.threshold && this.atDepth < this.options.depth) {
|
|
251
|
+
if (!this.options.quiet) {
|
|
252
|
+
process.stdout.write('\x1b[2K\r'); // Clear progress line
|
|
253
|
+
}
|
|
254
|
+
console.log(
|
|
255
|
+
`${this.prefix} ${walker.formatSize().padStart(13)} ${this.options.sizeUnit} ` +
|
|
256
|
+
`${walker.totalDirCount.toString().padStart(5)} ` +
|
|
257
|
+
`${walker.totalFileCount.toString().padStart(6)} ${entry.name}`
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
} catch (err) {
|
|
261
|
+
this.logError(entry.name, err as Error);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
} catch (err) {
|
|
266
|
+
this.logError('directory walk', err as Error);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export { WalkOptions };
|