@manojkmfsi/monodog 1.0.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/.eslintrc.cjs +15 -0
- package/.turbo/turbo-build.log +4 -0
- package/CHANGELOG.md +79 -0
- package/LICENCE +21 -0
- package/README.md +55 -0
- package/dist/config-loader.js +116 -0
- package/dist/get-db-url.js +10 -0
- package/dist/gitService.js +242 -0
- package/dist/index.js +1370 -0
- package/dist/serve.js +103 -0
- package/dist/setup.js +155 -0
- package/dist/utils/ci-status.js +446 -0
- package/dist/utils/helpers.js +237 -0
- package/dist/utils/monorepo-scanner.js +486 -0
- package/dist/utils/utilities.js +414 -0
- package/monodog-conf.example.json +16 -0
- package/monodog-conf.json +16 -0
- package/monodog-dashboard/README.md +58 -0
- package/monodog-dashboard/dist/assets/index-2d967652.js +71 -0
- package/monodog-dashboard/dist/assets/index-504dc418.css +1 -0
- package/monodog-dashboard/dist/index.html +15 -0
- package/package.json +50 -0
- package/prisma/migrations/20251017041048_init/migration.sql +19 -0
- package/prisma/migrations/20251017083007_add_package/migration.sql +21 -0
- package/prisma/migrations/20251021083705_alter_package/migration.sql +37 -0
- package/prisma/migrations/20251022085155_test/migration.sql +2 -0
- package/prisma/migrations/20251022160841_/migration.sql +35 -0
- package/prisma/migrations/20251023130158_rename_column_name/migration.sql +34 -0
- package/prisma/migrations/20251023174837_/migration.sql +34 -0
- package/prisma/migrations/20251023175830_uodate_schema/migration.sql +32 -0
- package/prisma/migrations/20251024103700_add_dependency_info/migration.sql +13 -0
- package/prisma/migrations/20251025192150_add_dependency_info/migration.sql +19 -0
- package/prisma/migrations/20251025192342_add_dependency_info/migration.sql +40 -0
- package/prisma/migrations/20251025204613_add_dependency_info/migration.sql +8 -0
- package/prisma/migrations/20251026071336_add_dependency_info/migration.sql +25 -0
- package/prisma/migrations/20251027062626_add_commit/migration.sql +10 -0
- package/prisma/migrations/20251027062748_add_commit/migration.sql +23 -0
- package/prisma/migrations/20251027092741_add_commit/migration.sql +17 -0
- package/prisma/migrations/20251027112736_add_health_status/migration.sql +16 -0
- package/prisma/migrations/20251027140546_init_packages/migration.sql +16 -0
- package/prisma/migrations/20251029073436_added_package_heath_key/migration.sql +34 -0
- package/prisma/migrations/20251029073830_added_package_health_key/migration.sql +49 -0
- package/prisma/migrations/20251111091920_/migration.sql +16 -0
- package/prisma/migrations/20251211155036_cascade_on_auto_delete/migration.sql +48 -0
- package/prisma/migrations/migration_lock.toml +3 -0
- package/prisma/schema.prisma +114 -0
- package/release.config.js +41 -0
- package/src/config-loader.ts +119 -0
- package/src/get-db-url.ts +11 -0
- package/src/gitService.ts +277 -0
- package/src/index.ts +1554 -0
- package/src/serve.ts +87 -0
- package/src/setup.ts +164 -0
- package/src/types/monorepo-scanner.d.ts +32 -0
- package/src/utils/ci-status.ts +639 -0
- package/src/utils/helpers.js +203 -0
- package/src/utils/helpers.js.map +1 -0
- package/src/utils/helpers.ts +238 -0
- package/src/utils/monorepo-scanner.ts +599 -0
- package/src/utils/utilities.ts +483 -0
- package/tsconfig.json +16 -0
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.resolveWorkspaceGlobs = resolveWorkspaceGlobs;
|
|
40
|
+
exports.getWorkspacesFromRoot = getWorkspacesFromRoot;
|
|
41
|
+
exports.parsePackageInfo = parsePackageInfo;
|
|
42
|
+
exports.scanMonorepo = scanMonorepo;
|
|
43
|
+
exports.generateMonorepoStats = generateMonorepoStats;
|
|
44
|
+
exports.findCircularDependencies = findCircularDependencies;
|
|
45
|
+
exports.generateDependencyGraph = generateDependencyGraph;
|
|
46
|
+
exports.checkOutdatedDependencies = checkOutdatedDependencies;
|
|
47
|
+
exports.getPackageSize = getPackageSize;
|
|
48
|
+
exports.calculatePackageHealth = calculatePackageHealth;
|
|
49
|
+
// import { Package } from '@prisma/client';
|
|
50
|
+
const fs = __importStar(require("fs"));
|
|
51
|
+
const path_1 = __importDefault(require("path"));
|
|
52
|
+
const config_loader_1 = require("../config-loader");
|
|
53
|
+
/**
|
|
54
|
+
* Resolves simple workspace globs (like 'packages/*', 'apps/*') into actual package directory paths.
|
|
55
|
+
* Note: This implementation only handles the 'folder/*' pattern and is not a full glob resolver.
|
|
56
|
+
*/
|
|
57
|
+
function resolveWorkspaceGlobs(rootDir, globs) {
|
|
58
|
+
const resolvedPaths = [];
|
|
59
|
+
for (const glob of globs) {
|
|
60
|
+
if (glob.endsWith('/*')) {
|
|
61
|
+
const baseDirName = glob.slice(0, -2); // e.g., 'packages'
|
|
62
|
+
const baseDirPath = path_1.default.join(rootDir, baseDirName);
|
|
63
|
+
if (fs.existsSync(baseDirPath) && fs.statSync(baseDirPath).isDirectory()) {
|
|
64
|
+
const subDirs = fs.readdirSync(baseDirPath, { withFileTypes: true })
|
|
65
|
+
.filter(dirent => dirent.isDirectory())
|
|
66
|
+
.map(dirent => path_1.default.join(baseDirName, dirent.name)); // e.g., 'packages/my-utils'
|
|
67
|
+
resolvedPaths.push(...subDirs);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
// Handle non-glob paths (e.g., 'packages/my-package') if it's explicitly listed
|
|
72
|
+
const directPath = path_1.default.join(rootDir, glob);
|
|
73
|
+
if (fs.existsSync(directPath) && fs.statSync(directPath).isDirectory()) {
|
|
74
|
+
resolvedPaths.push(glob);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return resolvedPaths;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Reads the root package.json and extracts the 'workspaces' field (array of globs).
|
|
82
|
+
*/
|
|
83
|
+
function getWorkspacesFromRoot(rootDir) {
|
|
84
|
+
const packageJsonPath = path_1.default.join(rootDir, 'package.json');
|
|
85
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
86
|
+
console.warn(`\n⚠️ Warning: No package.json found at root directory: ${rootDir}`);
|
|
87
|
+
return undefined;
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
91
|
+
// Handle both standard array and object format (used by yarn/pnpm)
|
|
92
|
+
if (Array.isArray(packageJson.workspaces)) {
|
|
93
|
+
return packageJson.workspaces;
|
|
94
|
+
}
|
|
95
|
+
else if (packageJson.workspaces && Array.isArray(packageJson.workspaces.packages)) {
|
|
96
|
+
return packageJson.workspaces.packages;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
catch (e) {
|
|
100
|
+
console.error(`\n❌ Error parsing package.json at ${packageJsonPath}. Skipping workspace detection.`);
|
|
101
|
+
}
|
|
102
|
+
return undefined;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Scans the monorepo and returns information about all packages
|
|
106
|
+
*/
|
|
107
|
+
function scanMonorepo(rootDir) {
|
|
108
|
+
const packages = [];
|
|
109
|
+
console.log('rootDir:', rootDir);
|
|
110
|
+
const workspacesGlobs = config_loader_1.appConfig.workspaces;
|
|
111
|
+
// Use provided workspaces globs if given, otherwise attempt to detect from root package.json
|
|
112
|
+
const detectedWorkspacesGlobs = workspacesGlobs.length > 0 ? workspacesGlobs : getWorkspacesFromRoot(rootDir);
|
|
113
|
+
if (detectedWorkspacesGlobs && detectedWorkspacesGlobs.length > 0) {
|
|
114
|
+
if (workspacesGlobs.length) {
|
|
115
|
+
console.log(`\n✅ Using provided workspaces globs: ${detectedWorkspacesGlobs.join(', ')}`);
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
console.log(`\n✅ Detected Monorepo Workspaces Globs: ${detectedWorkspacesGlobs.join(', ')}`);
|
|
119
|
+
}
|
|
120
|
+
// 1. Resolve the globs into concrete package directory paths
|
|
121
|
+
const resolvedPackagePaths = resolveWorkspaceGlobs(rootDir, detectedWorkspacesGlobs);
|
|
122
|
+
console.log(`[DEBUG] Resolved package directories (Total ${resolvedPackagePaths.length}):`);
|
|
123
|
+
console.warn(resolvedPackagePaths.length < workspacesGlobs.length ? 'Some workspaces globs provided are invalid.' : '');
|
|
124
|
+
// 2. Integration of the requested loop structure for package scanning
|
|
125
|
+
for (const workspacePath of resolvedPackagePaths) {
|
|
126
|
+
const fullPackagePath = path_1.default.join(rootDir, workspacePath);
|
|
127
|
+
// The package name would be read from the package.json inside this path
|
|
128
|
+
const packageName = path_1.default.basename(fullPackagePath);
|
|
129
|
+
console.log(`- Scanning path: ${workspacePath} (Package: ${packageName})`);
|
|
130
|
+
const packageInfo = parsePackageInfo(fullPackagePath, packageName);
|
|
131
|
+
if (packageInfo) {
|
|
132
|
+
packages.push(packageInfo);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
console.warn('\n⚠️ No workspace globs provided or detected. Returning empty package list.');
|
|
138
|
+
}
|
|
139
|
+
return packages;
|
|
140
|
+
}
|
|
141
|
+
/*** Parses package.json and determines package type */
|
|
142
|
+
function parsePackageInfo(packagePath, packageName, forcedType) {
|
|
143
|
+
const packageJsonPath = path_1.default.join(packagePath, 'package.json');
|
|
144
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
try {
|
|
148
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
149
|
+
// Determine package type
|
|
150
|
+
let packageType = 'lib';
|
|
151
|
+
if (forcedType) {
|
|
152
|
+
packageType = forcedType;
|
|
153
|
+
}
|
|
154
|
+
else if (packageJson.scripts && packageJson.scripts.start) {
|
|
155
|
+
packageType = 'app';
|
|
156
|
+
}
|
|
157
|
+
else if (packageJson.keywords && packageJson.keywords.includes('tool')) {
|
|
158
|
+
packageType = 'tool';
|
|
159
|
+
}
|
|
160
|
+
return {
|
|
161
|
+
name: packageJson.name || packageName,
|
|
162
|
+
version: packageJson.version || '0.0.0',
|
|
163
|
+
type: packageType,
|
|
164
|
+
path: packagePath,
|
|
165
|
+
dependencies: packageJson.dependencies || {},
|
|
166
|
+
devDependencies: packageJson.devDependencies || {},
|
|
167
|
+
peerDependencies: packageJson.peerDependencies || {},
|
|
168
|
+
scripts: packageJson.scripts || {},
|
|
169
|
+
maintainers: packageJson.maintainers || [],
|
|
170
|
+
description: packageJson.description,
|
|
171
|
+
license: packageJson.license,
|
|
172
|
+
repository: packageJson.repository || {},
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
catch (error) {
|
|
176
|
+
console.error(`Error parsing package.json for ${packageName}:`, error);
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Analyzes dependencies and determines their status
|
|
182
|
+
*/
|
|
183
|
+
// function analyzeDependencies(
|
|
184
|
+
// dependencies: Record<string, string>,
|
|
185
|
+
// type: 'production' | 'development' = 'production'
|
|
186
|
+
// ): DependencyInfo[] {
|
|
187
|
+
// return Object.entries(dependencies).map(([name, version]) => ({
|
|
188
|
+
// name,
|
|
189
|
+
// currentVersion: version,
|
|
190
|
+
// status: 'unknown', // Would be determined by npm registry check
|
|
191
|
+
// type,
|
|
192
|
+
// }));
|
|
193
|
+
// }
|
|
194
|
+
/**
|
|
195
|
+
* Calculates package health score based on various metrics
|
|
196
|
+
*/
|
|
197
|
+
function calculatePackageHealth(buildStatus, testCoverage, lintStatus, securityAudit) {
|
|
198
|
+
let score = 0;
|
|
199
|
+
// Build status (30 points)
|
|
200
|
+
switch (buildStatus) {
|
|
201
|
+
case 'success':
|
|
202
|
+
score += 30;
|
|
203
|
+
break;
|
|
204
|
+
case 'running':
|
|
205
|
+
score += 15;
|
|
206
|
+
break;
|
|
207
|
+
case 'failed':
|
|
208
|
+
score += 0;
|
|
209
|
+
break;
|
|
210
|
+
default:
|
|
211
|
+
score += 10;
|
|
212
|
+
}
|
|
213
|
+
// Test coverage (25 points)
|
|
214
|
+
score += Math.min(25, (testCoverage / 100) * 25);
|
|
215
|
+
// Lint status (25 points)
|
|
216
|
+
switch (lintStatus) {
|
|
217
|
+
case 'pass':
|
|
218
|
+
score += 25;
|
|
219
|
+
break;
|
|
220
|
+
case 'fail':
|
|
221
|
+
score += 0;
|
|
222
|
+
break;
|
|
223
|
+
default:
|
|
224
|
+
score += 10;
|
|
225
|
+
}
|
|
226
|
+
// Security audit (20 points)
|
|
227
|
+
switch (securityAudit) {
|
|
228
|
+
case 'pass':
|
|
229
|
+
score += 20;
|
|
230
|
+
break;
|
|
231
|
+
case 'fail':
|
|
232
|
+
score += 0;
|
|
233
|
+
break;
|
|
234
|
+
default:
|
|
235
|
+
score += 10;
|
|
236
|
+
}
|
|
237
|
+
return {
|
|
238
|
+
buildStatus,
|
|
239
|
+
testCoverage,
|
|
240
|
+
lintStatus,
|
|
241
|
+
securityAudit,
|
|
242
|
+
overallScore: Math.round(score),
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Generates comprehensive monorepo statistics
|
|
247
|
+
*/
|
|
248
|
+
function generateMonorepoStats(packages) {
|
|
249
|
+
const stats = {
|
|
250
|
+
totalPackages: packages.length,
|
|
251
|
+
apps: packages.filter(p => p.type === 'app').length,
|
|
252
|
+
libraries: packages.filter(p => p.type === 'lib').length,
|
|
253
|
+
tools: packages.filter(p => p.type === 'tool').length,
|
|
254
|
+
healthyPackages: 0,
|
|
255
|
+
warningPackages: 0,
|
|
256
|
+
errorPackages: 0,
|
|
257
|
+
outdatedDependencies: 0,
|
|
258
|
+
totalDependencies: 0,
|
|
259
|
+
};
|
|
260
|
+
// Calculate dependency counts
|
|
261
|
+
packages.forEach(pkg => {
|
|
262
|
+
stats.totalDependencies += Object.keys(pkg.dependencies).length;
|
|
263
|
+
stats.totalDependencies += Object.keys(pkg.devDependencies).length;
|
|
264
|
+
stats.totalDependencies += Object.keys(pkg.peerDependencies ?? {}).length;
|
|
265
|
+
});
|
|
266
|
+
return stats;
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Finds circular dependencies in the monorepo
|
|
270
|
+
*/
|
|
271
|
+
function findCircularDependencies(packages) {
|
|
272
|
+
const graph = new Map();
|
|
273
|
+
const visited = new Set();
|
|
274
|
+
const recursionStack = new Set();
|
|
275
|
+
const circularDeps = [];
|
|
276
|
+
// Build dependency graph
|
|
277
|
+
packages.forEach(pkg => {
|
|
278
|
+
graph.set(pkg.name, Object.keys(pkg.dependencies));
|
|
279
|
+
});
|
|
280
|
+
function dfs(node, path) {
|
|
281
|
+
if (recursionStack.has(node)) {
|
|
282
|
+
const cycleStart = path.indexOf(node);
|
|
283
|
+
circularDeps.push(path.slice(cycleStart));
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
if (visited.has(node)) {
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
visited.add(node);
|
|
290
|
+
recursionStack.add(node);
|
|
291
|
+
path.push(node);
|
|
292
|
+
const dependencies = graph.get(node) || [];
|
|
293
|
+
for (const dep of dependencies) {
|
|
294
|
+
if (graph.has(dep)) {
|
|
295
|
+
dfs(dep, [...path]);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
recursionStack.delete(node);
|
|
299
|
+
}
|
|
300
|
+
for (const node of graph.keys()) {
|
|
301
|
+
if (!visited.has(node)) {
|
|
302
|
+
dfs(node, []);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
return circularDeps;
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Generates a dependency graph for visualization
|
|
309
|
+
*/
|
|
310
|
+
function generateDependencyGraph(packages) {
|
|
311
|
+
const nodes = packages.map(pkg => ({
|
|
312
|
+
// id: pkg.name,
|
|
313
|
+
label: pkg.name,
|
|
314
|
+
type: pkg.type,
|
|
315
|
+
version: pkg.version,
|
|
316
|
+
dependencies: Object.keys(pkg.dependencies).length,
|
|
317
|
+
}));
|
|
318
|
+
const edges = [];
|
|
319
|
+
packages.forEach(pkg => {
|
|
320
|
+
Object.keys(pkg.dependencies).forEach(depName => {
|
|
321
|
+
// Only include internal dependencies
|
|
322
|
+
if (packages.some(p => p.name === depName)) {
|
|
323
|
+
edges.push({
|
|
324
|
+
from: pkg.name,
|
|
325
|
+
to: depName,
|
|
326
|
+
type: 'internal',
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
});
|
|
330
|
+
});
|
|
331
|
+
return { nodes, edges };
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Checks if a package has outdated dependencies
|
|
335
|
+
*/
|
|
336
|
+
function checkOutdatedDependencies(packageInfo) {
|
|
337
|
+
const outdated = [];
|
|
338
|
+
// This would typically involve checking against npm registry
|
|
339
|
+
// For now, we'll simulate with some basic checks
|
|
340
|
+
Object.entries(packageInfo.dependencies).forEach(([name, version]) => {
|
|
341
|
+
if (version.startsWith('^') || version.startsWith('~')) {
|
|
342
|
+
// Could be outdated, would need registry check
|
|
343
|
+
outdated.push({
|
|
344
|
+
name,
|
|
345
|
+
version: version,
|
|
346
|
+
status: 'unknown',
|
|
347
|
+
type: 'dependency',
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
});
|
|
351
|
+
return outdated;
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Formats version numbers for comparison
|
|
355
|
+
*/
|
|
356
|
+
// function parseVersion(version: string): number[] {
|
|
357
|
+
// return version
|
|
358
|
+
// .replace(/^[^0-9]*/, '')
|
|
359
|
+
// .split('.')
|
|
360
|
+
// .map(Number);
|
|
361
|
+
// }
|
|
362
|
+
/**
|
|
363
|
+
* Compares two version strings
|
|
364
|
+
*/
|
|
365
|
+
// function compareVersions(v1: string, v2: string): number {
|
|
366
|
+
// const parsed1 = parseVersion(v1);
|
|
367
|
+
// const parsed2 = parseVersion(v2);
|
|
368
|
+
// for (let i = 0; i < Math.max(parsed1.length, parsed2.length); i++) {
|
|
369
|
+
// const num1 = parsed1[i] || 0;
|
|
370
|
+
// const num2 = parsed2[i] || 0;
|
|
371
|
+
// if (num1 > num2) return 1;
|
|
372
|
+
// if (num1 < num2) return -1;
|
|
373
|
+
// }
|
|
374
|
+
// return 0;
|
|
375
|
+
// }
|
|
376
|
+
/**
|
|
377
|
+
* Gets package size information
|
|
378
|
+
*/
|
|
379
|
+
function getPackageSize(packagePath) {
|
|
380
|
+
try {
|
|
381
|
+
let totalSize = 0;
|
|
382
|
+
let fileCount = 0;
|
|
383
|
+
const calculateSize = (dirPath) => {
|
|
384
|
+
const items = fs.readdirSync(dirPath, { withFileTypes: true });
|
|
385
|
+
for (const item of items) {
|
|
386
|
+
const fullPath = path_1.default.join(dirPath, item.name);
|
|
387
|
+
if (item.isDirectory()) {
|
|
388
|
+
// Skip node_modules and other build artifacts
|
|
389
|
+
if (!['node_modules', 'dist', 'build', '.git'].includes(item.name)) {
|
|
390
|
+
calculateSize(fullPath);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
else {
|
|
394
|
+
try {
|
|
395
|
+
const stats = fs.statSync(fullPath);
|
|
396
|
+
totalSize += stats.size;
|
|
397
|
+
fileCount++;
|
|
398
|
+
}
|
|
399
|
+
catch (error) {
|
|
400
|
+
// Skip files we can't read
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
};
|
|
405
|
+
calculateSize(packagePath);
|
|
406
|
+
return {
|
|
407
|
+
size: totalSize,
|
|
408
|
+
files: fileCount,
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
catch (error) {
|
|
412
|
+
return { size: 0, files: 0 };
|
|
413
|
+
}
|
|
414
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Monodog: Monorepo Health Dashboard (Frontend)
|
|
2
|
+
|
|
3
|
+
## 🎯 Overview
|
|
4
|
+
|
|
5
|
+
This is the **client-side application** designed to consume data from the **Monorepo Analytics and Health API** backend service.
|
|
6
|
+
It provides a **real-time, visual dashboard** for tracking the health, dependencies, and overall status of all packages within the monorepo.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 🛠 Technology Stack
|
|
11
|
+
|
|
12
|
+
| Component | Technology | Description |
|
|
13
|
+
| ----------------- | ----------------------------- | --------------------------------------------------------------------- |
|
|
14
|
+
| **Framework** | React (Functional Components) | Core library for building the user interface. |
|
|
15
|
+
| **Styling** | Tailwind CSS | Utility-first framework for responsive, modern, and aesthetic design. |
|
|
16
|
+
| **Data Fetching** | Fetch API (Native JavaScript) | Handles communication with the backend Express API. |
|
|
17
|
+
| **Icons** | Lucide React | Simple, clean vector icons for visualization. |
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## ⚙️ Prerequisites
|
|
22
|
+
|
|
23
|
+
To run this application, ensure the following:
|
|
24
|
+
|
|
25
|
+
- **Node.js** and a package manager (`npm`, `yarn`, or `pnpm`) are installed.
|
|
26
|
+
- The **Monorepo Analytics API** backend service is running and accessible.
|
|
27
|
+
- Default backend URL: **http://localhost:8999**
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## 🚨 API Connection Details
|
|
32
|
+
|
|
33
|
+
The dashboard connects to the backend API using the following base URL:
|
|
34
|
+
|
|
35
|
+
```javascript
|
|
36
|
+
const API_BASE_URL = 'http://localhost:8999/api';
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## 🚀 Getting Started
|
|
40
|
+
|
|
41
|
+
### Installation
|
|
42
|
+
|
|
43
|
+
Clone the repository and install the dependencies:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
# Clone the repository
|
|
47
|
+
git clone https://github.com/mindfiredigital/MonoDog.git
|
|
48
|
+
cd packages/monoapp/monodog-dashboard
|
|
49
|
+
|
|
50
|
+
# Install dependencies
|
|
51
|
+
npm install
|
|
52
|
+
|
|
53
|
+
# build
|
|
54
|
+
npm run build
|
|
55
|
+
|
|
56
|
+
# run dashboard
|
|
57
|
+
npm run dev
|
|
58
|
+
```
|