codeinf 1.0.0
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/README.md +167 -0
- package/bin/cli.js +189 -0
- package/index.js +199 -0
- package/package.json +39 -0
package/README.md
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# codeinf
|
|
2
|
+
|
|
3
|
+
A CLI tool and library to analyze code statistics like lines of code, file counts, and more with filtering capabilities.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
### Global Installation (CLI usage)
|
|
8
|
+
```bash
|
|
9
|
+
npm install -g codeinf
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
### Local Installation (Programmatic usage)
|
|
13
|
+
```bash
|
|
14
|
+
npm install codeinf
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## CLI Usage
|
|
18
|
+
|
|
19
|
+
### Basic Usage
|
|
20
|
+
```bash
|
|
21
|
+
# Analyze current directory
|
|
22
|
+
codeinf
|
|
23
|
+
|
|
24
|
+
# Analyze specific directory
|
|
25
|
+
codeinf ./src
|
|
26
|
+
|
|
27
|
+
# Analyze with specific file extensions
|
|
28
|
+
codeinf -e js,ts,jsx,tsx
|
|
29
|
+
codeinf --extensions .js,.ts
|
|
30
|
+
|
|
31
|
+
# Output as JSON
|
|
32
|
+
codeinf --json
|
|
33
|
+
codeinf -f json
|
|
34
|
+
|
|
35
|
+
# Ignore specific directories
|
|
36
|
+
codeinf -i "test,docs,build"
|
|
37
|
+
|
|
38
|
+
# Show top 20 largest files
|
|
39
|
+
codeinf -t 20
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### CLI Options
|
|
43
|
+
|
|
44
|
+
| Option | Description |
|
|
45
|
+
|--------|-------------|
|
|
46
|
+
| `-h, --help` | Show help message |
|
|
47
|
+
| `-v, --version` | Show version |
|
|
48
|
+
| `-e, --ext, --extensions` | Filter by file extensions (comma-separated) |
|
|
49
|
+
| `-i, --ignore` | Additional ignore patterns (comma-separated) |
|
|
50
|
+
| `-f, --format` | Output format: `table` or `json` |
|
|
51
|
+
| `--json` | Shortcut for JSON output |
|
|
52
|
+
| `-t, --top` | Number of largest files to show (default: 10) |
|
|
53
|
+
|
|
54
|
+
## Programmatic API
|
|
55
|
+
|
|
56
|
+
### Basic Usage
|
|
57
|
+
|
|
58
|
+
```javascript
|
|
59
|
+
const { analyze, formatBytes } = require('codeinf');
|
|
60
|
+
|
|
61
|
+
// Analyze current directory
|
|
62
|
+
const stats = analyze('.');
|
|
63
|
+
console.log(stats);
|
|
64
|
+
|
|
65
|
+
// Analyze with options
|
|
66
|
+
const stats = analyze('./src', {
|
|
67
|
+
extensions: ['.js', '.ts'],
|
|
68
|
+
ignore: ['test', 'dist'],
|
|
69
|
+
recursive: true
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
console.log(`Total files: ${stats.summary.files}`);
|
|
73
|
+
console.log(`Total lines: ${stats.summary.totalLines}`);
|
|
74
|
+
console.log(`Code lines: ${stats.summary.codeLines}`);
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### API Reference
|
|
78
|
+
|
|
79
|
+
#### `analyze(targetPath, options)`
|
|
80
|
+
|
|
81
|
+
Analyzes code statistics for a given path.
|
|
82
|
+
|
|
83
|
+
**Parameters:**
|
|
84
|
+
- `targetPath` (string): Path to analyze (default: '.')
|
|
85
|
+
- `options` (Object):
|
|
86
|
+
- `extensions` (string[]): Filter by file extensions (e.g., ['.js', '.ts'])
|
|
87
|
+
- `ignore` (string[]): Additional patterns to ignore
|
|
88
|
+
- `recursive` (boolean): Scan recursively (default: true)
|
|
89
|
+
|
|
90
|
+
**Returns:**
|
|
91
|
+
```javascript
|
|
92
|
+
{
|
|
93
|
+
path: '/absolute/path',
|
|
94
|
+
summary: {
|
|
95
|
+
files: 42,
|
|
96
|
+
totalLines: 5000,
|
|
97
|
+
nonEmptyLines: 4500,
|
|
98
|
+
codeLines: 3800,
|
|
99
|
+
size: 1024000
|
|
100
|
+
},
|
|
101
|
+
byExtension: {
|
|
102
|
+
'.js': { files: 20, lines: 3000, size: 500000 },
|
|
103
|
+
'.ts': { files: 22, lines: 2000, size: 524000 }
|
|
104
|
+
},
|
|
105
|
+
largestFiles: [
|
|
106
|
+
{ path: '...', totalLines: 500, nonEmptyLines: 450, codeLines: 400, size: 10000, extension: '.js' }
|
|
107
|
+
],
|
|
108
|
+
allFiles: [...]
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
#### `scanDirectory(dir, options)`
|
|
113
|
+
|
|
114
|
+
Scans a directory and returns file statistics.
|
|
115
|
+
|
|
116
|
+
```javascript
|
|
117
|
+
const { scanDirectory } = require('codeinf');
|
|
118
|
+
|
|
119
|
+
const files = scanDirectory('./src', {
|
|
120
|
+
extensions: ['.js'],
|
|
121
|
+
ignore: ['node_modules'],
|
|
122
|
+
recursive: true
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
files.forEach(file => {
|
|
126
|
+
console.log(`${file.path}: ${file.totalLines} lines`);
|
|
127
|
+
});
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
#### `getFileStats(filePath)`
|
|
131
|
+
|
|
132
|
+
Gets statistics for a single file.
|
|
133
|
+
|
|
134
|
+
```javascript
|
|
135
|
+
const { getFileStats } = require('codeinf');
|
|
136
|
+
|
|
137
|
+
const stats = getFileStats('./src/index.js');
|
|
138
|
+
console.log(stats);
|
|
139
|
+
// { path, size, totalLines, nonEmptyLines, codeLines, extension }
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
#### `formatBytes(bytes)`
|
|
143
|
+
|
|
144
|
+
Formats bytes to human-readable string.
|
|
145
|
+
|
|
146
|
+
```javascript
|
|
147
|
+
const { formatBytes } = require('codeinf');
|
|
148
|
+
|
|
149
|
+
console.log(formatBytes(1024)); // "1 KB"
|
|
150
|
+
console.log(formatBytes(1048576)); // "1 MB"
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Default Ignore Patterns
|
|
154
|
+
|
|
155
|
+
The following directories are automatically ignored:
|
|
156
|
+
- `node_modules`
|
|
157
|
+
- `.git`, `.svn`, `.hg`
|
|
158
|
+
- `.DS_Store`
|
|
159
|
+
- `dist`, `build`
|
|
160
|
+
- `coverage`
|
|
161
|
+
- `.next`, `.nuxt`
|
|
162
|
+
- `.cache`
|
|
163
|
+
- `vendor`, `bin`, `obj`
|
|
164
|
+
|
|
165
|
+
## License
|
|
166
|
+
|
|
167
|
+
MIT
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { analyze, formatBytes } = require('../index');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
// Parse command line arguments
|
|
7
|
+
function parseArgs(args) {
|
|
8
|
+
const options = {
|
|
9
|
+
path: '.',
|
|
10
|
+
extensions: [],
|
|
11
|
+
ignore: [],
|
|
12
|
+
format: 'table',
|
|
13
|
+
top: 10,
|
|
14
|
+
help: false,
|
|
15
|
+
version: false
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
for (let i = 0; i < args.length; i++) {
|
|
19
|
+
const arg = args[i];
|
|
20
|
+
|
|
21
|
+
switch (arg) {
|
|
22
|
+
case '-h':
|
|
23
|
+
case '--help':
|
|
24
|
+
options.help = true;
|
|
25
|
+
break;
|
|
26
|
+
case '-v':
|
|
27
|
+
case '--version':
|
|
28
|
+
options.version = true;
|
|
29
|
+
break;
|
|
30
|
+
case '-e':
|
|
31
|
+
case '--ext':
|
|
32
|
+
case '--extensions':
|
|
33
|
+
i++;
|
|
34
|
+
if (args[i]) {
|
|
35
|
+
options.extensions = args[i].split(',').map(e => {
|
|
36
|
+
const ext = e.trim().toLowerCase();
|
|
37
|
+
return ext.startsWith('.') ? ext : '.' + ext;
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
break;
|
|
41
|
+
case '-i':
|
|
42
|
+
case '--ignore':
|
|
43
|
+
i++;
|
|
44
|
+
if (args[i]) {
|
|
45
|
+
options.ignore = args[i].split(',').map(s => s.trim());
|
|
46
|
+
}
|
|
47
|
+
break;
|
|
48
|
+
case '-f':
|
|
49
|
+
case '--format':
|
|
50
|
+
i++;
|
|
51
|
+
if (args[i]) {
|
|
52
|
+
options.format = args[i].toLowerCase();
|
|
53
|
+
}
|
|
54
|
+
break;
|
|
55
|
+
case '-t':
|
|
56
|
+
case '--top':
|
|
57
|
+
i++;
|
|
58
|
+
if (args[i]) {
|
|
59
|
+
options.top = parseInt(args[i], 10) || 10;
|
|
60
|
+
}
|
|
61
|
+
break;
|
|
62
|
+
case '--json':
|
|
63
|
+
options.format = 'json';
|
|
64
|
+
break;
|
|
65
|
+
default:
|
|
66
|
+
if (!arg.startsWith('-') && options.path === '.') {
|
|
67
|
+
options.path = arg;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return options;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Show help
|
|
76
|
+
function showHelp() {
|
|
77
|
+
console.log(`
|
|
78
|
+
codeinf - Code statistics analyzer
|
|
79
|
+
|
|
80
|
+
Usage: codeinf [path] [options]
|
|
81
|
+
|
|
82
|
+
Options:
|
|
83
|
+
-h, --help Show this help message
|
|
84
|
+
-v, --version Show version
|
|
85
|
+
-e, --ext <extensions> Filter by file extensions (comma-separated)
|
|
86
|
+
-i, --ignore <patterns> Additional ignore patterns (comma-separated)
|
|
87
|
+
-f, --format <format> Output format: table, json (default: table)
|
|
88
|
+
--json Output as JSON
|
|
89
|
+
-t, --top <number> Number of largest files to show (default: 10)
|
|
90
|
+
|
|
91
|
+
Examples:
|
|
92
|
+
codeinf Analyze current directory
|
|
93
|
+
codeinf ./src Analyze src directory
|
|
94
|
+
codeinf -e js,ts Analyze only JS and TS files
|
|
95
|
+
codeinf -e .js --json Output JS stats as JSON
|
|
96
|
+
codeinf -i "test,dist" Ignore test and dist folders
|
|
97
|
+
codeinf -t 20 Show top 20 largest files
|
|
98
|
+
`);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Show version
|
|
102
|
+
function showVersion() {
|
|
103
|
+
const pkg = require('../package.json');
|
|
104
|
+
console.log(pkg.version);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Format output as table
|
|
108
|
+
function formatTable(data) {
|
|
109
|
+
const { summary, byExtension, largestFiles, path: targetPath } = data;
|
|
110
|
+
|
|
111
|
+
console.log(`\n 📊 Code Statistics for: ${targetPath}\n`);
|
|
112
|
+
|
|
113
|
+
// Summary
|
|
114
|
+
console.log(' Summary:');
|
|
115
|
+
console.log(` Files: ${summary.files.toLocaleString()}`);
|
|
116
|
+
console.log(` Total Lines: ${summary.totalLines.toLocaleString()}`);
|
|
117
|
+
console.log(` Code Lines: ${summary.codeLines.toLocaleString()}`);
|
|
118
|
+
console.log(` Non-Empty: ${summary.nonEmptyLines.toLocaleString()}`);
|
|
119
|
+
console.log(` Total Size: ${formatBytes(summary.size)}`);
|
|
120
|
+
console.log();
|
|
121
|
+
|
|
122
|
+
// By Extension
|
|
123
|
+
if (Object.keys(byExtension).length > 0) {
|
|
124
|
+
console.log(' By Extension:');
|
|
125
|
+
const sorted = Object.entries(byExtension)
|
|
126
|
+
.sort((a, b) => b[1].files - a[1].files);
|
|
127
|
+
|
|
128
|
+
console.log(` ${'Extension'.padEnd(12)} ${'Files'.padStart(8)} ${'Lines'.padStart(10)} ${'Size'.padStart(10)}`);
|
|
129
|
+
console.log(` ${'-'.repeat(42)}`);
|
|
130
|
+
|
|
131
|
+
for (const [ext, stats] of sorted) {
|
|
132
|
+
const extName = ext.padEnd(12);
|
|
133
|
+
const files = stats.files.toLocaleString().padStart(8);
|
|
134
|
+
const lines = stats.lines.toLocaleString().padStart(10);
|
|
135
|
+
const size = formatBytes(stats.size).padStart(10);
|
|
136
|
+
console.log(` ${extName} ${files} ${lines} ${size}`);
|
|
137
|
+
}
|
|
138
|
+
console.log();
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Largest Files
|
|
142
|
+
if (largestFiles.length > 0) {
|
|
143
|
+
console.log(` Largest Files (Top ${largestFiles.length}):`);
|
|
144
|
+
console.log(` ${'Lines'.padStart(8)} ${'Size'.padStart(10)} ${'Path'.padStart(4)}`);
|
|
145
|
+
console.log(` ${'-'.repeat(60)}`);
|
|
146
|
+
|
|
147
|
+
for (const file of largestFiles) {
|
|
148
|
+
const lines = file.totalLines.toLocaleString().padStart(8);
|
|
149
|
+
const size = formatBytes(file.size).padStart(10);
|
|
150
|
+
const relPath = path.relative(targetPath, file.path) || file.path;
|
|
151
|
+
console.log(` ${lines} ${size} ${relPath}`);
|
|
152
|
+
}
|
|
153
|
+
console.log();
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Main function
|
|
158
|
+
async function main() {
|
|
159
|
+
const args = process.argv.slice(2);
|
|
160
|
+
const options = parseArgs(args);
|
|
161
|
+
|
|
162
|
+
if (options.help) {
|
|
163
|
+
showHelp();
|
|
164
|
+
process.exit(0);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (options.version) {
|
|
168
|
+
showVersion();
|
|
169
|
+
process.exit(0);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
try {
|
|
173
|
+
const result = analyze(options.path, {
|
|
174
|
+
extensions: options.extensions,
|
|
175
|
+
ignore: options.ignore
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
if (options.format === 'json') {
|
|
179
|
+
console.log(JSON.stringify(result, null, 2));
|
|
180
|
+
} else {
|
|
181
|
+
formatTable(result);
|
|
182
|
+
}
|
|
183
|
+
} catch (err) {
|
|
184
|
+
console.error(`Error: ${err.message}`);
|
|
185
|
+
process.exit(1);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
main();
|
package/index.js
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Default ignore patterns (node_modules, .git, etc.)
|
|
6
|
+
*/
|
|
7
|
+
const DEFAULT_IGNORES = [
|
|
8
|
+
'node_modules',
|
|
9
|
+
'.git',
|
|
10
|
+
'.svn',
|
|
11
|
+
'.hg',
|
|
12
|
+
'.DS_Store',
|
|
13
|
+
'dist',
|
|
14
|
+
'build',
|
|
15
|
+
'coverage',
|
|
16
|
+
'.next',
|
|
17
|
+
'.nuxt',
|
|
18
|
+
'.cache',
|
|
19
|
+
'vendor',
|
|
20
|
+
'bin',
|
|
21
|
+
'obj'
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Check if a path should be ignored
|
|
26
|
+
* @param {string} filePath - Path to check
|
|
27
|
+
* @param {string[]} ignorePatterns - Patterns to ignore
|
|
28
|
+
* @returns {boolean}
|
|
29
|
+
*/
|
|
30
|
+
function shouldIgnore(filePath, ignorePatterns = []) {
|
|
31
|
+
const allIgnores = [...DEFAULT_IGNORES, ...ignorePatterns];
|
|
32
|
+
const parts = filePath.split(path.sep);
|
|
33
|
+
return allIgnores.some(pattern =>
|
|
34
|
+
parts.some(part => part === pattern || part.match(new RegExp(pattern)))
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Get file statistics
|
|
40
|
+
* @param {string} filePath - Path to file
|
|
41
|
+
* @returns {Object|null}
|
|
42
|
+
*/
|
|
43
|
+
function getFileStats(filePath) {
|
|
44
|
+
try {
|
|
45
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
46
|
+
const lines = content.split('\n');
|
|
47
|
+
const nonEmptyLines = lines.filter(line => line.trim().length > 0);
|
|
48
|
+
const codeLines = lines.filter(line => {
|
|
49
|
+
const trimmed = line.trim();
|
|
50
|
+
return trimmed.length > 0 && !trimmed.startsWith('//') && !trimmed.startsWith('#');
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
path: filePath,
|
|
55
|
+
size: fs.statSync(filePath).size,
|
|
56
|
+
totalLines: lines.length,
|
|
57
|
+
nonEmptyLines: nonEmptyLines.length,
|
|
58
|
+
codeLines: codeLines.length,
|
|
59
|
+
extension: path.extname(filePath).toLowerCase()
|
|
60
|
+
};
|
|
61
|
+
} catch (err) {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Recursively scan directory for files
|
|
68
|
+
* @param {string} dir - Directory to scan
|
|
69
|
+
* @param {Object} options - Options
|
|
70
|
+
* @returns {Object[]}
|
|
71
|
+
*/
|
|
72
|
+
function scanDirectory(dir, options = {}) {
|
|
73
|
+
const {
|
|
74
|
+
extensions = [],
|
|
75
|
+
ignore = [],
|
|
76
|
+
recursive = true
|
|
77
|
+
} = options;
|
|
78
|
+
|
|
79
|
+
const results = [];
|
|
80
|
+
|
|
81
|
+
function scan(currentPath) {
|
|
82
|
+
if (shouldIgnore(currentPath, ignore)) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
const stats = fs.statSync(currentPath);
|
|
88
|
+
|
|
89
|
+
if (stats.isDirectory()) {
|
|
90
|
+
if (recursive) {
|
|
91
|
+
const items = fs.readdirSync(currentPath);
|
|
92
|
+
for (const item of items) {
|
|
93
|
+
scan(path.join(currentPath, item));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
} else {
|
|
97
|
+
const ext = path.extname(currentPath).toLowerCase();
|
|
98
|
+
if (extensions.length === 0 || extensions.includes(ext)) {
|
|
99
|
+
const fileStats = getFileStats(currentPath);
|
|
100
|
+
if (fileStats) {
|
|
101
|
+
results.push(fileStats);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
} catch (err) {
|
|
106
|
+
// Skip files we can't access
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
scan(dir);
|
|
111
|
+
return results;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Analyze code statistics
|
|
116
|
+
* @param {string} targetPath - Path to analyze
|
|
117
|
+
* @param {Object} options - Analysis options
|
|
118
|
+
* @returns {Object}
|
|
119
|
+
*/
|
|
120
|
+
function analyze(targetPath = '.', options = {}) {
|
|
121
|
+
const resolvedPath = path.resolve(targetPath);
|
|
122
|
+
|
|
123
|
+
if (!fs.existsSync(resolvedPath)) {
|
|
124
|
+
throw new Error(`Path not found: ${targetPath}`);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const stats = fs.statSync(resolvedPath);
|
|
128
|
+
let files = [];
|
|
129
|
+
|
|
130
|
+
if (stats.isDirectory()) {
|
|
131
|
+
files = scanDirectory(resolvedPath, options);
|
|
132
|
+
} else {
|
|
133
|
+
const fileStats = getFileStats(resolvedPath);
|
|
134
|
+
if (fileStats) {
|
|
135
|
+
files = [fileStats];
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Calculate totals
|
|
140
|
+
const totalStats = files.reduce((acc, file) => ({
|
|
141
|
+
files: acc.files + 1,
|
|
142
|
+
totalLines: acc.totalLines + file.totalLines,
|
|
143
|
+
nonEmptyLines: acc.nonEmptyLines + file.nonEmptyLines,
|
|
144
|
+
codeLines: acc.codeLines + file.codeLines,
|
|
145
|
+
size: acc.size + file.size
|
|
146
|
+
}), {
|
|
147
|
+
files: 0,
|
|
148
|
+
totalLines: 0,
|
|
149
|
+
nonEmptyLines: 0,
|
|
150
|
+
codeLines: 0,
|
|
151
|
+
size: 0
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
// Group by extension
|
|
155
|
+
const byExtension = files.reduce((acc, file) => {
|
|
156
|
+
const ext = file.extension || '(no extension)';
|
|
157
|
+
if (!acc[ext]) {
|
|
158
|
+
acc[ext] = { files: 0, lines: 0, size: 0 };
|
|
159
|
+
}
|
|
160
|
+
acc[ext].files++;
|
|
161
|
+
acc[ext].lines += file.totalLines;
|
|
162
|
+
acc[ext].size += file.size;
|
|
163
|
+
return acc;
|
|
164
|
+
}, {});
|
|
165
|
+
|
|
166
|
+
// Sort files by lines (descending)
|
|
167
|
+
const largestFiles = [...files]
|
|
168
|
+
.sort((a, b) => b.totalLines - a.totalLines)
|
|
169
|
+
.slice(0, 10);
|
|
170
|
+
|
|
171
|
+
return {
|
|
172
|
+
path: resolvedPath,
|
|
173
|
+
summary: totalStats,
|
|
174
|
+
byExtension,
|
|
175
|
+
largestFiles,
|
|
176
|
+
allFiles: files
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Format bytes to human readable
|
|
182
|
+
* @param {number} bytes
|
|
183
|
+
* @returns {string}
|
|
184
|
+
*/
|
|
185
|
+
function formatBytes(bytes) {
|
|
186
|
+
if (bytes === 0) return '0 B';
|
|
187
|
+
const k = 1024;
|
|
188
|
+
const sizes = ['B', 'KB', 'MB', 'GB'];
|
|
189
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
190
|
+
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
module.exports = {
|
|
194
|
+
analyze,
|
|
195
|
+
scanDirectory,
|
|
196
|
+
getFileStats,
|
|
197
|
+
formatBytes,
|
|
198
|
+
DEFAULT_IGNORES
|
|
199
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "codeinf",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A CLI tool to analyze code statistics like lines of code, file counts, and more with filtering capabilities",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"codeinf": "./bin/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"index.js",
|
|
11
|
+
"bin/",
|
|
12
|
+
"README.md"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"test": "node test.js"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"code",
|
|
19
|
+
"statistics",
|
|
20
|
+
"lines-of-code",
|
|
21
|
+
"loc",
|
|
22
|
+
"cli",
|
|
23
|
+
"analyzer",
|
|
24
|
+
"code-metrics"
|
|
25
|
+
],
|
|
26
|
+
"author": "",
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=14.0.0"
|
|
30
|
+
},
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "git+https://github.com/yourusername/codeinf.git"
|
|
34
|
+
},
|
|
35
|
+
"bugs": {
|
|
36
|
+
"url": "https://github.com/yourusername/codeinf/issues"
|
|
37
|
+
},
|
|
38
|
+
"homepage": "https://github.com/yourusername/codeinf#readme"
|
|
39
|
+
}
|