name: markdownlint-integration user-invocable: false description: 集成markdownlint到开发工作流中,包括CLI使用、编程API、CI/CD管道和编辑器集成。 allowed-tools: [Bash, Read]
Markdownlint 集成
掌握将markdownlint集成到开发工作流中,包括CLI使用、编程API(同步/异步/承诺)、CI/CD管道、预提交钩子和编辑器集成。
概述
Markdownlint可以集成到开发工作流的各个部分,以确保Markdown质量的一致性。这包括命令行工具、Node.js中的编程使用、持续集成管道、Git钩子和编辑器插件。
命令行界面
markdownlint-cli 安装
npm install -g markdownlint-cli
# 或作为开发依赖
npm install --save-dev markdownlint-cli
基本CLI使用
# 检查当前目录下所有Markdown文件
markdownlint '**/*.md'
# 检查特定文件
markdownlint README.md CONTRIBUTING.md
# 使用配置文件进行检查
markdownlint -c .markdownlint.json '**/*.md'
# 忽略特定文件
markdownlint '**/*.md' --ignore node_modules
# 自动修复违规
markdownlint -f '**/*.md'
# 输出到文件
markdownlint '**/*.md' -o linting-results.txt
高级CLI选项
# 使用自定义配置
markdownlint --config config/markdown-lint.json docs/
# 从文件忽略模式
markdownlint --ignore-path .gitignore '**/*.md'
# 使用多个忽略模式
markdownlint --ignore node_modules --ignore dist '**/*.md'
# 仅启用特定规则
markdownlint --rules MD001,MD003,MD013 '**/*.md'
# 禁用特定规则
markdownlint --disable MD013 '**/*.md'
# 以JSON格式显示输出
markdownlint --json '**/*.md'
# 静默模式(仅退出码)
markdownlint -q '**/*.md'
# 详细输出
markdownlint --verbose '**/*.md'
CLI配置文件
.markdownlint-cli.json:
{
"config": {
"default": true,
"MD013": {
"line_length": 100
}
},
"files": ["**/*.md"],
"ignores": [
"node_modules/**",
"dist/**",
"build/**"
]
}
使用方式:
markdownlint --config .markdownlint-cli.json
编程API
基于回调的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);
}
});
基于承诺的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);
}
同步API
import { lint as lintSync } from 'markdownlint/sync';
const options = {
files: ['README.md'],
strings: {
'inline-content': '# Test
Content here.'
},
config: {
default: true
}
};
const results = lintSync(options);
console.log(results.toString());
检查字符串
const markdownlint = require('markdownlint');
const options = {
strings: {
'content-1': '# Heading
Paragraph text.',
'content-2': '## Another heading
More content.'
},
config: {
default: true,
'first-line-heading': {
level: 1
}
}
};
markdownlint(options, (err, result) => {
if (!err) {
const resultString = result.toString();
console.log(resultString);
}
});
处理结果
import { lint } from 'markdownlint/promise';
const results = await lint({
files: ['docs/**/*.md'],
config: { default: true }
});
// 结果是以文件名为键的对象
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}`);
}
});
});
读取配置
const markdownlint = require('markdownlint');
const { readConfigSync } = require('markdownlint/sync');
// 从文件读取配置
const config = readConfigSync('.markdownlint.json');
const options = {
files: ['**/*.md'],
config: config
};
const results = markdownlint.sync(options);
console.log(results.toString());
使用自定义规则
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());
}
});
程序化应用修复
applyFix 函数
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 函数
const { applyFixes } = require('markdownlint');
const input = '# Heading
Paragraph';
const errors = [
{
lineNumber: 3,
ruleNames: ['MD012'],
ruleDescription: 'Multiple blank lines',
fixInfo: {
lineNumber: 3,
deleteCount: -1
}
}
];
const fixed = applyFixes(input, errors);
console.log(fixed); // '# Heading
Paragraph'
自动修复工作流
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 集成
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
使用markdownlint-cli2的GitHub Actions
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'
预提交钩子
使用Husky
安装Husky:
npm install --save-dev husky
npx husky install
创建预提交钩子:
npx husky add .husky/pre-commit "npm run lint:md"
添加脚本到package.json:
{
"scripts": {
"lint:md": "markdownlint '**/*.md' --ignore node_modules",
"lint:md:fix": "markdownlint -f '**/*.md' --ignore node_modules"
}
}
使用lint-staged
安装lint-staged:
npm install --save-dev lint-staged
在package.json中配置:
{
"lint-staged": {
"*.md": [
"markdownlint --fix",
"git add"
]
}
}
更新预提交钩子:
npx husky add .husky/pre-commit "npx lint-staged"
使用pre-commit框架
.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']
安装和使用:
pip install pre-commit
pre-commit install
pre-commit run --all-files
编辑器集成
Visual Studio Code
安装markdownlint扩展:
- 打开VS Code
- 转到扩展(Cmd+Shift+X)
- 搜索“markdownlint”
- 安装David Anson的“markdownlint”
在.vscode/settings.json中配置:
{
"markdownlint.config": {
"default": true,
"MD013": {
"line_length": 100
}
},
"markdownlint.ignore": [
"node_modules/**",
"dist/**"
],
"editor.codeActionsOnSave": {
"source.fixAll.markdownlint": true
}
}
Vim/Neovim
使用ALE(异步检查引擎):
" 在.vimrc或init.vim中
let g:ale_linters = {
\ 'markdown': ['markdownlint'],
\}
let g:ale_fixers = {
\ 'markdown': ['markdownlint'],
\}
let g:ale_markdown_markdownlint_options = '-c .markdownlint.json'
" 启用保存时修复
let g:ale_fix_on_save = 1
Sublime Text
通过Package Control安装:
- 安装Package Control
- 安装“SublimeLinter”
- 安装“SublimeLinter-contrib-markdownlint”
在首选项中配置:
{
"linters": {
"markdownlint": {
"args": ["-c", ".markdownlint.json"]
}
}
}
Atom
安装包:
apm install linter-markdownlint
在Atom设置或.atom/config.cson中配置:
"linter-markdownlint":
configPath: ".markdownlint.json"
npm脚本集成
package.json脚本
{
"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-env处理环境变量:
{
"scripts": {
"lint:md": "cross-env NODE_ENV=development markdownlint '**/*.md'"
},
"devDependencies": {
"cross-env": "^7.0.3"
}
}
Docker集成
Dockerfile
FROM node:18-alpine
WORKDIR /app
# 全局安装markdownlint
RUN npm install -g markdownlint-cli
# 复制Markdown文件
COPY . .
# 运行检查器
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"
运行方式:
docker-compose run markdownlint
单体仓库集成
工作空间配置
根目录.markdownlint.json:
{
"default": true,
"line-length": {
"line_length": 100
}
}
根目录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"
}
}
包级别packages/api/package.json:
{
"scripts": {
"lint:md": "markdownlint '**/*.md'"
}
}
使用Lerna
{
"scripts": {
"lint:md": "lerna run lint:md --stream"
}
}
使用Turborepo
turbo.json:
{
"pipeline": {
"lint:md": {
"outputs": []
}
}
}
报告和指标
生成HTML报告
使用自定义脚本:
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结果</h1>';
Object.keys(results).forEach(file => {
html += `<h2>${file}</h2>`;
html += '<ul>';
results[file].forEach(result => {
html += `<li>行 ${result.lineNumber}: ${result.ruleDescription}</li>`;
});
html += '</ul>';
});
html += '</body></html>';
return html;
}
工具使用的JSON输出
markdownlint '**/*.md' --json > results.json
使用jq处理:
markdownlint '**/*.md' --json | jq '.[] | select(length > 0)'
何时使用此技能
- 在新项目中设置markdownlint
- 将检查集成到CI/CD管道中
- 配置预提交钩子
- 自动化文档质量检查
- 设置编辑器集成
- 构建自定义检查工作流
- 创建自动修复脚本
- 实施文档标准
最佳实践
- CI/CD集成 - 始终在持续集成中运行检查
- 预提交钩子 - 在进入版本控制前捕获问题
- 编辑器集成 - 在编写时获得实时反馈
- 一致配置 - 在所有环境中使用相同的配置
- 尽可能自动修复 - 使用-f标志自动修复违规
- 快速失败 - 配置CI在检查错误时失败
- 忽略生成文件 - 排除构建产物和依赖
- 版本锁定 - 在package.json中固定markdownlint版本
- 文档标准 - 保持关于检查规则的文档
- 渐进式增强 - 从宽松规则开始,逐步收紧
- 团队沟通 - 在应用前讨论规则变更
- 定期更新 - 更新markdownlint以获取错误修复
- 性能优化 - 使用适当的通配符模式
- 错误报告 - 配置有意义的错误输出
- 自动修复前备份 - 运行修复前始终提交
常见陷阱
- 缺失忽略模式 - 检查node_modules或构建目录
- 错误的通配符模式 - CLI中不正确的文件匹配
- 配置未找到 - 配置文件位置错误
- 异步/同步不匹配 - 将同步API与异步规则一起使用
- CI超时 - 未优化检查过多文件
- 无退出码检查 - 未在检查错误时失败CI
- 覆盖文件 - 使用自动修复而无版本控制
- 缺失依赖 - 未在CI中安装markdownlint
- 平台差异 - 不同操作系统间的路径分隔符不同
- 大型二进制文件 - 意外检查非Markdown文件
- 过时缓存 - 缓存node_modules中的旧markdownlint
- 静默失败 - 在CI中未捕获错误输出
- 配置冲突 - 多个配置文件冲突
- 缺失编辑器配置 - 本地检查与CI不同
- 无预提交钩子 - 提交前未捕获问题