It does not read other files.
Imported names create no bindings, so a cache evicted in another module is invisible to the rule looking at this one.
How it works
MemGuard reads each file on its own, builds a small index of what the code does, and reports only where that index shows evidence of scale.
One file, followed all the way through. The panel keeps up as you read.
Walk the project for .js, .jsx, .ts and .tsx files. node_modules, .git, dist, build, coverage, .next and out are skipped unless your config says otherwise.
Each file goes through the TypeScript compiler API on its own. No program, no tsconfig resolution, no type checker, so a file that does not compile still gets read.
One walk builds the scope index: which binding a name refers to and where else it is used, whether an expression traces back to an await, and whether a node runs once per loop iteration.
The five rules subscribe to the syntax they care about. Each one asks the index for evidence before it reports anything.
A readable frame for a person, JSON for a pipeline, and an exit code that tells a failed run apart from a finding.
The threshold
A spread is ordinary code, and a tool that flags every one of them gets muted in a week. MEM001 and MEM002 see a copy and then look for a reason to care: does it run inside a loop, does its source trace back to an await, is it never modified after it is created? When the answer to all three is no, nothing is reported.
function withLatest(users, extra) {
const next = [...users];
next.push(extra);
return next;
}
// silent. the copy is modified, so it is doing work
function labels(users) {
const copy = [...users];
return copy.map(toLabel);
}
// MEM001. never modified after it is created
function tagsOf(users) {
for (const user of users) {
send([...user.tags]);
}
}
// MEM001. created on every iteration
async function recent(db) {
const rows = await db.query();
return [...rows].sort(byDate);
}
// MEM001. the source traces back to an awaitEach of these is a decision, not a gap waiting to be filled. Under-reporting is the safe direction for a tool you leave switched on.
Imported names create no bindings, so a cache evicted in another module is invisible to the rule looking at this one.
A name bound by const { a } = obj creates no binding at all, so rules stay silent rather than guess at a variable they cannot follow.
MEM005 reads a name like findAll as a clue. It cannot tell whether the call returns ten rows or ten million.
Changing how memory is held changes how an application behaves, so every finding ends at a suggestion.
Two commands, no install, and a report you can read before you decide anything.
Get the commands