Skip to content

Instantly share code, notes, and snippets.

@andkirby
Last active April 16, 2026 17:34
Show Gist options
  • Select an option

  • Save andkirby/c57b7d310dbbe157c6ef985e94c967c3 to your computer and use it in GitHub Desktop.

Select an option

Save andkirby/c57b7d310dbbe157c6ef985e94c967c3 to your computer and use it in GitHub Desktop.

Markdown Fence Validation (Pre-commit)

Blocks commits with unclosed or malformed markdown code fences.

Install

bun add -d markdownlint-cli2

Config

.markdownlint-cli2.jsonc in project root:

{
  "config": {
    "default": false,
    "MD031": true,  // blank lines around fences
    "MD040": true,  // language required on fences
    "MD046": true,  // consistent fence style
    "MD048": true   // backtick vs tilde consistency
  },
  "ignores": [
    "node_modules/**", "**/node_modules/**",
    "dist/**", "**/dist/**", "build/**",
    "playwright-report/**", "test-results/**", "coverage/**",
    ".git/**", ".idea/**"
    // add project-specific: generated-docs/**, research/**, etc.
  ]
}

Hook

.husky/lib/check-markdown-fences.sh:

#!/bin/sh
check_markdown_fences() {
  STAGED_MD=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.md$' || true)
  [ -z "$STAGED_MD" ] && return 0

  violations=0

  # Parity check: unclosed fences (markdownlint won't catch these)
  for file in $STAGED_MD; do
    [ -f "$file" ] || continue
    fence_count=$(grep -cE '^(`{3,}|~{3,})' "$file" || true)
    fence_count=${fence_count:-0}
    if [ $(( fence_count % 2 )) -ne 0 ]; then
      echo "❌ ERROR: Unclosed code fence in: $file ($fence_count fence markers)"
      grep -nE '^(`{3,}|~{3,})' "$file" | sed 's/^/   /'
      echo ""
      violations=$(( violations + 1 ))
    fi
  done

  # Style check via markdownlint
  if ! ./node_modules/.bin/markdownlint-cli2 $STAGED_MD 2>&1; then
    violations=$(( violations + 1 ))
  fi

  [ $violations -gt 0 ] && echo "❌ Commit blocked: markdown fence errors." && return 1
  return 0
}

Source and call in .husky/pre-commit:

. "$HOOK_DIR/lib/check-markdown-fences.sh"
check_markdown_fences

Notes

  • MD040 is not autofixable — the tool can't guess language identifiers.
  • --fix flag fixes MD031 (blank lines) automatically:
    markdownlint-cli2 --fix "**/*.md"
  • Parity check is line-based: counts ``` and ~~~ openers. Will miscount fences inside HTML blocks (rare in practice).
  • For existing repos with many MD040 violations, bulk-fix with sed:
    find docs/ -name "*.md" -exec sed -i '' 's/^```$/```text/' {} +
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment