scripts/onyxLintDisableReason.ts
Rama main · réplica de lectura del repositorio original.
/**
* Finds inline `eslint-disable` and `oxlint-disable` directives of the rules in `REASONED_RULES` that give no reason.
* Unlike the bans in `onyxLintBypass.ts`, these rules may be disabled, but only one line at a time
* and only with a reason after `--`. The reason then sits next to the exception, where a reviewer
* can judge it.
*
* A directive counts when it names the rule, in the `-line` or `-next-line` form, and its text after
* `--` holds fewer than `MIN_REASON_LENGTH` letters or digits. A block `eslint-disable` or `oxlint-disable` that
* names the rule, including the `// eslint-disable` line-comment form oxlint honors, always counts, even with a
* reason: one sentence cannot vouch for every read under it. Blanket
* directives that name no rule are left to `onyxLintBypass.ts`. A file Babel cannot parse fails closed:
* every disable naming the rule counts. An `eslint` config comment naming the rule always counts.
*/
import {
DIRECTIVE_SEARCH_TERM,
collectDirectiveMatches,
configDirectiveNamesBan,
directiveDescription,
directiveTargetsBan,
extractCommentsByRegex,
isCheckedSourceFile,
lineNumberAtOffset,
parseSource,
} from './onyxLintBypass';
/** Fewest letters and digits after `--` that count as a reason, so `-- ok`, `-- todo` or a row of dashes does not pass. */
const MIN_REASON_LENGTH = 12;
type ReasonedRule = {
id: string;
name: string;
appliesTo: (file: string) => boolean;
searchTerms: string[];
message: string;
};
const ONYX_SNAPSHOT_KEY_RULE: ReasonedRule = {
id: 'rulesdir/no-onyx-get-snapshot-key',
name: 'no-onyx-get-snapshot-key',
appliesTo: isCheckedSourceFile,
searchTerms: ['no-onyx-get-snapshot-key', DIRECTIVE_SEARCH_TERM],
message: `A disable of rulesdir/no-onyx-get-snapshot-key gives no reason, or is a block eslint-disable or oxlint-disable or an eslint config comment. Each exception must say, on the line it covers, why live data is right for this Search snapshot key, so a reviewer can judge it. Use eslint-disable-next-line or eslint-disable-line with a reason of at least ${MIN_REASON_LENGTH} letters or digits after \`--\`, for example: \`// eslint-disable-next-line rulesdir/no-onyx-get-snapshot-key -- replaces a module-level Onyx.connect used only by handlers, never read a snapshot\`.`,
};
const REASONED_RULES: ReasonedRule[] = [ONYX_SNAPSHOT_KEY_RULE];
type UnreasonedDisable = {
file: string;
line: number;
};
function hasReason(args: string): boolean {
return (directiveDescription(args).match(/[\p{L}\p{N}]/gu) ?? []).length >= MIN_REASON_LENGTH;
}
/** Every disable of `rule` in `source` that lacks an acceptable reason. Lines are 1-based. */
function collectUnreasonedDisables(source: string, filePath: string, rule: ReasonedRule = ONYX_SNAPSHOT_KEY_RULE): UnreasonedDisable[] {
const parsed = parseSource(source, filePath);
// Fail closed: when Babel cannot parse the file, comments come from a regex and every directive naming the rule counts
const comments = parsed ? parsed.comments : extractCommentsByRegex(source);
const unreasoned: UnreasonedDisable[] = [];
for (const match of collectDirectiveMatches(comments, source, 'disable')) {
if (!directiveTargetsBan(match.args, rule)) {
continue;
}
const isLineForm = match.kind === '-line' || match.kind === '-next-line';
if (parsed && isLineForm && hasReason(match.args)) {
continue;
}
unreasoned.push({file: filePath, line: lineNumberAtOffset(source, match.index)});
}
// A config comment turns the rule off for the whole file, so no reason can cover it
for (const match of collectDirectiveMatches(comments, source, 'config')) {
if (configDirectiveNamesBan(match.args, rule)) {
unreasoned.push({file: filePath, line: lineNumberAtOffset(source, match.index)});
}
}
return unreasoned.sort((a, b) => a.line - b.line);
}
export {MIN_REASON_LENGTH, ONYX_SNAPSHOT_KEY_RULE, REASONED_RULES, collectUnreasonedDisables};
export type {ReasonedRule};