mirror of
https://github.com/DavidAnson/markdownlint.git
synced 2025-12-16 14:00:13 +01:00
Add tags to README.md, new test and fix, comment rule code.
This commit is contained in:
parent
63a52e9dea
commit
0bd6dea637
5 changed files with 138 additions and 47 deletions
84
README.md
84
README.md
|
|
@ -29,37 +29,55 @@ cases come directly from that project.
|
|||
|
||||
## Rules
|
||||
|
||||
* MD001 - Header levels should only increment by one level at a time
|
||||
* MD002 - First header should be a h1 header
|
||||
* MD003 - Header style
|
||||
* MD004 - Unordered list style
|
||||
* MD005 - Inconsistent indentation for list items at the same level
|
||||
* MD006 - Consider starting bulleted lists at the beginning of the line
|
||||
* MD007 - Unordered list indentation
|
||||
* MD009 - Trailing spaces
|
||||
* MD010 - Hard tabs
|
||||
* MD011 - Reversed link syntax
|
||||
* MD012 - Multiple consecutive blank lines
|
||||
* MD013 - Line length
|
||||
* MD014 - Dollar signs used before commands without showing output
|
||||
* MD018 - No space after hash on atx style header
|
||||
* MD019 - Multiple spaces after hash on atx style header
|
||||
* MD020 - No space inside hashes on closed atx style header
|
||||
* MD021 - Multiple spaces inside hashes on closed atx style header
|
||||
* MD022 - Headers should be surrounded by blank lines
|
||||
* MD023 - Headers must start at the beginning of the line
|
||||
* MD024 - Multiple headers with the same content
|
||||
* MD025 - Multiple top level headers in the same document
|
||||
* MD026 - Trailing punctuation in header
|
||||
* MD027 - Multiple spaces after blockquote symbol
|
||||
* MD028 - Blank line inside blockquote
|
||||
* MD029 - Ordered list item prefix
|
||||
* MD030 - Spaces after list markers
|
||||
* MD031 - Fenced code blocks should be surrounded by blank lines
|
||||
* MD032 - Lists should be surrounded by blank lines
|
||||
* **MD001** - Header levels should only increment by one level at a time
|
||||
* **MD002** - First header should be a h1 header
|
||||
* **MD003** - Header style
|
||||
* **MD004** - Unordered list style
|
||||
* **MD005** - Inconsistent indentation for list items at the same level
|
||||
* **MD006** - Consider starting bulleted lists at the beginning of the line
|
||||
* **MD007** - Unordered list indentation
|
||||
* **MD009** - Trailing spaces
|
||||
* **MD010** - Hard tabs
|
||||
* **MD011** - Reversed link syntax
|
||||
* **MD012** - Multiple consecutive blank lines
|
||||
* **MD013** - Line length
|
||||
* **MD014** - Dollar signs used before commands without showing output
|
||||
* **MD018** - No space after hash on atx style header
|
||||
* **MD019** - Multiple spaces after hash on atx style header
|
||||
* **MD020** - No space inside hashes on closed atx style header
|
||||
* **MD021** - Multiple spaces inside hashes on closed atx style header
|
||||
* **MD022** - Headers should be surrounded by blank lines
|
||||
* **MD023** - Headers must start at the beginning of the line
|
||||
* **MD024** - Multiple headers with the same content
|
||||
* **MD025** - Multiple top level headers in the same document
|
||||
* **MD026** - Trailing punctuation in header
|
||||
* **MD027** - Multiple spaces after blockquote symbol
|
||||
* **MD028** - Blank line inside blockquote
|
||||
* **MD029** - Ordered list item prefix
|
||||
* **MD030** - Spaces after list markers
|
||||
* **MD031** - Fenced code blocks should be surrounded by blank lines
|
||||
* **MD032** - Lists should be surrounded by blank lines
|
||||
|
||||
See [Rules.md](doc/Rules.md) for more details.
|
||||
|
||||
## Tags
|
||||
|
||||
* **atx** - MD018, MD019
|
||||
* **atx_closed** - MD020, MD021
|
||||
* **blank_lines** - MD012, MD022, MD031, MD032
|
||||
* **blockquote** - MD027, MD028
|
||||
* **bullet** - MD004, MD005, MD006, MD007, MD032
|
||||
* **code** - MD014, MD031
|
||||
* **headers** - MD001, MD002, MD003, MD018, MD019, MD020, MD021, MD022, MD023,
|
||||
MD024, MD025, MD026
|
||||
* **indentation** - MD005, MD006, MD007, MD027
|
||||
* **line_length** - MD013
|
||||
* **links** - MD011
|
||||
* **ol** - MD029, MD030, MD032
|
||||
* **spaces** - MD018, MD019, MD020, MD021, MD023
|
||||
* **ul** - MD004, MD005, MD006, MD007, MD030, MD032
|
||||
* **whitespace** - MD009, MD010, MD012, MD027, MD028, MD030
|
||||
|
||||
## API
|
||||
|
||||
```js
|
||||
|
|
@ -93,7 +111,7 @@ Example: `[ "one.md", "dir/two.md" ]`
|
|||
|
||||
#### options.config
|
||||
|
||||
Type: `Object` mapping `String` to `Object | Boolean`
|
||||
Type: `Object` mapping `String` to `Boolean | Object`
|
||||
|
||||
Configures the rules to use.
|
||||
|
||||
|
|
@ -165,10 +183,10 @@ Outputs:
|
|||
|
||||
```json
|
||||
{
|
||||
'good.md': {},
|
||||
'bad.md': {
|
||||
MD010: [ 3 ],
|
||||
MD018: [ 1, 3 ]
|
||||
"good.md": {},
|
||||
"bad.md": {
|
||||
"MD010": [ 3 ],
|
||||
"MD018": [ 1, 3 ]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
|
|||
|
|
@ -5,13 +5,23 @@ var md = require("markdown-it")();
|
|||
var rules = require("./rules");
|
||||
var shared = require("./shared");
|
||||
|
||||
// Mapping from rule name to description
|
||||
// Mappings from rule to description and tag to rules
|
||||
var ruleToDescription = {};
|
||||
var tagToRules = {};
|
||||
rules.forEach(function forRule(rule) {
|
||||
ruleToDescription[rule.name] = rule.desc;
|
||||
// The following is useful for updating README.md
|
||||
// console.log("* " + rule.name + " - " + rule.desc);
|
||||
// console.log("* **" + rule.name + "** - " + rule.desc);
|
||||
rule.tags.forEach(function forTag(tag) {
|
||||
var tags = tagToRules[tag] || [];
|
||||
tags.push(rule.name);
|
||||
tagToRules[tag] = tags;
|
||||
});
|
||||
});
|
||||
// The following is useful for updating README.md
|
||||
// Object.keys(tagToRules).sort().forEach(function forTag(tag) {
|
||||
// console.log("* **" + tag + "** - " + tagToRules[tag].join(", "));
|
||||
// });
|
||||
|
||||
// Class for results with toString for pretty display
|
||||
function Results() { }
|
||||
|
|
@ -57,6 +67,10 @@ function lintFile(file, config, callback) {
|
|||
if (token.lines) {
|
||||
token.line = lines[token.lines[0]];
|
||||
token.lineNumber = token.lines[0] + 1;
|
||||
// Trim bottom of token to exclude whitespace lines
|
||||
while (!(lines[token.lines[1] - 1].trim())) {
|
||||
token.lines[1]--;
|
||||
}
|
||||
}
|
||||
});
|
||||
// Create parameters for rules
|
||||
|
|
|
|||
61
lib/rules.js
61
lib/rules.js
|
|
@ -2,10 +2,12 @@
|
|||
|
||||
var shared = require("./shared");
|
||||
|
||||
// Returns the indent for a token
|
||||
function indentFor(token) {
|
||||
return token.line.length - token.line.trimLeft().length;
|
||||
}
|
||||
|
||||
// Returns the heading style for a heading token
|
||||
function headingStyleFor(token) {
|
||||
if ((token.lines[1] - token.lines[0]) === 1) {
|
||||
if (/#\s*$/.test(token.line)) {
|
||||
|
|
@ -16,6 +18,7 @@ function headingStyleFor(token) {
|
|||
return "setext";
|
||||
}
|
||||
|
||||
// Returns the unordered list style for a list item token
|
||||
function unorderedListStyleFor(token) {
|
||||
switch (token.line.trimLeft().substr(0, 1)) {
|
||||
case "-":
|
||||
|
|
@ -28,31 +31,36 @@ function unorderedListStyleFor(token) {
|
|||
}
|
||||
}
|
||||
|
||||
function filterTokens(tokens, tokenA, tokenB) {
|
||||
// Filters a list of tokens by type
|
||||
function filterTokens(tokens, typeA, typeB) {
|
||||
return tokens.filter(function filterToken(token) {
|
||||
return ((token.type === tokenA) || (token.type === tokenB));
|
||||
return ((token.type === typeA) || (token.type === typeB));
|
||||
});
|
||||
}
|
||||
|
||||
// Calls the provided function for each line (with context)
|
||||
function forEachLine(params, callback) {
|
||||
var codeBlocks = [];
|
||||
// Identify lines in code blocks
|
||||
var codeLines = [];
|
||||
filterTokens(params.tokens, "code_block")
|
||||
.forEach(function forToken(token) {
|
||||
for (var i = token.lines[0]; i < token.lines[1]; i++) {
|
||||
codeBlocks.push(i);
|
||||
codeLines.push(i);
|
||||
}
|
||||
});
|
||||
// Identify lines in code fences (with info about transitions)
|
||||
var inFence = false;
|
||||
params.lines.forEach(function forLine(line, lineIndex) {
|
||||
var onFence = /^(```|~~~)/.test(line);
|
||||
if (onFence) {
|
||||
inFence = !inFence;
|
||||
}
|
||||
var codeBlock = (codeBlocks.indexOf(lineIndex) !== -1);
|
||||
callback(line, lineIndex, inFence || codeBlock, onFence);
|
||||
var inCodeBlock = (codeLines.indexOf(lineIndex) !== -1);
|
||||
callback(line, lineIndex, inFence || inCodeBlock, onFence);
|
||||
});
|
||||
}
|
||||
|
||||
// Calls the provided function for each heading's content
|
||||
function forEachHeading(params, callback) {
|
||||
var heading = null;
|
||||
params.tokens.forEach(function forToken(token) {
|
||||
|
|
@ -66,6 +74,7 @@ function forEachHeading(params, callback) {
|
|||
});
|
||||
}
|
||||
|
||||
// Returns (nested) lists as a flat array (in order)
|
||||
function flattenLists(tokens, filterBy) {
|
||||
var lists = [];
|
||||
var stack = [];
|
||||
|
|
@ -74,6 +83,7 @@ function flattenLists(tokens, filterBy) {
|
|||
tokens.forEach(function forToken(token) {
|
||||
if ((token.type === "bullet_list_open") ||
|
||||
(token.type === "ordered_list_open")) {
|
||||
// Save current context and start a new one
|
||||
stack.push(current);
|
||||
current = {
|
||||
"ordered": (token.type === "ordered_list_open"),
|
||||
|
|
@ -85,6 +95,7 @@ function flattenLists(tokens, filterBy) {
|
|||
};
|
||||
} else if ((token.type === "bullet_list_close") ||
|
||||
(token.type === "ordered_list_close")) {
|
||||
// Finalize current context and restore previous
|
||||
current.lastLineIndex = lastWithLines.lines[1];
|
||||
if ((filterBy === undefined) || (filterBy === current.ordered)) {
|
||||
lists.splice(current.insert, 0, current);
|
||||
|
|
@ -92,8 +103,10 @@ function flattenLists(tokens, filterBy) {
|
|||
}
|
||||
current = stack.pop();
|
||||
} else if (token.type === "list_item_open") {
|
||||
// Add list item
|
||||
current.items.push(token);
|
||||
} else if (token.lines) {
|
||||
// Track last token with lines
|
||||
lastWithLines = token;
|
||||
}
|
||||
});
|
||||
|
|
@ -104,6 +117,7 @@ module.exports = [
|
|||
{
|
||||
"name": "MD001",
|
||||
"desc": "Header levels should only increment by one level at a time",
|
||||
"tags": [ "headers" ],
|
||||
"func": function MD001(params, errors) {
|
||||
var prevLevel = 0;
|
||||
filterTokens(params.tokens, "heading_open")
|
||||
|
|
@ -119,6 +133,7 @@ module.exports = [
|
|||
{
|
||||
"name": "MD002",
|
||||
"desc": "First header should be a h1 header",
|
||||
"tags": [ "headers" ],
|
||||
"func": function MD002(params, errors) {
|
||||
params.tokens.every(function forToken(token) {
|
||||
if (token.type === "heading_open") {
|
||||
|
|
@ -135,6 +150,7 @@ module.exports = [
|
|||
{
|
||||
"name": "MD003",
|
||||
"desc": "Header style",
|
||||
"tags": [ "headers" ],
|
||||
"func": function MD003(params, errors) {
|
||||
var style = params.options.style || "consistent";
|
||||
var headings = filterTokens(params.tokens, "heading_open");
|
||||
|
|
@ -152,6 +168,7 @@ module.exports = [
|
|||
{
|
||||
"name": "MD004",
|
||||
"desc": "Unordered list style",
|
||||
"tags": [ "bullet", "ul" ],
|
||||
"func": function MD004(params, errors) {
|
||||
var style = params.options.style || "consistent";
|
||||
flattenLists(params.tokens, false).forEach(function forList(list) {
|
||||
|
|
@ -170,6 +187,7 @@ module.exports = [
|
|||
{
|
||||
"name": "MD005",
|
||||
"desc": "Inconsistent indentation for list items at the same level",
|
||||
"tags": [ "bullet", "ul", "indentation" ],
|
||||
"func": function MD005(params, errors) {
|
||||
flattenLists(params.tokens).forEach(function forList(list) {
|
||||
var indent = indentFor(list.items[0]);
|
||||
|
|
@ -185,6 +203,7 @@ module.exports = [
|
|||
{
|
||||
"name": "MD006",
|
||||
"desc": "Consider starting bulleted lists at the beginning of the line",
|
||||
"tags": [ "bullet", "ul", "indentation" ],
|
||||
"func": function MD006(params, errors) {
|
||||
flattenLists(params.tokens, false).forEach(function forList(list) {
|
||||
if (!list.nesting && indentFor(list.open)) {
|
||||
|
|
@ -197,6 +216,7 @@ module.exports = [
|
|||
{
|
||||
"name": "MD007",
|
||||
"desc": "Unordered list indentation",
|
||||
"tags": [ "bullet", "ul", "indentation" ],
|
||||
"func": function MD007(params, errors) {
|
||||
var optionsIndent = params.options.indent || 2;
|
||||
var prevIndent = 0;
|
||||
|
|
@ -214,6 +234,7 @@ module.exports = [
|
|||
{
|
||||
"name": "MD009",
|
||||
"desc": "Trailing spaces",
|
||||
"tags": [ "whitespace" ],
|
||||
"func": function MD009(params, errors) {
|
||||
params.lines.forEach(function forLine(line, lineIndex) {
|
||||
if (/\s$/.test(line)) {
|
||||
|
|
@ -226,6 +247,7 @@ module.exports = [
|
|||
{
|
||||
"name": "MD010",
|
||||
"desc": "Hard tabs",
|
||||
"tags": [ "whitespace" ],
|
||||
"func": function MD010(params, errors) {
|
||||
params.lines.forEach(function forLine(line, lineIndex) {
|
||||
if (/\t/.test(line)) {
|
||||
|
|
@ -238,6 +260,7 @@ module.exports = [
|
|||
{
|
||||
"name": "MD011",
|
||||
"desc": "Reversed link syntax",
|
||||
"tags": [ "links" ],
|
||||
"func": function MD011(params, errors) {
|
||||
filterTokens(params.tokens, "inline")
|
||||
.forEach(function forToken(token) {
|
||||
|
|
@ -254,6 +277,7 @@ module.exports = [
|
|||
{
|
||||
"name": "MD012",
|
||||
"desc": "Multiple consecutive blank lines",
|
||||
"tags": [ "whitespace", "blank_lines" ],
|
||||
"func": function MD012(params, errors) {
|
||||
var prevLine = "-";
|
||||
forEachLine(params, function forLine(line, lineIndex, inCode) {
|
||||
|
|
@ -269,11 +293,12 @@ module.exports = [
|
|||
{
|
||||
"name": "MD013",
|
||||
"desc": "Line length",
|
||||
"tags": [ "line_length" ],
|
||||
"func": function MD013(params, errors) {
|
||||
var lineLength = params.options.line_length || 80;
|
||||
var regex = new RegExp("^.{" + lineLength + "}.*\\s");
|
||||
var re = new RegExp("^.{" + lineLength + "}.*\\s");
|
||||
params.lines.forEach(function forLine(line, lineIndex) {
|
||||
if (regex.test(line)) {
|
||||
if (re.test(line)) {
|
||||
errors.push(lineIndex + 1);
|
||||
}
|
||||
});
|
||||
|
|
@ -283,6 +308,7 @@ module.exports = [
|
|||
{
|
||||
"name": "MD014",
|
||||
"desc": "Dollar signs used before commands without showing output",
|
||||
"tags": [ "code" ],
|
||||
"func": function MD014(params, errors) {
|
||||
filterTokens(params.tokens, "code_block", "fence")
|
||||
.forEach(function forToken(token) {
|
||||
|
|
@ -301,6 +327,7 @@ module.exports = [
|
|||
{
|
||||
"name": "MD018",
|
||||
"desc": "No space after hash on atx style header",
|
||||
"tags": [ "headers", "atx", "spaces" ],
|
||||
"func": function MD018(params, errors) {
|
||||
forEachLine(params, function forLine(line, lineIndex, inCode) {
|
||||
if (!inCode && /^#+[^#\s]/.test(line) && !/#$/.test(line)) {
|
||||
|
|
@ -313,6 +340,7 @@ module.exports = [
|
|||
{
|
||||
"name": "MD019",
|
||||
"desc": "Multiple spaces after hash on atx style header",
|
||||
"tags": [ "headers", "atx", "spaces" ],
|
||||
"func": function MD019(params, errors) {
|
||||
filterTokens(params.tokens, "heading_open")
|
||||
.forEach(function forToken(token) {
|
||||
|
|
@ -327,6 +355,7 @@ module.exports = [
|
|||
{
|
||||
"name": "MD020",
|
||||
"desc": "No space inside hashes on closed atx style header",
|
||||
"tags": [ "headers", "atx_closed", "spaces" ],
|
||||
"func": function MD020(params, errors) {
|
||||
forEachLine(params, function forLine(line, lineIndex, inCode) {
|
||||
if (!inCode && /^#+[^#]*[^\\]#+$/.test(line) &&
|
||||
|
|
@ -340,6 +369,7 @@ module.exports = [
|
|||
{
|
||||
"name": "MD021",
|
||||
"desc": "Multiple spaces inside hashes on closed atx style header",
|
||||
"tags": [ "headers", "atx_closed", "spaces" ],
|
||||
"func": function MD021(params, errors) {
|
||||
filterTokens(params.tokens, "heading_open")
|
||||
.forEach(function forToken(token) {
|
||||
|
|
@ -354,6 +384,7 @@ module.exports = [
|
|||
{
|
||||
"name": "MD022",
|
||||
"desc": "Headers should be surrounded by blank lines",
|
||||
"tags": [ "headers", "blank_lines" ],
|
||||
"func": function MD022(params, errors) {
|
||||
var prevHeadingLineNumber = 0;
|
||||
var prevMaxLineIndex = -1;
|
||||
|
|
@ -390,6 +421,7 @@ module.exports = [
|
|||
{
|
||||
"name": "MD023",
|
||||
"desc": "Headers must start at the beginning of the line",
|
||||
"tags": [ "headers", "spaces" ],
|
||||
"func": function MD023(params, errors) {
|
||||
filterTokens(params.tokens, "heading_open")
|
||||
.forEach(function forToken(token) {
|
||||
|
|
@ -403,6 +435,7 @@ module.exports = [
|
|||
{
|
||||
"name": "MD024",
|
||||
"desc": "Multiple headers with the same content",
|
||||
"tags": [ "headers" ],
|
||||
"func": function MD024(params, errors) {
|
||||
var knownContent = [];
|
||||
forEachHeading(params, function forHeading(heading, content) {
|
||||
|
|
@ -418,6 +451,7 @@ module.exports = [
|
|||
{
|
||||
"name": "MD025",
|
||||
"desc": "Multiple top level headers in the same document",
|
||||
"tags": [ "headers" ],
|
||||
"func": function MD025(params, errors) {
|
||||
var hasTopLevelHeading = false;
|
||||
filterTokens(params.tokens, "heading_open")
|
||||
|
|
@ -436,6 +470,7 @@ module.exports = [
|
|||
{
|
||||
"name": "MD026",
|
||||
"desc": "Trailing punctuation in header",
|
||||
"tags": [ "headers" ],
|
||||
"func": function MD026(params, errors) {
|
||||
var punctuation = params.options.punctuation || ".,;:!?";
|
||||
var re = new RegExp("[" + punctuation + "]$");
|
||||
|
|
@ -450,6 +485,7 @@ module.exports = [
|
|||
{
|
||||
"name": "MD027",
|
||||
"desc": "Multiple spaces after blockquote symbol",
|
||||
"tags": [ "blockquote", "whitespace", "indentation" ],
|
||||
"func": function MD027(params, errors) {
|
||||
var inBlockquote = false;
|
||||
params.tokens.forEach(function forToken(token) {
|
||||
|
|
@ -473,6 +509,7 @@ module.exports = [
|
|||
{
|
||||
"name": "MD028",
|
||||
"desc": "Blank line inside blockquote",
|
||||
"tags": [ "blockquote", "whitespace" ],
|
||||
"func": function MD028(params, errors) {
|
||||
var prevToken = {};
|
||||
params.tokens.forEach(function forToken(token) {
|
||||
|
|
@ -488,6 +525,7 @@ module.exports = [
|
|||
{
|
||||
"name": "MD029",
|
||||
"desc": "Ordered list item prefix",
|
||||
"tags": [ "ol" ],
|
||||
"func": function MD029(params, errors) {
|
||||
var style = params.options.style || "one";
|
||||
flattenLists(params.tokens, true).forEach(function forList(list) {
|
||||
|
|
@ -508,6 +546,7 @@ module.exports = [
|
|||
{
|
||||
"name": "MD030",
|
||||
"desc": "Spaces after list markers",
|
||||
"tags": [ "ol", "ul", "whitespace" ],
|
||||
"func": function MD030(params, errors) {
|
||||
var ulSingle = params.options.ul_single || 1;
|
||||
var olSingle = params.options.ol_single || 1;
|
||||
|
|
@ -532,9 +571,8 @@ module.exports = [
|
|||
{
|
||||
"name": "MD031",
|
||||
"desc": "Fenced code blocks should be surrounded by blank lines",
|
||||
"tags": [ "code", "blank_lines" ],
|
||||
"func": function MD031(params, errors) {
|
||||
// Some parsers have trouble detecting fenced code blocks without
|
||||
// surrounding whitespace, so examine the lines directly.
|
||||
forEachLine(params, function forLine(line, lineIndex, inCode, onFence) {
|
||||
if (onFence &&
|
||||
((inCode && (lineIndex - 1 >= 0) &&
|
||||
|
|
@ -550,9 +588,8 @@ module.exports = [
|
|||
{
|
||||
"name": "MD032",
|
||||
"desc": "Lists should be surrounded by blank lines",
|
||||
"tags": [ "bullet", "ul", "ol", "blank_lines" ],
|
||||
"func": function MD032(params, errors) {
|
||||
// Some parsers have trouble detecting lists without surrounding
|
||||
// whitespace, so examine the lines directly.
|
||||
var inList = false;
|
||||
var prevLine = "";
|
||||
forEachLine(params, function forLine(line, lineIndex, inCode, onFence) {
|
||||
|
|
|
|||
4
test/lists-and-headers.json
Normal file
4
test/lists-and-headers.json
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"default": true,
|
||||
"MD003": false
|
||||
}
|
||||
18
test/lists-and-headers.md
Normal file
18
test/lists-and-headers.md
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
* list
|
||||
* list
|
||||
|
||||
# Header 1
|
||||
|
||||
* list
|
||||
* list
|
||||
|
||||
# Header 2 #
|
||||
|
||||
* list
|
||||
* list
|
||||
|
||||
Header 3
|
||||
========
|
||||
|
||||
* list
|
||||
* list
|
||||
Loading…
Add table
Add a link
Reference in a new issue