mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-15 06:41:08 +02:00
feat(manascore): add security headers, skeleton loading, TODO count metrics
New metrics: - Security Headers (76%) — apps with setSecurityHeaders() in hooks - Skeleton Loading (76%) — apps with skeleton/loading state components - TODO/FIXME Count (22 total) — technical debt info metric (not weighted) Ecosystem Health Score: 72/100 (15 metrics total) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
f58a6d1d70
commit
3fb1eddc04
3 changed files with 180 additions and 18 deletions
|
|
@ -1,11 +1,11 @@
|
|||
{
|
||||
"generatedAt": "2026-03-31T12:04:45.568Z",
|
||||
"overallScore": 70,
|
||||
"generatedAt": "2026-03-31T12:12:26.680Z",
|
||||
"overallScore": 72,
|
||||
"scores": {
|
||||
"sharedPackages": 90,
|
||||
"iconConsistency": 89,
|
||||
"modalConsistency": 19,
|
||||
"errorHandling": 20,
|
||||
"errorHandling": 17,
|
||||
"i18nCoverage": 86,
|
||||
"localFirst": 93,
|
||||
"styleConsistency": 87,
|
||||
|
|
@ -13,7 +13,9 @@
|
|||
"typescriptStrict": 100,
|
||||
"testCoverage": 72,
|
||||
"pwaSupport": 2,
|
||||
"maintainability": 0
|
||||
"maintainability": 0,
|
||||
"securityHeaders": 76,
|
||||
"skeletonLoading": 76
|
||||
},
|
||||
"weights": {
|
||||
"sharedPackages": 20,
|
||||
|
|
@ -26,13 +28,15 @@
|
|||
"errorBoundaries": 8,
|
||||
"typescriptStrict": 7,
|
||||
"testCoverage": 7,
|
||||
"pwaSupport": 5,
|
||||
"maintainability": 5
|
||||
"pwaSupport": 4,
|
||||
"maintainability": 4,
|
||||
"securityHeaders": 5,
|
||||
"skeletonLoading": 3
|
||||
},
|
||||
"details": {
|
||||
"icons": {
|
||||
"adoption": 89,
|
||||
"phosphorFiles": 354,
|
||||
"phosphorFiles": 347,
|
||||
"inlineSvgFiles": 45,
|
||||
"perApp": {
|
||||
"calc": {
|
||||
|
|
@ -40,7 +44,7 @@
|
|||
"inlineSvg": 0
|
||||
},
|
||||
"calendar": {
|
||||
"phosphor": 35,
|
||||
"phosphor": 28,
|
||||
"inlineSvg": 0
|
||||
},
|
||||
"chat": {
|
||||
|
|
@ -155,9 +159,9 @@
|
|||
},
|
||||
"modals": {
|
||||
"adoption": 19,
|
||||
"total": 64,
|
||||
"total": 63,
|
||||
"sharedUsage": 12,
|
||||
"focusTrapUsage": 7
|
||||
"focusTrapUsage": 6
|
||||
},
|
||||
"packages": {
|
||||
"coreAdoption": 90,
|
||||
|
|
@ -206,9 +210,9 @@
|
|||
}
|
||||
},
|
||||
"errors": {
|
||||
"adoption": 20,
|
||||
"adoption": 17,
|
||||
"inline": 193,
|
||||
"shared": 47
|
||||
"shared": 40
|
||||
},
|
||||
"i18n": {
|
||||
"adoption": 86,
|
||||
|
|
@ -266,7 +270,7 @@
|
|||
"e2eAdoption": 14,
|
||||
"appsWithTests": 21,
|
||||
"appsWithE2e": 4,
|
||||
"totalTestFiles": 111,
|
||||
"totalTestFiles": 110,
|
||||
"noTests": [
|
||||
"calc",
|
||||
"inventar",
|
||||
|
|
@ -341,12 +345,12 @@
|
|||
{
|
||||
"app": "todo",
|
||||
"file": "lib/components/TaskItem.svelte",
|
||||
"lines": 1217
|
||||
"lines": 1194
|
||||
},
|
||||
{
|
||||
"app": "todo",
|
||||
"file": "lib/components/board-views/ViewEditorModal.svelte",
|
||||
"lines": 1190
|
||||
"lines": 1187
|
||||
},
|
||||
{
|
||||
"app": "contacts",
|
||||
|
|
@ -356,7 +360,7 @@
|
|||
{
|
||||
"app": "calendar",
|
||||
"file": "lib/components/calendar/WeekView.svelte",
|
||||
"lines": 1043
|
||||
"lines": 959
|
||||
},
|
||||
{
|
||||
"app": "zitare",
|
||||
|
|
@ -374,6 +378,26 @@
|
|||
"lines": 849
|
||||
}
|
||||
]
|
||||
},
|
||||
"todos": {
|
||||
"totalCount": 22,
|
||||
"perApp": {
|
||||
"manacore": 13,
|
||||
"contacts": 5,
|
||||
"todo": 2,
|
||||
"chat": 1,
|
||||
"picture": 1
|
||||
}
|
||||
},
|
||||
"securityHeaders": {
|
||||
"adoption": 76,
|
||||
"appsWithHeaders": 22,
|
||||
"missing": ["manavoxel", "moodlit", "news", "playground", "times", "uload", "wisekeep"]
|
||||
},
|
||||
"skeletons": {
|
||||
"adoption": 76,
|
||||
"appsWithSkeletons": 22,
|
||||
"missing": ["citycorners", "manadeck", "manavoxel", "planta", "presi", "times", "zitare"]
|
||||
}
|
||||
},
|
||||
"apps": [
|
||||
|
|
|
|||
|
|
@ -106,6 +106,18 @@ const categories = [
|
|||
icon: '📏',
|
||||
description: 'Dateien unter 500 Zeilen',
|
||||
},
|
||||
{
|
||||
key: 'securityHeaders',
|
||||
label: 'Security Headers',
|
||||
icon: '🔐',
|
||||
description: 'CSP, X-Frame-Options via setSecurityHeaders()',
|
||||
},
|
||||
{
|
||||
key: 'skeletonLoading',
|
||||
label: 'Skeleton Loading',
|
||||
icon: '💀',
|
||||
description: 'Skeleton-Komponenten für Loading States',
|
||||
},
|
||||
];
|
||||
|
||||
const packageDetails = details.packages.perPackage;
|
||||
|
|
@ -268,6 +280,34 @@ const generatedDate = new Date(ecosystemData.generatedAt).toLocaleDateString('de
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Technical Debt (TODO/FIXME) -->
|
||||
{
|
||||
details.todos && details.todos.totalCount > 0 && (
|
||||
<div class="mb-12">
|
||||
<h2 class="text-foreground mb-6 text-xl font-semibold">
|
||||
Technical Debt (TODO/FIXME/HACK)
|
||||
</h2>
|
||||
<div class="border-border/50 rounded-xl border bg-gradient-to-br from-white/5 to-white/[0.02] p-4">
|
||||
<div class="mb-3 flex items-center justify-between">
|
||||
<span class="text-muted-foreground text-sm">Gesamt im Codebase</span>
|
||||
<span
|
||||
class={`text-2xl font-bold ${details.todos.totalCount > 50 ? 'text-red-500' : details.todos.totalCount > 20 ? 'text-orange-500' : 'text-yellow-500'}`}
|
||||
>
|
||||
{details.todos.totalCount}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
{Object.entries(details.todos.perApp).map(([app, count]: [string, any]) => (
|
||||
<span class="text-muted-foreground border-border/50 rounded-full border px-3 py-1 text-xs">
|
||||
{app}: <span class="text-foreground font-medium">{count}</span>
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
<!-- File Size Top Offenders -->
|
||||
{
|
||||
details.fileSizes?.topOffenders?.length > 0 && (
|
||||
|
|
|
|||
|
|
@ -513,6 +513,94 @@ function measureFileSizes() {
|
|||
};
|
||||
}
|
||||
|
||||
function measureTodoFixmeCount() {
|
||||
console.log('📊 Measuring TODO/FIXME Count...');
|
||||
let totalCount = 0;
|
||||
const perApp = {};
|
||||
|
||||
for (const app of WEB_APPS) {
|
||||
const webSrc = join(APPS_DIR, app, 'apps/web/src');
|
||||
if (!existsSync(webSrc)) continue;
|
||||
|
||||
const count =
|
||||
grepOccurrences('TODO', webSrc, '*.svelte') +
|
||||
grepOccurrences('TODO', webSrc, '*.ts') +
|
||||
grepOccurrences('FIXME', webSrc, '*.svelte') +
|
||||
grepOccurrences('FIXME', webSrc, '*.ts') +
|
||||
grepOccurrences('HACK', webSrc, '*.svelte') +
|
||||
grepOccurrences('HACK', webSrc, '*.ts');
|
||||
|
||||
if (count > 0) {
|
||||
perApp[app] = count;
|
||||
totalCount += count;
|
||||
}
|
||||
}
|
||||
|
||||
const sorted = Object.entries(perApp).sort(([, a], [, b]) => b - a);
|
||||
console.log(` Total TODO/FIXME/HACK: ${totalCount}`);
|
||||
if (sorted.length > 0) {
|
||||
sorted.slice(0, 5).forEach(([app, count]) => console.log(` ${app}: ${count}`));
|
||||
}
|
||||
console.log('');
|
||||
|
||||
return { totalCount, perApp: Object.fromEntries(sorted) };
|
||||
}
|
||||
|
||||
function measureSecurityHeaders() {
|
||||
console.log('📊 Measuring Security Headers...');
|
||||
let appsWithHeaders = 0;
|
||||
const missing = [];
|
||||
|
||||
for (const app of WEB_APPS) {
|
||||
const webSrc = join(APPS_DIR, app, 'apps/web/src');
|
||||
if (!existsSync(webSrc)) continue;
|
||||
|
||||
const hasHeaders =
|
||||
grepCount('setSecurityHeaders', webSrc, '*.ts') > 0 ||
|
||||
grepCount('Content-Security-Policy', webSrc, '*.ts') > 0 ||
|
||||
grepCount('X-Frame-Options', webSrc, '*.ts') > 0;
|
||||
|
||||
if (hasHeaders) appsWithHeaders++;
|
||||
else missing.push(app);
|
||||
}
|
||||
|
||||
const adoption = Math.round((appsWithHeaders / WEB_APPS.length) * 100);
|
||||
console.log(` Apps with security headers: ${appsWithHeaders}/${WEB_APPS.length} (${adoption}%)`);
|
||||
if (missing.length > 0 && missing.length <= 10) console.log(` Missing: ${missing.join(', ')}`);
|
||||
console.log('');
|
||||
|
||||
return { adoption, appsWithHeaders, missing };
|
||||
}
|
||||
|
||||
function measureSkeletonLoading() {
|
||||
console.log('📊 Measuring Skeleton Loading States...');
|
||||
let appsWithSkeletons = 0;
|
||||
const missing = [];
|
||||
|
||||
for (const app of WEB_APPS) {
|
||||
const webSrc = join(APPS_DIR, app, 'apps/web/src');
|
||||
if (!existsSync(webSrc)) continue;
|
||||
|
||||
const hasSkeletons =
|
||||
fileCount('*Skeleton*', webSrc) > 0 ||
|
||||
fileCount('*skeleton*', webSrc) > 0 ||
|
||||
grepCount('Skeleton', webSrc, '*.svelte') > 0 ||
|
||||
grepCount('animate-pulse', webSrc, '*.svelte') > 0;
|
||||
|
||||
if (hasSkeletons) appsWithSkeletons++;
|
||||
else missing.push(app);
|
||||
}
|
||||
|
||||
const adoption = Math.round((appsWithSkeletons / WEB_APPS.length) * 100);
|
||||
console.log(
|
||||
` Apps with skeleton loading: ${appsWithSkeletons}/${WEB_APPS.length} (${adoption}%)`
|
||||
);
|
||||
if (missing.length > 0 && missing.length <= 10) console.log(` Missing: ${missing.join(', ')}`);
|
||||
console.log('');
|
||||
|
||||
return { adoption, appsWithSkeletons, missing };
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Main
|
||||
// ============================================================
|
||||
|
|
@ -532,6 +620,9 @@ const typescript = measureTypeScriptStrictness();
|
|||
const tests = measureTestCoverage();
|
||||
const pwa = measurePwaSupport();
|
||||
const fileSizes = measureFileSizes();
|
||||
const todos = measureTodoFixmeCount();
|
||||
const securityHeaders = measureSecurityHeaders();
|
||||
const skeletons = measureSkeletonLoading();
|
||||
|
||||
// Calculate overall scores
|
||||
const scores = {
|
||||
|
|
@ -547,6 +638,8 @@ const scores = {
|
|||
testCoverage: tests.adoption,
|
||||
pwaSupport: pwa.adoption,
|
||||
maintainability: fileSizes.adoption,
|
||||
securityHeaders: securityHeaders.adoption,
|
||||
skeletonLoading: skeletons.adoption,
|
||||
};
|
||||
|
||||
// Weighted overall score
|
||||
|
|
@ -561,8 +654,10 @@ const weights = {
|
|||
errorBoundaries: 8,
|
||||
typescriptStrict: 7,
|
||||
testCoverage: 7,
|
||||
pwaSupport: 5,
|
||||
maintainability: 5,
|
||||
pwaSupport: 4,
|
||||
maintainability: 4,
|
||||
securityHeaders: 5,
|
||||
skeletonLoading: 3,
|
||||
};
|
||||
|
||||
let totalWeight = 0;
|
||||
|
|
@ -616,6 +711,9 @@ const output = {
|
|||
tests,
|
||||
pwa,
|
||||
fileSizes,
|
||||
todos,
|
||||
securityHeaders,
|
||||
skeletons,
|
||||
},
|
||||
apps: WEB_APPS,
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue