#!/usr/bin/env node /* eslint-disable no-console */ import fs from 'node:fs' import path from 'node:path' import { spawnSync } from 'node:child_process' import { argv, cwd } from 'node:process' const ROOT = cwd() const SEARCH_ROOTS = ['apps', 'packages', 'plugins', 'services', 'integrations', 'docs'] const IGNORE_DIRS = new Set(['node_modules', '.git', 'dist', 'build', '.next', '.nuxt', '.turbo', 'coverage', '.cache', 'out', '.vite']) function walk(dir, out) { let entries = [] try { entries = fs.readdirSync(dir, { withFileTypes: true }) } catch { return } for (const entry of entries) { if (entry.name.startsWith('.')) { if (entry.name !== '.vitepress') continue } const full = path.join(dir, entry.name) if (entry.isDirectory()) { if (IGNORE_DIRS.has(entry.name)) continue walk(full, out) continue } if (entry.isFile() && entry.name === 'package.json') out.push(full) } } function collectPackageJsonFiles() { const files = [path.join(ROOT, 'package.json')] for (const base of SEARCH_ROOTS) { const full = path.join(ROOT, base) if (fs.existsSync(full)) walk(full, files) } return files } function safeReadJson(file) { try { return JSON.parse(fs.readFileSync(file, 'utf8')) } catch { return null } } function collectFromExportsValue(value, out, source) { if (typeof value === 'string') { out.push({ source, raw: value }) return } if (Array.isArray(value)) { for (const item of value) collectFromExportsValue(item, out, source) return } if (value && typeof value === 'object') { for (const [k, v] of Object.entries(value)) collectFromExportsValue(v, out, `${source}.${k}`) } } function collectEntryCandidates(pkg) { const out = [] const pushIfString = (source, value) => { if (typeof value === 'string' && value.trim()) out.push({ source, raw: value.trim() }) } pushIfString('main', pkg.main) pushIfString('module', pkg.module) pushIfString('types', pkg.types) pushIfString('typings', pkg.typings) if (typeof pkg.bin === 'string') { pushIfString('bin', pkg.bin) } else if (pkg.bin && typeof pkg.bin === 'object') { for (const [k, v] of Object.entries(pkg.bin)) pushIfString(`bin.${k}`, v) } if (typeof pkg.exports === 'string') { pushIfString('exports', pkg.exports) } else if (pkg.exports && typeof pkg.exports === 'object') { for (const [k, v] of Object.entries(pkg.exports)) collectFromExportsValue(v, out, `exports.${k}`) } return out } function cleanupExportTarget(raw) { if (!raw) return '' const q = raw.indexOf('?') const h = raw.indexOf('#') let end = raw.length if (q >= 0) end = Math.min(end, q) if (h >= 0) end = Math.min(end, h) return raw.slice(0, end).trim() } function resolveParentDir(pkgDir, rawTarget) { const target = cleanupExportTarget(rawTarget) // eslint-disable-next-line regexp/no-unused-capturing-group if (!target || /^(https?:|npm:|node:|data:)/.test(target)) return null const wildcardIndex = target.indexOf('*') const logicalTarget = wildcardIndex >= 0 ? target.slice(0, wildcardIndex) : target const abs = path.resolve(pkgDir, logicalTarget) const ext = path.extname(logicalTarget) let parentDir = abs if (ext) parentDir = path.dirname(abs) else if (logicalTarget.endsWith('/')) parentDir = abs else if (fs.existsSync(abs) && fs.statSync(abs).isFile()) parentDir = path.dirname(abs) return { raw: rawTarget, cleaned: target, parentDir, exists: fs.existsSync(parentDir) && fs.statSync(parentDir).isDirectory(), } } function runScc(dir) { const proc = spawnSync('scc', ['-a', '-f', 'json', dir], { encoding: 'utf8' }) if (proc.status !== 0) { return { ok: false, error: (proc.stderr || proc.stdout || `exit ${proc.status}`).trim(), } } let parsed try { parsed = JSON.parse(proc.stdout) } catch (err) { return { ok: false, error: `invalid scc json: ${String(err)}`, } } const sum = key => parsed.reduce((acc, row) => acc + Number(row[key] || 0), 0) return { ok: true, files: sum('Count'), lines: sum('Lines'), code: sum('Code'), comments: sum('Comment'), blanks: sum('Blank'), uloc: sum('ULOC'), } } function rel(p) { const rp = path.relative(ROOT, p) return rp || '.' } function toSccCommand(absDir) { return `scc -a -f json "${rel(absDir)}"` } function printGroup(title) { console.log(`\n=== ${title} ===`) } function printTable(rows, columns) { const widths = {} for (const col of columns) widths[col.key] = col.title.length for (const row of rows) { for (const col of columns) { const value = String(row[col.key] ?? '') widths[col.key] = Math.max(widths[col.key], value.length) } } const line = row => columns.map(col => String(row[col.key] ?? '').padEnd(widths[col.key])).join(' ') console.log(line(Object.fromEntries(columns.map(c => [c.key, c.title])))) console.log(columns.map(col => '-'.repeat(widths[col.key])).join(' ')) for (const row of rows) console.log(line(row)) } function main() { const asJson = argv.includes('--json') const pkgJsonFiles = collectPackageJsonFiles() const records = [] for (const pkgFile of pkgJsonFiles) { const pkg = safeReadJson(pkgFile) if (!pkg) continue const pkgDir = path.dirname(pkgFile) const pkgName = pkg.name || rel(pkgDir) const candidates = collectEntryCandidates(pkg) for (const candidate of candidates) { const resolved = resolveParentDir(pkgDir, candidate.raw) if (!resolved) continue records.push({ packageName: pkgName, packageDir: pkgDir, packageJson: pkgFile, source: candidate.source, entryRaw: resolved.raw, entryClean: resolved.cleaned, parentDir: resolved.parentDir, parentDirRel: rel(resolved.parentDir), parentExists: resolved.exists, }) } } const uniqueByPackageAndDir = new Map() for (const rec of records) { const key = `${rec.packageName}::${rec.parentDir}` const existing = uniqueByPackageAndDir.get(key) if (!existing) { uniqueByPackageAndDir.set(key, { ...rec, occurrenceCount: 1, sources: new Set([rec.source]), entries: new Set([rec.entryClean]), }) } else { existing.occurrenceCount += 1 existing.sources.add(rec.source) existing.entries.add(rec.entryClean) } } const sccCache = new Map() const deduped = Array.from(uniqueByPackageAndDir.values()) for (const rec of deduped) { if (!rec.parentExists) { rec.scc = { ok: false, error: 'directory_not_found' } continue } if (!sccCache.has(rec.parentDir)) sccCache.set(rec.parentDir, runScc(rec.parentDir)) rec.scc = sccCache.get(rec.parentDir) } const aggregateByParentDir = new Map() for (const rec of deduped) { const key = rec.parentDir const existing = aggregateByParentDir.get(key) if (!existing) { aggregateByParentDir.set(key, { parentDir: rec.parentDir, parentDirRel: rec.parentDirRel, packageCount: 1, packageNames: new Set([rec.packageName]), totalOccurrenceCount: rec.occurrenceCount, scc: rec.scc, }) } else { existing.packageCount += 1 existing.totalOccurrenceCount += rec.occurrenceCount existing.packageNames.add(rec.packageName) } } const byPackageRows = deduped .sort((a, b) => (b.occurrenceCount - a.occurrenceCount) || a.parentDirRel.localeCompare(b.parentDirRel)) .map(rec => ({ package: rec.packageName, parent_dir: rec.parentDirRel, occurrences: rec.occurrenceCount, sources: Array.from(rec.sources).sort().join(','), code: rec.scc.ok ? rec.scc.code : '', uloc: rec.scc.ok ? rec.scc.uloc : '', files: rec.scc.ok ? rec.scc.files : '', status: rec.scc.ok ? 'ok' : rec.scc.error, scc_cmd: toSccCommand(rec.parentDir), })) const aggregateRows = Array.from(aggregateByParentDir.values()) .sort((a, b) => (b.packageCount - a.packageCount) || a.parentDirRel.localeCompare(b.parentDirRel)) .map(rec => ({ parent_dir: rec.parentDirRel, package_count: rec.packageCount, packages: Array.from(rec.packageNames).sort().join(','), total_occurrences: rec.totalOccurrenceCount, code: rec.scc.ok ? rec.scc.code : '', uloc: rec.scc.ok ? rec.scc.uloc : '', files: rec.scc.ok ? rec.scc.files : '', status: rec.scc.ok ? 'ok' : rec.scc.error, scc_cmd: toSccCommand(rec.parentDir), })) if (asJson) { const output = { generatedAt: new Date().toISOString(), root: ROOT, packageJsonScanned: pkgJsonFiles.map(rel), uniquePackageEntrypointParents: byPackageRows, dedupedParentDirsAcrossPackages: aggregateRows, } console.log(JSON.stringify(output, null, 2)) return } printGroup('Per Package Entrypoint Parent (Deduped)') printTable( byPackageRows, [ { key: 'package', title: 'package' }, { key: 'parent_dir', title: 'parent_dir' }, { key: 'occurrences', title: 'occ' }, { key: 'code', title: 'code' }, { key: 'uloc', title: 'uloc' }, { key: 'files', title: 'files' }, { key: 'status', title: 'status' }, { key: 'scc_cmd', title: 'scc_cmd' }, ], ) printGroup('Parent Dir Aggregation Across Packages (Deduped)') printTable( aggregateRows, [ { key: 'parent_dir', title: 'parent_dir' }, { key: 'package_count', title: 'pkgs' }, { key: 'total_occurrences', title: 'occ_total' }, { key: 'code', title: 'code' }, { key: 'uloc', title: 'uloc' }, { key: 'files', title: 'files' }, { key: 'status', title: 'status' }, { key: 'scc_cmd', title: 'scc_cmd' }, ], ) } main()