Repository navigation
mdrefine 0.6.0
Changed
-
A workspace with a markdownlint or Prettier configuration and no mdrefine configuration now gets the settings converted from them instead of the
recommendedpreset, solintandformatcan report other rules, at other severities, ignore other files, and exit differently than in 0.5.0. Upgrading: to keep therecommendedpreset, create an mdrefine configuration, for example with"extends": ["recommended"], or pass--no-competitor-config; to adopt the converted settings, write them withmdrefine config convert --write. Files that the fallback cannot use apply nothing, and without a usable one therecommendedpreset applies as before: JavaScript and TypeScript configurations, which it never runs, TOML and JSON5 ones, and a Prettier configuration whose.prettierignoreprovably excludes all Markdown, so that the coexistence setup in the integration guide keeps checking documents; one with a negated entry never qualifies. The fallback applies none of the patterns of a.prettierignorewith a negated entry, which conversion cannot express, rather than apply its exclusions without their exceptions, which can select no documents..markdownlintignoreis not converted, so the documents it excludes are checked; the fallback names it. The fallback prints one line on stderr for the settings it uses and one for each file it leaves out, with paths from the workspace root, and none with--quiet;config explainlists the files left out underconversion.unused(#788). -
An error about a rule setting that cannot be used, such as an unknown rule or invalid options, names the configuration file that holds it (#788).
-
Bare
formaton files prints a diff and exits1when formatting would change a document. Scripts needing an exit-0preview should useformat --diff.--check --diffprints the diff and fails on changes. Remaining errors still exit1, and operational failures take precedence with exit2. Stdin keeps its formatted output and exit behavior (#788). -
lintandformatread stdin when--stdin-filepathis given without paths, and reject it with paths other than-. Help includes examples, workspace scope and exit codes; nested configuration help selects the named subcommand. Mistakencheck/fixcommands and lint modes suggestformat.rulesaccepts--root; empty selections print a notice on stderr, and formatting previews name--writeas the next step (#788). -
content/image-alt, inrecommended, also reports an HTML<img>element without analtattribute, where renderers certainly show it.alt="", which marks a decorative image, is accepted, and so are images hidden byaria-hidden="true",hidden,inert, or a hiding style outside GitHub, whose sanitizer removes those attributes. Blank Markdown alternative text is still reported, because Markdown cannot mark an image as decorative. Images in content that may not be shown, such as<template>or<video>, after HTML whose reading is uncertain, after an end tag that a browser may ignore inside a hidden element, in unused footnote definitions, and in VitePress near Vue syntax are not reported (#694). -
The documentation describes the behavior of the version it belongs to, without version notes; this changelog keeps the history. Each rule section opens with what the rule does, then states the severity that each preset gives it, as the rule index does (#746).
-
style/tableformats tables inside lists and block quotes with all of its options, as Prettier aligns them, where it used to leave them as written. Each row keeps the quote markers, indentation, and line ending before it, and widths are measured after them, so a second run changes nothing. A header on its list item's marker line keeps its leading pipe or its lack of one, because markdown-it reads- a | babove an indented delimiter row as a table outside the list. Tables in footnote definitions, indented tables outside containers, and tables that renderer protections hold stay as written (#710). -
style/headingremoves heading-owned indentation, including Setext text and underlines, while retaining container prefixes. Its newclosing: "add"option writes matching closing hash sequences on ATX headings, including the deeper levels of the Setext style. Renderer protections still apply. This interface addition belongs to the next minor release (#680, #700). -
style/block-spacingalso separates fenced code blocks that have a closing fence, and tables, from the blocks around them, as markdownlint'sMD031andMD058and Prettier do. Blocks inside list items and footnote definitions stay as written, and a table is separated only from blocks that every renderer reads as separate from it: text below a table, which GFM reads as another row, stays (#684, #697). -
style/headingwrites one space between ATX heading hashes and text, including tabs and retained closing sequences. Empty headings need no separator. Protected Forgejo and Gitea attribute suffixes stay as written (#676, #678). -
The comparison with Prettier and markdownlint lists every markdownlint rule and every Prettier Markdown option and formatting choice, with the issue that tracks each check not yet covered (#668).
-
Tokenizer identity verification partitions the unchanged deterministic corpus into bounded unit cases while retaining every source, selection and assertion under the existing deadline (#654).
-
Release evidence selects the latest check-run identity, preserving pending and unsuccessful reruns without letting an older completed null-start check hide a newer success (#647).
-
The shipped internal protection-source declaration uses its existing point type without an undeclared direct type import. Installed package checks cover that declaration with npm and strict pnpm; public entry points are unchanged (#655).
-
Maintainer guidance distinguishes intentional first-occurrence fixture rewrites from sanitizers, preserving intended bytes when triaging scanner alerts (#643).
-
Workspace memory verification separates document/cache lifecycle checks from a fresh compiled-product process's retained-text sample. The existing source allowance remains enforced, with numeric failure evidence and opt-in heap diagnostics (#413).
-
Filesystem discovery and selection, on-demand related-file lookup, and in-place write/recovery now have separate internal owners. Public discovery, selection and filesystem safety contracts are preserved (#620).
-
The Obsidian verification harness uses only its service-provided browser and ChromeDriver binaries, removing unused automatic-download FTP/PAC and
extract-zipdependencies. Missing or incomplete executables fail explicitly; native host downloads, caching and version selection retain their existing owners (#639, #640). -
Dependency updates follow parser, VitePress/reference and tooling maintenance units across dependency declarations. Existing compatibility deferrals remain, with explicit revisit checks for peers, tool pins and verified host baselines (#636, #637).
-
Five parser extensions used only to build the bundled parser are development dependencies. Installed consumers retain the required runtime utilities and public plugin types; package checks cover both parser paths and the shipped internal declarations. Release audits include bundled inputs regardless of npm's development flag (#624).
-
Maintained internal parser algorithms live in directly checked TypeScript source, with source guards and generated provenance linking their rationale, ownership and removal conditions to the dependency records. Parser behavior, extension compatibility and the bundled license graph are preserved (#622, #623).
-
Test suites separate engine, configuration, plugin, rule and comparison responsibilities. Shared parser/CLI support and authored native fixtures have explicit ownership, preserving their cases and native workload plans (#634).
-
Internal renderer protections have named collectors and one cached result; rule registration and semantic acceptance are separate from engine phase orchestration, preserving the public API and formatting behavior (#618, #619).
-
Workflow regression tests check YAML structure while retaining exact evidence identities. Contributor and release policy is authoritative; agent navigation permits cohesive internal modules, and optional review bots have no guaranteed availability (#635, #641, #642).
-
Native renderer reports validate the intended documents, widths, views and calibration controls, including unchanged formatting attempts. Obsidian PR/release checks pin the app, installer and Docker base and record observed runtime/image identity; weekly/manual checks retain explicit latest coverage (#632, #633).
-
Linux 24 CI collects unit and coverage evidence in one required, isolated job. Hosted release preparation reuses exact-commit push/main CI results while freshly building and testing its publication tarball; local preparation retains the complete checks (#628, #629).
-
Builds prepare fresh staged output and reject unexpected packaged files. CI and release preparation reuse one verified build within their invocation, while standalone commands remain self-contained (#626, #627).
-
Native PR checks classify semantic inputs and resolved dependency roles, avoid unnecessary hosts for VitePress-only dependency updates or offline-tool changes, and enforce applicable comparisons through one required result. Release still compares every native host at its exact commit (#630, #631).
-
Dependency automation also defers the markdown-it 15 lines of
@mdit/plugin-container,@mdit/plugin-emojiand@mdit/plugin-tasklistassessed in #608, #610 and #611, under the same reconsideration conditions as the earlier deferrals.
Added
-
Honor markdownlint inline configuration and suppression comments, Prettier ignore comments, and
requirePragmaduring competitor configuration fallback. Written mdrefine configurations ignore these comments. Runtime provenance keeps each tool's enabled checks separate, attributes combined rules' findings to their source checks, and withholds edits that disagree across suppression regions (#723). -
config converttranslates markdownlint and Prettier settings into a configuration, with exclusive--write, source and conflict reporting, and in-memory fallback when no mdrefine configuration exists. Disable fallback with--no-competitor-config; fallback never executes competitor scripts and preserves source-relative patterns within documented dialect subsets, reporting unsupported patterns for manual migration. Directory exclusions retain their descendants when converting inside an excluded directory. Prettier brace literals and terminal globstars retain their matching branches; ignore conversion reports its case-sensitive approximation. Converted overrides accumulate throughruleUpdates. A markdownlint-cli2 file withoutconfigreads as markdownlint's defaults, as markdownlint-cli2 does, and MD007'sstart_indentedsetsstyle/quote-marker'slistItemsto false, which the two would otherwise undo. The library exportsconvertConfig,competitorMappingsand theConversionReport,CompetitorMappingandCompetitorCompatibilitytypes frommdrefine, andloadConfig,loadCompetitorConfig,competitorFilesandLoadedConfigfrommdrefine/node;lintandformattake acompatibilityoption (#669). Without--root,--writecreates the configuration wherelintlooks for the workspace's configuration and prints its path;--config,--dialect,--no-competitor-config, and a--rootthat is not a directory are usage errors. An extensionless.prettierrcis read as YAML, as Prettier reads it, and errors in competitor configuration files name the file (#788). -
Opt-in
style/line-endingselects LF, CRLF, CR, or the first ending withendOfLine, preserving hard breaks and literal content bytes (#712). -
External formatter entries can select YAML and TOML
frontMatterbodies, with the existing command limits and a guard for surrounding Markdown. Metadata semantics remain the configured tool's responsibility; no YAML or TOML printer is bundled (#718). -
Opt-in
content/html-elementreports HTML elements, once each at the opening tag, in HTML blocks and inline HTML, with markdownlint's MD033 optionsallowedElementsandtableAllowedElements, compared without letter case. Comments, mdrefine's suppression comments included, autolinks such as<user@example.com>, code, and the text of raw-text elements are not elements. VitePress components count as elements; VitePress HTML blocks stay opaque. Where the reading of the HTML becomes uncertain, as after an unclosed comment, the rest is not reported (#685). -
Two style rules in
recommendedwrite the prefixes of block quotes and their contents.style/quote-markerwrites one space after each>, removing extra spaces and adding a missing one, and takes markdownlint's MD027listItemsoption, under which it moves a list that starts a quote's line with its lines.style/lazy-continuationwrites a quote's>and a list item's or footnote definition's indentation on lazy continuation lines, keeping a footnote's first line on its label's line. Both leave the source as written where the preferred form could read otherwise: indented code, a lazy line that could start a block, and, in Obsidian, a tag directly after>, a callout's marker after two spaces, and the lazy lines whose prefixes change Live Preview, which are reported as left as written (#682, #713). -
Opt-in
structure/heading-templatechecks a configured heading sequence with wildcards and optional case matching, using the document's independent outline (#692). -
Opt-in
content/proper-namesreports incorrect capitalization of configured names in prose, with optional code-block and HTML checking; inline code and destinations remain excluded (#693). -
Opt-in
style/code-blockchecksconsistent,fenced, orindentedcode form, converting indented blocks to safe fences outside VitePress and reporting other mismatches without losing code or metadata (#695). -
style/block-spacingtakeslinesAboveandlinesBelow, the blank lines that a heading needs above and below it, as one count or one per level, andincludeFrontMatter, which also separates a heading directly below front matter. The counts are minimums, and the default stays one blank line. A configuration whose counts exceedstyle/blank-lines'maximumis refused when it is read, instead of leaving the two rules to undo each other (#679). -
structure/heading-incrementtakesfrontMatterTitle, with the values ofstructure/initial-heading's, which treats a front-matter title as a level-1 heading, so that the first heading may be at most level 2. It is off by default, because the supported renderers do not show such a title as a heading (#699). -
Three report-only rules, in no preset, cover markdownlint's MD037, MD038, and MD039:
content/emphasis-spacingreports emphasis or strong markers with blank space just inside them, as in** bold **, where the parser reads emphasis once the spaces are gone;content/inline-code-spacingreports blank space at the ends of a code span that renderers show as part of the code; andlinks/text-spacingreports blank space at the ends of link text. They leave alone a single*or_with spaces on both sides, as ina * b * c, arithmetic, markers inside words, wildcard patterns such as*.md *, identifiers, emoji codes, the content of inline HTML elements such as<code>, JSX comments, and code whose blank space shows syntax or indentation, as in-, or keeps a backtick apart (#689, #690, #691). -
Opt-in
content/line-lengthreports heading, code and table overflow with per-kind limits, switches and display-column measurement (#673). -
style/wraptakesmode: "never"to join paragraphs onto one line with the same safety protections as wrapping; preset defaults remain unchanged (#711). -
Opt-in
style/link-titlenormalizes link, image, and definition title delimiters withdouble,single,parentheses, andconsistentchoices. It retains source escapes and line breaks and never changes destinations (#716). -
Opt-in
style/definition-spacingwrites one space after a reference definition's colon and before its title, also where the colon has none. Multiline definitions, tabbed definitions, footnotes, and Obsidian spacing stay as written (#717). -
Opt-in
links/reversedreports likely reversed links such as(text)[https://example.com], without a fix, since a fix would turn text into a link. Citations, versions, numbers, call syntax and defined labels are not reported (#672). -
Opt-in
links/bare-urlreports bare URLs and email addresses, and adds angle brackets where the dialect already links the address and the brackets keep the same link. CommonMark, which shows them as text, is only reported (#686). -
Opt-in
links/allowed-notationrestricts link and image notations with markdownlint's MD054 options, all allowed by default, and turns a title-free link whose text is its URL into an autolink where that renders the same (#696). -
Opt-in
style/no-tabsreports tabs withcodeBlocks,ignoredCodeLanguages, andspacesPerTab, replacing safe prose and indentation tabs while keeping literal content and Obsidian list nesting (#671). -
Opt-in
style/prose-spacecollapses repeated prose spaces without moving line breaks, preserving literal content, indentation and hard breaks (#720). -
links/validtakesignoredFragments, a regular expression for fragments not to check, validated when the configuration is read, andexactCase, which also reports a fragment that reaches its anchor only because the renderer ignores letter case. By default fragments still match as each dialect's renderer matches them, and anchors are still generated per dialect.Workspace.resolvereturns the anchor's own spelling asfragmentAnchor(#705). -
links/undefined-referencetakesshortcutSyntax, which also reports an undefined shortcut reference such as[label], andignoredLabels. Shortcuts leave alone task marks, alert and callout markers, Obsidian inline footnotes, Forgejo and Gitea shortlinks, inline HTML elements, and URLs written as text, delimited as GFM's autolink literals are, besides the existing indexes, code, math, and HTML. Both are off by default (#706). -
style/trailing-spacetakeshardBreakSpacesfor exact space-style breaks,listItemEmptyLinesto keep blank list-line indentation, and report-onlycodeBlocks.style/thematic-breakaccepts any exact break text and follows the first exact text withconsistent.style/list-indenttakesstartIndentedandstartIndentfor first-level indentation. Preset defaults stay unchanged; existing protections remain, and the thematic-break protection for a compact hyphen line now also covers longer runs such as-----(#702, #687, #701). -
style/inline-codewrites a code span with the fewest backticks its content allows, keeping padding and table cell escapes, and keeps its delimiters where a shorter run could pair with an earlier one.style/code-fenceremoves spaces and tabs before the info string, keeping metadata and nested fence lengths. These extend the existing rules without new options (#714, #715). -
style/tabletakespipeStylefor explicit, document-consistent, or per-table borders,style: "tight",alignedDelimiterfor header-aligned delimiters, andpadding: "alignment"for padding by column alignment. Defaults remain aligned cells with both borders and padding on the right; required pipes and renderer protections remain (#708, #709, #719). -
links/unused-definitionandlinks/emptytakeignoredDefinitions, the labels of definitions written as comments, which default to["//"].recommendedno longer reports[//]: # (comment)while no reference uses its label; footnotes are still reported (#707). -
style/emphasisandstyle/strongacceptmarker: "consistent": the first parsed emphasis or strong text chooses its marker for the document. Preset defaults stay unchanged (#703, #704). -
Three report-only rules, in no preset, cover markdownlint's MD026, MD036, and MD059 with their options:
structure/heading-punctuationreports a heading that ends with sentence punctuation,structure/emphasis-headinga paragraph of only emphasized or strong text, andlinks/descriptive-texta link whose whole text, such as "here", does not say where it leads. They read headings and links as each dialect's renderer shows them, and leave abbreviations, ellipses, and emoji shortcodes at the end of a heading alone (#681, #688, #698). -
Three report-only rules, in no preset, cover markdownlint's MD014, MD018, MD020, and MD028:
structure/unspaced-headingreports text such as#Headingor#Heading#that would be a heading with a space after its hashes,structure/split-quotea blank line between two block quotes, andcontent/shell-prompta shell code block whose every command starts with a$or%prompt and that shows no output. They leave Obsidian tags, issue references such as#123, shebangs, and a blank line before an alert or callout alone (#674, #675, #677, #683).
Fixed
-
style/wrapindents a quoted list item's continuation lines to the item's content where the quote marker directly precedes the list marker, as in>10) text. It indented them one column short, andstyle/lazy-continuationthen added the column back, so formatting oscillated and left the document unchanged (#781). -
In VitePress, rules leave as written a list item whose first line markdown-it reads as a table header, as in
* a | babove--|--: markdown-it tries a table before a list, so it shows a table whose first cell holds the marker.style/list-markerandstyle/list-indentproposed edits there that the engine refused, andstyle/tableformatted the table by the list's reading. The list rules also keep a list whose item's table markdown-it reads only because of its indentation, as in* a | babove--|--, and the lists next to such a list, which another marker would join to it (#763). -
style/headingkeeps the indentation of a heading line that is---or+++alone, such as an underline---, below a first line of the same fence that no line closes, where removing it would close front matter and make the lines above it front matter. It proposed that edit, which the engine refused, in GitHub, Forgejo, Gitea and CommonMark (#764). -
In VitePress, a list item or paragraph that a component line such as
<MyComponent />ends is left as written, as other blocks with a component that mdast cannot represent are. VitePress ends the paragraph at the component line, sostyle/lazy-continuationproposed indenting a lazy line below it, which would move that line into the list item, and the engine refused it. The list rules keep a list directly beside such a list as written, because another marker would join the two (#769). -
GitHub link checks and workspace indexes read definitions directly above an interrupting table as visible text, including in lists and quotes, while keeping formatting from hiding that text. A Setext underline line such as
===directly below the definitions keeps them, as on GitHub, andstyle/wrapno longer joins it with the next line, which made GitHub show the definitions as text (#732). -
links/validaccepts a GitHub file fragment whoseuser-content-prefix is not lowercase, such as#User-Content-setup, as GitHub's file view does, and itsexactCaseoption reports the lowercase spelling (#739). -
style/wrap,content/emphasis-spacing,style/line-endingandstructure/heading-templateread changed text again with the configuration's syntax plugins, as the document is read, rather than without them (#752). -
Emphasis and strong marker rules keep pairs crossing protected Obsidian HTML or VitePress component blocks without proposing refused edits (#766, #768).
-
A table whose header row is a complete HTML tag alone on its line, directly below text, as in
<b title="|">above|-|-|, is now read as a table, as GitHub, Forgejo, Gitea and VitePress read it, instead of as HTML. Rules formatted it by the wrong reading.style/block-spacingleaves such a table directly below its text, where a blank line would make the tag line HTML, and adds no blank line that would end a block quote above a lazy header line. In Forgejo and Gitea, rules leave a table in a list item or block quote as written where the lines below it continue it lazily, which those renderers read as more rows. In VitePress,style/tableformats only the tables that VitePress reads on the same lines and leaves the others, such as a one-column table whose header has no pipe, as written (#770). -
A lazy line below a list item's or block quote's paragraph that could start an HTML block of the kind that cannot interrupt a paragraph, such as
<b>alone on its line, now continues the paragraph, as CommonMark, Forgejo, Gitea, VitePress and Obsidian read it, instead of ending the container. Rules formatted it by the wrong reading:style/block-spacingadded a blank line after the tag, and the item lost its last line. GitHub reads it otherwise: it ends the containers there and shows the tag and the lines below it, up to a blank line, as HTML, with Markdown such as*as literal text. In the GitHub dialect, rules now leave those containers and lines as written, also where CommonMark reads the paragraph text as a Setext heading or as link definitions before the tag line. HTML that may interrupt a paragraph, such as<div>or a comment, still ends the paragraph (#762). -
In Obsidian, rules no longer change a list item that starts on a footnote's label line, as in
[^1]: - item, below a run of three or more backticks that nothing closes. Live Preview shows such an item as code carried from the run (#670). -
style/block-spacingno longer proposes a blank line that the engine then refuses as a change of meaning. Below TOML front matter, it keeps a block quote that the reading without front matter continues below a list, and in VitePress it adds nothing inside a block of VitePress's own reading, such as a paragraph that it continues lazily over<b>or a footnote definition below a list item, or an unclosed fence that it reads below a table (#748). -
In VitePress,
style/wrapreflows a paragraph that holds braces or^[only in code spans, such as`{ "a": 1 }`beside a link or in a task item: only attribute blocks and inline footnotes that VitePress reads, and Vue whitespace between inline elements, keep a paragraph as written, and other rules no longer leave such code spans alone either. A paragraph that VitePress syntax keeps as written is reported byreportUnreflowedwith its reason instead of being skipped silently (#755). -
Forgejo and Gitea retain native table rows misread as ordered lists, tables hidden inside quoted paragraphs, and URL-adjacent backslash hard breaks. Shared edit protections cover builtin rules and plugins while leaving independent prose editable (#661, #663, #664).