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:
Till JS 2026-03-31 14:13:04 +02:00
parent f58a6d1d70
commit 3fb1eddc04
3 changed files with 180 additions and 18 deletions

View file

@ -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,
};