// @ts-check import { addErrorDetailIf } from "../helpers/helpers.cjs"; import { getDescendantsByType, getParentOfType } from "../helpers/micromark-helpers.cjs"; import { filterByTypesCached } from "./cache.mjs"; const markerToStyle = { "-": "dash", "+": "plus", "*": "asterisk" }; const styleToMarker = { "dash": "-", "plus": "+", "asterisk": "*" }; const differentItemStyle = { "dash": "plus", "plus": "asterisk", "asterisk": "dash" }; const validStyles = new Set([ "asterisk", "consistent", "dash", "plus", "sublist" ]); /** @type {import("markdownlint").Rule} */ export default { "names": [ "MD004", "ul-style" ], "description": "Unordered list style", "tags": [ "bullet", "ul" ], "parser": "micromark", "function": function MD004(params, onError) { const style = String(params.config.style || "consistent"); let expectedStyle = validStyles.has(style) ? style : "dash"; const nestingStyles = []; for (const listUnordered of filterByTypesCached([ "listUnordered" ])) { let nesting = 0; if (style === "sublist") { /** @type {import("markdownlint").MicromarkToken | null} */ let parent = listUnordered; // @ts-ignore while ((parent = getParentOfType(parent, [ "listOrdered", "listUnordered" ]))) { nesting++; } } const listItemMarkers = getDescendantsByType(listUnordered, [ "listItemPrefix", "listItemMarker" ]); for (const listItemMarker of listItemMarkers) { const itemStyle = markerToStyle[listItemMarker.text]; if (style === "sublist") { if (!nestingStyles[nesting]) { nestingStyles[nesting] = (itemStyle === nestingStyles[nesting - 1]) ? differentItemStyle[itemStyle] : itemStyle; } expectedStyle = nestingStyles[nesting]; } else if (expectedStyle === "consistent") { expectedStyle = itemStyle; } const column = listItemMarker.startColumn; const length = listItemMarker.endColumn - listItemMarker.startColumn; addErrorDetailIf( onError, listItemMarker.startLine, expectedStyle, itemStyle, undefined, undefined, [ column, length ], { "editColumn": column, "deleteCount": length, "insertText": styleToMarker[expectedStyle] } ); } } } };