Markdown Formatter

unlisted

by jjm.one

Format Markdown with Prettier, markdownlint, EditorConfig, and Obsidian-safe preservation rules.

Updated 16d agoLGPL-3.0
View on GitHub

Markdown Formatter

CI CodeQL OpenSSF Scorecard Latest release Downloads Obsidian 1.13.7+ Code style: Prettier License: LGPL v3

An Obsidian Markdown formatter shipped as both a desktop plugin and a standalone CI/local CLI. Both run the same pipeline — Prettier, markdownlint, .editorconfig, a shared project policy, and Obsidian-safe syntax preservation — so a note formats identically in Obsidian and in CI.

Repository: https://github.com/jjm-one/Markdown-Formatter-for-Obsidian

Pre-1.0. Not in Obsidian's Community Plugins browser yet — install with BRAT or manually. The formatting policy may still change between 0.x releases; keep backups.

Documentation

Start here:

  • Obsidian plugin guide — install, first format, every setting and command, troubleshooting. Written for people who have never used a formatter.
  • Command-line tool — format a whole folder from a terminal: every option and exit code, plus git-hook, CI, and Docker recipes.

Reference:

What it does

  • Formats the active note on demand (command, hotkey, ribbon) or automatically on modify, open, or close — every automatic trigger is off by default.
  • Resolves .editorconfig and trusted Prettier configuration per note, and applies configurable markdownlint fixes.
  • Reads a committed vault-level .markdown-formatter.json policy and a gitignore-style .markdown-formatter-ignore, both shared by the plugin and the CLI.
  • Preserves Obsidian syntax by default: Properties/frontmatter, wikilinks, embeds, callouts, block IDs, comments, MathJax, tags, inline footnotes, highlights, and fenced code blocks.
  • Every configuration source reloads automatically — no Obsidian restart needed.

Install the plugin

BRAT (recommended). Install BRAT from Community Plugins, run BRAT: Add a beta plugin for testing, and paste https://github.com/jjm-one/Markdown-Formatter-for-Obsidian. BRAT keeps it updated.

Manual. Download main.js, manifest.json, and styles.css from the latest release into <Vault>/.obsidian/plugins/markdown-formatter/, reload Obsidian, and enable it.

Full walkthrough and the rolling develop-latest / main-latest development channels: Obsidian plugin guide → Installing.

Use the CLI

Every release attaches markdown-formatter-cli.cjs, a bundled Node.js 24 CLI that runs the same formatter core:

node markdown-formatter-cli.cjs check .    # exit 1 if formatting is needed; never writes
node markdown-formatter-cli.cjs format .   # rewrite changed files in place

Exit codes: 0 clean · 1 changes required (check only) · 2 error. Ready-made Docker, GitHub Actions, and GitLab CI setups are in examples/.

All options, verbosity levels, exclusions, and CI recipes: Command-line tool.

Configure

Drop .markdown-formatter.json at the vault root to share one policy across a vault. Every key is optional and overrides the corresponding default:

{
  "proseWrap": "always",
  "markdownStructures": { "tables": "preserve" },
  "markdownlint": { "MD013": true }
}

Precedence: built-in defaults → Obsidian settings UI → vault project file. A complete, commented starter is .markdown-formatter.example.json; the full model, every key, and the default markdownlint rule set are in Configuration.

Excluding notes

List gitignore-style patterns in .markdown-formatter-ignore at the vault root — * matches within a path segment, ** crosses directories, ! re-includes. The same file is honored by the plugin and the CLI. Details and the project-config ignore block: Configuration.

Triggers

TriggerSettingDefault
Manual command / hotkey / ribbonalways available
Continuous after a vault write (debounced)formatOnModifyoff
On file openformatOnOpenoff
On last Markdown view closeformatOnCloseoff

Desktop only

The plugin uses Node.js-backed Prettier/markdownlint and filesystem configuration discovery, so it declares isDesktopOnly: true and does not run on Obsidian mobile — see Architecture.

Develop

npm install
npm run dev   # watch build
npm run ci    # full gate: lint, types, tests + coverage, build, package

Run the suite with npm test. See Testing, Architecture, and Contributing.

Releases & automation

Releases are GitHub Releases from SemVer tags (no v prefix; tags with a pre-release suffix such as -alpha or -beta publish as pre-releases), built and attested by an automated workflow that attaches main.js, manifest.json, styles.css, and the standalone CLI. Rolling develop-latest and main-latest prereleases track the newest successful build of each branch.

Supply-chain hardening:

  • CodeQL, Semgrep, and OpenSSF Scorecard on pushes and a schedule; npm audit in CI.
  • The formatter core is fuzzed with jazzer.js on relevant PRs and weekly.
  • Releases carry SLSA build provenance.
  • Every GitHub Action is SHA-pinned and every workflow keeps least-privilege token scopes (enforced by npm run check:repository).
  • Dependabot updates npm, Actions, and the example Docker image weekly, with grouped low-risk auto-merge.

License

Copyright © 2026 jjm.one. Licensed under the GNU Lesser General Public License v3.0 only (LGPL-3.0-only) — see LICENSE and the texts under LICENSES/. The production bundle includes MIT-licensed third-party components; see THIRD_PARTY_NOTICES.md.

Disclaimer

This software is provided "as is", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose, and non-infringement. To the maximum extent permitted by applicable law, the authors and contributors accept no liability for any claim, damages, data loss, or other consequences arising from the use of, or inability to use, this software. Formatting rewrites file contents — keep backups or version control. These terms follow sections 15–17 of the GNU GPL v3, as incorporated by the LGPL v3.0; see LICENSE.

For plugin developers

Search results and similarity scores are powered by semantic analysis of your plugin's README. If your plugin isn't appearing for searches you'd expect, try updating your README to clearly describe your plugin's purpose, features, and use cases.