codeworth 1.0.0 → 1.1.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/README.md +16 -5
- package/index.js +101 -21
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -10,7 +10,15 @@ npm install -g codeworth
|
|
|
10
10
|
|
|
11
11
|
## Usage
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
### Automatic Scan
|
|
14
|
+
Run in any project root to automatically count lines and value the project:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
codeworth
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
### Manual Input
|
|
21
|
+
Provide a specific line count:
|
|
14
22
|
|
|
15
23
|
```bash
|
|
16
24
|
codeworth 26024
|
|
@@ -23,11 +31,14 @@ codeworth 26024
|
|
|
23
31
|
### Examples
|
|
24
32
|
|
|
25
33
|
```bash
|
|
26
|
-
#
|
|
27
|
-
codeworth
|
|
34
|
+
# Auto-scan current directory
|
|
35
|
+
codeworth
|
|
36
|
+
|
|
37
|
+
# Auto-scan with custom rate
|
|
38
|
+
codeworth --rate 25
|
|
28
39
|
|
|
29
|
-
#
|
|
30
|
-
codeworth 5000
|
|
40
|
+
# Manual input
|
|
41
|
+
codeworth 5000
|
|
31
42
|
```
|
|
32
43
|
|
|
33
44
|
## Output
|
package/index.js
CHANGED
|
@@ -1,9 +1,21 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import process from 'node:process';
|
|
4
|
+
import fs from 'node:fs';
|
|
5
|
+
import path from 'node:path';
|
|
4
6
|
|
|
5
7
|
// Configuration
|
|
6
8
|
const DEFAULT_RATE = 10.77;
|
|
9
|
+
const IGNORED_DIRS = new Set(['node_modules', '.git', 'dist', 'build', 'coverage', '.idea', '.vscode', '.next', 'target', '.venv', 'venv']);
|
|
10
|
+
const IGNORED_FILES = new Set(['.DS_Store', 'package-lock.json', 'yarn.lock', 'pnpm-lock.yaml', 'bun.lockb']);
|
|
11
|
+
const BINARY_EXTENSIONS = new Set([
|
|
12
|
+
'.png', '.jpg', '.jpeg', '.gif', '.ico', '.svg', '.webp',
|
|
13
|
+
'.pdf', '.zip', '.tar', '.gz', '.7z', '.rar',
|
|
14
|
+
'.exe', '.dll', '.so', '.dylib', '.bin',
|
|
15
|
+
'.mp3', '.mp4', '.wav', '.mov', '.avi',
|
|
16
|
+
'.ttf', '.otf', '.woff', '.woff2', '.eot',
|
|
17
|
+
'.pyc', '.class', '.jar'
|
|
18
|
+
]);
|
|
7
19
|
|
|
8
20
|
// Helper to format currency
|
|
9
21
|
const formatCurrency = (amount) => {
|
|
@@ -19,33 +31,77 @@ const formatNumber = (num) => {
|
|
|
19
31
|
return new Intl.NumberFormat('en-US').format(num);
|
|
20
32
|
};
|
|
21
33
|
|
|
22
|
-
//
|
|
23
|
-
const
|
|
24
|
-
const
|
|
34
|
+
// Check if file is likely binary
|
|
35
|
+
const isBinary = (filePath) => {
|
|
36
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
37
|
+
if (BINARY_EXTENSIONS.has(ext)) return true;
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
const buffer = fs.readFileSync(filePath); // Read full file to avoid complexity, or just first chunk
|
|
41
|
+
// Check first 1024 bytes for null byte
|
|
42
|
+
const checkLength = Math.min(buffer.length, 1024);
|
|
43
|
+
for (let i = 0; i < checkLength; i++) {
|
|
44
|
+
if (buffer[i] === 0) return true;
|
|
45
|
+
}
|
|
46
|
+
return false;
|
|
47
|
+
} catch (e) {
|
|
48
|
+
return true; // Treat unreadable as binary/skip
|
|
49
|
+
}
|
|
50
|
+
};
|
|
25
51
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
52
|
+
// Count lines in a file
|
|
53
|
+
const countLines = (filePath) => {
|
|
54
|
+
if (isBinary(filePath)) return 0;
|
|
55
|
+
try {
|
|
56
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
57
|
+
return content.split(/\r\n|\r|\n/).length;
|
|
58
|
+
} catch (e) {
|
|
59
|
+
return 0;
|
|
60
|
+
}
|
|
61
|
+
};
|
|
30
62
|
|
|
31
|
-
|
|
32
|
-
|
|
63
|
+
// Recursively scan directory
|
|
64
|
+
const scanDirectory = (dir) => {
|
|
65
|
+
let totalLines = 0;
|
|
66
|
+
let fileCount = 0;
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
70
|
+
|
|
71
|
+
for (const entry of entries) {
|
|
72
|
+
const resPath = path.join(dir, entry.name);
|
|
73
|
+
|
|
74
|
+
if (entry.isDirectory()) {
|
|
75
|
+
if (!IGNORED_DIRS.has(entry.name)) {
|
|
76
|
+
const result = scanDirectory(resPath);
|
|
77
|
+
totalLines += result.lines;
|
|
78
|
+
fileCount += result.files;
|
|
79
|
+
}
|
|
80
|
+
} else if (entry.isFile()) {
|
|
81
|
+
if (!IGNORED_FILES.has(entry.name)) {
|
|
82
|
+
const lines = countLines(resPath);
|
|
83
|
+
totalLines += lines;
|
|
84
|
+
fileCount++;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
} catch (e) {
|
|
89
|
+
// Ignore access errors
|
|
90
|
+
}
|
|
33
91
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
--help Show this help message
|
|
92
|
+
return { lines: totalLines, files: fileCount };
|
|
93
|
+
};
|
|
37
94
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
`);
|
|
42
|
-
process.exit(args.length === 0 ? 1 : 0);
|
|
43
|
-
}
|
|
95
|
+
// Main execution
|
|
96
|
+
const main = () => {
|
|
97
|
+
const args = process.argv.slice(2);
|
|
44
98
|
|
|
45
|
-
//
|
|
99
|
+
// Check query args
|
|
46
100
|
let loc = null;
|
|
47
101
|
let rate = DEFAULT_RATE;
|
|
102
|
+
let showHelp = false;
|
|
48
103
|
|
|
104
|
+
// Simple arg parsing
|
|
49
105
|
for (let i = 0; i < args.length; i++) {
|
|
50
106
|
const arg = args[i];
|
|
51
107
|
|
|
@@ -57,14 +113,38 @@ Example:
|
|
|
57
113
|
}
|
|
58
114
|
rate = parseFloat(nextArg);
|
|
59
115
|
i++; // Skip next arg
|
|
116
|
+
} else if (arg === '--help' || arg === '-h') {
|
|
117
|
+
showHelp = true;
|
|
60
118
|
} else if (!isNaN(parseInt(arg.replace(/,/g, ''), 10)) && loc === null) {
|
|
61
119
|
loc = parseInt(arg.replace(/,/g, ''), 10);
|
|
62
120
|
}
|
|
63
121
|
}
|
|
64
122
|
|
|
123
|
+
if (showHelp) {
|
|
124
|
+
console.log(`
|
|
125
|
+
Usage: codeworth [lines-of-code] [options]
|
|
126
|
+
|
|
127
|
+
Arguments:
|
|
128
|
+
[lines-of-code] Total lines of code (numeric). If omitted, scans current directory.
|
|
129
|
+
|
|
130
|
+
Options:
|
|
131
|
+
--rate <number> Override rate per line (default: ${DEFAULT_RATE})
|
|
132
|
+
--help Show this help message
|
|
133
|
+
|
|
134
|
+
Example:
|
|
135
|
+
codeworth # Auto-scan current directory
|
|
136
|
+
codeworth 26024 # Calculate for specific LOC
|
|
137
|
+
codeworth --rate 15 # Auto-scan with custom rate
|
|
138
|
+
`);
|
|
139
|
+
process.exit(0);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Auto-scan if no LOC provided
|
|
65
143
|
if (loc === null) {
|
|
66
|
-
console.
|
|
67
|
-
process.
|
|
144
|
+
console.log('Scanning current directory...');
|
|
145
|
+
const result = scanDirectory(process.cwd());
|
|
146
|
+
loc = result.lines;
|
|
147
|
+
console.log(`Scanned ${formatNumber(result.files)} files.`);
|
|
68
148
|
}
|
|
69
149
|
|
|
70
150
|
// Calculate
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeworth",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "CLI tool to calculate traditional software valuation based on LOC.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -9,6 +9,9 @@
|
|
|
9
9
|
"type": "module",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"author": "David Weaver",
|
|
12
|
+
"scripts": {
|
|
13
|
+
"test": "echo \"No tests specified\" && exit 0"
|
|
14
|
+
},
|
|
12
15
|
"keywords": [
|
|
13
16
|
"valuation",
|
|
14
17
|
"loc",
|