@manojkmfsi/monodog 1.1.6 → 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 +12 -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
package/src/utils/utilities.ts
DELETED
|
@@ -1,427 +0,0 @@
|
|
|
1
|
-
// import { Package } from '@prisma/client';
|
|
2
|
-
import * as fs from 'fs';
|
|
3
|
-
import path from 'path';
|
|
4
|
-
import { appConfig } from '../config-loader';
|
|
5
|
-
import { AppLogger } from '../middleware/logger';
|
|
6
|
-
import {calculatePackageHealth} from './health-utils';
|
|
7
|
-
import * as yaml from 'js-yaml';
|
|
8
|
-
|
|
9
|
-
import type { PackageInfo, DependencyInfo, MonorepoStats } from '../types';
|
|
10
|
-
|
|
11
|
-
export type { PackageInfo, DependencyInfo, MonorepoStats } from '../types';
|
|
12
|
-
export { type PackageHealth } from '../types';
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Resolves simple workspace globs (like 'packages/*', 'apps/*') into actual package directory paths.
|
|
16
|
-
* Note: This implementation only handles the 'folder/*' pattern and is not a full glob resolver.
|
|
17
|
-
*/
|
|
18
|
-
export function resolveWorkspaceGlobs(rootDir: string, globs: string[]): string[] {
|
|
19
|
-
const resolvedPaths: string[] = [];
|
|
20
|
-
|
|
21
|
-
for (const glob of globs) {
|
|
22
|
-
if (glob.endsWith('/*')) {
|
|
23
|
-
const baseDirName = glob.slice(0, -2); // e.g., 'packages'
|
|
24
|
-
const baseDirPath = path.join(rootDir, baseDirName);
|
|
25
|
-
|
|
26
|
-
if (fs.existsSync(baseDirPath) && fs.statSync(baseDirPath).isDirectory()) {
|
|
27
|
-
const subDirs = fs.readdirSync(baseDirPath, { withFileTypes: true })
|
|
28
|
-
.filter(dirent => dirent.isDirectory())
|
|
29
|
-
.map(dirent => path.join(baseDirName, dirent.name)); // e.g., 'packages/my-utils'
|
|
30
|
-
|
|
31
|
-
resolvedPaths.push(...subDirs);
|
|
32
|
-
}
|
|
33
|
-
} else {
|
|
34
|
-
// Handle non-glob paths (e.g., 'packages/my-package') if it's explicitly listed
|
|
35
|
-
const directPath = path.join(rootDir, glob);
|
|
36
|
-
if (fs.existsSync(directPath) && fs.statSync(directPath).isDirectory()) {
|
|
37
|
-
resolvedPaths.push(glob);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
return resolvedPaths;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Parses pnpm-workspace.yaml and extracts workspace globs
|
|
46
|
-
*/
|
|
47
|
-
function getWorkspacesFromPnpmYaml(rootDir: string): string[] | undefined {
|
|
48
|
-
const workspaceYamlPath = path.join(rootDir, 'pnpm-workspace.yaml');
|
|
49
|
-
if (!fs.existsSync(workspaceYamlPath)) {
|
|
50
|
-
return undefined;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
try {
|
|
54
|
-
const yamlContent = fs.readFileSync(workspaceYamlPath, 'utf-8');
|
|
55
|
-
const yamlData = yaml.load(yamlContent) as { packages?: (string | string[]) };
|
|
56
|
-
|
|
57
|
-
if (yamlData && yamlData.packages) {
|
|
58
|
-
// Filter out exclusion patterns (lines starting with '!')
|
|
59
|
-
const packages = Array.isArray(yamlData.packages)
|
|
60
|
-
? yamlData.packages.filter((pkg) => typeof pkg === 'string' && !pkg.startsWith('!'))
|
|
61
|
-
: [];
|
|
62
|
-
|
|
63
|
-
if (packages.length > 0) {
|
|
64
|
-
return packages;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
} catch (e) {
|
|
68
|
-
AppLogger.error(`Error parsing pnpm-workspace.yaml at ${workspaceYamlPath}:`, e as Error);
|
|
69
|
-
}
|
|
70
|
-
return undefined;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Reads workspace configuration from package.json or pnpm-workspace.yaml
|
|
75
|
-
* Priority: package.json (if exists) -> pnpm-workspace.yaml
|
|
76
|
-
*/
|
|
77
|
-
export function getWorkspacesFromRoot(rootDir: string): string[] | undefined {
|
|
78
|
-
const packageJsonPath = path.join(rootDir, 'package.json');
|
|
79
|
-
|
|
80
|
-
// Try package.json first
|
|
81
|
-
if (fs.existsSync(packageJsonPath)) {
|
|
82
|
-
try {
|
|
83
|
-
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
84
|
-
|
|
85
|
-
// Handle both standard array and object format (used by yarn/pnpm)
|
|
86
|
-
if (Array.isArray(packageJson.workspaces)) {
|
|
87
|
-
AppLogger.info('Workspace configuration found in package.json');
|
|
88
|
-
return packageJson.workspaces;
|
|
89
|
-
} else if (packageJson.workspaces && Array.isArray(packageJson.workspaces.packages)) {
|
|
90
|
-
AppLogger.info('Workspace configuration found in package.json');
|
|
91
|
-
return packageJson.workspaces.packages;
|
|
92
|
-
}
|
|
93
|
-
} catch (e) {
|
|
94
|
-
AppLogger.error(`Error parsing package.json at ${packageJsonPath}. Attempting to read pnpm-workspace.yaml...`, e as Error);
|
|
95
|
-
}
|
|
96
|
-
} else {
|
|
97
|
-
AppLogger.warn(`No package.json found at root directory: ${rootDir}`);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// Fallback to pnpm-workspace.yaml
|
|
101
|
-
const pnpmWorkspaces = getWorkspacesFromPnpmYaml(rootDir);
|
|
102
|
-
if (pnpmWorkspaces && pnpmWorkspaces.length > 0) {
|
|
103
|
-
AppLogger.info('Workspace configuration found in pnpm-workspace.yaml');
|
|
104
|
-
return pnpmWorkspaces;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
AppLogger.warn('No workspace configuration found in package.json or pnpm-workspace.yaml');
|
|
108
|
-
return undefined;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Scans the monorepo and returns information about all packages
|
|
113
|
-
*/
|
|
114
|
-
function scanMonorepo(rootDir: string): PackageInfo[] {
|
|
115
|
-
const packages: PackageInfo[] = [];
|
|
116
|
-
AppLogger.debug('rootDir: ' + rootDir);
|
|
117
|
-
const workspacesGlobs = appConfig.workspaces;
|
|
118
|
-
// Use provided workspaces globs if given, otherwise attempt to detect from root package.json or pnpm-workspace.yaml
|
|
119
|
-
const detectedWorkspacesGlobs = workspacesGlobs.length > 0 ? workspacesGlobs : getWorkspacesFromRoot(rootDir);
|
|
120
|
-
if (detectedWorkspacesGlobs && detectedWorkspacesGlobs.length > 0) {
|
|
121
|
-
if (workspacesGlobs.length) {
|
|
122
|
-
AppLogger.info(`Using provided workspaces globs: ${detectedWorkspacesGlobs.join(', ')}`);
|
|
123
|
-
} else {
|
|
124
|
-
AppLogger.info(`Detected Monorepo Workspaces Globs: ${detectedWorkspacesGlobs.join(', ')}`);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// 1. Resolve the globs into concrete package directory paths
|
|
128
|
-
const resolvedPackagePaths = resolveWorkspaceGlobs(rootDir, detectedWorkspacesGlobs);
|
|
129
|
-
|
|
130
|
-
AppLogger.debug(`Resolved package directories (Total ${resolvedPackagePaths.length})`);
|
|
131
|
-
if (resolvedPackagePaths.length < workspacesGlobs.length) {
|
|
132
|
-
AppLogger.warn('Some workspaces globs provided are invalid.');
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// 2. Integration of the requested loop structure for package scanning
|
|
136
|
-
for (const workspacePath of resolvedPackagePaths) {
|
|
137
|
-
const fullPackagePath = path.join(rootDir, workspacePath);
|
|
138
|
-
// The package name would be read from the package.json inside this path
|
|
139
|
-
const packageName = path.basename(fullPackagePath);
|
|
140
|
-
|
|
141
|
-
AppLogger.debug(`- Scanning path: ${workspacePath} (Package: ${packageName})`);
|
|
142
|
-
|
|
143
|
-
const packageInfo = parsePackageInfo(fullPackagePath, packageName);
|
|
144
|
-
if (packageInfo) {
|
|
145
|
-
packages.push(packageInfo);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
} else {
|
|
149
|
-
AppLogger.warn('No workspace globs provided or detected. Returning empty package list.');
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
return packages;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
/*** Parses package.json and determines package type */
|
|
156
|
-
export function parsePackageInfo(
|
|
157
|
-
packagePath: string,
|
|
158
|
-
packageName: string,
|
|
159
|
-
forcedType?: 'app' | 'lib' | 'tool'
|
|
160
|
-
): PackageInfo | null {
|
|
161
|
-
const packageJsonPath = path.join(packagePath, 'package.json');
|
|
162
|
-
|
|
163
|
-
if (!fs.existsSync(packageJsonPath)) {
|
|
164
|
-
return null;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
try {
|
|
168
|
-
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
169
|
-
|
|
170
|
-
// Determine package type
|
|
171
|
-
let packageType: 'app' | 'lib' | 'tool' = 'lib';
|
|
172
|
-
if (forcedType) {
|
|
173
|
-
packageType = forcedType;
|
|
174
|
-
} else if (packageJson.scripts && packageJson.scripts.start) {
|
|
175
|
-
packageType = 'app';
|
|
176
|
-
} else if (packageJson.keywords && packageJson.keywords.includes('tool')) {
|
|
177
|
-
packageType = 'tool';
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
return {
|
|
181
|
-
name: packageJson.name || packageName,
|
|
182
|
-
version: packageJson.version || '0.0.0',
|
|
183
|
-
type: packageType,
|
|
184
|
-
path: packagePath,
|
|
185
|
-
dependencies: packageJson.dependencies || {},
|
|
186
|
-
devDependencies: packageJson.devDependencies || {},
|
|
187
|
-
peerDependencies: packageJson.peerDependencies || {},
|
|
188
|
-
scripts: packageJson.scripts || {},
|
|
189
|
-
maintainers: packageJson.maintainers || [],
|
|
190
|
-
description: packageJson.description,
|
|
191
|
-
license: packageJson.license,
|
|
192
|
-
repository: packageJson.repository || {},
|
|
193
|
-
};
|
|
194
|
-
} catch (error) {
|
|
195
|
-
AppLogger.error(`Error parsing package.json for ${packageName}`, error as Error);
|
|
196
|
-
return null;
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
/**
|
|
201
|
-
* Generates comprehensive monorepo statistics
|
|
202
|
-
*/
|
|
203
|
-
function generateMonorepoStats(packages: PackageInfo[]): MonorepoStats {
|
|
204
|
-
const stats: MonorepoStats = {
|
|
205
|
-
totalPackages: packages.length,
|
|
206
|
-
apps: packages.filter(p => p.type === 'app').length,
|
|
207
|
-
libraries: packages.filter(p => p.type === 'lib').length,
|
|
208
|
-
tools: packages.filter(p => p.type === 'tool').length,
|
|
209
|
-
healthyPackages: 0,
|
|
210
|
-
warningPackages: 0,
|
|
211
|
-
errorPackages: 0,
|
|
212
|
-
outdatedDependencies: 0,
|
|
213
|
-
totalDependencies: 0,
|
|
214
|
-
};
|
|
215
|
-
|
|
216
|
-
// Calculate dependency counts
|
|
217
|
-
packages.forEach(pkg => {
|
|
218
|
-
stats.totalDependencies += Object.keys(pkg.dependencies).length;
|
|
219
|
-
stats.totalDependencies += Object.keys(pkg.devDependencies).length;
|
|
220
|
-
stats.totalDependencies += Object.keys(pkg.peerDependencies ?? {}).length;
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
return stats;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
/**
|
|
227
|
-
* Finds circular dependencies in the monorepo
|
|
228
|
-
*/
|
|
229
|
-
function findCircularDependencies(packages: PackageInfo[]): string[][] {
|
|
230
|
-
const graph = new Map<string, string[]>();
|
|
231
|
-
const visited = new Set<string>();
|
|
232
|
-
const recursionStack = new Set<string>();
|
|
233
|
-
const circularDeps: string[][] = [];
|
|
234
|
-
|
|
235
|
-
// Build dependency graph
|
|
236
|
-
packages.forEach(pkg => {
|
|
237
|
-
graph.set(pkg.name, Object.keys(pkg.dependencies));
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
function dfs(node: string, path: string[]): void {
|
|
241
|
-
if (recursionStack.has(node)) {
|
|
242
|
-
const cycleStart = path.indexOf(node);
|
|
243
|
-
circularDeps.push(path.slice(cycleStart));
|
|
244
|
-
return;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
if (visited.has(node)) {
|
|
248
|
-
return;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
visited.add(node);
|
|
252
|
-
recursionStack.add(node);
|
|
253
|
-
path.push(node);
|
|
254
|
-
|
|
255
|
-
const dependencies = graph.get(node) || [];
|
|
256
|
-
for (const dep of dependencies) {
|
|
257
|
-
if (graph.has(dep)) {
|
|
258
|
-
dfs(dep, [...path]);
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
recursionStack.delete(node);
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
for (const node of graph.keys()) {
|
|
266
|
-
if (!visited.has(node)) {
|
|
267
|
-
dfs(node, []);
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
return circularDeps;
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
/**
|
|
275
|
-
* Generates a dependency graph for visualization
|
|
276
|
-
*/
|
|
277
|
-
function generateDependencyGraph(packages: PackageInfo[]) {
|
|
278
|
-
const nodes = packages.map(pkg => ({
|
|
279
|
-
// id: pkg.name,
|
|
280
|
-
label: pkg.name,
|
|
281
|
-
type: pkg.type,
|
|
282
|
-
version: pkg.version,
|
|
283
|
-
dependencies: Object.keys(pkg.dependencies).length,
|
|
284
|
-
}));
|
|
285
|
-
|
|
286
|
-
const edges: Array<{ from: string; to: string; type: string }> = [];
|
|
287
|
-
|
|
288
|
-
packages.forEach(pkg => {
|
|
289
|
-
Object.keys(pkg.dependencies).forEach(depName => {
|
|
290
|
-
// Only include internal dependencies
|
|
291
|
-
if (packages.some(p => p.name === depName)) {
|
|
292
|
-
edges.push({
|
|
293
|
-
from: pkg.name,
|
|
294
|
-
to: depName,
|
|
295
|
-
type: 'internal',
|
|
296
|
-
});
|
|
297
|
-
}
|
|
298
|
-
});
|
|
299
|
-
});
|
|
300
|
-
|
|
301
|
-
return { nodes, edges };
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
/**
|
|
305
|
-
* Checks if a package has outdated dependencies
|
|
306
|
-
*/
|
|
307
|
-
function checkOutdatedDependencies(packageInfo: PackageInfo): DependencyInfo[] {
|
|
308
|
-
const outdated: DependencyInfo[] = [];
|
|
309
|
-
|
|
310
|
-
// This would typically involve checking against npm registry
|
|
311
|
-
// For now, we'll simulate with some basic checks
|
|
312
|
-
Object.entries(packageInfo.dependencies).forEach(([name, version]) => {
|
|
313
|
-
if (version.startsWith('^') || version.startsWith('~')) {
|
|
314
|
-
// Could be outdated, would need registry check
|
|
315
|
-
outdated.push({
|
|
316
|
-
name,
|
|
317
|
-
version: version,
|
|
318
|
-
status: 'unknown',
|
|
319
|
-
type: 'dependency',
|
|
320
|
-
});
|
|
321
|
-
}
|
|
322
|
-
});
|
|
323
|
-
|
|
324
|
-
return outdated;
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
/**
|
|
328
|
-
* Gets package size information
|
|
329
|
-
*/
|
|
330
|
-
function getPackageSize(packagePath: string): {
|
|
331
|
-
size: number;
|
|
332
|
-
files: number;
|
|
333
|
-
} {
|
|
334
|
-
try {
|
|
335
|
-
let totalSize = 0;
|
|
336
|
-
let fileCount = 0;
|
|
337
|
-
|
|
338
|
-
const calculateSize = (dirPath: string): void => {
|
|
339
|
-
const items = fs.readdirSync(dirPath, { withFileTypes: true });
|
|
340
|
-
|
|
341
|
-
for (const item of items) {
|
|
342
|
-
const fullPath = path.join(dirPath, item.name);
|
|
343
|
-
|
|
344
|
-
if (item.isDirectory()) {
|
|
345
|
-
// Skip node_modules and other build artifacts
|
|
346
|
-
if (!['node_modules', 'dist', 'build', '.git'].includes(item.name)) {
|
|
347
|
-
calculateSize(fullPath);
|
|
348
|
-
}
|
|
349
|
-
} else {
|
|
350
|
-
try {
|
|
351
|
-
const stats = fs.statSync(fullPath);
|
|
352
|
-
totalSize += stats.size;
|
|
353
|
-
fileCount++;
|
|
354
|
-
} catch (error) {
|
|
355
|
-
// Skip files we can't read
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
};
|
|
360
|
-
|
|
361
|
-
calculateSize(packagePath);
|
|
362
|
-
|
|
363
|
-
return {
|
|
364
|
-
size: totalSize,
|
|
365
|
-
files: fileCount,
|
|
366
|
-
};
|
|
367
|
-
} catch (error) {
|
|
368
|
-
return { size: 0, files: 0 };
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
/**
|
|
373
|
-
* Find the monorepo root by looking for package.json with workspaces or pnpm-workspace.yaml
|
|
374
|
-
*/
|
|
375
|
-
function findMonorepoRoot(): string {
|
|
376
|
-
let currentDir = __dirname;
|
|
377
|
-
|
|
378
|
-
while (currentDir !== path.parse(currentDir).root) {
|
|
379
|
-
const packageJsonPath = path.join(currentDir, 'package.json');
|
|
380
|
-
const pnpmWorkspacePath = path.join(currentDir, 'pnpm-workspace.yaml');
|
|
381
|
-
|
|
382
|
-
// Check if this directory has package.json with workspaces or pnpm-workspace.yaml
|
|
383
|
-
if (fs.existsSync(packageJsonPath)) {
|
|
384
|
-
try {
|
|
385
|
-
const packageJson = JSON.parse(
|
|
386
|
-
fs.readFileSync(packageJsonPath, 'utf8')
|
|
387
|
-
);
|
|
388
|
-
// If it has workspaces or is the root monorepo package
|
|
389
|
-
if (packageJson.workspaces || fs.existsSync(pnpmWorkspacePath)) {
|
|
390
|
-
AppLogger.debug('Found monorepo root: ' + currentDir);
|
|
391
|
-
return currentDir;
|
|
392
|
-
}
|
|
393
|
-
} catch (error) {
|
|
394
|
-
// Continue searching if package.json is invalid
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
// Check if we're at the git root
|
|
399
|
-
const gitPath = path.join(currentDir, '.git');
|
|
400
|
-
if (fs.existsSync(gitPath)) {
|
|
401
|
-
AppLogger.debug('Found git root (likely monorepo root): ' + currentDir);
|
|
402
|
-
return currentDir;
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
// Go up one directory
|
|
406
|
-
const parentDir = path.dirname(currentDir);
|
|
407
|
-
if (parentDir === currentDir) break; // Prevent infinite loop
|
|
408
|
-
currentDir = parentDir;
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
// Fallback to process.cwd() if we can't find the root
|
|
412
|
-
AppLogger.warn(
|
|
413
|
-
'Could not find monorepo root, using process.cwd(): ' + process.cwd()
|
|
414
|
-
);
|
|
415
|
-
return process.cwd();
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
export {
|
|
419
|
-
scanMonorepo,
|
|
420
|
-
generateMonorepoStats,
|
|
421
|
-
findCircularDependencies,
|
|
422
|
-
generateDependencyGraph,
|
|
423
|
-
checkOutdatedDependencies,
|
|
424
|
-
getPackageSize,
|
|
425
|
-
calculatePackageHealth,
|
|
426
|
-
findMonorepoRoot,
|
|
427
|
-
};
|