Skip to content

mdrefine 0.2.0-rc.2

Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 06 Oct 15:37
Immutable release. Only release title and notes can be modified.
9ca96b0

mdrefine 0.2.0-rc.2

Second release candidate of the 0.2 feature release of Markdown Tools. npm
package: mdrefine; executable: mdtools. Requires Node.js 22.12 or newer.
Upgrade with:

npm install --save-dev --save-exact mdrefine@0.2.0-rc.2

Changes below are relative to 0.2.0-rc.1. Release candidates use npm's rc
channel, and latest follows the newest prerelease until the first stable
release.

Check before upgrading

Most of this release makes the tool more careful, but five changes can alter
what an existing setup does:

  • The dialect assumed when none is named. A workspace without a
    configuration file, or with one that names no dialect, was read as CommonMark.
    It is now read as GitHub Markdown, and documents inside an Obsidian vault as
    Obsidian notes. Name "dialect": "commonmark" to keep the old reading.
  • What counts as $ math. Two sums of money in a paragraph are no longer
    taken for a formula. Text that was left alone because of that is now wrapped
    and restyled like other prose.
  • Messages on stderr. The lint summary, the messages for paths and
    configuration files that cannot be used, and the messages for plugins that
    cannot be loaded are worded differently. Exit codes and --json output keep
    their meaning; a script should read those.
  • Plugin packages with two entries. A plugin package that declares both an
    import and a require entry is now loaded through import.
  • Library hosts. A source loader given to createWorkspace can be called
    more than once for the same file, and link resolutions have a new status and
    new fields. See compatibility.

A configuration that names its dialect, as the documentation has always
recommended, formats and lints the same documents as before. On a large
real-world Obsidian vault of several thousand notes, 0.2.0-rc.1 and this
version gave the same result for every file.

A safer reading when no dialect is named

In CommonMark a table, or a run of footnote definitions, is an ordinary
paragraph, so formatting without a configuration joined their lines, and in a
vault it joined lines that Obsidian shows separately and could break a wikilink
in two. When nothing names a dialect, the tool now assumes github, whose
syntax the Forgejo, Gitea, and Obsidian dialects build on, and obsidian for a
document inside a vault
(PR #130).

Only the dialect is assumed. The rules of the github and obsidian presets
still need the preset. Library callers get github as the default of lint,
format, resolveConfig, and createWorkspace, and can pass defaultDialect
to say what to assume without overriding a dialect that the configuration names.

Obsidian vaults wherever they are

A vault is recognized by its .obsidian folder, and a document belongs to the
nearest vault at or above its own folder
(PR #138):

  • A vault in a folder of a larger workspace, such as docs/vault/ in a
    repository, is read in the Obsidian dialect while the documents around it stay
    GitHub Markdown.
  • A folder of a vault used as the workspace root finds the vault above it.
  • A repository checked out inside a vault is not read as the vault's notes.
  • Whether paragraphs may be reflowed is read from each vault's own
    .obsidian/app.json, so vaults with different "Strict line breaks" settings
    can share a workspace.

Links follow the vault as well. In a vault below the workspace root, an Obsidian
link reaches only the vault's files, by paths counted from the vault's folder,
and links/path writes "style": "root" paths from there. A path that leads
out of the vault to an existing file is reported as
Local target is outside the vault
(PR #146).

When the workspace root is only a folder of a vault, the tool no longer takes
the folder for the whole vault. 0.2.0-rc.1 could shorten a link with
"style": "shortest" until it named a different note, and called every note
outside the folder missing. A link whose target the rest of the vault could
hold, or outdo, is now left as written and reported as
Local target could not be checked; explicit relative paths are still certain
and rewritten
(PR #147).

Two things that stopped every command now do not: a file named .obsidian,
which is ignored, and an .obsidian/app.json that is not valid JSON, which
leaves the line-break setting unverified with a warning.

Paths that cannot be read

A directory that the system does not permit the tool to read no longer stops the
run. It is skipped with one warning, the rest of the workspace is processed, and
a link into it is reported as Local target could not be checked, not as
missing. A selected document that cannot be read is reported with an
engine/unreadable-file error for that file; the others are processed, nothing
is written, and the exit code is 2. With --json, skipped paths are listed
under skipped
(PR #133).

Dollar math follows the renderer

Text between two $ signs was read as math in every dialect but CommonMark, so
a sentence such as Costs $5 *per* item and $6 *per* box. had part of its text
exempt from style rules, and a paragraph with two amounts far apart could not be
wrapped between them. Each dialect now applies the conditions that its renderer
was observed to apply
(PR #150 and
PR #164):

  • On GitHub, Forgejo, and Gitea, the opening $ may not follow a letter or
    digit, and the next $ may not be followed by a letter, a digit, or _.
    GitHub also wants a single $ to touch its formula.
  • In Obsidian, the opening $ may not be followed by a space, and a $ closes
    the formula only if no space stands before it and no digit follows it.
    Obsidian's Reading view and Live Preview differ in two cases; the tool follows
    the one that shows more as a formula.

From $5 to 10$ is a formula to all four renderers and stays one for the tool.
Display math is unchanged everywhere. The conditions come from about 150 cases
rendered by the renderers themselves, which are now part of the tests.

Plugins

  • A plugin named as a package is resolved the way Node.js resolves an import
    written in the configuration file, with a fallback to its CommonJS rules. A
    package that declares only an import entry now loads
    (PR #125).
  • Conditions set when Node.js is started, with --conditions or --no-addons
    on its command line or in NODE_OPTIONS, apply to that lookup
    (PR #144).
  • A plugin that cannot be loaded is reported as
    Cannot load plugin "NAME" named in FILE: REASON, and one without a usable
    default export as Invalid plugin "NAME" named in FILE. A plugin named by a
    path where no file is gets the reason no file at PATH.
  • The configuration reference describes the plugins key, and the plugin guide
    describes how a rule resolves a link: the call, the statuses, and the fields
    of the result
    (PR #158).

Clearer messages

  • lint ends with a count of what it reported, as in
    3 file(s) linted; 1 error(s), 2 warning(s). It used to end with
    0 would change, whatever it found.
  • A path given on the command line that does not exist, is of the wrong kind, or
    may not be read is answered in the tool's words, such as
    Workspace root does not exist: …, Configuration file cannot be read: …,
    Input does not exist: …, or Excluded path cannot be read: …, where the
    system's raw message used to appear.
  • An error in a configuration file names the file
    (PR #143).

Memory, speed, and installation size

  • The link index keeps the anchors of each linked document, not its parsed form.
    Linting a large real-world vault needed about half the memory
    (PR #135).
  • For the Obsidian dialect, the tables for finding a note by name held about 1.6
    kB for every file of the workspace, attachments included. They now hold about
    70 bytes per file
    (PR #149).
  • The package no longer installs KaTeX, which the tool never used: 4.6 MB less
    on disk, a faster start, and no KaTeX advisory in a consumer's npm audit.
    The math tokenizer is now the project's own, adapted from
    micromark-extension-math; its license notice ships as THIRD_PARTY_NOTICES
    (PR #150).
  • A document that is one very long list parses in about half the time, through
    an update of the syntax-tree builder
    (PR #155).

The benchmark page has new reference results and states two
limits of the parser that were measured for this release: one document needs
about 70 to 130 times its size in memory while it is parsed, and a single
paragraph of 100 kB or more that is full of inline constructs parses slowly.

Examples, benchmarks, and documentation

The package now ships working examples: starting configurations, workspaces
before and after formatting, a plugin, and library scripts, described on the
examples page. npm run benchmark measures the tool on
generated documents. The tests are split into a unit suite and an integration
suite that runs the built command line on real files and checks the examples and
the documentation against the tool
(PR #111). Code
blocks on the documentation site fit 80 columns
(PR #129).

Compatibility and verification

Existing 0.2.0-rc.1 configurations remain accepted, and the Node.js minimum is
unchanged. The API and configuration remain provisional until the first stable
release.

For library hosts:

  • lint, format, and resolveConfig accept defaultDialect. resolveConfig
    takes it as its fourth argument.
  • WorkspaceOptions adds unreadable, vaults, and rootInVault.
  • LinkResolution adds the status unreadable and the fields unreadable,
    vault, rooted, and outside. A rule must not rewrite a resolved link
    whose unreadable field is set; the built-in rules do not, and the engine
    refuses an edit that respells such a link.
  • A Markdown source given as a loader function can be called again when a later
    link to the same file is checked for a dialect with different anchors. It was
    called at most once before. Loaders must return the same text each time.
  • A loader that throws an error with the code EACCES or EPERM marks the file
    as unreadable instead of failing the run.

Runtime dependencies: import-meta-resolve@4.2.0,
micromark-factory-space@2.1.0, and micromark-util-character@2.1.1 are new
direct dependencies; micromark-extension-math is gone, and with it katex;
mdast-util-from-markdown is 2.1.0. Development tools have also been updated.

Known limits that this release documents and does not remove: GitHub accepts an
escaped \$ as the start of a formula and the tool does not; a workspace root
inside a vault cannot check links to notes outside it; and the two parser limits
named above. See design and limitations for the detailed
boundaries.

The release was checked with 902 automated tests on Linux and Windows, with the
installed package on all three CI targets, and by comparing reports before and
after on a large real-world vault.

🤖 Generated with Claude Fable 5.1 via Claude Code