perf(roadmap): hoist readdirSync out of phase loop in analyze command (#1899)
cmdRoadmapAnalyze called fs.readdirSync(phasesDir) inside the per-phase while loop, causing O(N^2) directory reads for N phases. At 50 phases this produced 100 redundant syscalls; at 100 phases, 200. Move the directory listing before the loop and build a lookup array that is reused for each phase match. This reduces the pattern from O(N^2) to O(N) directory reads. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -129,6 +129,15 @@ function cmdRoadmapAnalyze(cwd, raw) {
|
||||
const phases = [];
|
||||
let match;
|
||||
|
||||
// Build phase directory lookup once (O(1) readdir instead of O(N) per phase)
|
||||
const _phaseDirNames = (() => {
|
||||
try {
|
||||
return fs.readdirSync(phasesDir, { withFileTypes: true })
|
||||
.filter(e => e.isDirectory())
|
||||
.map(e => e.name);
|
||||
} catch { return []; }
|
||||
})();
|
||||
|
||||
while ((match = phasePattern.exec(content)) !== null) {
|
||||
const phaseNum = match[1];
|
||||
const phaseName = match[2].replace(/\(INSERTED\)/i, '').trim();
|
||||
@@ -155,9 +164,7 @@ function cmdRoadmapAnalyze(cwd, raw) {
|
||||
let hasResearch = false;
|
||||
|
||||
try {
|
||||
const entries = fs.readdirSync(phasesDir, { withFileTypes: true });
|
||||
const dirs = entries.filter(e => e.isDirectory()).map(e => e.name);
|
||||
const dirMatch = dirs.find(d => phaseTokenMatches(d, normalized));
|
||||
const dirMatch = _phaseDirNames.find(d => phaseTokenMatches(d, normalized));
|
||||
|
||||
if (dirMatch) {
|
||||
const phaseFiles = fs.readdirSync(path.join(phasesDir, dirMatch));
|
||||
|
||||
Reference in New Issue
Block a user