@monodog/utils 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/helpers.d.ts +87 -0
- package/helpers.js +333 -0
- package/helpers.js.map +1 -0
- package/helpers.ts +445 -0
- package/package.json +18 -0
- package/tsconfig.json +22 -0
package/helpers.d.ts
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
export interface PackageInfo {
|
|
2
|
+
name: string;
|
|
3
|
+
version: string;
|
|
4
|
+
type: string;
|
|
5
|
+
path: string;
|
|
6
|
+
dependencies: Record<string, string>;
|
|
7
|
+
devDependencies: Record<string, string>;
|
|
8
|
+
peerDependencies: Record<string, string>;
|
|
9
|
+
scripts: Record<string, string>;
|
|
10
|
+
maintainers: string[];
|
|
11
|
+
description?: string;
|
|
12
|
+
license?: string;
|
|
13
|
+
repository?: Record<string, string>;
|
|
14
|
+
}
|
|
15
|
+
export interface DependencyInfo {
|
|
16
|
+
name: string;
|
|
17
|
+
version: string;
|
|
18
|
+
type: 'dependency' | 'devDependency' | 'peerDependency';
|
|
19
|
+
latest?: string;
|
|
20
|
+
status?: 'up-to-date' | 'outdated' | 'major-update' | 'unknown';
|
|
21
|
+
outdated?: boolean;
|
|
22
|
+
}
|
|
23
|
+
export interface PackageHealth {
|
|
24
|
+
buildStatus: 'success' | 'failed' | 'running' | 'unknown';
|
|
25
|
+
testCoverage: number;
|
|
26
|
+
lintStatus: 'pass' | 'fail' | 'unknown';
|
|
27
|
+
securityAudit: 'pass' | 'fail' | 'unknown';
|
|
28
|
+
overallScore: number;
|
|
29
|
+
}
|
|
30
|
+
export interface MonorepoStats {
|
|
31
|
+
totalPackages: number;
|
|
32
|
+
apps: number;
|
|
33
|
+
libraries: number;
|
|
34
|
+
tools: number;
|
|
35
|
+
healthyPackages: number;
|
|
36
|
+
warningPackages: number;
|
|
37
|
+
errorPackages: number;
|
|
38
|
+
outdatedDependencies: number;
|
|
39
|
+
totalDependencies: number;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Scans the monorepo and returns information about all packages
|
|
43
|
+
*/
|
|
44
|
+
declare function scanMonorepo(rootDir: string): PackageInfo[];
|
|
45
|
+
/**
|
|
46
|
+
* Analyzes dependencies and determines their status
|
|
47
|
+
*/
|
|
48
|
+
/**
|
|
49
|
+
* Calculates package health score based on various metrics
|
|
50
|
+
*/
|
|
51
|
+
declare function calculatePackageHealth(buildStatus: PackageHealth['buildStatus'], testCoverage: number, lintStatus: PackageHealth['lintStatus'], securityAudit: PackageHealth['securityAudit']): PackageHealth;
|
|
52
|
+
/**
|
|
53
|
+
* Generates comprehensive monorepo statistics
|
|
54
|
+
*/
|
|
55
|
+
declare function generateMonorepoStats(packages: PackageInfo[]): MonorepoStats;
|
|
56
|
+
/**
|
|
57
|
+
* Finds circular dependencies in the monorepo
|
|
58
|
+
*/
|
|
59
|
+
declare function findCircularDependencies(packages: PackageInfo[]): string[][];
|
|
60
|
+
/**
|
|
61
|
+
* Generates a dependency graph for visualization
|
|
62
|
+
*/
|
|
63
|
+
declare function generateDependencyGraph(packages: PackageInfo[]): {
|
|
64
|
+
nodes: {
|
|
65
|
+
label: string;
|
|
66
|
+
type: string;
|
|
67
|
+
version: string;
|
|
68
|
+
dependencies: number;
|
|
69
|
+
}[];
|
|
70
|
+
edges: {
|
|
71
|
+
from: string;
|
|
72
|
+
to: string;
|
|
73
|
+
type: string;
|
|
74
|
+
}[];
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* Checks if a package has outdated dependencies
|
|
78
|
+
*/
|
|
79
|
+
declare function checkOutdatedDependencies(packageInfo: PackageInfo): DependencyInfo[];
|
|
80
|
+
/**
|
|
81
|
+
* Gets package size information
|
|
82
|
+
*/
|
|
83
|
+
declare function getPackageSize(packagePath: string): {
|
|
84
|
+
size: number;
|
|
85
|
+
files: number;
|
|
86
|
+
};
|
|
87
|
+
export { scanMonorepo, generateMonorepoStats, findCircularDependencies, generateDependencyGraph, checkOutdatedDependencies, getPackageSize, calculatePackageHealth, };
|
package/helpers.js
ADDED
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
// import { Package } from '@prisma/client';
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
/**
|
|
5
|
+
* Scans the monorepo and returns information about all packages
|
|
6
|
+
*/
|
|
7
|
+
function scanMonorepo(rootDir) {
|
|
8
|
+
const packages = [];
|
|
9
|
+
console.log('rootDir', rootDir);
|
|
10
|
+
// Scan packages directory
|
|
11
|
+
const packagesDir = path.join(rootDir, 'packages');
|
|
12
|
+
if (fs.existsSync(packagesDir)) {
|
|
13
|
+
const packageDirs = fs
|
|
14
|
+
.readdirSync(packagesDir, { withFileTypes: true })
|
|
15
|
+
.filter(dirent => dirent.isDirectory())
|
|
16
|
+
.map(dirent => dirent.name);
|
|
17
|
+
for (const packageName of packageDirs) {
|
|
18
|
+
const packagePath = path.join(packagesDir, packageName);
|
|
19
|
+
const packageInfo = parsePackageInfo(packagePath, packageName);
|
|
20
|
+
if (packageInfo) {
|
|
21
|
+
packages.push(packageInfo);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
// Scan apps directory
|
|
26
|
+
const appsDir = path.join(rootDir, 'apps');
|
|
27
|
+
if (fs.existsSync(appsDir)) {
|
|
28
|
+
const appDirs = fs
|
|
29
|
+
.readdirSync(appsDir, { withFileTypes: true })
|
|
30
|
+
.filter(dirent => dirent.isDirectory())
|
|
31
|
+
.map(dirent => dirent.name);
|
|
32
|
+
for (const appName of appDirs) {
|
|
33
|
+
const appPath = path.join(appsDir, appName);
|
|
34
|
+
const appInfo = parsePackageInfo(appPath, appName, 'app');
|
|
35
|
+
if (appInfo) {
|
|
36
|
+
packages.push(appInfo);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
// Scan libs directory
|
|
41
|
+
const libsDir = path.join(rootDir, 'libs');
|
|
42
|
+
if (fs.existsSync(libsDir)) {
|
|
43
|
+
const libDirs = fs
|
|
44
|
+
.readdirSync(libsDir, { withFileTypes: true })
|
|
45
|
+
.filter(dirent => dirent.isDirectory())
|
|
46
|
+
.map(dirent => dirent.name);
|
|
47
|
+
for (const libName of libDirs) {
|
|
48
|
+
const libPath = path.join(libsDir, libName);
|
|
49
|
+
const libInfo = parsePackageInfo(libPath, libName, 'lib');
|
|
50
|
+
if (libInfo) {
|
|
51
|
+
packages.push(libInfo);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return packages;
|
|
56
|
+
}
|
|
57
|
+
/*** Parses package.json and determines package type */
|
|
58
|
+
function parsePackageInfo(packagePath, packageName, forcedType) {
|
|
59
|
+
const packageJsonPath = path.join(packagePath, 'package.json');
|
|
60
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
try {
|
|
64
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
65
|
+
// Determine package type
|
|
66
|
+
let packageType = 'lib';
|
|
67
|
+
if (forcedType) {
|
|
68
|
+
packageType = forcedType;
|
|
69
|
+
}
|
|
70
|
+
else if (packageJson.scripts && packageJson.scripts.start) {
|
|
71
|
+
packageType = 'app';
|
|
72
|
+
}
|
|
73
|
+
else if (packageJson.keywords && packageJson.keywords.includes('tool')) {
|
|
74
|
+
packageType = 'tool';
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
name: packageJson.name || packageName,
|
|
78
|
+
version: packageJson.version || '0.0.0',
|
|
79
|
+
type: packageType,
|
|
80
|
+
path: packagePath,
|
|
81
|
+
dependencies: packageJson.dependencies || {},
|
|
82
|
+
devDependencies: packageJson.devDependencies || {},
|
|
83
|
+
peerDependencies: packageJson.peerDependencies || {},
|
|
84
|
+
scripts: packageJson.scripts || {},
|
|
85
|
+
maintainers: packageJson.maintainers || [],
|
|
86
|
+
description: packageJson.description,
|
|
87
|
+
license: packageJson.license,
|
|
88
|
+
repository: packageJson.repository || {},
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
console.error(`Error parsing package.json for ${packageName}:`, error);
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Analyzes dependencies and determines their status
|
|
98
|
+
*/
|
|
99
|
+
// function analyzeDependencies(
|
|
100
|
+
// dependencies: Record<string, string>,
|
|
101
|
+
// type: 'production' | 'development' = 'production'
|
|
102
|
+
// ): DependencyInfo[] {
|
|
103
|
+
// return Object.entries(dependencies).map(([name, version]) => ({
|
|
104
|
+
// name,
|
|
105
|
+
// currentVersion: version,
|
|
106
|
+
// status: 'unknown', // Would be determined by npm registry check
|
|
107
|
+
// type,
|
|
108
|
+
// }));
|
|
109
|
+
// }
|
|
110
|
+
/**
|
|
111
|
+
* Calculates package health score based on various metrics
|
|
112
|
+
*/
|
|
113
|
+
function calculatePackageHealth(buildStatus, testCoverage, lintStatus, securityAudit) {
|
|
114
|
+
let score = 0;
|
|
115
|
+
// Build status (30 points)
|
|
116
|
+
switch (buildStatus) {
|
|
117
|
+
case 'success':
|
|
118
|
+
score += 30;
|
|
119
|
+
break;
|
|
120
|
+
case 'running':
|
|
121
|
+
score += 15;
|
|
122
|
+
break;
|
|
123
|
+
case 'failed':
|
|
124
|
+
score += 0;
|
|
125
|
+
break;
|
|
126
|
+
default:
|
|
127
|
+
score += 10;
|
|
128
|
+
}
|
|
129
|
+
// Test coverage (25 points)
|
|
130
|
+
score += Math.min(25, (testCoverage / 100) * 25);
|
|
131
|
+
// Lint status (25 points)
|
|
132
|
+
switch (lintStatus) {
|
|
133
|
+
case 'pass':
|
|
134
|
+
score += 25;
|
|
135
|
+
break;
|
|
136
|
+
case 'fail':
|
|
137
|
+
score += 0;
|
|
138
|
+
break;
|
|
139
|
+
default:
|
|
140
|
+
score += 10;
|
|
141
|
+
}
|
|
142
|
+
// Security audit (20 points)
|
|
143
|
+
switch (securityAudit) {
|
|
144
|
+
case 'pass':
|
|
145
|
+
score += 20;
|
|
146
|
+
break;
|
|
147
|
+
case 'fail':
|
|
148
|
+
score += 0;
|
|
149
|
+
break;
|
|
150
|
+
default:
|
|
151
|
+
score += 10;
|
|
152
|
+
}
|
|
153
|
+
return {
|
|
154
|
+
buildStatus,
|
|
155
|
+
testCoverage,
|
|
156
|
+
lintStatus,
|
|
157
|
+
securityAudit,
|
|
158
|
+
overallScore: Math.round(score),
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Generates comprehensive monorepo statistics
|
|
163
|
+
*/
|
|
164
|
+
function generateMonorepoStats(packages) {
|
|
165
|
+
const stats = {
|
|
166
|
+
totalPackages: packages.length,
|
|
167
|
+
apps: packages.filter(p => p.type === 'app').length,
|
|
168
|
+
libraries: packages.filter(p => p.type === 'lib').length,
|
|
169
|
+
tools: packages.filter(p => p.type === 'tool').length,
|
|
170
|
+
healthyPackages: 0,
|
|
171
|
+
warningPackages: 0,
|
|
172
|
+
errorPackages: 0,
|
|
173
|
+
outdatedDependencies: 0,
|
|
174
|
+
totalDependencies: 0,
|
|
175
|
+
};
|
|
176
|
+
// Calculate dependency counts
|
|
177
|
+
packages.forEach(pkg => {
|
|
178
|
+
stats.totalDependencies += Object.keys(pkg.dependencies).length;
|
|
179
|
+
stats.totalDependencies += Object.keys(pkg.devDependencies).length;
|
|
180
|
+
stats.totalDependencies += Object.keys(pkg.peerDependencies ?? {}).length;
|
|
181
|
+
});
|
|
182
|
+
return stats;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Finds circular dependencies in the monorepo
|
|
186
|
+
*/
|
|
187
|
+
function findCircularDependencies(packages) {
|
|
188
|
+
const graph = new Map();
|
|
189
|
+
const visited = new Set();
|
|
190
|
+
const recursionStack = new Set();
|
|
191
|
+
const circularDeps = [];
|
|
192
|
+
// Build dependency graph
|
|
193
|
+
packages.forEach(pkg => {
|
|
194
|
+
graph.set(pkg.name, Object.keys(pkg.dependencies));
|
|
195
|
+
});
|
|
196
|
+
function dfs(node, path) {
|
|
197
|
+
if (recursionStack.has(node)) {
|
|
198
|
+
const cycleStart = path.indexOf(node);
|
|
199
|
+
circularDeps.push(path.slice(cycleStart));
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
if (visited.has(node)) {
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
visited.add(node);
|
|
206
|
+
recursionStack.add(node);
|
|
207
|
+
path.push(node);
|
|
208
|
+
const dependencies = graph.get(node) || [];
|
|
209
|
+
for (const dep of dependencies) {
|
|
210
|
+
if (graph.has(dep)) {
|
|
211
|
+
dfs(dep, [...path]);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
recursionStack.delete(node);
|
|
215
|
+
}
|
|
216
|
+
for (const node of graph.keys()) {
|
|
217
|
+
if (!visited.has(node)) {
|
|
218
|
+
dfs(node, []);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return circularDeps;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Generates a dependency graph for visualization
|
|
225
|
+
*/
|
|
226
|
+
function generateDependencyGraph(packages) {
|
|
227
|
+
const nodes = packages.map(pkg => ({
|
|
228
|
+
// id: pkg.name,
|
|
229
|
+
label: pkg.name,
|
|
230
|
+
type: pkg.type,
|
|
231
|
+
version: pkg.version,
|
|
232
|
+
dependencies: Object.keys(pkg.dependencies).length,
|
|
233
|
+
}));
|
|
234
|
+
const edges = [];
|
|
235
|
+
packages.forEach(pkg => {
|
|
236
|
+
Object.keys(pkg.dependencies).forEach(depName => {
|
|
237
|
+
// Only include internal dependencies
|
|
238
|
+
if (packages.some(p => p.name === depName)) {
|
|
239
|
+
edges.push({
|
|
240
|
+
from: pkg.name,
|
|
241
|
+
to: depName,
|
|
242
|
+
type: 'internal',
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
});
|
|
247
|
+
return { nodes, edges };
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Checks if a package has outdated dependencies
|
|
251
|
+
*/
|
|
252
|
+
function checkOutdatedDependencies(packageInfo) {
|
|
253
|
+
const outdated = [];
|
|
254
|
+
// This would typically involve checking against npm registry
|
|
255
|
+
// For now, we'll simulate with some basic checks
|
|
256
|
+
Object.entries(packageInfo.dependencies).forEach(([name, version]) => {
|
|
257
|
+
if (version.startsWith('^') || version.startsWith('~')) {
|
|
258
|
+
// Could be outdated, would need registry check
|
|
259
|
+
outdated.push({
|
|
260
|
+
name,
|
|
261
|
+
version: version,
|
|
262
|
+
status: 'unknown',
|
|
263
|
+
type: 'dependency',
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
return outdated;
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Formats version numbers for comparison
|
|
271
|
+
*/
|
|
272
|
+
// function parseVersion(version: string): number[] {
|
|
273
|
+
// return version
|
|
274
|
+
// .replace(/^[^0-9]*/, '')
|
|
275
|
+
// .split('.')
|
|
276
|
+
// .map(Number);
|
|
277
|
+
// }
|
|
278
|
+
/**
|
|
279
|
+
* Compares two version strings
|
|
280
|
+
*/
|
|
281
|
+
// function compareVersions(v1: string, v2: string): number {
|
|
282
|
+
// const parsed1 = parseVersion(v1);
|
|
283
|
+
// const parsed2 = parseVersion(v2);
|
|
284
|
+
// for (let i = 0; i < Math.max(parsed1.length, parsed2.length); i++) {
|
|
285
|
+
// const num1 = parsed1[i] || 0;
|
|
286
|
+
// const num2 = parsed2[i] || 0;
|
|
287
|
+
// if (num1 > num2) return 1;
|
|
288
|
+
// if (num1 < num2) return -1;
|
|
289
|
+
// }
|
|
290
|
+
// return 0;
|
|
291
|
+
// }
|
|
292
|
+
/**
|
|
293
|
+
* Gets package size information
|
|
294
|
+
*/
|
|
295
|
+
function getPackageSize(packagePath) {
|
|
296
|
+
try {
|
|
297
|
+
let totalSize = 0;
|
|
298
|
+
let fileCount = 0;
|
|
299
|
+
const calculateSize = (dirPath) => {
|
|
300
|
+
const items = fs.readdirSync(dirPath, { withFileTypes: true });
|
|
301
|
+
for (const item of items) {
|
|
302
|
+
const fullPath = path.join(dirPath, item.name);
|
|
303
|
+
if (item.isDirectory()) {
|
|
304
|
+
// Skip node_modules and other build artifacts
|
|
305
|
+
if (!['node_modules', 'dist', 'build', '.git'].includes(item.name)) {
|
|
306
|
+
calculateSize(fullPath);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
else {
|
|
310
|
+
try {
|
|
311
|
+
const stats = fs.statSync(fullPath);
|
|
312
|
+
totalSize += stats.size;
|
|
313
|
+
fileCount++;
|
|
314
|
+
}
|
|
315
|
+
catch (error) {
|
|
316
|
+
// Skip files we can't read
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
calculateSize(packagePath);
|
|
322
|
+
return {
|
|
323
|
+
size: totalSize,
|
|
324
|
+
files: fileCount,
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
catch (error) {
|
|
328
|
+
return { size: 0, files: 0 };
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
export { scanMonorepo, generateMonorepoStats, findCircularDependencies, generateDependencyGraph, checkOutdatedDependencies, getPackageSize,
|
|
332
|
+
// analyzeDependencies,
|
|
333
|
+
calculatePackageHealth, };
|
package/helpers.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helpers.js","sourceRoot":"","sources":["helpers.ts"],"names":[],"mappings":"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"}
|
package/helpers.ts
ADDED
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
// import { Package } from '@prisma/client';
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
|
|
5
|
+
export interface PackageInfo {
|
|
6
|
+
name: string;
|
|
7
|
+
version: string;
|
|
8
|
+
type: string; //'app' | 'lib' | 'tool';
|
|
9
|
+
path: string;
|
|
10
|
+
dependencies: Record<string, string>;
|
|
11
|
+
devDependencies: Record<string, string>;
|
|
12
|
+
peerDependencies: Record<string, string>;
|
|
13
|
+
scripts: Record<string, string>;
|
|
14
|
+
maintainers: string[];
|
|
15
|
+
description?: string;
|
|
16
|
+
license?: string;
|
|
17
|
+
repository?: Record<string, string>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface DependencyInfo {
|
|
21
|
+
// name: string;
|
|
22
|
+
// currentVersion: string;
|
|
23
|
+
// latestVersion?: string;
|
|
24
|
+
// status: 'up-to-date' | 'outdated' | 'major-update' | 'unknown';
|
|
25
|
+
// type: 'production' | 'development';
|
|
26
|
+
name: string;
|
|
27
|
+
version: string;
|
|
28
|
+
type: 'dependency' | 'devDependency' | 'peerDependency';
|
|
29
|
+
latest?: string;
|
|
30
|
+
status?: 'up-to-date' | 'outdated' | 'major-update' | 'unknown';
|
|
31
|
+
outdated?: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface PackageHealth {
|
|
35
|
+
buildStatus: 'success' | 'failed' | 'running' | 'unknown';
|
|
36
|
+
testCoverage: number;
|
|
37
|
+
lintStatus: 'pass' | 'fail' | 'unknown';
|
|
38
|
+
securityAudit: 'pass' | 'fail' | 'unknown';
|
|
39
|
+
overallScore: number;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface MonorepoStats {
|
|
43
|
+
totalPackages: number;
|
|
44
|
+
apps: number;
|
|
45
|
+
libraries: number;
|
|
46
|
+
tools: number;
|
|
47
|
+
healthyPackages: number;
|
|
48
|
+
warningPackages: number;
|
|
49
|
+
errorPackages: number;
|
|
50
|
+
outdatedDependencies: number;
|
|
51
|
+
totalDependencies: number;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Scans the monorepo and returns information about all packages
|
|
56
|
+
*/
|
|
57
|
+
function scanMonorepo(rootDir: string): PackageInfo[] {
|
|
58
|
+
const packages: PackageInfo[] = [];
|
|
59
|
+
console.log('rootDir', rootDir);
|
|
60
|
+
// Scan packages directory
|
|
61
|
+
const packagesDir = path.join(rootDir, 'packages');
|
|
62
|
+
if (fs.existsSync(packagesDir)) {
|
|
63
|
+
const packageDirs = fs
|
|
64
|
+
.readdirSync(packagesDir, { withFileTypes: true })
|
|
65
|
+
.filter(dirent => dirent.isDirectory())
|
|
66
|
+
.map(dirent => dirent.name);
|
|
67
|
+
|
|
68
|
+
for (const packageName of packageDirs) {
|
|
69
|
+
const packagePath = path.join(packagesDir, packageName);
|
|
70
|
+
const packageInfo = parsePackageInfo(packagePath, packageName);
|
|
71
|
+
if (packageInfo) {
|
|
72
|
+
packages.push(packageInfo);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Scan apps directory
|
|
78
|
+
const appsDir = path.join(rootDir, 'apps');
|
|
79
|
+
if (fs.existsSync(appsDir)) {
|
|
80
|
+
const appDirs = fs
|
|
81
|
+
.readdirSync(appsDir, { withFileTypes: true })
|
|
82
|
+
.filter(dirent => dirent.isDirectory())
|
|
83
|
+
.map(dirent => dirent.name);
|
|
84
|
+
|
|
85
|
+
for (const appName of appDirs) {
|
|
86
|
+
const appPath = path.join(appsDir, appName);
|
|
87
|
+
const appInfo = parsePackageInfo(appPath, appName, 'app');
|
|
88
|
+
if (appInfo) {
|
|
89
|
+
packages.push(appInfo);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Scan libs directory
|
|
95
|
+
const libsDir = path.join(rootDir, 'libs');
|
|
96
|
+
if (fs.existsSync(libsDir)) {
|
|
97
|
+
const libDirs = fs
|
|
98
|
+
.readdirSync(libsDir, { withFileTypes: true })
|
|
99
|
+
.filter(dirent => dirent.isDirectory())
|
|
100
|
+
.map(dirent => dirent.name);
|
|
101
|
+
|
|
102
|
+
for (const libName of libDirs) {
|
|
103
|
+
const libPath = path.join(libsDir, libName);
|
|
104
|
+
const libInfo = parsePackageInfo(libPath, libName, 'lib');
|
|
105
|
+
if (libInfo) {
|
|
106
|
+
packages.push(libInfo);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return packages;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/*** Parses package.json and determines package type */
|
|
115
|
+
function parsePackageInfo(
|
|
116
|
+
packagePath: string,
|
|
117
|
+
packageName: string,
|
|
118
|
+
forcedType?: 'app' | 'lib' | 'tool'
|
|
119
|
+
): PackageInfo | null {
|
|
120
|
+
const packageJsonPath = path.join(packagePath, 'package.json');
|
|
121
|
+
|
|
122
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
try {
|
|
127
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
128
|
+
|
|
129
|
+
// Determine package type
|
|
130
|
+
let packageType: 'app' | 'lib' | 'tool' = 'lib';
|
|
131
|
+
if (forcedType) {
|
|
132
|
+
packageType = forcedType;
|
|
133
|
+
} else if (packageJson.scripts && packageJson.scripts.start) {
|
|
134
|
+
packageType = 'app';
|
|
135
|
+
} else if (packageJson.keywords && packageJson.keywords.includes('tool')) {
|
|
136
|
+
packageType = 'tool';
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return {
|
|
140
|
+
name: packageJson.name || packageName,
|
|
141
|
+
version: packageJson.version || '0.0.0',
|
|
142
|
+
type: packageType,
|
|
143
|
+
path: packagePath,
|
|
144
|
+
dependencies: packageJson.dependencies || {},
|
|
145
|
+
devDependencies: packageJson.devDependencies || {},
|
|
146
|
+
peerDependencies: packageJson.peerDependencies || {},
|
|
147
|
+
scripts: packageJson.scripts || {},
|
|
148
|
+
maintainers: packageJson.maintainers || [],
|
|
149
|
+
description: packageJson.description,
|
|
150
|
+
license: packageJson.license,
|
|
151
|
+
repository: packageJson.repository || {},
|
|
152
|
+
};
|
|
153
|
+
} catch (error) {
|
|
154
|
+
console.error(`Error parsing package.json for ${packageName}:`, error);
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Analyzes dependencies and determines their status
|
|
161
|
+
*/
|
|
162
|
+
// function analyzeDependencies(
|
|
163
|
+
// dependencies: Record<string, string>,
|
|
164
|
+
// type: 'production' | 'development' = 'production'
|
|
165
|
+
// ): DependencyInfo[] {
|
|
166
|
+
// return Object.entries(dependencies).map(([name, version]) => ({
|
|
167
|
+
// name,
|
|
168
|
+
// currentVersion: version,
|
|
169
|
+
// status: 'unknown', // Would be determined by npm registry check
|
|
170
|
+
// type,
|
|
171
|
+
// }));
|
|
172
|
+
// }
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Calculates package health score based on various metrics
|
|
176
|
+
*/
|
|
177
|
+
function calculatePackageHealth(
|
|
178
|
+
buildStatus: PackageHealth['buildStatus'],
|
|
179
|
+
testCoverage: number,
|
|
180
|
+
lintStatus: PackageHealth['lintStatus'],
|
|
181
|
+
securityAudit: PackageHealth['securityAudit']
|
|
182
|
+
): PackageHealth {
|
|
183
|
+
let score = 0;
|
|
184
|
+
|
|
185
|
+
// Build status (30 points)
|
|
186
|
+
switch (buildStatus) {
|
|
187
|
+
case 'success':
|
|
188
|
+
score += 30;
|
|
189
|
+
break;
|
|
190
|
+
case 'running':
|
|
191
|
+
score += 15;
|
|
192
|
+
break;
|
|
193
|
+
case 'failed':
|
|
194
|
+
score += 0;
|
|
195
|
+
break;
|
|
196
|
+
default:
|
|
197
|
+
score += 10;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Test coverage (25 points)
|
|
201
|
+
score += Math.min(25, (testCoverage / 100) * 25);
|
|
202
|
+
|
|
203
|
+
// Lint status (25 points)
|
|
204
|
+
switch (lintStatus) {
|
|
205
|
+
case 'pass':
|
|
206
|
+
score += 25;
|
|
207
|
+
break;
|
|
208
|
+
case 'fail':
|
|
209
|
+
score += 0;
|
|
210
|
+
break;
|
|
211
|
+
default:
|
|
212
|
+
score += 10;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Security audit (20 points)
|
|
216
|
+
switch (securityAudit) {
|
|
217
|
+
case 'pass':
|
|
218
|
+
score += 20;
|
|
219
|
+
break;
|
|
220
|
+
case 'fail':
|
|
221
|
+
score += 0;
|
|
222
|
+
break;
|
|
223
|
+
default:
|
|
224
|
+
score += 10;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return {
|
|
228
|
+
buildStatus,
|
|
229
|
+
testCoverage,
|
|
230
|
+
lintStatus,
|
|
231
|
+
securityAudit,
|
|
232
|
+
overallScore: Math.round(score),
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Generates comprehensive monorepo statistics
|
|
238
|
+
*/
|
|
239
|
+
function generateMonorepoStats(packages: PackageInfo[]): MonorepoStats {
|
|
240
|
+
const stats: MonorepoStats = {
|
|
241
|
+
totalPackages: packages.length,
|
|
242
|
+
apps: packages.filter(p => p.type === 'app').length,
|
|
243
|
+
libraries: packages.filter(p => p.type === 'lib').length,
|
|
244
|
+
tools: packages.filter(p => p.type === 'tool').length,
|
|
245
|
+
healthyPackages: 0,
|
|
246
|
+
warningPackages: 0,
|
|
247
|
+
errorPackages: 0,
|
|
248
|
+
outdatedDependencies: 0,
|
|
249
|
+
totalDependencies: 0,
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
// Calculate dependency counts
|
|
253
|
+
packages.forEach(pkg => {
|
|
254
|
+
stats.totalDependencies += Object.keys(pkg.dependencies).length;
|
|
255
|
+
stats.totalDependencies += Object.keys(pkg.devDependencies).length;
|
|
256
|
+
stats.totalDependencies += Object.keys(pkg.peerDependencies ?? {}).length;
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
return stats;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Finds circular dependencies in the monorepo
|
|
264
|
+
*/
|
|
265
|
+
function findCircularDependencies(packages: PackageInfo[]): string[][] {
|
|
266
|
+
const graph = new Map<string, string[]>();
|
|
267
|
+
const visited = new Set<string>();
|
|
268
|
+
const recursionStack = new Set<string>();
|
|
269
|
+
const circularDeps: string[][] = [];
|
|
270
|
+
|
|
271
|
+
// Build dependency graph
|
|
272
|
+
packages.forEach(pkg => {
|
|
273
|
+
graph.set(pkg.name, Object.keys(pkg.dependencies));
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
function dfs(node: string, path: string[]): void {
|
|
277
|
+
if (recursionStack.has(node)) {
|
|
278
|
+
const cycleStart = path.indexOf(node);
|
|
279
|
+
circularDeps.push(path.slice(cycleStart));
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (visited.has(node)) {
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
visited.add(node);
|
|
288
|
+
recursionStack.add(node);
|
|
289
|
+
path.push(node);
|
|
290
|
+
|
|
291
|
+
const dependencies = graph.get(node) || [];
|
|
292
|
+
for (const dep of dependencies) {
|
|
293
|
+
if (graph.has(dep)) {
|
|
294
|
+
dfs(dep, [...path]);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
recursionStack.delete(node);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
for (const node of graph.keys()) {
|
|
302
|
+
if (!visited.has(node)) {
|
|
303
|
+
dfs(node, []);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return circularDeps;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Generates a dependency graph for visualization
|
|
312
|
+
*/
|
|
313
|
+
function generateDependencyGraph(packages: PackageInfo[]) {
|
|
314
|
+
const nodes = packages.map(pkg => ({
|
|
315
|
+
// id: pkg.name,
|
|
316
|
+
label: pkg.name,
|
|
317
|
+
type: pkg.type,
|
|
318
|
+
version: pkg.version,
|
|
319
|
+
dependencies: Object.keys(pkg.dependencies).length,
|
|
320
|
+
}));
|
|
321
|
+
|
|
322
|
+
const edges: Array<{ from: string; to: string; type: string }> = [];
|
|
323
|
+
|
|
324
|
+
packages.forEach(pkg => {
|
|
325
|
+
Object.keys(pkg.dependencies).forEach(depName => {
|
|
326
|
+
// Only include internal dependencies
|
|
327
|
+
if (packages.some(p => p.name === depName)) {
|
|
328
|
+
edges.push({
|
|
329
|
+
from: pkg.name,
|
|
330
|
+
to: depName,
|
|
331
|
+
type: 'internal',
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
return { nodes, edges };
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Checks if a package has outdated dependencies
|
|
342
|
+
*/
|
|
343
|
+
function checkOutdatedDependencies(packageInfo: PackageInfo): DependencyInfo[] {
|
|
344
|
+
const outdated: DependencyInfo[] = [];
|
|
345
|
+
|
|
346
|
+
// This would typically involve checking against npm registry
|
|
347
|
+
// For now, we'll simulate with some basic checks
|
|
348
|
+
Object.entries(packageInfo.dependencies).forEach(([name, version]) => {
|
|
349
|
+
if (version.startsWith('^') || version.startsWith('~')) {
|
|
350
|
+
// Could be outdated, would need registry check
|
|
351
|
+
outdated.push({
|
|
352
|
+
name,
|
|
353
|
+
version: version,
|
|
354
|
+
status: 'unknown',
|
|
355
|
+
type: 'dependency',
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
return outdated;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Formats version numbers for comparison
|
|
365
|
+
*/
|
|
366
|
+
// function parseVersion(version: string): number[] {
|
|
367
|
+
// return version
|
|
368
|
+
// .replace(/^[^0-9]*/, '')
|
|
369
|
+
// .split('.')
|
|
370
|
+
// .map(Number);
|
|
371
|
+
// }
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Compares two version strings
|
|
375
|
+
*/
|
|
376
|
+
// function compareVersions(v1: string, v2: string): number {
|
|
377
|
+
// const parsed1 = parseVersion(v1);
|
|
378
|
+
// const parsed2 = parseVersion(v2);
|
|
379
|
+
|
|
380
|
+
// for (let i = 0; i < Math.max(parsed1.length, parsed2.length); i++) {
|
|
381
|
+
// const num1 = parsed1[i] || 0;
|
|
382
|
+
// const num2 = parsed2[i] || 0;
|
|
383
|
+
|
|
384
|
+
// if (num1 > num2) return 1;
|
|
385
|
+
// if (num1 < num2) return -1;
|
|
386
|
+
// }
|
|
387
|
+
|
|
388
|
+
// return 0;
|
|
389
|
+
// }
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Gets package size information
|
|
393
|
+
*/
|
|
394
|
+
function getPackageSize(packagePath: string): {
|
|
395
|
+
size: number;
|
|
396
|
+
files: number;
|
|
397
|
+
} {
|
|
398
|
+
try {
|
|
399
|
+
let totalSize = 0;
|
|
400
|
+
let fileCount = 0;
|
|
401
|
+
|
|
402
|
+
const calculateSize = (dirPath: string): void => {
|
|
403
|
+
const items = fs.readdirSync(dirPath, { withFileTypes: true });
|
|
404
|
+
|
|
405
|
+
for (const item of items) {
|
|
406
|
+
const fullPath = path.join(dirPath, item.name);
|
|
407
|
+
|
|
408
|
+
if (item.isDirectory()) {
|
|
409
|
+
// Skip node_modules and other build artifacts
|
|
410
|
+
if (!['node_modules', 'dist', 'build', '.git'].includes(item.name)) {
|
|
411
|
+
calculateSize(fullPath);
|
|
412
|
+
}
|
|
413
|
+
} else {
|
|
414
|
+
try {
|
|
415
|
+
const stats = fs.statSync(fullPath);
|
|
416
|
+
totalSize += stats.size;
|
|
417
|
+
fileCount++;
|
|
418
|
+
} catch (error) {
|
|
419
|
+
// Skip files we can't read
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
calculateSize(packagePath);
|
|
426
|
+
|
|
427
|
+
return {
|
|
428
|
+
size: totalSize,
|
|
429
|
+
files: fileCount,
|
|
430
|
+
};
|
|
431
|
+
} catch (error) {
|
|
432
|
+
return { size: 0, files: 0 };
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
export {
|
|
437
|
+
scanMonorepo,
|
|
438
|
+
generateMonorepoStats,
|
|
439
|
+
findCircularDependencies,
|
|
440
|
+
generateDependencyGraph,
|
|
441
|
+
checkOutdatedDependencies,
|
|
442
|
+
getPackageSize,
|
|
443
|
+
// analyzeDependencies,
|
|
444
|
+
calculatePackageHealth,
|
|
445
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@monodog/utils",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Shared utility functions for monodog monorepo dashboard",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsc --build",
|
|
11
|
+
"test": "jest",
|
|
12
|
+
"clean": "rm -rf dist node_modules/.cache"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"@types/node": "^20.10.0",
|
|
16
|
+
"typescript": "^5.2.2"
|
|
17
|
+
}
|
|
18
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
// Extends the shared Node configuration for server/utility environments
|
|
3
|
+
"extends": "@lakinmindfire/tsconfig/node.json",
|
|
4
|
+
|
|
5
|
+
"compilerOptions": {
|
|
6
|
+
// Output compilation results to the 'dist' directory
|
|
7
|
+
"outDir": "dist",
|
|
8
|
+
"declaration": true,
|
|
9
|
+
"sourceMap": true,
|
|
10
|
+
"esModuleInterop": true,
|
|
11
|
+
"module": "CommonJS",
|
|
12
|
+
"moduleResolution": "node",
|
|
13
|
+
"verbatimModuleSyntax": false,
|
|
14
|
+
"downlevelIteration": true,
|
|
15
|
+
},
|
|
16
|
+
|
|
17
|
+
// Include all TypeScript files in the 'src' folder
|
|
18
|
+
"include": ["index.ts", "helpers.ts"],
|
|
19
|
+
|
|
20
|
+
// Exclude node_modules and the output directory
|
|
21
|
+
"exclude": ["node_modules", "dist", "**/*.test.ts"]
|
|
22
|
+
}
|