UNPKG

markdownlint

Version:

A Node.js style checker and lint tool for Markdown/CommonMark files.

97 lines (92 loc) 3.52 kB
// @ts-check import { addErrorDetailIf } from "../helpers/helpers.cjs"; import { getDescendantsByType } from "../helpers/micromark-helpers.cjs"; import { filterByTypesCached } from "./cache.mjs"; const listStyleExamples = { "one": "1/1/1", "ordered": "1/2/3", "zero": "0/0/0" }; const listStyles = Object.keys(listStyleExamples); /** * Gets the column and text of an ordered list item prefix token. * * @param {import("markdownlint").MicromarkToken} listItemPrefix List item prefix token. * @returns {{startColumn: number, endColumn: number, text: string, number: number}} List item value column and text. */ function getOrderedListItemValue(listItemPrefix) { const listItemValue = getDescendantsByType(listItemPrefix, [ "listItemValue" ])[0]; const { startColumn, endColumn, text } = listItemValue; return { startColumn, endColumn, text, "number": Number(text) }; } /** @type {import("markdownlint").Rule} */ export default { "names": [ "MD029", "ol-prefix" ], "description": "Ordered list item prefix", "tags": [ "ol" ], "parser": "micromark", "function": function MD029(params, onError) { const style = String(params.config.style); for (const listOrdered of filterByTypesCached([ "listOrdered" ])) { const listItemPrefixes = getDescendantsByType(listOrdered, [ "listItemPrefix" ]); let expectedNumber = 1; let incrementing = false; let rightAligned = false; let zeroPrefixed = false; if (listItemPrefixes.length > 0) { // Check for right alignment const firstPrefixEndColumn = listItemPrefixes[0].endColumn; rightAligned = listItemPrefixes.every((prefix) => prefix.endColumn === firstPrefixEndColumn); if (listItemPrefixes.length > 1) { // Check for incrementing number pattern 1/2/3 or 0/1/2 const firstValue = getOrderedListItemValue(listItemPrefixes[0]); const secondValue = getOrderedListItemValue(listItemPrefixes[1]); if ((secondValue.number !== 1) || (firstValue.number === 0)) { incrementing = true; if (firstValue.number === 0) { expectedNumber = 0; } } zeroPrefixed = firstValue.text.startsWith("0"); } } // Determine effective style const listStyle = listStyles.includes(style) ? style : (incrementing ? "ordered" : "one"); if (listStyle === "zero") { expectedNumber = 0; } else if (listStyle === "one") { expectedNumber = 1; } // Validate each list item marker for (const listItemPrefix of listItemPrefixes) { const expectedText = expectedNumber.toString(); const { endColumn, startColumn, "number": actualNumber } = getOrderedListItemValue(listItemPrefix); const length = endColumn - startColumn; const fixInfo = { "editColumn": startColumn, "deleteCount": length, "insertText": rightAligned ? expectedText.padStart(length, (zeroPrefixed ? "0" : " ")) : expectedText }; addErrorDetailIf( onError, listItemPrefix.startLine, expectedNumber, actualNumber, // @ts-ignore "Style: " + listStyleExamples[listStyle], undefined, [ listItemPrefix.startColumn, listItemPrefix.endColumn - listItemPrefix.startColumn ], fixInfo ); if (listStyle === "ordered") { expectedNumber++; } } } } };