A new rule that cannot be turned on because 200 existing files break it, so it never gets turned on at all.
Add "Gradual rule rollout" from ntent to this repo.
Fetch https://ntent.app/r/f/graduating-warning as plain text. Merge it into eslint.config.mjs, at the top of the file, beside the rules block, without disturbing what is already there.
Then check it: Lint warns on the listed files and errors everywhere else. Move your clock past the date without emptying the list and the config refuses to load, naming what is left.Reads https://ntent.app/r/f/graduating-warning
Read the code
eslint.config.mjs · at the top of the file, beside the rules block
36 lines
import { designRules, copyRules } from './scripts/eslint-rules.mjs'
// GRANDFATHER THE FILES, NOT THE RULE.
//
// The usual version of this is a severity of 'warn' and a comment carrying the
// date, which never graduates: nothing reads a comment. Two things fix that.
// The rule is an error from day one, so nothing NEW can break it. The files
// that already break it are listed by name, and the list expires.
//
// Severity cannot be split inside one no-restricted-syntax entry, so the
// exemption is a second config block scoped to those files. Flat config applies
// the last matching block, which is what makes this work.
const GRADUATES_ON = '2026-11-01'
const LEGACY = [
// 14 files on the day this landed. Reasoning in DECISIONS.md so nobody has to
// rediscover the scope. Delete lines as you fix them.
'src/features/reports/SummaryTable.tsx',
'src/features/billing/InvoiceRow.tsx',
]
if (Date.now() >= Date.parse(GRADUATES_ON) && LEGACY.length) {
throw new Error(
`The arbitrary-size rollout graduated on ${GRADUATES_ON} and ${LEGACY.length} file(s) ` +
`are still exempt. Fix them, or agree a new date in DECISIONS.md and move it here.`,
)
}
export default [
{ rules: { 'no-restricted-syntax': ['error', ...designRules, ...copyRules] } },
// Warnings here, errors everywhere else. Same rules either way: the exemption
// is the severity, never the rule, so a file on this list is still told.
{
files: LEGACY,
rules: { 'no-restricted-syntax': ['warn', ...designRules, ...copyRules] },
},
]Success check: Lint warns on the listed files and errors everywhere else. Move your clock past the date without emptying the list and the config refuses to load, naming what is left.