@manojkmfsi/monodog 1.1.7 → 1.1.8
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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +6 -0
- package/package.json +1 -1
- package/src/config/swagger-config.ts +0 -344
- package/src/config-loader.ts +0 -97
- package/src/constants/index.ts +0 -13
- package/src/constants/middleware.ts +0 -83
- package/src/constants/port.ts +0 -20
- package/src/constants/security.ts +0 -78
- package/src/controllers/commit-controller.ts +0 -31
- package/src/controllers/config-controller.ts +0 -42
- package/src/controllers/health-controller.ts +0 -24
- package/src/controllers/package-controller.ts +0 -67
- package/src/get-db-url.ts +0 -9
- package/src/index.ts +0 -9
- package/src/middleware/dashboard-startup.ts +0 -161
- package/src/middleware/error-handler.ts +0 -50
- package/src/middleware/index.ts +0 -20
- package/src/middleware/logger.ts +0 -65
- package/src/middleware/security.ts +0 -90
- package/src/middleware/server-startup.ts +0 -153
- package/src/middleware/swagger-middleware.ts +0 -58
- package/src/repositories/commit-repository.ts +0 -108
- package/src/repositories/dependency-repository.ts +0 -110
- package/src/repositories/index.ts +0 -10
- package/src/repositories/package-health-repository.ts +0 -75
- package/src/repositories/package-repository.ts +0 -142
- package/src/repositories/prisma-client.ts +0 -25
- package/src/routes/commit-routes.ts +0 -10
- package/src/routes/config-routes.ts +0 -14
- package/src/routes/health-routes.ts +0 -14
- package/src/routes/package-routes.ts +0 -22
- package/src/serve.ts +0 -41
- package/src/services/commit-service.ts +0 -44
- package/src/services/config-service.ts +0 -391
- package/src/services/git-service.ts +0 -140
- package/src/services/health-service.ts +0 -162
- package/src/services/package-service.ts +0 -228
- package/src/setup.ts +0 -78
- package/src/types/ci.ts +0 -122
- package/src/types/config.ts +0 -22
- package/src/types/database.ts +0 -67
- package/src/types/git.ts +0 -33
- package/src/types/health.ts +0 -11
- package/src/types/index.ts +0 -23
- package/src/types/package.ts +0 -39
- package/src/types/scanner.ts +0 -31
- package/src/types/swagger-jsdoc.d.ts +0 -15
- package/src/utils/ci-status.ts +0 -535
- package/src/utils/db-utils.ts +0 -92
- package/src/utils/health-utils.ts +0 -67
- package/src/utils/monorepo-scanner.ts +0 -576
- package/src/utils/utilities.ts +0 -427
|
@@ -1,576 +0,0 @@
|
|
|
1
|
-
// Monorepo Scanner
|
|
2
|
-
import fs from 'fs';
|
|
3
|
-
import path from 'path';
|
|
4
|
-
import { execSync } from 'child_process';
|
|
5
|
-
import { AppLogger } from '../middleware/logger';
|
|
6
|
-
import type {
|
|
7
|
-
PackageInfo,
|
|
8
|
-
DependencyInfo,
|
|
9
|
-
PackageHealth,
|
|
10
|
-
MonorepoStats,
|
|
11
|
-
ScanResult,
|
|
12
|
-
PackageReport,
|
|
13
|
-
} from '../types';
|
|
14
|
-
import {
|
|
15
|
-
scanMonorepo,
|
|
16
|
-
calculatePackageHealth,
|
|
17
|
-
generateMonorepoStats,
|
|
18
|
-
findCircularDependencies,
|
|
19
|
-
generateDependencyGraph,
|
|
20
|
-
checkOutdatedDependencies,
|
|
21
|
-
getPackageSize,
|
|
22
|
-
} from './utilities';
|
|
23
|
-
|
|
24
|
-
export class MonorepoScanner {
|
|
25
|
-
private rootDir: string;
|
|
26
|
-
private cache: Map<string, any> = new Map();
|
|
27
|
-
private cacheExpiry = 5 * 60 * 1000; // 5 minutes
|
|
28
|
-
|
|
29
|
-
constructor(rootDir: string = process.cwd()) {
|
|
30
|
-
this.rootDir = rootDir;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Performs a complete scan of the monorepo
|
|
35
|
-
*/
|
|
36
|
-
async scan(): Promise<ScanResult> {
|
|
37
|
-
const startTime = Date.now();
|
|
38
|
-
|
|
39
|
-
try {
|
|
40
|
-
// Check cache first
|
|
41
|
-
const cacheKey = 'full-scan';
|
|
42
|
-
const cached = this.getFromCache(cacheKey);
|
|
43
|
-
if (cached) {
|
|
44
|
-
return cached;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
AppLogger.debug('Starting monorepo scan...');
|
|
48
|
-
|
|
49
|
-
// Scan all packages
|
|
50
|
-
const packages = scanMonorepo(this.rootDir);
|
|
51
|
-
AppLogger.debug(`Found ${packages.length} packages`);
|
|
52
|
-
|
|
53
|
-
// Generate statistics
|
|
54
|
-
const stats = generateMonorepoStats(packages);
|
|
55
|
-
|
|
56
|
-
// Generate dependency graph
|
|
57
|
-
const dependencyGraph = generateDependencyGraph(packages);
|
|
58
|
-
|
|
59
|
-
// Find circular dependencies
|
|
60
|
-
const circularDependencies = findCircularDependencies(packages);
|
|
61
|
-
|
|
62
|
-
// Check for outdated packages
|
|
63
|
-
const outdatedPackages = this.findOutdatedPackages(packages);
|
|
64
|
-
|
|
65
|
-
const result: ScanResult = {
|
|
66
|
-
packages,
|
|
67
|
-
stats,
|
|
68
|
-
dependencyGraph,
|
|
69
|
-
circularDependencies,
|
|
70
|
-
outdatedPackages,
|
|
71
|
-
scanTimestamp: new Date(),
|
|
72
|
-
scanDuration: Date.now() - startTime,
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
// Cache the result
|
|
76
|
-
this.setCache(cacheKey, result);
|
|
77
|
-
|
|
78
|
-
AppLogger.debug(`Scan completed in ${result.scanDuration}ms`);
|
|
79
|
-
return result;
|
|
80
|
-
} catch (error) {
|
|
81
|
-
AppLogger.error('Error during scan', error as Error);
|
|
82
|
-
throw error;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Generates detailed reports for all packages
|
|
88
|
-
*/
|
|
89
|
-
async generatePackageReports(): Promise<PackageReport[]> {
|
|
90
|
-
const packages = scanMonorepo(this.rootDir);
|
|
91
|
-
const reports: PackageReport[] = [];
|
|
92
|
-
|
|
93
|
-
for (const pkg of packages) {
|
|
94
|
-
try {
|
|
95
|
-
const report = await this.generatePackageReport(pkg);
|
|
96
|
-
reports.push(report);
|
|
97
|
-
} catch (error) {
|
|
98
|
-
AppLogger.error(`Error generating report for ${pkg.name}`, error as Error);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
return reports;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Generates a detailed report for a specific package
|
|
107
|
-
*/
|
|
108
|
-
async generatePackageReport(pkg: PackageInfo): Promise<PackageReport> {
|
|
109
|
-
const health = await this.assessPackageHealth(pkg);
|
|
110
|
-
const size = getPackageSize(pkg.path);
|
|
111
|
-
const outdatedDeps = checkOutdatedDependencies(pkg);
|
|
112
|
-
const lastModified = this.getLastModified(pkg.path);
|
|
113
|
-
const gitInfo = await this.getGitInfo(pkg.path);
|
|
114
|
-
|
|
115
|
-
return {
|
|
116
|
-
package: pkg,
|
|
117
|
-
health,
|
|
118
|
-
size,
|
|
119
|
-
outdatedDeps,
|
|
120
|
-
lastModified,
|
|
121
|
-
gitInfo,
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Assesses the health of a package
|
|
127
|
-
*/
|
|
128
|
-
private async assessPackageHealth(pkg: PackageInfo): Promise<PackageHealth> {
|
|
129
|
-
// Check build status
|
|
130
|
-
const buildStatus = await this.checkBuildStatus(pkg);
|
|
131
|
-
|
|
132
|
-
// Check test coverage
|
|
133
|
-
const testCoverage = await this.checkTestCoverage(pkg);
|
|
134
|
-
|
|
135
|
-
// Check lint status
|
|
136
|
-
const lintStatus = await this.checkLintStatus(pkg);
|
|
137
|
-
|
|
138
|
-
// Check security audit
|
|
139
|
-
const securityAudit = await this.checkSecurityAudit(pkg);
|
|
140
|
-
|
|
141
|
-
return calculatePackageHealth(
|
|
142
|
-
buildStatus,
|
|
143
|
-
testCoverage,
|
|
144
|
-
lintStatus,
|
|
145
|
-
securityAudit
|
|
146
|
-
);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Checks if a package builds successfully
|
|
151
|
-
*/
|
|
152
|
-
async checkBuildStatus(
|
|
153
|
-
pkg: PackageInfo
|
|
154
|
-
): Promise<PackageHealth['buildStatus']> {
|
|
155
|
-
try {
|
|
156
|
-
if (pkg.scripts.build) {
|
|
157
|
-
// Try to run build command
|
|
158
|
-
execSync('npm run build', {
|
|
159
|
-
cwd: pkg.path,
|
|
160
|
-
stdio: 'pipe',
|
|
161
|
-
timeout: 30000,
|
|
162
|
-
});
|
|
163
|
-
return 'success';
|
|
164
|
-
}
|
|
165
|
-
return 'unknown';
|
|
166
|
-
} catch (error) {
|
|
167
|
-
return 'failed';
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Checks test coverage for a package
|
|
173
|
-
*/
|
|
174
|
-
// async checkTestCoverage(pkg: PackageInfo): Promise<number> {
|
|
175
|
-
// try {
|
|
176
|
-
// if (pkg.scripts.test) {
|
|
177
|
-
// // This would typically run tests and parse coverage reports
|
|
178
|
-
// // For now, return a mock value
|
|
179
|
-
// return Math.floor(Math.random() * 100);
|
|
180
|
-
// }
|
|
181
|
-
// return 0;
|
|
182
|
-
// } catch (error) {
|
|
183
|
-
// return 0;
|
|
184
|
-
// }
|
|
185
|
-
// }
|
|
186
|
-
async checkTestCoverage(pkg: PackageInfo): Promise<number> {
|
|
187
|
-
try {
|
|
188
|
-
// First, check for existing coverage reports
|
|
189
|
-
const coveragePaths = [
|
|
190
|
-
path.join(pkg.path, 'coverage', 'coverage-summary.json'),
|
|
191
|
-
path.join(pkg.path, 'coverage', 'lcov.info'),
|
|
192
|
-
path.join(pkg.path, 'coverage', 'clover.xml'),
|
|
193
|
-
path.join(pkg.path, 'coverage.json'),
|
|
194
|
-
];
|
|
195
|
-
|
|
196
|
-
// Look for any coverage file that exists
|
|
197
|
-
for (const coveragePath of coveragePaths) {
|
|
198
|
-
if (fs.existsSync(coveragePath)) {
|
|
199
|
-
if (coveragePath.endsWith('coverage-summary.json')) {
|
|
200
|
-
try {
|
|
201
|
-
const coverage = JSON.parse(
|
|
202
|
-
fs.readFileSync(coveragePath, 'utf8')
|
|
203
|
-
);
|
|
204
|
-
return (
|
|
205
|
-
coverage.total?.lines?.pct ||
|
|
206
|
-
coverage.total?.statements?.pct ||
|
|
207
|
-
0
|
|
208
|
-
);
|
|
209
|
-
} catch (error) {
|
|
210
|
-
AppLogger.warn(`Error parsing coverage file for ${pkg.name}`);
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
// If we find any coverage file but can't parse it, assume coverage exists
|
|
214
|
-
return 0; // Default coverage if files exist but can't parse
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
// If no coverage files found and package has test script
|
|
219
|
-
if (pkg.scripts.test) {
|
|
220
|
-
// Return a reasonable default based on whether tests are likely to have coverage
|
|
221
|
-
const hasCoverageSetup =
|
|
222
|
-
pkg.scripts.test.includes('--coverage') ||
|
|
223
|
-
pkg.scripts.test.includes('coverage') ||
|
|
224
|
-
pkg.devDependencies?.jest ||
|
|
225
|
-
pkg.devDependencies?.nyc ||
|
|
226
|
-
pkg.devDependencies?.['@types/jest'];
|
|
227
|
-
|
|
228
|
-
return hasCoverageSetup ? 30 : 0; // Reasonable defaults
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
return 0;
|
|
232
|
-
} catch (error) {
|
|
233
|
-
AppLogger.warn(`Error checking coverage for ${pkg.name}`);
|
|
234
|
-
return 0;
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
/**
|
|
239
|
-
* Checks lint status for a package
|
|
240
|
-
*/
|
|
241
|
-
async checkLintStatus(
|
|
242
|
-
pkg: PackageInfo
|
|
243
|
-
): Promise<PackageHealth['lintStatus']> {
|
|
244
|
-
try {
|
|
245
|
-
if (pkg.scripts.lint) {
|
|
246
|
-
// Try to run lint command
|
|
247
|
-
execSync('npm run lint', {
|
|
248
|
-
cwd: pkg.path,
|
|
249
|
-
stdio: 'pipe',
|
|
250
|
-
timeout: 10000,
|
|
251
|
-
});
|
|
252
|
-
return 'pass';
|
|
253
|
-
}
|
|
254
|
-
return 'unknown';
|
|
255
|
-
} catch (error) {
|
|
256
|
-
return 'fail';
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
/**
|
|
261
|
-
* Checks security audit for a package
|
|
262
|
-
*/
|
|
263
|
-
async checkSecurityAudit(
|
|
264
|
-
pkg: PackageInfo
|
|
265
|
-
): Promise<PackageHealth['securityAudit']> {
|
|
266
|
-
try {
|
|
267
|
-
// Run npm audit
|
|
268
|
-
const result = execSync('npm audit --json', {
|
|
269
|
-
cwd: pkg.path,
|
|
270
|
-
stdio: 'pipe',
|
|
271
|
-
timeout: 15000,
|
|
272
|
-
});
|
|
273
|
-
|
|
274
|
-
const audit = JSON.parse(result.toString());
|
|
275
|
-
return audit.metadata.vulnerabilities.total === 0 ? 'pass' : 'fail';
|
|
276
|
-
} catch (error) {
|
|
277
|
-
return 'unknown';
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
/**
|
|
282
|
-
* Finds packages with outdated dependencies
|
|
283
|
-
*/
|
|
284
|
-
private findOutdatedPackages(packages: PackageInfo[]): string[] {
|
|
285
|
-
const outdated: string[] = [];
|
|
286
|
-
|
|
287
|
-
for (const pkg of packages) {
|
|
288
|
-
const outdatedDeps = checkOutdatedDependencies(pkg);
|
|
289
|
-
if (outdatedDeps.length > 0) {
|
|
290
|
-
outdated.push(pkg.name);
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
return outdated;
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
/**
|
|
298
|
-
* Gets the last modified date of a package
|
|
299
|
-
*/
|
|
300
|
-
private getLastModified(packagePath: string): Date {
|
|
301
|
-
try {
|
|
302
|
-
const stats = fs.statSync(packagePath);
|
|
303
|
-
return stats.mtime;
|
|
304
|
-
} catch (error) {
|
|
305
|
-
return new Date();
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
/**
|
|
310
|
-
* Gets git information for a package
|
|
311
|
-
*/
|
|
312
|
-
private async getGitInfo(
|
|
313
|
-
packagePath: string
|
|
314
|
-
): Promise<PackageReport['gitInfo'] | undefined> {
|
|
315
|
-
try {
|
|
316
|
-
// Check if this is a git repository
|
|
317
|
-
const gitPath = path.join(packagePath, '.git');
|
|
318
|
-
if (!fs.existsSync(gitPath)) {
|
|
319
|
-
return undefined;
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
// Get last commit info
|
|
323
|
-
const lastCommit = execSync('git rev-parse HEAD', {
|
|
324
|
-
cwd: packagePath,
|
|
325
|
-
stdio: 'pipe',
|
|
326
|
-
})
|
|
327
|
-
.toString()
|
|
328
|
-
.trim();
|
|
329
|
-
|
|
330
|
-
const lastCommitDate = new Date(
|
|
331
|
-
execSync('git log -1 --format=%cd', {
|
|
332
|
-
cwd: packagePath,
|
|
333
|
-
stdio: 'pipe',
|
|
334
|
-
})
|
|
335
|
-
.toString()
|
|
336
|
-
.trim()
|
|
337
|
-
);
|
|
338
|
-
|
|
339
|
-
const author = execSync('git log -1 --format=%an', {
|
|
340
|
-
cwd: packagePath,
|
|
341
|
-
stdio: 'pipe',
|
|
342
|
-
})
|
|
343
|
-
.toString()
|
|
344
|
-
.trim();
|
|
345
|
-
|
|
346
|
-
const branch = execSync('git branch --show-current', {
|
|
347
|
-
cwd: packagePath,
|
|
348
|
-
stdio: 'pipe',
|
|
349
|
-
})
|
|
350
|
-
.toString()
|
|
351
|
-
.trim();
|
|
352
|
-
|
|
353
|
-
return {
|
|
354
|
-
lastCommit: lastCommit.substring(0, 7),
|
|
355
|
-
lastCommitDate,
|
|
356
|
-
author,
|
|
357
|
-
branch,
|
|
358
|
-
};
|
|
359
|
-
} catch (error) {
|
|
360
|
-
return undefined;
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
/**
|
|
365
|
-
* Scans for specific file types in packages
|
|
366
|
-
*/
|
|
367
|
-
scanForFileTypes(fileTypes: string[]): Record<string, string[]> {
|
|
368
|
-
const results: Record<string, string[]> = {};
|
|
369
|
-
|
|
370
|
-
for (const fileType of fileTypes) {
|
|
371
|
-
results[fileType] = [];
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
const packages = scanMonorepo(this.rootDir);
|
|
375
|
-
|
|
376
|
-
for (const pkg of packages) {
|
|
377
|
-
this.scanPackageForFiles(pkg.path, fileTypes, results);
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
return results;
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
/**
|
|
384
|
-
* Recursively scans a package directory for specific file types
|
|
385
|
-
*/
|
|
386
|
-
private scanPackageForFiles(
|
|
387
|
-
packagePath: string,
|
|
388
|
-
fileTypes: string[],
|
|
389
|
-
results: Record<string, string[]>
|
|
390
|
-
): void {
|
|
391
|
-
try {
|
|
392
|
-
const items = fs.readdirSync(packagePath, { withFileTypes: true });
|
|
393
|
-
|
|
394
|
-
for (const item of items) {
|
|
395
|
-
const fullPath = path.join(packagePath, item.name);
|
|
396
|
-
|
|
397
|
-
if (item.isDirectory()) {
|
|
398
|
-
// Skip certain directories
|
|
399
|
-
if (!['node_modules', 'dist', 'build', '.git'].includes(item.name)) {
|
|
400
|
-
this.scanPackageForFiles(fullPath, fileTypes, results);
|
|
401
|
-
}
|
|
402
|
-
} else {
|
|
403
|
-
// Check file extension
|
|
404
|
-
const ext = path.extname(item.name);
|
|
405
|
-
if (fileTypes.includes(ext)) {
|
|
406
|
-
results[ext].push(fullPath);
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
} catch (error) {
|
|
411
|
-
// Skip directories we can't read
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
/**
|
|
416
|
-
* Gets cache value if not expired
|
|
417
|
-
*/
|
|
418
|
-
private getFromCache(key: string): any {
|
|
419
|
-
const cached = this.cache.get(key);
|
|
420
|
-
if (cached && Date.now() - cached.timestamp < this.cacheExpiry) {
|
|
421
|
-
return cached.data;
|
|
422
|
-
}
|
|
423
|
-
return null;
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
/**
|
|
427
|
-
* Sets cache value with timestamp
|
|
428
|
-
*/
|
|
429
|
-
private setCache(key: string, data: any): void {
|
|
430
|
-
this.cache.set(key, {
|
|
431
|
-
data,
|
|
432
|
-
timestamp: Date.now(),
|
|
433
|
-
});
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
/**
|
|
437
|
-
* Clears the cache
|
|
438
|
-
*/
|
|
439
|
-
clearCache(): void {
|
|
440
|
-
this.cache.clear();
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
/**
|
|
444
|
-
* Exports scan results to various formats
|
|
445
|
-
*/
|
|
446
|
-
exportResults(results: ScanResult, format: 'json' | 'csv' | 'html'): string {
|
|
447
|
-
switch (format) {
|
|
448
|
-
case 'json':
|
|
449
|
-
return JSON.stringify(results, null, 2);
|
|
450
|
-
case 'csv':
|
|
451
|
-
return this.exportToCSV(results);
|
|
452
|
-
case 'html':
|
|
453
|
-
return this.exportToHTML(results);
|
|
454
|
-
default:
|
|
455
|
-
throw new Error(`Unsupported format: ${format}`);
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
/**
|
|
460
|
-
* Exports results to CSV format
|
|
461
|
-
*/
|
|
462
|
-
private exportToCSV(results: ScanResult): string {
|
|
463
|
-
const headers = [
|
|
464
|
-
'Package',
|
|
465
|
-
'Type',
|
|
466
|
-
'Version',
|
|
467
|
-
'Dependencies',
|
|
468
|
-
'Health Score',
|
|
469
|
-
];
|
|
470
|
-
const rows = results.packages.map(pkg => [
|
|
471
|
-
pkg.name,
|
|
472
|
-
pkg.type,
|
|
473
|
-
pkg.version,
|
|
474
|
-
Object.keys(pkg.dependencies).length,
|
|
475
|
-
'N/A', // Would need health calculation
|
|
476
|
-
]);
|
|
477
|
-
|
|
478
|
-
return [headers, ...rows]
|
|
479
|
-
.map(row => row.map(cell => `"${cell}"`).join(','))
|
|
480
|
-
.join('\n');
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
/**
|
|
484
|
-
* Exports results to HTML format
|
|
485
|
-
*/
|
|
486
|
-
private exportToHTML(results: ScanResult): string {
|
|
487
|
-
return `
|
|
488
|
-
<!DOCTYPE html>
|
|
489
|
-
<html>
|
|
490
|
-
<head>
|
|
491
|
-
<title>Monorepo Scan Report</title>
|
|
492
|
-
<style>
|
|
493
|
-
body { font-family: Arial, sans-serif; margin: 20px; }
|
|
494
|
-
table { border-collapse: collapse; width: 100%; }
|
|
495
|
-
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
|
|
496
|
-
th { background-color: #f2f2f2; }
|
|
497
|
-
</style>
|
|
498
|
-
</head>
|
|
499
|
-
<body>
|
|
500
|
-
<h1>Monorepo Scan Report</h1>
|
|
501
|
-
<p>Generated: ${results.scanTimestamp}</p>
|
|
502
|
-
<p>Duration: ${results.scanDuration}ms</p>
|
|
503
|
-
|
|
504
|
-
<h2>Summary</h2>
|
|
505
|
-
<ul>
|
|
506
|
-
<li>Total Packages: ${results.stats.totalPackages}</li>
|
|
507
|
-
<li>Applications: ${results.stats.apps}</li>
|
|
508
|
-
<li>Libraries: ${results.stats.libraries}</li>
|
|
509
|
-
<li>Tools: ${results.stats.tools}</li>
|
|
510
|
-
</ul>
|
|
511
|
-
|
|
512
|
-
<h2>Packages</h2>
|
|
513
|
-
<table>
|
|
514
|
-
<tr>
|
|
515
|
-
<th>Name</th>
|
|
516
|
-
<th>Type</th>
|
|
517
|
-
<th>Version</th>
|
|
518
|
-
<th>Dependencies</th>
|
|
519
|
-
</tr>
|
|
520
|
-
${results.packages
|
|
521
|
-
.map(
|
|
522
|
-
pkg => `
|
|
523
|
-
<tr>
|
|
524
|
-
<td>${pkg.name}</td>
|
|
525
|
-
<td>${pkg.type}</td>
|
|
526
|
-
<td>${pkg.version}</td>
|
|
527
|
-
<td>${Object.keys(pkg.dependencies).length}</td>
|
|
528
|
-
</tr>
|
|
529
|
-
`
|
|
530
|
-
)
|
|
531
|
-
.join('')}
|
|
532
|
-
</table>
|
|
533
|
-
</body>
|
|
534
|
-
</html>
|
|
535
|
-
`;
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
// Export default instance
|
|
540
|
-
export const scanner = new MonorepoScanner();
|
|
541
|
-
|
|
542
|
-
// Export convenience functions
|
|
543
|
-
export async function quickScan(): Promise<ScanResult> {
|
|
544
|
-
return scanner.scan();
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
export async function generateReports(): Promise<PackageReport[]> {
|
|
548
|
-
return scanner.generatePackageReports();
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
export function scanForFiles(fileTypes: string[]): Record<string, string[]> {
|
|
552
|
-
return scanner.scanForFileTypes(fileTypes);
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
// Fix these function signatures - they should accept single PackageInfo objects
|
|
556
|
-
export async function funCheckBuildStatus(
|
|
557
|
-
pkg: PackageInfo
|
|
558
|
-
): Promise<PackageHealth['buildStatus']> {
|
|
559
|
-
return scanner.checkBuildStatus(pkg);
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
export async function funCheckTestCoverage(pkg: PackageInfo): Promise<number> {
|
|
563
|
-
return scanner.checkTestCoverage(pkg);
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
export async function funCheckLintStatus(
|
|
567
|
-
pkg: PackageInfo
|
|
568
|
-
): Promise<PackageHealth['lintStatus']> {
|
|
569
|
-
return scanner.checkLintStatus(pkg);
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
export async function funCheckSecurityAudit(
|
|
573
|
-
pkg: PackageInfo
|
|
574
|
-
): Promise<PackageHealth['securityAudit']> {
|
|
575
|
-
return scanner.checkSecurityAudit(pkg);
|
|
576
|
-
}
|