Some checks failed
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 1m1s
CD Pipeline / post-deploy-checks (push) Has been cancelled
CD Pipeline / build-and-deploy (push) Has been cancelled
131 lines
3.0 KiB
JavaScript
131 lines
3.0 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
import { readdirSync, readFileSync, statSync } from 'node:fs'
|
|
import { join, relative } from 'node:path'
|
|
|
|
const appRoot = join(process.cwd(), 'src', 'app')
|
|
|
|
const textLikePatterns = [
|
|
/<p\b/g,
|
|
/<span\b/g,
|
|
/<li\b/g,
|
|
/<h[1-6]\b/g,
|
|
/<ReactMarkdown\b/g,
|
|
/prose\b/g,
|
|
/leading-\d/g,
|
|
]
|
|
|
|
const controlPatterns = [
|
|
/<button\b/g,
|
|
/<select\b/g,
|
|
/<input\b/g,
|
|
/<Link\b/g,
|
|
/role=["']tab/g,
|
|
/type=["']checkbox/g,
|
|
]
|
|
|
|
const layoutPatterns = [
|
|
/grid-cols-/g,
|
|
/flex\b/g,
|
|
/overflow-y-auto/g,
|
|
/sticky\b/g,
|
|
]
|
|
|
|
const textWallPatterns = [
|
|
/max-w-(?:2xl|3xl|4xl|5xl|6xl|7xl)/g,
|
|
/line-clamp-[4-9]/g,
|
|
/prose\b/g,
|
|
/<ReactMarkdown\b/g,
|
|
]
|
|
|
|
const manualTerms = [
|
|
/\u4eba\u5de5/g,
|
|
/\u624b\u52d5/g,
|
|
/manual/gi,
|
|
/needs_human/gi,
|
|
/human_/gi,
|
|
]
|
|
|
|
const fixedTaxonomyPatterns = [
|
|
/const\s+CATEGORIES\s*=/g,
|
|
]
|
|
|
|
function collectPages(dir, acc = []) {
|
|
for (const name of readdirSync(dir)) {
|
|
const fullPath = join(dir, name)
|
|
const stats = statSync(fullPath)
|
|
if (stats.isDirectory()) {
|
|
collectPages(fullPath, acc)
|
|
} else if (name === 'page.tsx') {
|
|
acc.push(fullPath)
|
|
}
|
|
}
|
|
return acc
|
|
}
|
|
|
|
function countMatches(source, patterns) {
|
|
return patterns.reduce((sum, pattern) => sum + (source.match(pattern) ?? []).length, 0)
|
|
}
|
|
|
|
function routeFromPage(filePath) {
|
|
return relative(appRoot, filePath)
|
|
.replace(/\/page\.tsx$/, '')
|
|
.replace(/\[locale\]/, ':locale')
|
|
}
|
|
|
|
const pages = collectPages(appRoot)
|
|
.map(filePath => {
|
|
const source = readFileSync(filePath, 'utf8')
|
|
const lineCount = source.split('\n').length
|
|
const textSignals = countMatches(source, textLikePatterns)
|
|
const controls = countMatches(source, controlPatterns)
|
|
const layoutSignals = countMatches(source, layoutPatterns)
|
|
const textWallSignals = countMatches(source, textWallPatterns)
|
|
const manualSignals = countMatches(source, manualTerms)
|
|
const fixedTaxonomySignals = countMatches(source, fixedTaxonomyPatterns)
|
|
const nestedCardSignals = (source.match(/rounded-(?:md|lg|xl)[^`'"]*border/g) ?? []).length
|
|
const score =
|
|
Math.round(
|
|
(textSignals * 1.2)
|
|
+ (textWallSignals * 8)
|
|
+ (manualSignals * 4)
|
|
+ (fixedTaxonomySignals * 10)
|
|
+ (nestedCardSignals * 0.7)
|
|
- (controls * 1.5)
|
|
- (layoutSignals * 0.4),
|
|
)
|
|
|
|
return {
|
|
route: routeFromPage(filePath),
|
|
file: relative(process.cwd(), filePath),
|
|
lines: lineCount,
|
|
score,
|
|
textSignals,
|
|
controls,
|
|
layoutSignals,
|
|
textWallSignals,
|
|
manualSignals,
|
|
fixedTaxonomySignals,
|
|
nestedCardSignals,
|
|
}
|
|
})
|
|
.sort((a, b) => b.score - a.score)
|
|
|
|
console.log('AWOOOI_UI_DENSITY_AUDIT')
|
|
console.table(
|
|
pages.slice(0, 20).map(page => ({
|
|
route: page.route,
|
|
score: page.score,
|
|
lines: page.lines,
|
|
text: page.textSignals,
|
|
controls: page.controls,
|
|
manual: page.manualSignals,
|
|
fixedTaxonomy: page.fixedTaxonomySignals,
|
|
})),
|
|
)
|
|
|
|
console.log(JSON.stringify({
|
|
page_count: pages.length,
|
|
top_routes: pages.slice(0, 20),
|
|
}, null, 2))
|