Files
orca/config/scripts/ci-unit-dependency-graph.mjs
T
Neil cca14bd604 Stop building unused import candidates in the test planner (#24611)
Replace the existing ordered extension map/find with a loop that forms each candidate only when its existing lookup is reached.
2026-10-03 00:17:01 -07:00

90 lines
2.3 KiB
JavaScript

import { globSync, readFileSync } from 'node:fs'
import { posix, join } from 'node:path'
import ts from 'typescript-api'
const EXTENSIONS = [
'',
'.ts',
'.tsx',
'.mjs',
'.js',
'.cjs',
'.json',
'/index.ts',
'/index.tsx',
'/index.js'
]
const INDIRECT_INPUT =
/\b(?:readFile\w*|readdir\w*|glob\w*|spawn\w*|execFile\w*|execSync|runProcess\w*|fork|Worker)\b|\bimport\s*\(\s*[^'"\s]|\brequire\s*\(\s*[^'"\s]|\bnew\s+URL\s*\(/
function localPath(file, specifier) {
if (specifier.startsWith('.')) {
return posix.join(posix.dirname(file), specifier)
}
if (specifier.startsWith('@renderer/')) {
return `src/renderer/src/${specifier.slice(10)}`
}
if (specifier.startsWith('@/')) {
return `src/renderer/src/${specifier.slice(2)}`
}
return null
}
export function buildUnitDependencyGraph(sources) {
const reverse = new Map()
const opaque = new Set()
for (const [file, source] of sources) {
if (INDIRECT_INPUT.test(source) || file.startsWith('config/') || file.startsWith('tests/')) {
opaque.add(file)
}
for (const imported of ts.preProcessFile(source, true, true).importedFiles) {
const path = localPath(file, imported.fileName)
if (path === null) {
continue
}
let resolved
for (const extension of EXTENSIONS) {
const candidate = path + extension
if (sources.has(candidate)) {
resolved = candidate
break
}
}
if (!resolved) {
opaque.add(file)
continue
}
if (!reverse.has(resolved)) {
reverse.set(resolved, new Set())
}
reverse.get(resolved).add(file)
}
}
return { reverse, opaque }
}
export function collectUnitDependencyGraph(root = process.cwd()) {
const files = globSync(
[
'src/**/*.{ts,tsx,js,mjs,cjs,json}',
'config/**/*.{ts,tsx,js,mjs,cjs,json}',
'tests/**/*.{ts,tsx,js,mjs,cjs,json}'
],
{ cwd: root }
)
const sources = new Map(
files.map((file) => [file.replaceAll('\\', '/'), readFileSync(join(root, file), 'utf8')])
)
return { ...buildUnitDependencyGraph(sources), files: new Set(sources.keys()) }
}
export function unitConsumers(seeds, reverse) {
const result = new Set(seeds)
for (const file of result) {
for (const consumer of reverse.get(file) ?? []) {
result.add(consumer)
}
}
return result
}