Markdownlint Integration Master integrating markdownlint into development workflows including CLI usage, programmatic API (sync/async/promise), CI/CD pipelines, pre-commit hooks, and editor integration. Overview Markdownlint can be integrated into various parts of your development workflow to ensure consistent markdown quality. This includes command-line tools, programmatic usage in Node.js, continuous integration pipelines, Git hooks, and editor plugins. Command-Line Interface markdownlint-cli Installation npm install -g markdownlint-cli
or as dev dependency
npm install --save-dev markdownlint-cli Basic CLI Usage
Lint all markdown files in current directory
markdownlint '*/.md'
Lint specific files
markdownlint README.md CONTRIBUTING.md
Lint with configuration file
markdownlint -c .markdownlint.json '*/.md'
Ignore specific files
markdownlint '*/.md' --ignore node_modules
Fix violations automatically
markdownlint -f '*/.md'
Output to file
markdownlint '*/.md' -o linting-results.txt Advanced CLI Options
Use custom config
markdownlint --config config/markdown-lint.json docs/
Ignore patterns from file
markdownlint --ignore-path .gitignore '*/.md'
Use multiple ignore patterns
markdownlint --ignore node_modules --ignore dist '*/.md'
Enable specific rules only
markdownlint --rules MD001,MD003,MD013 '*/.md'
Disable specific rules
markdownlint --disable MD013 '*/.md'
Show output in JSON format
markdownlint --json '*/.md'
Quiet mode (exit code only)
markdownlint -q '*/.md'
Verbose output
markdownlint
--verbose
'/*.md'
CLI Configuration File
.markdownlint-cli.json
:
{
"config"
:
{
"default"
:
true
,
"MD013"
:
{
"line_length"
:
100
}
}
,
"files"
:
[
"/.md"
]
,
"ignores"
:
[
"node_modules/"
,
"dist/"
,
"build/"
]
}
Use with:
markdownlint
--config
.markdownlint-cli.json
Programmatic API
Callback-Based API
const
markdownlint
=
require
(
'markdownlint'
)
;
const
options
=
{
files
:
[
'good.md'
,
'bad.md'
]
,
config
:
{
default
:
true
,
'line-length'
:
{
line_length
:
100
}
}
}
;
markdownlint
(
options
,
(
err
,
result
)
=>
{
if
(
!
err
)
{
console
.
log
(
result
.
toString
(
)
)
;
}
else
{
console
.
error
(
err
)
;
}
}
)
;
Promise-Based API
import
{
lint
as
lintPromise
}
from
'markdownlint/promise'
;
const
options
=
{
files
:
[
'README.md'
,
'docs//.md'
]
,
config
:
{
default
:
true
,
'no-inline-html'
:
{
allowed_elements
:
[
'br'
,
'img'
]
}
}
}
;
try
{
const
results
=
await
lintPromise
(
options
)
;
console
.
dir
(
results
,
{
colors
:
true
,
depth
:
null
}
)
;
}
catch
(
err
)
{
console
.
error
(
err
)
;
}
Synchronous API
import
{
lint
as
lintSync
}
from
'markdownlint/sync'
;
const
options
=
{
files
:
[
'README.md'
]
,
strings
:
{
'inline-content'
:
'# Test\n\nContent here.'
}
,
config
:
{
default
:
true
}
}
;
const
results
=
lintSync
(
options
)
;
console
.
log
(
results
.
toString
(
)
)
;
Linting Strings
const
markdownlint
=
require
(
'markdownlint'
)
;
const
options
=
{
strings
:
{
'content-1'
:
'# Heading\n\nParagraph text.'
,
'content-2'
:
'## Another heading\n\nMore content.'
}
,
config
:
{
default
:
true
,
'first-line-heading'
:
{
level
:
1
}
}
}
;
markdownlint
(
options
,
(
err
,
result
)
=>
{
if
(
!
err
)
{
const
resultString
=
result
.
toString
(
)
;
console
.
log
(
resultString
)
;
}
}
)
;
Working with Results
import
{
lint
}
from
'markdownlint/promise'
;
const
results
=
await
lint
(
{
files
:
[
'docs//*.md'
]
,
config
:
{
default
:
true
}
}
)
;
// Results is an object keyed by filename
Object
.
keys
(
results
)
.
forEach
(
file
=>
{
const
fileResults
=
results
[
file
]
;
fileResults
.
forEach
(
result
=>
{
console
.
log
(
${
file
}
:
${
result
.
lineNumber
}
${
result
.
ruleNames
.
join
(
'/'
)
}
${
result
.
ruleDescription
}
)
;
if
(
result
.
errorDetail
)
{
console
.
log
(
Detail:
${
result
.
errorDetail
}
)
;
}
if
(
result
.
errorContext
)
{
console
.
log
(
Context:
${
result
.
errorContext
}
)
;
}
}
)
;
}
)
;
Reading Configuration
const
markdownlint
=
require
(
'markdownlint'
)
;
const
{
readConfigSync
}
=
require
(
'markdownlint/sync'
)
;
// Read configuration from file
const
config
=
readConfigSync
(
'.markdownlint.json'
)
;
const
options
=
{
files
:
[
'/*.md'
]
,
config
:
config
}
;
const
results
=
markdownlint
.
sync
(
options
)
;
console
.
log
(
results
.
toString
(
)
)
;
Using Custom Rules
const
markdownlint
=
require
(
'markdownlint'
)
;
const
customRule
=
require
(
'./custom-rules/heading-capitalization'
)
;
const
options
=
{
files
:
[
'README.md'
]
,
config
:
{
default
:
true
,
'heading-capitalization'
:
true
}
,
customRules
:
[
customRule
]
}
;
markdownlint
(
options
,
(
err
,
result
)
=>
{
if
(
!
err
)
{
console
.
log
(
result
.
toString
(
)
)
;
}
}
)
;
Applying Fixes Programmatically
applyFix Function
const
{
applyFix
}
=
require
(
'markdownlint'
)
;
const
line
=
' Text with extra spaces '
;
const
fixInfo
=
{
editColumn
:
1
,
deleteCount
:
2
,
insertText
:
''
}
;
const
fixed
=
applyFix
(
line
,
fixInfo
)
;
console
.
log
(
fixed
)
;
// 'Text with extra spaces '
applyFixes Function
const
{
applyFixes
}
=
require
(
'markdownlint'
)
;
const
input
=
'# Heading\n\n\nParagraph'
;
const
errors
=
[
{
lineNumber
:
3
,
ruleNames
:
[
'MD012'
]
,
ruleDescription
:
'Multiple blank lines'
,
fixInfo
:
{
lineNumber
:
3
,
deleteCount
:
-
1
}
}
]
;
const
fixed
=
applyFixes
(
input
,
errors
)
;
console
.
log
(
fixed
)
;
// '# Heading\n\nParagraph'
Auto-Fix Workflow
const
fs
=
require
(
'fs'
)
;
const
markdownlint
=
require
(
'markdownlint'
)
;
const
{
applyFixes
}
=
require
(
'markdownlint'
)
;
const
file
=
'README.md'
;
const
content
=
fs
.
readFileSync
(
file
,
'utf8'
)
;
const
options
=
{
strings
:
{
[
file
]
:
content
}
,
config
:
{
default
:
true
}
}
;
markdownlint
(
options
,
(
err
,
result
)
=>
{
if
(
!
err
)
{
const
errors
=
result
[
file
]
||
[
]
;
if
(
errors
.
length
0 ) { const fixed = applyFixes ( content , errors ) ; fs . writeFileSync ( file , fixed , 'utf8' ) ; console . log (
Fixed ${ errors . length } issues in ${ file }) ; } } } ) ; CI/CD Integration GitHub Actions .github/workflows/markdownlint.yml : name : Markdownlint user-invocable : false on : push : branches : [ main , develop ] pull_request : branches : [ main ] jobs : lint : runs-on : ubuntu - latest steps : - name : Checkout code uses : actions/checkout@v3 - name : Setup Node.js uses : actions/setup - node@v3 with : node-version : '18' - name : Install dependencies run : npm ci - name : Run markdownlint run : npx markdownlint ' /*.md' - - ignore node_modules - name : Annotate PR with results if : failure() run : | npx markdownlint '/.md' --ignore node_modules -o markdownlint-results.txt cat markdownlint-results.txt GitHub Actions with markdownlint-cli2 name : Markdownlint user-invocable : false on : [ push , pull_request ] jobs : lint : runs-on : ubuntu - latest steps : - uses : actions/checkout@v3 - name : Run markdownlint - cli2 uses : DavidAnson/markdownlint - cli2 - action@v9 with : paths : '/.md' GitLab CI .gitlab-ci.yml : markdownlint : image : node : 18 - alpine stage : test before_script : - npm install - g markdownlint - cli script : - markdownlint ' /*.md' - - ignore node_modules only : - merge_requests - main artifacts : when : on_failure paths : - markdownlint - results.txt CircleCI .circleci/config.yml : version : 2.1 jobs : markdownlint : docker : - image : cimg/node : 18.0 steps : - checkout - run : name : Install markdownlint command : npm install - g markdownlint - cli - run : name : Run linter command : markdownlint ' /.md' - - ignore node_modules workflows : version : 2 build_and_test : jobs : - markdownlint Jenkins Pipeline Jenkinsfile : pipeline { agent any stages { stage ( 'Lint Markdown' ) { steps { sh 'npm install -g markdownlint-cli' sh 'markdownlint "/.md" --ignore node_modules' } } } post { always { cleanWs ( ) } failure { echo 'Markdownlint found issues!' } } } Azure Pipelines azure-pipelines.yml : trigger : - main pool : vmImage : 'ubuntu-latest' steps : - task : NodeTool@0 inputs : versionSpec : '18.x' displayName : 'Install Node.js' - script : | npm install -g markdownlint-cli displayName : 'Install markdownlint' - script : | markdownlint '/*.md' --ignore node_modules displayName : 'Run markdownlint' Pre-commit Hooks Using Husky Install Husky: npm install --save-dev husky npx husky install Create pre-commit hook: npx husky add .husky/pre-commit "npm run lint:md" Add script to package.json : { "scripts" : { "lint:md" : "markdownlint '/.md' --ignore node_modules" , "lint:md:fix" : "markdownlint -f '/.md' --ignore node_modules" } } Using lint-staged Install lint-staged: npm install --save-dev lint-staged Configure in package.json : { "lint-staged" : { ".md" : [ "markdownlint --fix" , "git add" ] } } Update pre-commit hook: npx husky add .husky/pre-commit "npx lint-staged" Using pre-commit Framework .pre-commit-config.yaml : repos : - repo : https : //github.com/igorshubovych/markdownlint - cli rev : v0.37.0 hooks : - id : markdownlint args : [ '--fix' ] - repo : https : //github.com/DavidAnson/markdownlint - cli2 rev : v0.10.0 hooks : - id : markdownlint - cli2 args : [ '--fix' ] Install and use: pip install pre-commit pre-commit install pre-commit run --all-files Editor Integration Visual Studio Code Install the markdownlint extension: Open VS Code Go to Extensions (Cmd+Shift+X) Search for "markdownlint" Install "markdownlint" by David Anson Configure in .vscode/settings.json : { "markdownlint.config" : { "default" : true , "MD013" : { "line_length" : 100 } } , "markdownlint.ignore" : [ "node_modules/" , "dist/" ] , "editor.codeActionsOnSave" : { "source.fixAll.markdownlint" : true } } Vim/Neovim Using ALE (Asynchronous Lint Engine): " In .vimrc or init.vim let g : ale_linters = { \ 'markdown' : [ 'markdownlint' ] , \ } let g : ale_fixers = { \ 'markdown' : [ 'markdownlint' ] , \ } let g : ale_markdown_markdownlint_options = '-c .markdownlint.json' " Enable fixing on save let g : ale_fix_on_save = 1 Sublime Text Install via Package Control: Install Package Control Install "SublimeLinter" Install "SublimeLinter-contrib-markdownlint" Configure in preferences: { "linters" : { "markdownlint" : { "args" : [ "-c" , ".markdownlint.json" ] } } } Atom Install packages: apm install linter-markdownlint Configure in Atom settings or .atom/config.cson : "linter-markdownlint": configPath: ".markdownlint.json" npm Scripts Integration package.json Scripts { "scripts" : { "lint" : "npm run lint:md" , "lint:md" : "markdownlint '/.md' --ignore node_modules" , "lint:md:fix" : "markdownlint -f '/*.md' --ignore node_modules" , "lint:md:ci" : "markdownlint '/.md' --ignore node_modules -o markdownlint-report.txt" , "test" : "npm run lint && npm run test:unit" , "precommit" : "lint-staged" } } Cross-Platform Compatibility Using cross-env for environment variables: { "scripts" : { "lint:md" : "cross-env NODE_ENV=development markdownlint '/.md'" } , "devDependencies" : { "cross-env" : "^7.0.3" } } Docker Integration Dockerfile FROM node:18-alpine WORKDIR /app
Install markdownlint globally
RUN npm install -g markdownlint-cli
Copy markdown files
COPY . .
Run linter
CMD [ "markdownlint" , "*/.md" , "--ignore" , "node_modules" ] docker-compose.yml version : '3.8' services : markdownlint : image : node : 18 - alpine working_dir : /app volumes : - . : /app command :
sh -c "npm install -g markdownlint-cli && markdownlint '/*.md' --ignore node_modules" Run with: docker-compose run markdownlint Monorepo Integration Workspace Configuration Root .markdownlint.json : { "default" : true , "line-length" : { "line_length" : 100 } } Root package.json : { "scripts" : { "lint:md" : "markdownlint '/.md' --ignore node_modules" , "lint:md:packages" : "lerna run lint:md" , "lint:md:all" : "npm run lint:md && npm run lint:md:packages" } } Package-level packages/api/package.json : { "scripts" : { "lint:md" : "markdownlint '/.md'" } } Using Lerna { "scripts" : { "lint:md" : "lerna run lint:md --stream" } } Using Turborepo turbo.json : { "pipeline" : { "lint:md" : { "outputs" : [ ] } } } Reporting and Metrics Generate HTML Report Using a custom script: const fs = require ( 'fs' ) ; const markdownlint = require ( 'markdownlint' ) ; const options = { files : [ '*/.md' ] , config : { default : true } } ; markdownlint ( options , ( err , results ) => { if ( ! err ) { const html = generateHtmlReport ( results ) ; fs . writeFileSync ( 'markdownlint-report.html' , html ) ; } } ) ; function generateHtmlReport ( results ) { let html = '
Markdownlint Report ' ; html += 'Markdownlint Results
' ; Object . keys ( results ) . forEach ( file => { html += `
${ file }
;
html
+=
'<ul>'
;
results
[
file
]
.
forEach
(
result
=>
{
html
+=
` ; } ) ; html += '' ; } ) ; html += '' ; return html ; } JSON Output for Tooling markdownlint '*/.md' --json
results.json Process with jq: markdownlint '*/.md' --json | jq '.[] | select(length > 0)' When to Use This Skill Setting up markdownlint in new projects Integrating linting into CI/CD pipelines Configuring pre-commit hooks Automating documentation quality checks Setting up editor integration Building custom linting workflows Creating automated fix scripts Implementing documentation standards Best Practices CI/CD Integration - Always run linting in continuous integration Pre-commit Hooks - Catch issues before they reach version control Editor Integration - Get real-time feedback while writing Consistent Configuration - Use same config across all environments Auto-fix When Possible - Use -f flag to automatically fix violations Fail Fast - Configure CI to fail on linting errors Ignore Generated Files - Exclude build artifacts and dependencies Version Lock - Pin markdownlint version in package.json Document Standards - Keep documentation on linting rules Progressive Enhancement - Start with relaxed rules, tighten over time Team Communication - Discuss rule changes before applying Regular Updates - Keep markdownlint updated for bug fixes Performance Optimization - Use appropriate glob patterns Error Reporting - Configure meaningful error output Backup Before Auto-fix - Always commit before running fixes Common Pitfalls Missing Ignore Patterns - Linting node_modules or build directories Wrong Glob Patterns - Incorrect file matching in CLI Config Not Found - Configuration file in wrong location Async/Sync Mismatch - Using sync API with async rules CI Timeout - Linting too many files without optimization No Exit Code Check - Not failing CI on linting errors Overwriting Files - Using auto-fix without version control Missing Dependencies - Not installing markdownlint in CI Platform Differences - Path separators differ between OS Large Binary Files - Accidentally linting non-markdown files Outdated Cache - Cached node_modules with old markdownlint Silent Failures - Not capturing error output in CI Config Conflicts - Multiple config files conflicting Missing Editor Config - Local linting differs from CI No Pre-commit Hook - Issues not caught before commit Resources markdownlint GitHub Repository markdownlint-cli markdownlint-cli2 markdownlint VS Code Extension GitHub Actions Integration API Documentation