feat(prefer-throw-if-no-entry): add rule - #139
Conversation
31838cb to
b443174
Compare
|
can you explain this one a bit more? what's the benefit of try/catching it with |
It's a performance rule, to promote
Exactly. Real errors such as |
this is the "how", but what is the "why"? try { statSync(p, {throwIfNoEntry: false}); } catch {}this is one of the fixed outputs, but in what situation would someone do this? wouldn't the reason to set it to |
|
It only "suppresses" Edit: btw |
|
i think i'm missing some context still though. yes this is a rule to promote
we both fully understand how they work. i'm not asking that, i'm asking why someone would migrate from throwing to not throwing. |
|
Performance: Granted, might be a bit of a niche rule but a shame to overlook in certain fs-heavy situations. References:
|
b443174 to
c1b66a6
Compare
|
aha i understand my source of confusion now. given this as a "before": try {
fs.statSync(...);
} catch {
// if ENOENT do something because it wasn't found
// otherwise, rethrow
}the "after" is actually this: const stat = fs.statSync(..., { throwIfNoEntry: false });
if (!stat) {
// do something because it wasn't found
}i.e. the real benefit is when you're using a the example in the test cases isn't this. it would gain still because it is unconditionally throwing away all errors anyway, so never throwing the not-found one will speed it up. but it doesn't seem like code anyone would write (doing a maybe its worth making this clearer somehow in the description. the message already touches on it |
The message and description now state that the try/catch stays for real errors (EACCES, ...); only the common not-found case moves out of the catch into a return-value check. Replace the no-op empty-catch example with a realistic isDir probe so the test cases show the intended pattern.
|
Added an example and tried to clarify in docs. Regardless of nested try/catch blocks, I think we should always catch such known errors. Another way to look at it: Before: try {
use(statSync(p));
} catch (err) {
if (err.code === 'ENOENT') return; // missing
logger.warn(err); // real error
}After: try {
const stat = statSync(p, { throwIfNoEntry: false });
if (!stat) return; // missing, no throw
use(stat);
} catch (err) {
logger.warn(err); // real error, still caught
}
Maybe the before/after example above is more clear. It's what Node.js and TS internally do because it's faster. |
|
new messaging looks much better! seems a lot clearer now. thanks a bunch for helping me better understand 🙏 |
|
Should've clarified intent/reasoning better in the description right away. Anyway, thanks for merging! Like how this plugin is evolving 😇 |
Summary
prefer-throw-if-no-entrystatSync/lstatSynccalls insidetry/catchwhen they do not pass{ throwIfNoEntry: false }{ throwIfNoEntry: false }where the stat options object can be safely editedNotes
This is suggestion-only and not part of the recommended config. It skips
try/finallywithoutcatch, nested functions, calls that already pass{ throwIfNoEntry: false }, andfstatSyncbecause file-descriptor stat calls do not have a missing path entry case.Node
v24.9.0benchmark for 200,000 missingstatSynccalls:679.6ms{ throwIfNoEntry: false }miss path:147.3msTest plan
npm test -- src/rules/prefer-throw-if-no-entry.test.tsnpm run lintnpm run build