// Fails the build if a DOM `on…` handler property is assigned an expression-bodied arrow function, e.g. // input.onkeydown = (e) => e.key === "Enter" && submit(); // Such a handler returns `false` for every other key, and a DOM `on…` handler that returns `false` cancels the // event, so the field silently stops accepting typing. Use a block body: `(e) => { if (…) submit(); }`. import { readFileSync, readdirSync, statSync } from "node:fs"; import { join } from "node:path"; // An `on…` handler property assigned an arrow with an expression body (not `{`) that can evaluate to false. const handler = /\.on[a-z]+\s*=\s*(?:async\s*)?\([^)]*\)\s*=>(?!\s*\{)(.*)$/; const canBeFalse = /&&|\|\||[=!]==?|[<>]=?/; const files = []; (function walk(dir) { for (const name of readdirSync(dir)) { const p = join(dir, name); if (statSync(p).isDirectory()) walk(p); else if (p.endsWith(".ts")) files.push(p); } })("src"); let failed = false; for (const file of files) { readFileSync(file, "utf8") .split("\n") .forEach((line, i) => { const m = handler.exec(line); if (m && canBeFalse.test(m[1])) { console.error(`${file}:${i + 1}: on… handler needs a block body (a returned false cancels the event)\n ${line.trim()}`); failed = true; } }); } if (failed) process.exit(1);