diff --git a/cli/commands/diff.mjs b/cli/commands/diff.mjs index 856eea1..fddc857 100644 --- a/cli/commands/diff.mjs +++ b/cli/commands/diff.mjs @@ -346,10 +346,12 @@ function diffTests(dir, config = {}) { if (docTests.size === 0 && codeTests.size === 0) return null; // Glob-aware matching (documented entries are often patterns or basenames). - const codeArr = [...codeTests]; + // PERFORMANCE OPTIMIZATION: Pre-compute expensive basenames outside loops and + // use a single-pass iteration with Sets to eliminate O(N*M) array comparisons. + const codeObjects = [...codeTests].map(c => ({ original: c, base: basename(c) })); // PERFORMANCE OPTIMIZATION: Pre-compile regular expressions to avoid O(N*M) - // instantiation bottlenecks inside the nested .filter and .some loops below. + // instantiation bottlenecks inside loops below. const docMatchers = [...docTests].map(docEntry => { const entry = String(docEntry).trim(); const hasSlash = entry.includes('/'); @@ -363,17 +365,24 @@ function diffTests(dir, config = {}) { }; }); - const matches = (matcher, codeRel) => { - const subject = matcher.hasSlash ? codeRel : basename(codeRel); - return matcher.rx.test(subject); - }; + const matchedDocs = new Set(); + const matchedCode = new Set(); + + for (const m of docMatchers) { + for (const c of codeObjects) { + if (m.rx.test(m.hasSlash ? c.original : c.base)) { + matchedDocs.add(m.original); + matchedCode.add(c.original); + } + } + } return { title: 'Test Files', icon: '🧪', - onlyInDocs: docMatchers.filter(m => !codeArr.some(c => matches(m, c))).map(m => m.original), - onlyInCode: codeArr.filter(c => !docMatchers.some(m => matches(m, c))), - matched: docMatchers.filter(m => codeArr.some(c => matches(m, c))).map(m => m.original), + onlyInDocs: docMatchers.filter(m => !matchedDocs.has(m.original)).map(m => m.original), + onlyInCode: codeObjects.filter(c => !matchedCode.has(c.original)).map(c => c.original), + matched: docMatchers.filter(m => matchedDocs.has(m.original)).map(m => m.original), }; } diff --git a/cli/validators/docs-diff.mjs b/cli/validators/docs-diff.mjs index f9fef2b..f9d1f0b 100644 --- a/cli/validators/docs-diff.mjs +++ b/cli/validators/docs-diff.mjs @@ -168,10 +168,12 @@ function diffTests(dir, config) { // bare basenames or full paths. Treat each documented entry as a glob and // match it against code test paths (or basenames when the entry has no slash). // Exact-string comparison produced the false "N documented but not found". - const codeArr = [...codeTests]; + // PERFORMANCE OPTIMIZATION: Pre-compute expensive basenames outside loops and + // use a single-pass iteration with Sets to eliminate O(N*M) array comparisons. + const codeObjects = [...codeTests].map(c => ({ original: c, base: basename(c) })); // PERFORMANCE OPTIMIZATION: Pre-compile regular expressions to avoid O(N*M) - // instantiation bottlenecks inside the nested .filter and .some loops below. + // instantiation bottlenecks inside loops below. const docMatchers = [...docTests].map(docEntry => { const entry = String(docEntry).trim(); const hasSlash = entry.includes('/'); @@ -188,15 +190,22 @@ function diffTests(dir, config) { }; }); - const matches = (matcher, codeRel) => { - const subject = matcher.hasSlash ? codeRel : basename(codeRel); - return matcher.rx.test(subject); - }; + const matchedDocs = new Set(); + const matchedCode = new Set(); + + for (const m of docMatchers) { + for (const c of codeObjects) { + if (m.rx.test(m.hasSlash ? c.original : c.base)) { + matchedDocs.add(m.original); + matchedCode.add(c.original); + } + } + } return { title: 'Test Files', - onlyInDocs: docMatchers.filter(m => !codeArr.some(c => matches(m, c))).map(m => m.original), - onlyInCode: codeArr.filter(c => !docMatchers.some(m => matches(m, c))), + onlyInDocs: docMatchers.filter(m => !matchedDocs.has(m.original)).map(m => m.original), + onlyInCode: codeObjects.filter(c => !matchedCode.has(c.original)).map(c => c.original), }; }