@bobfrankston/tswalk 1.1.8 → 1.1.9
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/index.js +14 -0
- package/index.js.map +1 -1
- package/package.json +21 -4
- package/.claude/settings.local.json +0 -9
- package/.editorconfig +0 -9
- package/.gitattributes +0 -2
- package/.vscode/launch.json +0 -32
- package/.vscode/settings.json +0 -23
- package/.vscode/tasks-ts.json +0 -23
- package/.vscode/tasks.json +0 -20
- package/diskwalker.ts +0 -281
- package/index.ts +0 -138
- package/programming.md +0 -40
- package/tsconfig.json +0 -14
- package/utils.ts +0 -100
package/index.js
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
import { DiskWalker } from './diskwalker.js';
|
|
3
3
|
import { defaultWalkOptions } from './utils.js';
|
|
4
4
|
import { styleText } from 'node:util';
|
|
5
|
+
import packageJson from './package.json' with { type: 'json' };
|
|
6
|
+
const version = packageJson.version;
|
|
7
|
+
function showVersion() {
|
|
8
|
+
console.log(`tswalk v${version}`);
|
|
9
|
+
process.exit(0);
|
|
10
|
+
}
|
|
5
11
|
function showUsage() {
|
|
6
12
|
console.log(`
|
|
7
13
|
Usage: tswalk [path] [depth] [options]
|
|
@@ -19,6 +25,7 @@ Options:
|
|
|
19
25
|
-v Verbose output (show all errors including ENOENT/EPERM)
|
|
20
26
|
-q Quiet mode (no progress updates, minimal errors)
|
|
21
27
|
-acl Force ACL operations (Windows only)
|
|
28
|
+
--version Show version number
|
|
22
29
|
|
|
23
30
|
Progress:
|
|
24
31
|
Shows current directory being processed every second in blue.
|
|
@@ -44,6 +51,13 @@ async function main() {
|
|
|
44
51
|
if (args.includes('-h') || args.includes('-help') || args.includes('--help')) {
|
|
45
52
|
showUsage();
|
|
46
53
|
}
|
|
54
|
+
if (args.includes('-v') && args.length === 1) {
|
|
55
|
+
// If -v is the only argument, show version
|
|
56
|
+
showVersion();
|
|
57
|
+
}
|
|
58
|
+
if (args.includes('--version')) {
|
|
59
|
+
showVersion();
|
|
60
|
+
}
|
|
47
61
|
const options = {};
|
|
48
62
|
let topPath = '.';
|
|
49
63
|
let foundPath = false;
|
package/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,UAAU,EAAe,MAAM,iBAAiB,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,21 +1,38 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bobfrankston/tswalk",
|
|
3
|
-
"version": "1.1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.1.9",
|
|
4
|
+
"description": "Fast TypeScript directory tree walker with file search, size analysis, and pattern matching",
|
|
5
5
|
"main": "index.js",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"import": "./index.js",
|
|
9
|
+
"types": "./index.d.ts"
|
|
10
|
+
}
|
|
11
|
+
},
|
|
6
12
|
"bin": {
|
|
7
13
|
"tswalk": "./index.js"
|
|
8
14
|
},
|
|
9
|
-
"
|
|
15
|
+
"files": [
|
|
16
|
+
"*.js",
|
|
17
|
+
"*.d.ts",
|
|
18
|
+
"*.js.map",
|
|
19
|
+
"bin/"
|
|
20
|
+
],
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">=24.0.0"
|
|
23
|
+
},
|
|
10
24
|
"scripts": {
|
|
11
25
|
"build": "tsc",
|
|
26
|
+
"clean": "rimraf *.js *.d.ts *.js.map bin/*.js",
|
|
12
27
|
"prerelease:local": "git add -A && (git diff-index --quiet HEAD || git commit -m \"Pre-release commit\")",
|
|
13
28
|
"preversion": "npm run build && git add -A",
|
|
14
29
|
"postversion": "git push && git push --tags",
|
|
15
30
|
"release": "npm run prerelease:local && npm version patch && npm publish",
|
|
16
31
|
"release:ps1": "releaseapp.ps1",
|
|
17
32
|
"test": "echo \"Error: no test specified\" && exit 1",
|
|
18
|
-
"installer": "npm run release && npm install -g @bobfrankston/tswalk && wsl npm install -g @bobfrankston/tswalk"
|
|
33
|
+
"installer": "npm run release && npm install -g @bobfrankston/tswalk && wsl npm install -g @bobfrankston/tswalk",
|
|
34
|
+
"dev": "tsc --watch",
|
|
35
|
+
"start": "node index.js"
|
|
19
36
|
},
|
|
20
37
|
"keywords": [
|
|
21
38
|
"typescript",
|
package/.editorconfig
DELETED
package/.gitattributes
DELETED
package/.vscode/launch.json
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
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}/index.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
|
-
"c:\\users\\bob\\onedrive\\pictures",
|
|
29
|
-
]
|
|
30
|
-
}
|
|
31
|
-
]
|
|
32
|
-
}
|
package/.vscode/settings.json
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
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
|
-
"workbench.colorTheme": "Visual Studio 2019 Dark"
|
|
23
|
-
}
|
package/.vscode/tasks-ts.json
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
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/.vscode/tasks.json
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": "2.0.0",
|
|
3
|
-
"tasks": [
|
|
4
|
-
{
|
|
5
|
-
"label": "tsc: watch",
|
|
6
|
-
"type": "shell",
|
|
7
|
-
"command": "tsc",
|
|
8
|
-
"args": ["--watch"],
|
|
9
|
-
"runOptions": {
|
|
10
|
-
"runOn": "folderOpen"
|
|
11
|
-
},
|
|
12
|
-
"problemMatcher": "$tsc-watch",
|
|
13
|
-
"isBackground": true,
|
|
14
|
-
"group": {
|
|
15
|
-
"kind": "build",
|
|
16
|
-
"isDefault": true
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
]
|
|
20
|
-
}
|
package/diskwalker.ts
DELETED
|
@@ -1,281 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* DISKWALKER.TS - Simplified Directory Walker Implementation
|
|
3
|
-
*
|
|
4
|
-
* Single directory walker implementation using Node.js built-in fs/fs.promises APIs.
|
|
5
|
-
* Provides directory traversal and file operations using standard filesystem calls.
|
|
6
|
-
*
|
|
7
|
-
* Key features:
|
|
8
|
-
* - Uses fs.readdir() and fs.stat() for directory enumeration
|
|
9
|
-
* - Simple, lightweight implementation with minimal dependencies
|
|
10
|
-
* - Safe regex compilation for -f patterns (fixes "Nothing to repeat" errors)
|
|
11
|
-
* - Supports glob patterns (*.js) and regex literals (/pattern/flags)
|
|
12
|
-
* - Comprehensive error handling for filesystem operations
|
|
13
|
-
*
|
|
14
|
-
* Recent improvements:
|
|
15
|
-
* - Fixed regex compilation to safely handle patterns like "*.exe" and "/*.js/"
|
|
16
|
-
* - Consolidated from dual implementation to single fs-based approach
|
|
17
|
-
* - Removed dirutil dependency for simplicity and similar performance
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
import * as fs from 'fs';
|
|
21
|
-
import * as fp from 'fs/promises'
|
|
22
|
-
import * as path from 'path';
|
|
23
|
-
import { spawn } from 'child_process';
|
|
24
|
-
import { styleText } from 'node:util';
|
|
25
|
-
import { compilePatterns, WalkOptions, defaultWalkOptions, suppressedErrors } from './utils.js';
|
|
26
|
-
|
|
27
|
-
type ProgressCallback = (walker: WalkTree, currentPath: string) => void;
|
|
28
|
-
|
|
29
|
-
export class DiskWalker {
|
|
30
|
-
private static defaultOptions: WalkOptions = defaultWalkOptions;
|
|
31
|
-
|
|
32
|
-
private options: WalkOptions;
|
|
33
|
-
private walker: WalkTree;
|
|
34
|
-
private lastProgressTime: number = 0;
|
|
35
|
-
|
|
36
|
-
constructor(private topPath: string = '.', options: Partial<WalkOptions> = {}) {
|
|
37
|
-
this.options = { ...DiskWalker.defaultOptions, ...options };
|
|
38
|
-
this.topPath = path.resolve(topPath);
|
|
39
|
-
this.walker = new WalkTree(this.topPath, this.options, this.showProgress.bind(this));
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
private truncatePath(fullPath: string, maxLength: number = 64): string {
|
|
43
|
-
if (fullPath.length <= maxLength) return fullPath;
|
|
44
|
-
const half = Math.floor((maxLength - 3) / 2);
|
|
45
|
-
const start = fullPath.slice(0, half);
|
|
46
|
-
const end = fullPath.slice(-half);
|
|
47
|
-
return `${start}...${end}`;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
private showProgress(walker: WalkTree, currentPath: string): void {
|
|
51
|
-
const now = Date.now();
|
|
52
|
-
if (now - this.lastProgressTime > 1000 && !this.options.quiet) {
|
|
53
|
-
const truncatedPath = this.truncatePath(currentPath);
|
|
54
|
-
process.stdout.write('\x1b[2K\r'); // Clear entire line and return to start
|
|
55
|
-
const line = styleText(['blue'], `${walker.formatSize().padStart(13)} ${this.options.sizeUnit} ${truncatedPath}`);
|
|
56
|
-
process.stdout.write(line);
|
|
57
|
-
|
|
58
|
-
this.lastProgressTime = now;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
async walk(): Promise<void> {
|
|
63
|
-
try {
|
|
64
|
-
// Show column headers (only for directory listing, not file finding)
|
|
65
|
-
if (!this.options.quiet && !this.options.finding && !this.options.cmd) {
|
|
66
|
-
const prefix = ''; // No prefix at depth 0
|
|
67
|
-
console.log(`${prefix} ${'Size'.padStart(13)} ${this.options.sizeUnit} ${'Dirs'.padStart(5)} ${'Files'.padStart(6)} Directory`);
|
|
68
|
-
console.log(`${prefix} ${'-'.repeat(13)} ${'-'.repeat(this.options.sizeUnit.length)} ${'-'.repeat(5)} ${'-'.repeat(6)} ${'-'.repeat(9)}`);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
await this.walker.walk();
|
|
72
|
-
if (!this.options.quiet) {
|
|
73
|
-
process.stdout.write('\x1b[2K\r'); // Clear progress line
|
|
74
|
-
}
|
|
75
|
-
console.log(`${this.walker.formatSize()} ${this.options.sizeUnit} ${styleText(['blue'], this.topPath)}`);
|
|
76
|
-
} catch (err) {
|
|
77
|
-
console.error('Failed:', (err as Error).message);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
class WalkTree {
|
|
83
|
-
private length: number = 0;
|
|
84
|
-
private compressedLength: number = 0;
|
|
85
|
-
private fileCount: number = 0;
|
|
86
|
-
private dirCount: number = 0;
|
|
87
|
-
private totalFileCount: number = 0;
|
|
88
|
-
private totalDirCount: number = 0;
|
|
89
|
-
private otherCount: number = 0;
|
|
90
|
-
private prefix: string;
|
|
91
|
-
private nextTitleUpdate: Date = new Date(0);
|
|
92
|
-
private seenErrors = new Set<string>();
|
|
93
|
-
private findingRegexes: RegExp[] | null = null;
|
|
94
|
-
|
|
95
|
-
constructor(
|
|
96
|
-
private dirPath: string,
|
|
97
|
-
private options: WalkOptions,
|
|
98
|
-
private progressCallback: ProgressCallback,
|
|
99
|
-
private atDepth: number = 0
|
|
100
|
-
) {
|
|
101
|
-
// Precompile find patterns into safe RegExp objects to avoid invalid regex errors
|
|
102
|
-
if (this.options.finding) {
|
|
103
|
-
this.findingRegexes = compilePatterns(this.options.finding);
|
|
104
|
-
}
|
|
105
|
-
this.prefix = ' '.repeat(atDepth * 5);
|
|
106
|
-
if (options.verbose) {
|
|
107
|
-
this.prefix = atDepth.toString().padStart(2, '0') + ' ' + this.prefix;
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
public formatSize(): string {
|
|
112
|
-
const divisor = this.options.sizeUnit === 'GB' ? (1024 * 1024 * 1024) : (1024 * 1024);
|
|
113
|
-
const size = this.length / divisor;
|
|
114
|
-
|
|
115
|
-
// For MB, show whole numbers; for GB, show 2 decimal places
|
|
116
|
-
const fractionDigits = this.options.sizeUnit === 'MB' ? 0 : 2;
|
|
117
|
-
const formatted = size.toLocaleString(undefined, { minimumFractionDigits: fractionDigits, maximumFractionDigits: fractionDigits });
|
|
118
|
-
|
|
119
|
-
if (this.options.showCompressed && this.length !== this.compressedLength) {
|
|
120
|
-
const compressedSize = this.compressedLength / divisor;
|
|
121
|
-
const compressedFormatted = compressedSize.toLocaleString(undefined, { minimumFractionDigits: fractionDigits, maximumFractionDigits: fractionDigits });
|
|
122
|
-
return `${formatted}/${compressedFormatted}`;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
return formatted;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
private logError(context: string, err: Error): void {
|
|
129
|
-
if (!this.options.verbose &&
|
|
130
|
-
(this.options.quiet || suppressedErrors.some(e => err.message.includes(e)))) {
|
|
131
|
-
return;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
const errorKey = `${context}:${err.message}:${this.dirPath}`;
|
|
135
|
-
if (!this.seenErrors.has(errorKey)) {
|
|
136
|
-
console.error(styleText(['red'], `Error processing ${context}: ${err.message}`));
|
|
137
|
-
this.seenErrors.add(errorKey);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
private async isSymlink(testPath: string): Promise<boolean> {
|
|
142
|
-
try {
|
|
143
|
-
const stats = await fp.lstat(testPath);
|
|
144
|
-
return stats.isSymbolicLink();
|
|
145
|
-
} catch {
|
|
146
|
-
return false;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
private async processDirectory(): Promise<void> {
|
|
151
|
-
try {
|
|
152
|
-
if (Date.now() > this.nextTitleUpdate.getTime()) {
|
|
153
|
-
process.title = this.dirPath;
|
|
154
|
-
this.nextTitleUpdate = new Date(Date.now() + 500);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
const files = await fp.readdir(this.dirPath, { withFileTypes: true });
|
|
158
|
-
|
|
159
|
-
for (const file of files) {
|
|
160
|
-
if (!file.isDirectory()) {
|
|
161
|
-
this.fileCount++;
|
|
162
|
-
this.totalFileCount++;
|
|
163
|
-
|
|
164
|
-
try {
|
|
165
|
-
const stats = await fp.stat(path.join(this.dirPath, file.name));
|
|
166
|
-
this.length += stats.size;
|
|
167
|
-
this.compressedLength += stats.size;
|
|
168
|
-
} catch (err) {
|
|
169
|
-
this.logError(file.name, err as Error);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
} catch (err) {
|
|
174
|
-
this.logError('directory', err as Error);
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
private async executeCommand(): Promise<void> {
|
|
179
|
-
if (!this.options.cmd) return;
|
|
180
|
-
|
|
181
|
-
const currentDir = process.cwd();
|
|
182
|
-
try {
|
|
183
|
-
process.chdir(this.dirPath);
|
|
184
|
-
await new Promise((resolve, reject) => {
|
|
185
|
-
const proc = spawn(this.options.cmd, [], { shell: true });
|
|
186
|
-
proc.on('close', resolve);
|
|
187
|
-
proc.on('error', reject);
|
|
188
|
-
});
|
|
189
|
-
} catch (err) {
|
|
190
|
-
this.logError('command execution', err as Error);
|
|
191
|
-
} finally {
|
|
192
|
-
process.chdir(currentDir);
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
private async findFiles(): Promise<void> {
|
|
197
|
-
if (!this.options.finding) return;
|
|
198
|
-
try {
|
|
199
|
-
const files = await fp.readdir(this.dirPath);
|
|
200
|
-
const regexes = this.findingRegexes || [];
|
|
201
|
-
|
|
202
|
-
for (const file of files) {
|
|
203
|
-
if (regexes.length && regexes.some(rx => rx.test(file))) {
|
|
204
|
-
try {
|
|
205
|
-
const stats = await fp.stat(path.join(this.dirPath, file));
|
|
206
|
-
const size = stats.size / 1024;
|
|
207
|
-
process.stdout.write('\x1b[2K\r'); // Clear progress line
|
|
208
|
-
console.log(
|
|
209
|
-
`${stats.mtime.toISOString().slice(0, 19).replace('T', ' ')} ` +
|
|
210
|
-
`${size.toFixed(1).padStart(8)}KB ${this.prefix} ${path.join(this.dirPath, file)}`
|
|
211
|
-
);
|
|
212
|
-
} catch (err) {
|
|
213
|
-
this.logError(file, err as Error);
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
} catch (err) {
|
|
218
|
-
this.logError('file search', err as Error);
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
public async walk(): Promise<void> {
|
|
223
|
-
try {
|
|
224
|
-
const isSymlink = await this.isSymlink(this.dirPath);
|
|
225
|
-
if (isSymlink) return;
|
|
226
|
-
|
|
227
|
-
this.progressCallback(this, this.dirPath);
|
|
228
|
-
|
|
229
|
-
if (this.options.cmd || this.options.finding || this.options.forceAcl) {
|
|
230
|
-
if (this.options.forceAcl) {
|
|
231
|
-
// ACL operations not implemented
|
|
232
|
-
}
|
|
233
|
-
if (this.options.cmd) {
|
|
234
|
-
await this.executeCommand();
|
|
235
|
-
}
|
|
236
|
-
if (this.options.finding) {
|
|
237
|
-
await this.findFiles();
|
|
238
|
-
}
|
|
239
|
-
} else {
|
|
240
|
-
await this.processDirectory();
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
const entries = await fp.readdir(this.dirPath, { withFileTypes: true });
|
|
244
|
-
|
|
245
|
-
for (const entry of entries) {
|
|
246
|
-
if (entry.isDirectory()) {
|
|
247
|
-
const subPath = path.join(this.dirPath, entry.name);
|
|
248
|
-
const walker = new WalkTree(subPath, this.options, this.progressCallback, this.atDepth + 1);
|
|
249
|
-
|
|
250
|
-
try {
|
|
251
|
-
await walker.walk();
|
|
252
|
-
this.dirCount++;
|
|
253
|
-
this.length += walker.length;
|
|
254
|
-
this.compressedLength += walker.compressedLength;
|
|
255
|
-
this.totalFileCount += walker.totalFileCount;
|
|
256
|
-
this.totalDirCount += walker.totalDirCount + 1;
|
|
257
|
-
|
|
258
|
-
const divisor = this.options.sizeUnit === 'GB' ? (1024 * 1024 * 1024) : (1024 * 1024);
|
|
259
|
-
const size = walker.length / divisor;
|
|
260
|
-
if (size >= this.options.threshold && this.atDepth < this.options.depth) {
|
|
261
|
-
if (!this.options.quiet) {
|
|
262
|
-
process.stdout.write('\x1b[2K\r'); // Clear progress line
|
|
263
|
-
}
|
|
264
|
-
console.log(
|
|
265
|
-
`${this.prefix} ${walker.formatSize().padStart(13)} ${this.options.sizeUnit} ` +
|
|
266
|
-
`${walker.totalDirCount.toString().padStart(5)} ` +
|
|
267
|
-
`${walker.totalFileCount.toString().padStart(6)} ${styleText(['blue'], entry.name)}`
|
|
268
|
-
);
|
|
269
|
-
}
|
|
270
|
-
} catch (err) {
|
|
271
|
-
this.logError(entry.name, err as Error);
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
} catch (err) {
|
|
276
|
-
this.logError('directory walk', err as Error);
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
export { WalkOptions } from './utils.js';
|
package/index.ts
DELETED
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { DiskWalker, WalkOptions } from './diskwalker.js';
|
|
3
|
-
import { defaultWalkOptions } from './utils.js';
|
|
4
|
-
import { styleText } from 'node:util';
|
|
5
|
-
|
|
6
|
-
function showUsage() {
|
|
7
|
-
console.log(`
|
|
8
|
-
Usage: tswalk [path] [depth] [options]
|
|
9
|
-
path Directory to analyze (default: current directory)
|
|
10
|
-
depth Maximum depth to show in output (default: 1)
|
|
11
|
-
|
|
12
|
-
Options:
|
|
13
|
-
-d, -depth Set maximum depth to show in output
|
|
14
|
-
-t Threshold in MB/GB for showing directories
|
|
15
|
-
-mb Show sizes in megabytes (default)
|
|
16
|
-
-gb Show sizes in gigabytes
|
|
17
|
-
-c Show compressed sizes (Windows only)
|
|
18
|
-
-r "cmd" Run command in each directory
|
|
19
|
-
-f pattern Find files matching pattern (can use ;-separated patterns)
|
|
20
|
-
-v Verbose output (show all errors including ENOENT/EPERM)
|
|
21
|
-
-q Quiet mode (no progress updates, minimal errors)
|
|
22
|
-
-acl Force ACL operations (Windows only)
|
|
23
|
-
|
|
24
|
-
Progress:
|
|
25
|
-
Shows current directory being processed every second in blue.
|
|
26
|
-
Long paths are truncated in progress display.
|
|
27
|
-
|
|
28
|
-
Error Handling:
|
|
29
|
-
Default: Shows significant errors (excluding ENOENT/EPERM)
|
|
30
|
-
-v: Shows all errors including access denied and not found
|
|
31
|
-
-q: Shows only fatal errors
|
|
32
|
-
|
|
33
|
-
Examples:
|
|
34
|
-
tswalk . # Analyze current directory
|
|
35
|
-
tswalk /path 3 # Show 3 levels deep
|
|
36
|
-
tswalk /path -d 3 -gb # Same, but show sizes in GB
|
|
37
|
-
tswalk /path -t 100 -gb # Show dirs > 100GB
|
|
38
|
-
tswalk . -f "*.ts;*.js" # Find TypeScript and JavaScript files
|
|
39
|
-
tswalk /path -v # Show all errors while processing
|
|
40
|
-
`);
|
|
41
|
-
process.exit(0);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
async function main() {
|
|
45
|
-
const args = process.argv.slice(2);
|
|
46
|
-
|
|
47
|
-
if (args.includes('-h') || args.includes('-help') || args.includes('--help')) {
|
|
48
|
-
showUsage();
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const options: Partial<WalkOptions> = {};
|
|
52
|
-
let topPath = '.';
|
|
53
|
-
let foundPath = false;
|
|
54
|
-
let foundDepth = false;
|
|
55
|
-
|
|
56
|
-
for (let i = 0; i < args.length; i++) {
|
|
57
|
-
const arg = args[i];
|
|
58
|
-
|
|
59
|
-
if (arg.startsWith('-')) {
|
|
60
|
-
switch (arg) {
|
|
61
|
-
case '-r':
|
|
62
|
-
case '-run':
|
|
63
|
-
options.cmd = args[++i];
|
|
64
|
-
break;
|
|
65
|
-
case '-d':
|
|
66
|
-
case '-depth':
|
|
67
|
-
options.depth = parseInt(args[++i]);
|
|
68
|
-
foundDepth = true;
|
|
69
|
-
break;
|
|
70
|
-
case '-t':
|
|
71
|
-
case '-threshold':
|
|
72
|
-
options.threshold = parseInt(args[++i]);
|
|
73
|
-
break;
|
|
74
|
-
case '-c':
|
|
75
|
-
case '-compressed':
|
|
76
|
-
options.showCompressed = true;
|
|
77
|
-
break;
|
|
78
|
-
case '-f':
|
|
79
|
-
case '-find':
|
|
80
|
-
options.finding = args[++i].split(';');
|
|
81
|
-
break;
|
|
82
|
-
case '-v':
|
|
83
|
-
case '-verbose':
|
|
84
|
-
options.verbose = true;
|
|
85
|
-
break;
|
|
86
|
-
case '-q':
|
|
87
|
-
case '-quiet':
|
|
88
|
-
options.quiet = true;
|
|
89
|
-
break;
|
|
90
|
-
case '-mb':
|
|
91
|
-
options.sizeUnit = 'MB';
|
|
92
|
-
break;
|
|
93
|
-
case '-gb':
|
|
94
|
-
options.sizeUnit = 'GB';
|
|
95
|
-
break;
|
|
96
|
-
case '-acl':
|
|
97
|
-
options.forceAcl = true;
|
|
98
|
-
break;
|
|
99
|
-
case '-h':
|
|
100
|
-
case '-help':
|
|
101
|
-
case '--help':
|
|
102
|
-
showUsage();
|
|
103
|
-
break;
|
|
104
|
-
default:
|
|
105
|
-
console.error(styleText(['red'], 'Invalid option:'), arg);
|
|
106
|
-
showUsage();
|
|
107
|
-
}
|
|
108
|
-
} else {
|
|
109
|
-
// Not a flag
|
|
110
|
-
if (!foundPath) {
|
|
111
|
-
topPath = arg;
|
|
112
|
-
foundPath = true;
|
|
113
|
-
} else if (!foundDepth && !isNaN(Number(arg))) {
|
|
114
|
-
options.depth = parseInt(arg);
|
|
115
|
-
foundDepth = true;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
if (options.quiet && options.verbose) {
|
|
121
|
-
console.warn(styleText(['yellow'], 'Warning: Both -q and -v specified; verbose output will be used'));
|
|
122
|
-
options.quiet = false;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
try {
|
|
126
|
-
const fullOptions = { ...defaultWalkOptions, ...options };
|
|
127
|
-
const walker = new DiskWalker(topPath, fullOptions);
|
|
128
|
-
await walker.walk();
|
|
129
|
-
} catch (err) {
|
|
130
|
-
console.error(styleText(['red'], '\nFatal error:'), err);
|
|
131
|
-
process.exit(1);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
main().catch(err => {
|
|
136
|
-
console.error(styleText(['red'], '\nFatal error:'), err);
|
|
137
|
-
process.exit(1);
|
|
138
|
-
});
|
package/programming.md
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
# Programming Notes
|
|
2
|
-
|
|
3
|
-
## Version Handling Best Practices
|
|
4
|
-
|
|
5
|
-
### Always Import Version from package.json
|
|
6
|
-
- Never hardcode version numbers in source code
|
|
7
|
-
- Import version from `package.json` using JSON import
|
|
8
|
-
- This ensures version stays in sync with package releases
|
|
9
|
-
|
|
10
|
-
```typescript
|
|
11
|
-
// Good: Import from package.json
|
|
12
|
-
import { readFileSync } from 'fs';
|
|
13
|
-
import { fileURLToPath } from 'url';
|
|
14
|
-
import { dirname, join } from 'path';
|
|
15
|
-
|
|
16
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
17
|
-
const __dirname = dirname(__filename);
|
|
18
|
-
const packageJson = JSON.parse(readFileSync(join(__dirname, 'package.json'), 'utf8'));
|
|
19
|
-
const VERSION = packageJson.version;
|
|
20
|
-
|
|
21
|
-
// Bad: Hardcoded version
|
|
22
|
-
const VERSION = "1.1.6"; // Will get out of sync!
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
### Version Display Guidelines
|
|
26
|
-
- Don't show version by default - tools should be quiet
|
|
27
|
-
- Only show version when explicitly requested with `--version` flag
|
|
28
|
-
- Keep startup clean and focused on the task
|
|
29
|
-
|
|
30
|
-
### UI/UX Improvements
|
|
31
|
-
- **Column headers** make output much more readable and professional
|
|
32
|
-
- Headers should align with data columns
|
|
33
|
-
- Use consistent spacing and formatting
|
|
34
|
-
- Consider users who need to parse or read the output
|
|
35
|
-
|
|
36
|
-
## Code Organization
|
|
37
|
-
- Keep version logic separate from main functionality
|
|
38
|
-
- Use clear, descriptive variable names
|
|
39
|
-
- Document architectural decisions in code comments
|
|
40
|
-
- Maintain consistent error handling patterns
|
package/tsconfig.json
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "esnext",
|
|
4
|
-
"module": "NodeNext",
|
|
5
|
-
"moduleResolution": "NodeNext",
|
|
6
|
-
"sourceMap": true,
|
|
7
|
-
"newLine": "lf",
|
|
8
|
-
"esModuleInterop": true,
|
|
9
|
-
"forceConsistentCasingInFileNames": true,
|
|
10
|
-
"strict": true,
|
|
11
|
-
"strictNullChecks": false,
|
|
12
|
-
"skipLibCheck": true
|
|
13
|
-
}
|
|
14
|
-
}
|
package/utils.ts
DELETED
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Common utilities for tswalk directory walking implementations
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Convert user-supplied patterns into safe RegExp objects.
|
|
7
|
-
* Supports regex literals like /pattern/flags and simple glob patterns using * and ?
|
|
8
|
-
*
|
|
9
|
-
* @param patterns Array of user-provided patterns (can be glob or regex literals)
|
|
10
|
-
* @returns Array of compiled RegExp objects that won't throw "Nothing to repeat" errors
|
|
11
|
-
*/
|
|
12
|
-
export function compilePatterns(patterns: string[]): RegExp[] {
|
|
13
|
-
const res: RegExp[] = [];
|
|
14
|
-
for (const p of patterns) {
|
|
15
|
-
if (!p) continue;
|
|
16
|
-
try {
|
|
17
|
-
// Handle regex literals like /pattern/flags
|
|
18
|
-
if (p.startsWith('/') && p.lastIndexOf('/') > 0) {
|
|
19
|
-
const last = p.lastIndexOf('/');
|
|
20
|
-
const body = p.slice(1, last);
|
|
21
|
-
const flags = p.slice(last + 1);
|
|
22
|
-
res.push(new RegExp(body, flags));
|
|
23
|
-
continue;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// Treat as glob: escape regex metacharacters, then translate \* -> .* and \? -> .
|
|
27
|
-
const escaped = p.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
28
|
-
.replace(/\\\*/g, '.*')
|
|
29
|
-
.replace(/\\\?/g, '.');
|
|
30
|
-
res.push(new RegExp('^' + escaped + '$', 'i'));
|
|
31
|
-
} catch (err) {
|
|
32
|
-
// Fallback: escape everything and do a case-insensitive literal match
|
|
33
|
-
const safe = p.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
34
|
-
res.push(new RegExp('^' + safe + '$', 'i'));
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
return res;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Common interface for directory walking options
|
|
42
|
-
*/
|
|
43
|
-
export interface WalkOptions {
|
|
44
|
-
depth: number;
|
|
45
|
-
threshold: number;
|
|
46
|
-
cmd: string;
|
|
47
|
-
verbose: boolean;
|
|
48
|
-
showCompressed: boolean;
|
|
49
|
-
finding: string[] | null;
|
|
50
|
-
forceAcl: boolean;
|
|
51
|
-
quiet: boolean;
|
|
52
|
-
sizeUnit: 'MB' | 'GB';
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Default options for directory walking
|
|
57
|
-
*/
|
|
58
|
-
export const defaultWalkOptions: WalkOptions = {
|
|
59
|
-
depth: 1,
|
|
60
|
-
threshold: 0,
|
|
61
|
-
cmd: '',
|
|
62
|
-
verbose: false,
|
|
63
|
-
showCompressed: false,
|
|
64
|
-
finding: null,
|
|
65
|
-
forceAcl: false,
|
|
66
|
-
quiet: false,
|
|
67
|
-
sizeUnit: 'MB'
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Error types to suppress unless verbose mode is enabled
|
|
72
|
-
*/
|
|
73
|
-
export const suppressedErrors = ['ENOENT', 'EPERM', 'EACCES'];
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Directory entry interface - common format for both enumeration strategies
|
|
77
|
-
*/
|
|
78
|
-
export interface DirectoryEntry {
|
|
79
|
-
name: string;
|
|
80
|
-
isFile: boolean;
|
|
81
|
-
isDirectory: boolean;
|
|
82
|
-
isSymlink: boolean;
|
|
83
|
-
size: number;
|
|
84
|
-
mtime: number;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Directory enumeration strategy interface
|
|
89
|
-
*/
|
|
90
|
-
export interface DirectoryEnumerator {
|
|
91
|
-
/**
|
|
92
|
-
* List directory contents with file metadata
|
|
93
|
-
*/
|
|
94
|
-
listDirectory(dirPath: string): Promise<DirectoryEntry[]> | DirectoryEntry[];
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Get disk block size for accurate space calculations (optional)
|
|
98
|
-
*/
|
|
99
|
-
getBlockSize?(dirPath: string): number;
|
|
100
|
-
}
|