Skip to content
markdownlint-integration logo

Markdownlint Integration

markdownlint-integration

Integrate markdownlint into development workflows including CLI usage, programmatic API, CI/CD pipelines, and editor integration.

SKILL.md

Full skill instructions

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

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

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:

  1. Open VS Code
  2. Go to Extensions (Cmd+Shift+X)
  3. Search for "markdownlint"
  4. 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:

  1. Install Package Control
  2. Install "SublimeLinter"
  3. 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 = '<html><head><title>Markdownlint Report</​title></​head><body>';
  html += '<h1>Markdownlint Results</​h1>';

  Object.keys(results).forEach(file => {
    html += `<h2>${file}</​h2>`;
    html += '<ul>';

    results[file].forEach(result => {
      html += `<li>Line ${result.lineNumber}: ${result.ruleDescription}</​li>`;
    });

    html += '</​ul>';
  });

  html += '</​body></​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

  1. CI/​CD Integration - Always run linting in continuous integration
  2. Pre-commit Hooks - Catch issues before they reach version control
  3. Editor Integration - Get real-time feedback while writing
  4. Consistent Configuration - Use same config across all environments
  5. Auto-fix When Possible - Use -f flag to automatically fix violations
  6. Fail Fast - Configure CI to fail on linting errors
  7. Ignore Generated Files - Exclude build artifacts and dependencies
  8. Version Lock - Pin markdownlint version in package.json
  9. Document Standards - Keep documentation on linting rules
  10. Progressive Enhancement - Start with relaxed rules, tighten over time
  11. Team Communication - Discuss rule changes before applying
  12. Regular Updates - Keep markdownlint updated for bug fixes
  13. Performance Optimization - Use appropriate glob patterns
  14. Error Reporting - Configure meaningful error output
  15. Backup Before Auto-fix - Always commit before running fixes

Common Pitfalls

  1. Missing Ignore Patterns - Linting node_modules or build directories
  2. Wrong Glob Patterns - Incorrect file matching in CLI
  3. Config Not Found - Configuration file in wrong location
  4. Async/​Sync Mismatch - Using sync API with async rules
  5. CI Timeout - Linting too many files without optimization
  6. No Exit Code Check - Not failing CI on linting errors
  7. Overwriting Files - Using auto-fix without version control
  8. Missing Dependencies - Not installing markdownlint in CI
  9. Platform Differences - Path separators differ between OS
  10. Large Binary Files - Accidentally linting non-markdown files
  11. Outdated Cache - Cached node_modules with old markdownlint
  12. Silent Failures - Not capturing error output in CI
  13. Config Conflicts - Multiple config files conflicting
  14. Missing Editor Config - Local linting differs from CI
  15. No Pre-commit Hook - Issues not caught before commit

Resources

More skills from majiayu000

xiaohongshu logo
majiayu000/claude-arsenal

xiaohongshu

xiaohongshu

286 148
View
agent-task-conductor logo
majiayu000/claude-skill-registry

agent-task-conductor

Conduct multi-agent task orchestration and workflow coordination.

663 1
View
conductor-setup logo
majiayu000/claude-skill-registry

conductor-setup

Initialize project with Conductor artifacts (product definition,

663 1
View
animation-designer logo
majiayu000/claude-skill-registry

animation-designer

Expert in web animations, transitions, and motion design using Framer Motion and CSS

663 1
View
diagramming logo
majiayu000/claude-skill-registry

diagramming

Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...

663 1
View
h3-pg logo
majiayu000/claude-skill-registry-data

h3-pg

PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.

23 1
View
conductor-development logo
majiayu000/claude-skill-registry

conductor-development

Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...

663 1
View
conductor-status logo
majiayu000/claude-skill-registry

conductor-status

Display project status, active tracks, and next actions

663 1
View
dockerization logo
majiayu000/claude-skill-registry

dockerization

Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.

663 1
View

Popular AI tools

Kaiber logo
Video

Kaiber

Generate, edit, and beat-sync AI video with leading models in one workspace.

Paid
View
Vimcal logo
Productivity

Vimcal

The world's fastest calendar for remote work

Free
View

Transform Your Design with AI Designer by ImgCreator.ai

Freemium
View
Akool AI logo
Content & writing

Akool AI

Revolutionizing Video Production with AI-Powered Creativity

Paid
View

Extend an image past the frame and let AI fill the new aspect ratio.

Freemium
View
StarByFace logo
Security

StarByFace

Discover your celebrity doppelgänger with StarByFace!

Free
View
C

ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.

Freemium
View
Opus Clip logo
Coding & apps

Opus Clip

Opus.ai: Revolutionize Your Web Experience

Free
View