Add MD042 no-empty-links "No empty links" (fixes #24).

This commit is contained in:
David Anson 2016-06-27 22:19:02 -07:00
parent efe9c9e73c
commit 2612a96ae8
7 changed files with 95 additions and 17 deletions

View file

@ -163,7 +163,7 @@
"max-len": [2, 80, 4],
"max-nested-callbacks": [2, 3],
"max-params": [2, 5],
"max-statements": [2, 20],
"max-statements": [2, 21],
"new-cap": 2,
"new-parens": 2,
"newline-after-var": 0,

View file

@ -81,6 +81,7 @@ playground for learning and exploring.
* **MD039** *no-space-in-links* - Spaces inside link text
* **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
See [Rules.md](doc/Rules.md) for more details.
@ -101,7 +102,7 @@ See [Rules.md](doc/Rules.md) for more details.
* **indentation** - MD005, MD006, MD007, MD027
* **language** - MD040
* **line_length** - MD013
* **links** - MD011, MD034, MD039
* **links** - MD011, MD034, MD039, MD042
* **ol** - MD029, MD030, MD032
* **spaces** - MD018, MD019, MD020, MD021, MD023
* **ul** - MD004, MD005, MD006, MD007, MD030, MD032

View file

@ -1004,3 +1004,25 @@ To fix this, add a header to the top of your file:
This is a file with a top level header
```
## MD042 - No empty links
Tags: links
Aliases: no-empty-links
This rule is triggered when an empty link is encountered:
[an empty link]()
To fix the violation, provide a destination for the link:
[a valid link](https://example.com/)
Empty fragments will trigger this rule:
[an empty fragment](#)
But non-empty fragments will not:
[a valid fragment](#fragment)

View file

@ -891,5 +891,21 @@ module.exports = [
errors.push(1);
}
}
},
{
"name": "MD042",
"desc": "No empty links",
"tags": [ "links" ],
"aliases": [ "no-empty-links" ],
"func": function MD034(params, errors) {
forEachInlineChild(params, "link_open", function forToken(token) {
token.attrs.forEach(function forAttr(attr) {
if (attr[0] === "href" && (!attr[1] || (attr[1] === "#"))) {
errors.push(token.lineNumber);
}
});
});
}
}
];

View file

@ -73,3 +73,5 @@ Code `with ` space {MD038}
```
code fence without language {MD040:73}
```
[empty link]() {MD042}

31
test/empty-links.md Normal file
View file

@ -0,0 +1,31 @@
# Heading
## Empty links
[text]() {MD042}
[text](<>) {MD042}
[text](#) {MD042}
[text][frag] {MD042}
[frag]: #
## Non-empty links
[text](link)
[text](link "title")
[text](<link>)
[text](#frag)
[text][ref]
[ref]: link
[text]
[text]: link

View file

@ -543,7 +543,8 @@ module.exports.styleAll = function styleAll(test) {
"MD038": [ 69 ],
"MD039": [ 71 ],
"MD040": [ 73 ],
"MD041": [ 1 ]
"MD041": [ 1 ],
"MD042": [ 77 ]
}
};
test.deepEqual(actualResult, expectedResult, "Undetected issues.");
@ -580,7 +581,8 @@ module.exports.styleRelaxed = function styleRelaxed(test) {
"MD031": [ 50 ],
"MD032": [ 51 ],
"MD035": [ 61 ],
"MD036": [ 65 ]
"MD036": [ 65 ],
"MD042": [ 77 ]
}
};
test.deepEqual(actualResult, expectedResult, "Undetected issues.");
@ -731,7 +733,7 @@ module.exports.missingStringValue = function missingStringValue(test) {
};
module.exports.ruleNamesUpperCase = function ruleNamesUpperCase(test) {
test.expect(37);
test.expect(38);
rules.forEach(function forRule(rule) {
test.equal(rule.name, rule.name.toUpperCase(), "Rule name not upper-case.");
});
@ -739,7 +741,7 @@ module.exports.ruleNamesUpperCase = function ruleNamesUpperCase(test) {
};
module.exports.uniqueAliases = function uniqueAliases(test) {
test.expect(74);
test.expect(76);
var tags = [];
rules.forEach(function forRule(rule) {
Array.prototype.push.apply(tags, rule.tags);
@ -756,7 +758,7 @@ module.exports.uniqueAliases = function uniqueAliases(test) {
};
module.exports.readme = function readme(test) {
test.expect(97);
test.expect(99);
var tagToRules = {};
rules.forEach(function forRule(rule) {
rule.tags.forEach(function forTag(tag) {
@ -817,7 +819,7 @@ module.exports.readme = function readme(test) {
};
module.exports.doc = function doc(test) {
test.expect(274);
test.expect(281);
fs.readFile("doc/Rules.md", shared.utf8Encoding,
function readFile(err, contents) {
test.ifError(err);
@ -848,14 +850,16 @@ module.exports.doc = function doc(test) {
ruleHasTags = ruleHasAliases = false;
test.ok(rule,
"Missing rule implementation for " + token.content + ".");
test.equal(token.content, rule.name + " - " + rule.desc,
"Rule mismatch.");
ruleUsesParams = rule.func.toString()
.match(/params\.options\.[_a-z]*/gi);
if (ruleUsesParams) {
ruleUsesParams = ruleUsesParams.map(function forUse(use) {
return use.split(".").pop();
});
if (rule) {
test.equal(token.content, rule.name + " - " + rule.desc,
"Rule mismatch.");
ruleUsesParams = rule.func.toString()
.match(/params\.options\.[_a-z]*/gi);
if (ruleUsesParams) {
ruleUsesParams = ruleUsesParams.map(function forUse(use) {
return use.split(".").pop();
});
}
}
} else if (/^Tags: /.test(token.content) && rule) {
test.deepEqual(token.content.split(tagAliasParameterRe).slice(1),
@ -882,7 +886,9 @@ module.exports.doc = function doc(test) {
var ruleLeft = rulesLeft.shift();
test.ok(!ruleLeft,
"Missing rule documentation for " + (ruleLeft || {}).name + ".");
testTagsAliasesParams();
if (rule) {
testTagsAliasesParams();
}
test.done();
});
};