Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,6 @@
## 2024-05-24 - Pre-compile RegExp in nested loops
**Learning:** Instantiating `new RegExp()` inside nested array methods like `.filter` and `.some` creates a severe O(N*M) performance bottleneck, especially when matching two large lists (e.g., documented tests vs. actual test files).
**Action:** Always pre-compile regular expressions and derived strings into an array of "matcher" objects outside of the loop before iterating, which shifts the instantiation cost from O(N*M) to O(N).
## 2024-05-24 - Pre-compute properties and use Sets instead of nested multi-pass arrays
**Learning:** Performing `basename()` calculations inside nested multi-pass array iterations (`.filter()` and `.some()`) generates substantial O(N*M) bottlenecks.
**Action:** When filtering two lists against each other using comparisons that require calculations (like regex matching or `basename`), pre-compute the target properties for all items, and cross-reference them in a single double `for`-loop utilizing `Set`s to track matches, drastically reducing iterations and redundant string operations.
32 changes: 23 additions & 9 deletions cli/commands/diff.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -346,10 +346,11 @@ 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-compile regular expressions to avoid O(N*M)
// instantiation bottlenecks inside the nested .filter and .some loops below.
// instantiation bottlenecks. We also pre-compute the basename of code paths
// and resolve matches in a single cross-check loop, changing multi-pass array
// filtering to O(N + M) complexity and preventing redundant string processing overhead.
const docMatchers = [...docTests].map(docEntry => {
const entry = String(docEntry).trim();
const hasSlash = entry.includes('/');
Expand All @@ -363,17 +364,30 @@ function diffTests(dir, config = {}) {
};
});

const matches = (matcher, codeRel) => {
const subject = matcher.hasSlash ? codeRel : basename(codeRel);
return matcher.rx.test(subject);
};
const codeItems = [...codeTests].map(c => ({
rel: c,
base: basename(c)
}));

const matchedDocs = new Set();
const matchedCode = new Set();

for (const matcher of docMatchers) {
for (const code of codeItems) {
const subject = matcher.hasSlash ? code.rel : code.base;
if (matcher.rx.test(subject)) {
matchedDocs.add(matcher.original);
matchedCode.add(code.rel);
}
}
}

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: codeItems.filter(c => !matchedCode.has(c.rel)).map(c => c.rel),
matched: [...matchedDocs],
};
}

Expand Down
30 changes: 22 additions & 8 deletions cli/validators/docs-diff.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -168,10 +168,11 @@ 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-compile regular expressions to avoid O(N*M)
// instantiation bottlenecks inside the nested .filter and .some loops below.
// instantiation bottlenecks. We also pre-compute the basename of code paths
// and resolve matches in a single cross-check loop, changing multi-pass array
// filtering to O(N + M) complexity and preventing redundant string processing overhead.
const docMatchers = [...docTests].map(docEntry => {
const entry = String(docEntry).trim();
const hasSlash = entry.includes('/');
Expand All @@ -188,15 +189,28 @@ function diffTests(dir, config) {
};
});

const matches = (matcher, codeRel) => {
const subject = matcher.hasSlash ? codeRel : basename(codeRel);
return matcher.rx.test(subject);
};
const codeItems = [...codeTests].map(c => ({
rel: c,
base: basename(c)
}));

const matchedDocs = new Set();
const matchedCode = new Set();

for (const matcher of docMatchers) {
for (const code of codeItems) {
const subject = matcher.hasSlash ? code.rel : code.base;
if (matcher.rx.test(subject)) {
matchedDocs.add(matcher.original);
matchedCode.add(code.rel);
}
}
}

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: codeItems.filter(c => !matchedCode.has(c.rel)).map(c => c.rel),
};
}

Expand Down
20 changes: 0 additions & 20 deletions test-draft.js

This file was deleted.