@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/diskwalker2.js ADDED
@@ -0,0 +1,280 @@
1
+ import * as path from 'path';
2
+ import { spawn } from 'child_process';
3
+ import { styleText } from 'node:util';
4
+ import { listDirectory, getDriveInfo } from '@bobfrankston/dirutil';
5
+ // Error types to suppress unless verbose
6
+ const suppressedErrors = ['ENOENT', 'EPERM', 'EACCES'];
7
+ // Directories to suppress errors from
8
+ const suppressedDirectories = ['$RECYCLE.BIN', 'System Volume Information'];
9
+ export class DiskWalker {
10
+ topPath;
11
+ static defaultOptions = {
12
+ depth: 1,
13
+ threshold: 0,
14
+ cmd: '',
15
+ verbose: false,
16
+ showCompressed: false,
17
+ finding: null,
18
+ forceAcl: false,
19
+ quiet: false,
20
+ sizeUnit: 'MB'
21
+ };
22
+ options;
23
+ walker;
24
+ lastProgressTime = 0;
25
+ static blockSizeCache = new Map();
26
+ constructor(topPath = '.', options = {}) {
27
+ this.topPath = topPath;
28
+ this.options = { ...DiskWalker.defaultOptions, ...options };
29
+ this.topPath = path.resolve(topPath);
30
+ this.walker = new WalkTree(this.topPath, this.options, this.showProgress.bind(this));
31
+ }
32
+ truncatePath(fullPath, maxLength = 64) {
33
+ if (fullPath.length <= maxLength)
34
+ return fullPath;
35
+ const half = Math.floor((maxLength - 3) / 2);
36
+ const start = fullPath.slice(0, half);
37
+ const end = fullPath.slice(-half);
38
+ return `${start}...${end}`;
39
+ }
40
+ showProgress(walker, currentPath) {
41
+ const now = Date.now();
42
+ if (now - this.lastProgressTime > 1000 && !this.options.quiet) {
43
+ const truncatedPath = this.truncatePath(currentPath);
44
+ process.stdout.write('\x1b[2K\r'); // Clear entire line and return to start
45
+ const line = styleText(['blue'], `${walker.formatSize().padStart(13)} ${this.options.sizeUnit} ${truncatedPath}`);
46
+ process.stdout.write(line);
47
+ this.lastProgressTime = now;
48
+ }
49
+ }
50
+ async walk() {
51
+ try {
52
+ await this.walker.walk();
53
+ if (!this.options.quiet) {
54
+ process.stdout.write('\x1b[2K\r'); // Clear progress line
55
+ }
56
+ const pather = `${styleText(['blue'], `${path.dirname(this.topPath)}${path.sep}`)}${styleText(['bgBlack', 'bold', 'yellow'], path.basename(this.topPath))}`;
57
+ console.log(`${this.walker.formatSize()} ${this.options.sizeUnit} ${pather}`);
58
+ }
59
+ catch (err) {
60
+ console.error('Failed:', err.message);
61
+ }
62
+ }
63
+ }
64
+ class WalkTree {
65
+ dirPath;
66
+ options;
67
+ progressCallback;
68
+ atDepth;
69
+ length = 0;
70
+ compressedLength = 0;
71
+ fileCount = 0;
72
+ dirCount = 0;
73
+ totalFileCount = 0;
74
+ totalDirCount = 0;
75
+ otherCount = 0;
76
+ prefix;
77
+ nextTitleUpdate = new Date(0);
78
+ seenErrors = new Set();
79
+ blockSize = 4096; // default 4KB, will be updated
80
+ constructor(dirPath, options, progressCallback, atDepth = 0) {
81
+ this.dirPath = dirPath;
82
+ this.options = options;
83
+ this.progressCallback = progressCallback;
84
+ this.atDepth = atDepth;
85
+ this.prefix = ' '.repeat(5 + atDepth * 5);
86
+ if (options.verbose) {
87
+ this.prefix = atDepth.toString().padStart(2, '0') + ' ' + this.prefix;
88
+ }
89
+ }
90
+ getDiskBlockSize(drivePath) {
91
+ const drive = path.parse(drivePath).root;
92
+ if (DiskWalker.blockSizeCache.has(drive)) {
93
+ return DiskWalker.blockSizeCache.get(drive);
94
+ }
95
+ try {
96
+ const driveInfo = getDriveInfo(drivePath);
97
+ const blockSize = driveInfo?.blockSize || 4096;
98
+ DiskWalker.blockSizeCache.set(drive, blockSize);
99
+ return blockSize;
100
+ }
101
+ catch (err) {
102
+ // Fallback to default 4KB block size
103
+ DiskWalker.blockSizeCache.set(drive, 4096);
104
+ return 4096;
105
+ }
106
+ }
107
+ roundUpToBlockSize(fileSize, blockSize) {
108
+ return Math.ceil(fileSize / blockSize) * blockSize;
109
+ }
110
+ formatSize() {
111
+ const divisor = this.options.sizeUnit === 'GB' ? (1024 * 1024 * 1024) : (1024 * 1024);
112
+ const size = this.length / divisor;
113
+ const formatted = size.toLocaleString(undefined, { minimumFractionDigits: 2 });
114
+ if (this.options.showCompressed && this.length !== this.compressedLength) {
115
+ const compressedSize = this.compressedLength / divisor;
116
+ return `${formatted}/${compressedSize.toLocaleString(undefined, { minimumFractionDigits: 2 })}`;
117
+ }
118
+ return formatted;
119
+ }
120
+ logError(context, err) {
121
+ // Only suppress errors if explicitly quiet, or if it's a known unimportant error
122
+ if (this.options.quiet ||
123
+ (!this.options.verbose &&
124
+ (suppressedErrors.some(e => err.message.includes(e)) ||
125
+ suppressedDirectories.some(d => this.dirPath.includes(d))))) {
126
+ return;
127
+ }
128
+ const errorKey = `${context}:${err.message}:${this.dirPath}`;
129
+ if (!this.seenErrors.has(errorKey)) {
130
+ console.error(styleText(['red'], `Error processing ${context}: ${err.message}`));
131
+ this.seenErrors.add(errorKey);
132
+ }
133
+ }
134
+ processFilesFromEntries(entries) {
135
+ try {
136
+ if (Date.now() > this.nextTitleUpdate.getTime()) {
137
+ process.title = this.dirPath;
138
+ this.nextTitleUpdate = new Date(Date.now() + 500);
139
+ }
140
+ // Get block size once for this directory's drive
141
+ if (this.blockSize === 4096) { // Only query if we haven't set it yet
142
+ this.blockSize = this.getDiskBlockSize(this.dirPath);
143
+ }
144
+ const fileEntries = entries.filter(entry => entry.isFile);
145
+ for (const entry of fileEntries) {
146
+ this.fileCount++;
147
+ this.totalFileCount++;
148
+ const fileSize = entry.size || 0;
149
+ const blockAlignedSize = this.roundUpToBlockSize(fileSize, this.blockSize);
150
+ this.length += blockAlignedSize;
151
+ this.compressedLength += fileSize; // Keep original size for compressed display
152
+ }
153
+ }
154
+ catch (err) {
155
+ this.logError('directory', err);
156
+ }
157
+ }
158
+ async executeCommand() {
159
+ if (!this.options.cmd)
160
+ return;
161
+ const currentDir = process.cwd();
162
+ try {
163
+ process.chdir(this.dirPath);
164
+ await new Promise((resolve, reject) => {
165
+ const proc = spawn(this.options.cmd, [], { shell: true });
166
+ proc.on('close', resolve);
167
+ proc.on('error', reject);
168
+ });
169
+ }
170
+ catch (err) {
171
+ this.logError('command execution', err);
172
+ }
173
+ finally {
174
+ process.chdir(currentDir);
175
+ }
176
+ }
177
+ async findFilesFromEntries(entries) {
178
+ if (!this.options.finding)
179
+ return;
180
+ try {
181
+ for (const pattern of this.options.finding) {
182
+ for (const entry of entries) {
183
+ if (entry.name.match(pattern)) {
184
+ const size = (entry.size || 0) / 1024;
185
+ const mtime = new Date(entry.mtime);
186
+ process.stdout.write('\x1b[2K\r'); // Clear progress line
187
+ console.log(`${mtime.toISOString().slice(0, 19).replace('T', ' ')} ` +
188
+ `${size.toFixed(1).padStart(8)}KB ${this.prefix} ${path.join(this.dirPath, entry.name)}`);
189
+ }
190
+ }
191
+ }
192
+ }
193
+ catch (err) {
194
+ this.logError('file search', err);
195
+ }
196
+ }
197
+ async walk() {
198
+ try {
199
+ this.progressCallback(this, this.dirPath);
200
+ // try {
201
+ // // console.log(`DEBUG: Accessing directory: ${this.dirPath}`);
202
+ // // fs.accessSync(this.dirPath);
203
+ // // console.log(`DEBUG: Accessed directory: ${this.dirPath}`);
204
+ // fs.readdirSync(this.dirPath); // Ensure we can read the directory
205
+ // // console.log(`DEBUG: Read directory: ${this.dirPath}`);
206
+ // }
207
+ // catch (e: any) {
208
+ // // console.error(`DEBUG: Failed to access or read directory ${this.dirPath}: ${e.message}`);
209
+ // debugger;
210
+ // throw e
211
+ // }
212
+ // Get directory entries once
213
+ // console.log(`DEBUG: Walking directory: ${this.dirPath}`);
214
+ const entries = listDirectory(this.dirPath, { stats: true });
215
+ // console.log(`DEBUG: Walked directory: ${this.dirPath}`);
216
+ if (this.options.verbose) {
217
+ console.log(`DEBUG: Found ${entries.length} entries in ${this.dirPath}`);
218
+ entries.forEach(e => console.log(` ${e.name}: isDir=${e.isDirectory}, isFile=${e.isFile}`));
219
+ }
220
+ if (this.options.cmd || this.options.finding || this.options.forceAcl) {
221
+ if (this.options.forceAcl) {
222
+ // ACL operations not implemented
223
+ }
224
+ if (this.options.cmd) {
225
+ await this.executeCommand();
226
+ }
227
+ if (this.options.finding) {
228
+ // Use the entries we already have for finding
229
+ await this.findFilesFromEntries(entries);
230
+ }
231
+ }
232
+ else {
233
+ // Process files from the entries we already have
234
+ this.processFilesFromEntries(entries);
235
+ }
236
+ // Process directories for recursion (skip symlinks by default)
237
+ const directoryEntries = entries.filter(entry => entry.isDirectory && !entry.isSymlink);
238
+ for (const entry of directoryEntries) {
239
+ try {
240
+ const subPath = path.join(this.dirPath, entry.name);
241
+ const walker = new WalkTree(subPath, this.options, this.progressCallback, this.atDepth + 1);
242
+ await walker.walk();
243
+ if (this.options.verbose) {
244
+ console.log(`DEBUG: Completed walking ${entry.name}, about to process results`);
245
+ }
246
+ this.dirCount++;
247
+ this.length += walker.length;
248
+ this.compressedLength += walker.compressedLength;
249
+ this.totalFileCount += walker.totalFileCount;
250
+ this.totalDirCount += walker.totalDirCount + 1;
251
+ const divisor = this.options.sizeUnit === 'GB' ? (1024 * 1024 * 1024) : (1024 * 1024);
252
+ const size = walker.length / divisor;
253
+ if (this.options.verbose) {
254
+ console.log(`DEBUG: ${entry.name}: size=${size}, threshold=${this.options.threshold}, depth=${this.atDepth}/${this.options.depth}, length=${walker.length}`);
255
+ console.log(`DEBUG: condition check: (${this.options.threshold} === 0 || ${size} >= ${this.options.threshold}) && ${this.atDepth} < ${this.options.depth} = ${(this.options.threshold === 0 || size >= this.options.threshold) && this.atDepth < this.options.depth}`);
256
+ }
257
+ if ((this.options.threshold === 0 || size >= this.options.threshold) && this.atDepth < this.options.depth) {
258
+ if (!this.options.quiet) {
259
+ process.stdout.write('\x1b[2K\r'); // Clear progress line
260
+ }
261
+ console.log(`${this.prefix} ${walker.formatSize().padStart(13)} ${this.options.sizeUnit} ` +
262
+ `${walker.totalDirCount.toString().padStart(5)} ` +
263
+ `${walker.totalFileCount.toString().padStart(6)} ${entry.name}`);
264
+ }
265
+ }
266
+ catch (err) {
267
+ if (this.options.verbose) {
268
+ console.log(`DEBUG: Error processing ${entry.name}: ${err}`);
269
+ }
270
+ this.logError(entry.name, err);
271
+ // Continue with next directory instead of aborting
272
+ }
273
+ }
274
+ }
275
+ catch (err) {
276
+ this.logError('directory walk', err);
277
+ }
278
+ }
279
+ }
280
+ //# sourceMappingURL=diskwalker2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diskwalker2.js","sourceRoot":"","sources":["diskwalker2.ts"],"names":[],"mappings":"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"}
package/diskwalker2.ts ADDED
@@ -0,0 +1,319 @@
1
+ import fs from 'node:fs';
2
+ import * as path from 'path';
3
+ import { spawn } from 'child_process';
4
+ import { styleText } from 'node:util';
5
+ import { listDirectory, getDriveInfo, type DirectoryEntry } from '@bobfrankston/dirutil';
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
+ // Directories to suppress errors from
25
+ const suppressedDirectories = ['$RECYCLE.BIN', 'System Volume Information'];
26
+
27
+ export class DiskWalker {
28
+ private static defaultOptions: WalkOptions = {
29
+ depth: 1,
30
+ threshold: 0,
31
+ cmd: '',
32
+ verbose: false,
33
+ showCompressed: false,
34
+ finding: null,
35
+ forceAcl: false,
36
+ quiet: false,
37
+ sizeUnit: 'MB'
38
+ };
39
+
40
+ private options: WalkOptions;
41
+ private walker: WalkTree;
42
+ private lastProgressTime: number = 0;
43
+ public static blockSizeCache: Map<string, number> = new Map();
44
+
45
+ constructor(private topPath: string = '.', options: Partial<WalkOptions> = {}) {
46
+ this.options = { ...DiskWalker.defaultOptions, ...options };
47
+ this.topPath = path.resolve(topPath);
48
+ this.walker = new WalkTree(this.topPath, this.options, this.showProgress.bind(this));
49
+ }
50
+
51
+ private truncatePath(fullPath: string, maxLength: number = 64): string {
52
+ if (fullPath.length <= maxLength) return fullPath;
53
+ const half = Math.floor((maxLength - 3) / 2);
54
+ const start = fullPath.slice(0, half);
55
+ const end = fullPath.slice(-half);
56
+ return `${start}...${end}`;
57
+ }
58
+
59
+ private showProgress(walker: WalkTree, currentPath: string): void {
60
+ const now = Date.now();
61
+ if (now - this.lastProgressTime > 1000 && !this.options.quiet) {
62
+ const truncatedPath = this.truncatePath(currentPath);
63
+ process.stdout.write('\x1b[2K\r'); // Clear entire line and return to start
64
+ const line = styleText(['blue'], `${walker.formatSize().padStart(13)} ${this.options.sizeUnit} ${truncatedPath}`);
65
+ process.stdout.write(line);
66
+
67
+ this.lastProgressTime = now;
68
+ }
69
+ }
70
+
71
+ async walk(): Promise<void> {
72
+ try {
73
+ await this.walker.walk();
74
+ if (!this.options.quiet) {
75
+ process.stdout.write('\x1b[2K\r'); // Clear progress line
76
+ }
77
+ const pather = `${styleText(['blue'], `${path.dirname(this.topPath)}${path.sep}`)}${styleText(['bgBlack', 'bold', 'yellow'], path.basename(this.topPath))}`
78
+ console.log(`${this.walker.formatSize()} ${this.options.sizeUnit} ${pather}`);
79
+ } catch (err: any) {
80
+ console.error('Failed:', (err as Error).message);
81
+ }
82
+ }
83
+ }
84
+
85
+ class WalkTree {
86
+ private length: number = 0;
87
+ private compressedLength: number = 0;
88
+ private fileCount: number = 0;
89
+ private dirCount: number = 0;
90
+ private totalFileCount: number = 0;
91
+ private totalDirCount: number = 0;
92
+ private otherCount: number = 0;
93
+ private prefix: string;
94
+ private nextTitleUpdate: Date = new Date(0);
95
+ private seenErrors = new Set<string>();
96
+ private blockSize: number = 4096; // default 4KB, will be updated
97
+
98
+ constructor(
99
+ private dirPath: string,
100
+ private options: WalkOptions,
101
+ private progressCallback: ProgressCallback,
102
+ private atDepth: number = 0
103
+ ) {
104
+ this.prefix = ' '.repeat(5 + atDepth * 5);
105
+ if (options.verbose) {
106
+ this.prefix = atDepth.toString().padStart(2, '0') + ' ' + this.prefix;
107
+ }
108
+ }
109
+
110
+ private getDiskBlockSize(drivePath: string): number {
111
+ const drive = path.parse(drivePath).root;
112
+
113
+ if (DiskWalker.blockSizeCache.has(drive)) {
114
+ return DiskWalker.blockSizeCache.get(drive)!;
115
+ }
116
+
117
+ try {
118
+ const driveInfo = getDriveInfo(drivePath);
119
+ const blockSize = driveInfo?.blockSize || 4096;
120
+ DiskWalker.blockSizeCache.set(drive, blockSize);
121
+ return blockSize;
122
+ } catch (err: any) {
123
+ // Fallback to default 4KB block size
124
+ DiskWalker.blockSizeCache.set(drive, 4096);
125
+ return 4096;
126
+ }
127
+ }
128
+
129
+ private roundUpToBlockSize(fileSize: number, blockSize: number): number {
130
+ return Math.ceil(fileSize / blockSize) * blockSize;
131
+ }
132
+
133
+ public formatSize(): string {
134
+ const divisor = this.options.sizeUnit === 'GB' ? (1024 * 1024 * 1024) : (1024 * 1024);
135
+ const size = this.length / divisor;
136
+ const formatted = size.toLocaleString(undefined, { minimumFractionDigits: 2 });
137
+
138
+ if (this.options.showCompressed && this.length !== this.compressedLength) {
139
+ const compressedSize = this.compressedLength / divisor;
140
+ return `${formatted}/${compressedSize.toLocaleString(undefined, { minimumFractionDigits: 2 })}`;
141
+ }
142
+
143
+ return formatted;
144
+ }
145
+
146
+ private logError(context: string, err: Error): void {
147
+ // Only suppress errors if explicitly quiet, or if it's a known unimportant error
148
+ if (this.options.quiet ||
149
+ (!this.options.verbose &&
150
+ (suppressedErrors.some(e => err.message.includes(e)) ||
151
+ suppressedDirectories.some(d => this.dirPath.includes(d))))) {
152
+ return;
153
+ }
154
+
155
+ const errorKey = `${context}:${err.message}:${this.dirPath}`;
156
+ if (!this.seenErrors.has(errorKey)) {
157
+ console.error(styleText(['red'], `Error processing ${context}: ${err.message}`));
158
+ this.seenErrors.add(errorKey);
159
+ }
160
+ }
161
+
162
+ private processFilesFromEntries(entries: DirectoryEntry[]): void {
163
+ try {
164
+ if (Date.now() > this.nextTitleUpdate.getTime()) {
165
+ process.title = this.dirPath;
166
+ this.nextTitleUpdate = new Date(Date.now() + 500);
167
+ }
168
+
169
+ // Get block size once for this directory's drive
170
+ if (this.blockSize === 4096) { // Only query if we haven't set it yet
171
+ this.blockSize = this.getDiskBlockSize(this.dirPath);
172
+ }
173
+
174
+ const fileEntries = entries.filter(entry => entry.isFile);
175
+ for (const entry of fileEntries) {
176
+ this.fileCount++;
177
+ this.totalFileCount++;
178
+ const fileSize = entry.size || 0;
179
+ const blockAlignedSize = this.roundUpToBlockSize(fileSize, this.blockSize);
180
+ this.length += blockAlignedSize;
181
+ this.compressedLength += fileSize; // Keep original size for compressed display
182
+ }
183
+ } catch (err: any) {
184
+ this.logError('directory', err as Error);
185
+ }
186
+ }
187
+
188
+ private async executeCommand(): Promise<void> {
189
+ if (!this.options.cmd) return;
190
+
191
+ const currentDir = process.cwd();
192
+ try {
193
+ process.chdir(this.dirPath);
194
+ await new Promise((resolve, reject) => {
195
+ const proc = spawn(this.options.cmd, [], { shell: true });
196
+ proc.on('close', resolve);
197
+ proc.on('error', reject);
198
+ });
199
+ } catch (err: any) {
200
+ this.logError('command execution', err as Error);
201
+ } finally {
202
+ process.chdir(currentDir);
203
+ }
204
+ }
205
+
206
+ private async findFilesFromEntries(entries: DirectoryEntry[]): Promise<void> {
207
+ if (!this.options.finding) return;
208
+
209
+ try {
210
+ for (const pattern of this.options.finding) {
211
+ for (const entry of entries) {
212
+ if (entry.name.match(pattern)) {
213
+ const size = (entry.size || 0) / 1024;
214
+ const mtime = new Date(entry.mtime);
215
+ process.stdout.write('\x1b[2K\r'); // Clear progress line
216
+ console.log(
217
+ `${mtime.toISOString().slice(0, 19).replace('T', ' ')} ` +
218
+ `${size.toFixed(1).padStart(8)}KB ${this.prefix} ${path.join(this.dirPath, entry.name)}`
219
+ );
220
+ }
221
+ }
222
+ }
223
+ } catch (err: any) {
224
+ this.logError('file search', err as Error);
225
+ }
226
+ }
227
+
228
+ public async walk(): Promise<void> {
229
+ try {
230
+ this.progressCallback(this, this.dirPath);
231
+
232
+ // try {
233
+ // // console.log(`DEBUG: Accessing directory: ${this.dirPath}`);
234
+ // // fs.accessSync(this.dirPath);
235
+ // // console.log(`DEBUG: Accessed directory: ${this.dirPath}`);
236
+ // fs.readdirSync(this.dirPath); // Ensure we can read the directory
237
+ // // console.log(`DEBUG: Read directory: ${this.dirPath}`);
238
+ // }
239
+ // catch (e: any) {
240
+ // // console.error(`DEBUG: Failed to access or read directory ${this.dirPath}: ${e.message}`);
241
+ // debugger;
242
+ // throw e
243
+ // }
244
+
245
+
246
+ // Get directory entries once
247
+ // console.log(`DEBUG: Walking directory: ${this.dirPath}`);
248
+ const entries = listDirectory(this.dirPath, { stats: true });
249
+ // console.log(`DEBUG: Walked directory: ${this.dirPath}`);
250
+
251
+ if (this.options.verbose) {
252
+ console.log(`DEBUG: Found ${entries.length} entries in ${this.dirPath}`);
253
+ entries.forEach(e => console.log(` ${e.name}: isDir=${e.isDirectory}, isFile=${e.isFile}`));
254
+ }
255
+
256
+ if (this.options.cmd || this.options.finding || this.options.forceAcl) {
257
+ if (this.options.forceAcl) {
258
+ // ACL operations not implemented
259
+ }
260
+ if (this.options.cmd) {
261
+ await this.executeCommand();
262
+ }
263
+ if (this.options.finding) {
264
+ // Use the entries we already have for finding
265
+ await this.findFilesFromEntries(entries);
266
+ }
267
+ } else {
268
+ // Process files from the entries we already have
269
+ this.processFilesFromEntries(entries);
270
+ }
271
+
272
+ // Process directories for recursion (skip symlinks by default)
273
+ const directoryEntries = entries.filter(entry => entry.isDirectory && !entry.isSymlink);
274
+ for (const entry of directoryEntries) {
275
+ try {
276
+ const subPath = path.join(this.dirPath, entry.name);
277
+ const walker = new WalkTree(subPath, this.options, this.progressCallback, this.atDepth + 1);
278
+
279
+ await walker.walk();
280
+ if (this.options.verbose) {
281
+ console.log(`DEBUG: Completed walking ${entry.name}, about to process results`);
282
+ }
283
+ this.dirCount++;
284
+ this.length += walker.length;
285
+ this.compressedLength += walker.compressedLength;
286
+ this.totalFileCount += walker.totalFileCount;
287
+ this.totalDirCount += walker.totalDirCount + 1;
288
+
289
+ const divisor = this.options.sizeUnit === 'GB' ? (1024 * 1024 * 1024) : (1024 * 1024);
290
+ const size = walker.length / divisor;
291
+ if (this.options.verbose) {
292
+ console.log(`DEBUG: ${entry.name}: size=${size}, threshold=${this.options.threshold}, depth=${this.atDepth}/${this.options.depth}, length=${walker.length}`);
293
+ console.log(`DEBUG: condition check: (${this.options.threshold} === 0 || ${size} >= ${this.options.threshold}) && ${this.atDepth} < ${this.options.depth} = ${(this.options.threshold === 0 || size >= this.options.threshold) && this.atDepth < this.options.depth}`);
294
+ }
295
+ if ((this.options.threshold === 0 || size >= this.options.threshold) && this.atDepth < this.options.depth) {
296
+ if (!this.options.quiet) {
297
+ process.stdout.write('\x1b[2K\r'); // Clear progress line
298
+ }
299
+ console.log(
300
+ `${this.prefix} ${walker.formatSize().padStart(13)} ${this.options.sizeUnit} ` +
301
+ `${walker.totalDirCount.toString().padStart(5)} ` +
302
+ `${walker.totalFileCount.toString().padStart(6)} ${entry.name}`
303
+ );
304
+ }
305
+ } catch (err: any) {
306
+ if (this.options.verbose) {
307
+ console.log(`DEBUG: Error processing ${entry.name}: ${err}`);
308
+ }
309
+ this.logError(entry.name, err as Error);
310
+ // Continue with next directory instead of aborting
311
+ }
312
+ }
313
+ } catch (err: any) {
314
+ this.logError('directory walk', err as Error);
315
+ }
316
+ }
317
+ }
318
+
319
+ export { WalkOptions };