// @ts-check /** * @typedef {object} ErrorInfo * @property {number} lineNumber * @property {string} [context] * @property {string} [detail] * @property {[number, number]} [range] */ /** * @typedef {object} Params * @property {string[]} lines */ /** @typedef {(error: ErrorInfo) => void} OnError */ /** * @typedef {object} Rule * @property {string[]} names * @property {string} description * @property {string[]} tags * @property {(params: Params, onError: OnError) => void} function */ /** @type {Rule} */ export default { names: ["one-sentence-per-line"], description: "Each sentence must be on its own line", tags: ["sentences"], function: function (params, onError) { let inFence = false; params.lines.forEach((line, index) => { if (/^```/.test(line)) { inFence = !inFence; return; } if (inFence) return; // Skip headings, blank lines, HTML, table rows if (/^(#|\s*[|<]|>|\s*$)/.test(line)) return; // Strip list marker before checking const text = line.replace(/^\s*(?:[-*+]|\d+\.)\s+/, ""); if (/[.!?]\s+[A-Z]/.test(text)) { onError({ lineNumber: index + 1, context: text.trim() }); } }); }, };