mirror of
https://github.com/DavidAnson/markdownlint.git
synced 2025-09-21 21:30:47 +02:00
Add MD043 required-headers "Required header structure" (fixes #22).
This commit is contained in:
parent
2612a96ae8
commit
c8ecec1953
26 changed files with 316 additions and 10 deletions
|
@ -82,6 +82,7 @@ playground for learning and exploring.
|
|||
* **MD040** *fenced-code-language* - Fenced code blocks should have a language specified
|
||||
* **MD041** *first-line-h1* - First line in file should be a top level header
|
||||
* **MD042** *no-empty-links* - No empty links
|
||||
* **MD043** *required-headers* - Required header structure
|
||||
|
||||
See [Rules.md](doc/Rules.md) for more details.
|
||||
|
||||
|
@ -96,7 +97,7 @@ See [Rules.md](doc/Rules.md) for more details.
|
|||
* **emphasis** - MD036, MD037
|
||||
* **hard_tab** - MD010
|
||||
* **headers** - MD001, MD002, MD003, MD018, MD019, MD020, MD021, MD022, MD023,
|
||||
MD024, MD025, MD026, MD036, MD041
|
||||
MD024, MD025, MD026, MD036, MD041, MD043
|
||||
* **hr** - MD035
|
||||
* **html** - MD033
|
||||
* **indentation** - MD005, MD006, MD007, MD027
|
||||
|
|
55
doc/Rules.md
55
doc/Rules.md
|
@ -222,7 +222,7 @@ Tags: whitespace
|
|||
|
||||
Aliases: no-trailing-spaces
|
||||
|
||||
Parameters: br_spaces (number; default: 0)
|
||||
Parameters: br_spaces (number; default 0)
|
||||
|
||||
This rule is triggered on any lines that end with whitespace. To fix this,
|
||||
find the line that is triggered and remove any trailing spaces from the end.
|
||||
|
@ -651,7 +651,7 @@ Tags: ol, ul, whitespace
|
|||
|
||||
Aliases: list-marker-space
|
||||
|
||||
Parameters: ul_single, ol_single, ul_multi, ol_multi (number, default 1)
|
||||
Parameters: ul_single, ol_single, ul_multi, ol_multi (number; default 1)
|
||||
|
||||
This rule checks for the number of spaces between a list marker (e.g. '`-`',
|
||||
'`*`', '`+`' or '`1.`') and the text of the list item.
|
||||
|
@ -1026,3 +1026,54 @@ Empty fragments will trigger this rule:
|
|||
But non-empty fragments will not:
|
||||
|
||||
[a valid fragment](#fragment)
|
||||
|
||||
## MD043 - Required header structure
|
||||
|
||||
Tags: headers
|
||||
|
||||
Aliases: required-headers
|
||||
|
||||
Parameters: headers (array of string; default `null` for disabled)
|
||||
|
||||
This rule is triggered when the headers in a file do not match the array of
|
||||
headers passed to the rule. It can be used to enforce a standard header
|
||||
structure for a set of files.
|
||||
|
||||
To require exactly the following structure:
|
||||
|
||||
# Head
|
||||
## Item
|
||||
### Detail
|
||||
|
||||
Set the `headers` parameter to:
|
||||
|
||||
[
|
||||
"# Head",
|
||||
"## Item",
|
||||
"### Detail"
|
||||
]
|
||||
|
||||
To allow optional headers as with the following structure:
|
||||
|
||||
# Head
|
||||
## Item
|
||||
### Detail (optional)
|
||||
## Foot
|
||||
### Notes (optional)
|
||||
|
||||
Use the special value `"*"` meaning "one or more unspecified headers" and set
|
||||
the `headers` parameter to:
|
||||
|
||||
[
|
||||
"# Head",
|
||||
"## Item",
|
||||
"*",
|
||||
"## Foot",
|
||||
"*"
|
||||
]
|
||||
|
||||
When an error is detected, this rule outputs the line number of the first
|
||||
problematic header (otherwise, it outputs the last line number of the file).
|
||||
|
||||
Note that while the `headers` parameter uses the "## Text" ATX header style for
|
||||
simplicity, a file may use any supported header style.
|
||||
|
|
38
lib/rules.js
38
lib/rules.js
|
@ -898,7 +898,7 @@ module.exports = [
|
|||
"desc": "No empty links",
|
||||
"tags": [ "links" ],
|
||||
"aliases": [ "no-empty-links" ],
|
||||
"func": function MD034(params, errors) {
|
||||
"func": function MD042(params, errors) {
|
||||
forEachInlineChild(params, "link_open", function forToken(token) {
|
||||
token.attrs.forEach(function forAttr(attr) {
|
||||
if (attr[0] === "href" && (!attr[1] || (attr[1] === "#"))) {
|
||||
|
@ -907,5 +907,41 @@ module.exports = [
|
|||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"name": "MD043",
|
||||
"desc": "Required header structure",
|
||||
"tags": [ "headers" ],
|
||||
"aliases": [ "required-headers" ],
|
||||
"func": function MD043(params, errors) {
|
||||
var requiredHeaders = params.options.headers;
|
||||
if (requiredHeaders) {
|
||||
var levels = {};
|
||||
[ 1, 2, 3, 4, 5, 6 ].forEach(function forLevel(level) {
|
||||
levels["h" + level] = "######".substr(-level);
|
||||
});
|
||||
var i = 0;
|
||||
var optional = false;
|
||||
forEachHeading(params, function forHeading(heading, content) {
|
||||
if (!errors.length) {
|
||||
var actual = levels[heading.tag] + " " + content;
|
||||
var expected = requiredHeaders[i++] || "";
|
||||
if (expected === "*") {
|
||||
optional = true;
|
||||
} else if (expected.toLowerCase() === actual.toLowerCase()) {
|
||||
optional = false;
|
||||
} else if (optional) {
|
||||
i--;
|
||||
} else {
|
||||
errors.push(heading.lineNumber);
|
||||
}
|
||||
}
|
||||
});
|
||||
if ((i < requiredHeaders.length) && !errors.length) {
|
||||
errors.push(params.lines.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
];
|
||||
|
|
|
@ -1,4 +1,11 @@
|
|||
{
|
||||
"default": true,
|
||||
"MD041": true
|
||||
"MD041": true,
|
||||
"MD043": {
|
||||
"headers": [
|
||||
"## Header 1 {MD002} {MD041}",
|
||||
"#### Header 2 {MD001}",
|
||||
"# Broken"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
#### Header 2 {MD001}
|
||||
|
||||
# Header 3 {MD003} #
|
||||
# Header 3 {MD003} {MD043} #
|
||||
|
||||
* list
|
||||
+ list {MD004} {MD006} {MD007} {MD030}
|
||||
|
|
|
@ -632,6 +632,44 @@ module.exports.customFrontMatter = function customFrontMatter(test) {
|
|||
});
|
||||
};
|
||||
|
||||
module.exports.readmeHeaders = function readmeHeaders(test) {
|
||||
test.expect(2);
|
||||
markdownlint({
|
||||
"files": "README.md",
|
||||
"config": {
|
||||
"default": false,
|
||||
"MD043": {
|
||||
"headers": [
|
||||
"# markdownlint",
|
||||
"## Install",
|
||||
"## Overview",
|
||||
"### Related",
|
||||
"## Demonstration",
|
||||
"## Rules / Aliases",
|
||||
"## Tags",
|
||||
"## Configuration",
|
||||
"## API",
|
||||
"### options",
|
||||
"#### options.files",
|
||||
"#### options.strings",
|
||||
"#### options.frontMatter",
|
||||
"#### options.config",
|
||||
"### callback",
|
||||
"### result",
|
||||
"## Usage",
|
||||
"## Browser",
|
||||
"## History"
|
||||
]
|
||||
}
|
||||
}
|
||||
}, function callback(err, result) {
|
||||
test.ifError(err);
|
||||
var expected = { "README.md": {} };
|
||||
test.deepEqual(result, expected, "Unexpected issues.");
|
||||
test.done();
|
||||
});
|
||||
};
|
||||
|
||||
module.exports.filesArrayNotModified = function filesArrayNotModified(test) {
|
||||
test.expect(2);
|
||||
var files = [
|
||||
|
@ -733,7 +771,7 @@ module.exports.missingStringValue = function missingStringValue(test) {
|
|||
};
|
||||
|
||||
module.exports.ruleNamesUpperCase = function ruleNamesUpperCase(test) {
|
||||
test.expect(38);
|
||||
test.expect(39);
|
||||
rules.forEach(function forRule(rule) {
|
||||
test.equal(rule.name, rule.name.toUpperCase(), "Rule name not upper-case.");
|
||||
});
|
||||
|
@ -741,7 +779,7 @@ module.exports.ruleNamesUpperCase = function ruleNamesUpperCase(test) {
|
|||
};
|
||||
|
||||
module.exports.uniqueAliases = function uniqueAliases(test) {
|
||||
test.expect(76);
|
||||
test.expect(78);
|
||||
var tags = [];
|
||||
rules.forEach(function forRule(rule) {
|
||||
Array.prototype.push.apply(tags, rule.tags);
|
||||
|
@ -758,7 +796,7 @@ module.exports.uniqueAliases = function uniqueAliases(test) {
|
|||
};
|
||||
|
||||
module.exports.readme = function readme(test) {
|
||||
test.expect(99);
|
||||
test.expect(101);
|
||||
var tagToRules = {};
|
||||
rules.forEach(function forRule(rule) {
|
||||
rule.tags.forEach(function forTag(tag) {
|
||||
|
@ -819,7 +857,7 @@ module.exports.readme = function readme(test) {
|
|||
};
|
||||
|
||||
module.exports.doc = function doc(test) {
|
||||
test.expect(281);
|
||||
test.expect(289);
|
||||
fs.readFile("doc/Rules.md", shared.utf8Encoding,
|
||||
function readFile(err, contents) {
|
||||
test.ifError(err);
|
||||
|
|
6
test/required-headers-all-optional.json
Normal file
6
test/required-headers-all-optional.json
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"default": true,
|
||||
"MD043": {
|
||||
"headers": [ "*" ]
|
||||
}
|
||||
}
|
11
test/required-headers-all-optional.md
Normal file
11
test/required-headers-all-optional.md
Normal file
|
@ -0,0 +1,11 @@
|
|||
# One
|
||||
|
||||
## Two
|
||||
|
||||
### THREE
|
||||
|
||||
#### four
|
||||
|
||||
##### Five
|
||||
|
||||
###### SiX
|
13
test/required-headers-all-present.json
Normal file
13
test/required-headers-all-present.json
Normal file
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"default": true,
|
||||
"MD043": {
|
||||
"headers": [
|
||||
"# One",
|
||||
"## Two",
|
||||
"### Three",
|
||||
"## Four",
|
||||
"## Five",
|
||||
"### Six"
|
||||
]
|
||||
}
|
||||
}
|
11
test/required-headers-all-present.md
Normal file
11
test/required-headers-all-present.md
Normal file
|
@ -0,0 +1,11 @@
|
|||
# One
|
||||
|
||||
## Two
|
||||
|
||||
### THREE
|
||||
|
||||
## four
|
||||
|
||||
## Five
|
||||
|
||||
### SiX
|
10
test/required-headers-missing-first.json
Normal file
10
test/required-headers-missing-first.json
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"default": true,
|
||||
"MD043": {
|
||||
"headers": [
|
||||
"# One",
|
||||
"## Two",
|
||||
"### Three"
|
||||
]
|
||||
}
|
||||
}
|
5
test/required-headers-missing-first.md
Normal file
5
test/required-headers-missing-first.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
text
|
||||
|
||||
## Two {MD002} {MD043}
|
||||
|
||||
### Three
|
10
test/required-headers-missing-last.json
Normal file
10
test/required-headers-missing-last.json
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"default": true,
|
||||
"MD043": {
|
||||
"headers": [
|
||||
"# One",
|
||||
"## Two",
|
||||
"### Three"
|
||||
]
|
||||
}
|
||||
}
|
7
test/required-headers-missing-last.md
Normal file
7
test/required-headers-missing-last.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
One
|
||||
===
|
||||
|
||||
Two
|
||||
---
|
||||
|
||||
{MD043}
|
11
test/required-headers-missing-middle.json
Normal file
11
test/required-headers-missing-middle.json
Normal file
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"default": true,
|
||||
"MD043": {
|
||||
"headers": [
|
||||
"# One",
|
||||
"## Two",
|
||||
"### Three",
|
||||
"#### Four"
|
||||
]
|
||||
}
|
||||
}
|
5
test/required-headers-missing-middle.md
Normal file
5
test/required-headers-missing-middle.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
# One #
|
||||
|
||||
### Three {MD001} {MD043} ###
|
||||
|
||||
#### Four ####
|
6
test/required-headers-none.json
Normal file
6
test/required-headers-none.json
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"default": true,
|
||||
"MD043": {
|
||||
"headers": []
|
||||
}
|
||||
}
|
5
test/required-headers-none.md
Normal file
5
test/required-headers-none.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
# One {MD043}
|
||||
|
||||
## Two
|
||||
|
||||
### Three
|
10
test/required-headers-optional-first.json
Normal file
10
test/required-headers-optional-first.json
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"default": true,
|
||||
"MD043": {
|
||||
"headers": [
|
||||
"*",
|
||||
"### Three",
|
||||
"#### Four"
|
||||
]
|
||||
}
|
||||
}
|
7
test/required-headers-optional-first.md
Normal file
7
test/required-headers-optional-first.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
# One
|
||||
|
||||
## Two
|
||||
|
||||
### Three
|
||||
|
||||
#### Four
|
10
test/required-headers-optional-last.json
Normal file
10
test/required-headers-optional-last.json
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"default": true,
|
||||
"MD043": {
|
||||
"headers": [
|
||||
"# One",
|
||||
"## Two",
|
||||
"*"
|
||||
]
|
||||
}
|
||||
}
|
7
test/required-headers-optional-last.md
Normal file
7
test/required-headers-optional-last.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
# One
|
||||
|
||||
## Two
|
||||
|
||||
### Three
|
||||
|
||||
#### Four
|
12
test/required-headers-optional-middle.json
Normal file
12
test/required-headers-optional-middle.json
Normal file
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"default": true,
|
||||
"MD043": {
|
||||
"headers": [
|
||||
"# One",
|
||||
"*",
|
||||
"### Three",
|
||||
"*",
|
||||
"##### Five"
|
||||
]
|
||||
}
|
||||
}
|
9
test/required-headers-optional-middle.md
Normal file
9
test/required-headers-optional-middle.md
Normal file
|
@ -0,0 +1,9 @@
|
|||
# One #
|
||||
|
||||
## Two ##
|
||||
|
||||
### Three ###
|
||||
|
||||
#### Four ####
|
||||
|
||||
##### Five #####
|
11
test/required-headers-optional-redundant.json
Normal file
11
test/required-headers-optional-redundant.json
Normal file
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"default": true,
|
||||
"MD043": {
|
||||
"headers": [
|
||||
"# One",
|
||||
"*",
|
||||
"*",
|
||||
"#### Four"
|
||||
]
|
||||
}
|
||||
}
|
7
test/required-headers-optional-redundant.md
Normal file
7
test/required-headers-optional-redundant.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
# One
|
||||
|
||||
## Two
|
||||
|
||||
### Three
|
||||
|
||||
#### Four
|
Loading…
Add table
Add a link
Reference in a new issue