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-len": [2, 80, 4],
"max-nested-callbacks": [2, 3], "max-nested-callbacks": [2, 3],
"max-params": [2, 5], "max-params": [2, 5],
"max-statements": [2, 20], "max-statements": [2, 21],
"new-cap": 2, "new-cap": 2,
"new-parens": 2, "new-parens": 2,
"newline-after-var": 0, "newline-after-var": 0,

View file

@ -81,6 +81,7 @@ playground for learning and exploring.
* **MD039** *no-space-in-links* - Spaces inside link text * **MD039** *no-space-in-links* - Spaces inside link text
* **MD040** *fenced-code-language* - Fenced code blocks should have a language specified * **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 * **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. 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 * **indentation** - MD005, MD006, MD007, MD027
* **language** - MD040 * **language** - MD040
* **line_length** - MD013 * **line_length** - MD013
* **links** - MD011, MD034, MD039 * **links** - MD011, MD034, MD039, MD042
* **ol** - MD029, MD030, MD032 * **ol** - MD029, MD030, MD032
* **spaces** - MD018, MD019, MD020, MD021, MD023 * **spaces** - MD018, MD019, MD020, MD021, MD023
* **ul** - MD004, MD005, MD006, MD007, MD030, MD032 * **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 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); 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} 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 ], "MD038": [ 69 ],
"MD039": [ 71 ], "MD039": [ 71 ],
"MD040": [ 73 ], "MD040": [ 73 ],
"MD041": [ 1 ] "MD041": [ 1 ],
"MD042": [ 77 ]
} }
}; };
test.deepEqual(actualResult, expectedResult, "Undetected issues."); test.deepEqual(actualResult, expectedResult, "Undetected issues.");
@ -580,7 +581,8 @@ module.exports.styleRelaxed = function styleRelaxed(test) {
"MD031": [ 50 ], "MD031": [ 50 ],
"MD032": [ 51 ], "MD032": [ 51 ],
"MD035": [ 61 ], "MD035": [ 61 ],
"MD036": [ 65 ] "MD036": [ 65 ],
"MD042": [ 77 ]
} }
}; };
test.deepEqual(actualResult, expectedResult, "Undetected issues."); test.deepEqual(actualResult, expectedResult, "Undetected issues.");
@ -731,7 +733,7 @@ module.exports.missingStringValue = function missingStringValue(test) {
}; };
module.exports.ruleNamesUpperCase = function ruleNamesUpperCase(test) { module.exports.ruleNamesUpperCase = function ruleNamesUpperCase(test) {
test.expect(37); test.expect(38);
rules.forEach(function forRule(rule) { rules.forEach(function forRule(rule) {
test.equal(rule.name, rule.name.toUpperCase(), "Rule name not upper-case."); 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) { module.exports.uniqueAliases = function uniqueAliases(test) {
test.expect(74); test.expect(76);
var tags = []; var tags = [];
rules.forEach(function forRule(rule) { rules.forEach(function forRule(rule) {
Array.prototype.push.apply(tags, rule.tags); Array.prototype.push.apply(tags, rule.tags);
@ -756,7 +758,7 @@ module.exports.uniqueAliases = function uniqueAliases(test) {
}; };
module.exports.readme = function readme(test) { module.exports.readme = function readme(test) {
test.expect(97); test.expect(99);
var tagToRules = {}; var tagToRules = {};
rules.forEach(function forRule(rule) { rules.forEach(function forRule(rule) {
rule.tags.forEach(function forTag(tag) { rule.tags.forEach(function forTag(tag) {
@ -817,7 +819,7 @@ module.exports.readme = function readme(test) {
}; };
module.exports.doc = function doc(test) { module.exports.doc = function doc(test) {
test.expect(274); test.expect(281);
fs.readFile("doc/Rules.md", shared.utf8Encoding, fs.readFile("doc/Rules.md", shared.utf8Encoding,
function readFile(err, contents) { function readFile(err, contents) {
test.ifError(err); test.ifError(err);
@ -848,6 +850,7 @@ module.exports.doc = function doc(test) {
ruleHasTags = ruleHasAliases = false; ruleHasTags = ruleHasAliases = false;
test.ok(rule, test.ok(rule,
"Missing rule implementation for " + token.content + "."); "Missing rule implementation for " + token.content + ".");
if (rule) {
test.equal(token.content, rule.name + " - " + rule.desc, test.equal(token.content, rule.name + " - " + rule.desc,
"Rule mismatch."); "Rule mismatch.");
ruleUsesParams = rule.func.toString() ruleUsesParams = rule.func.toString()
@ -857,6 +860,7 @@ module.exports.doc = function doc(test) {
return use.split(".").pop(); return use.split(".").pop();
}); });
} }
}
} else if (/^Tags: /.test(token.content) && rule) { } else if (/^Tags: /.test(token.content) && rule) {
test.deepEqual(token.content.split(tagAliasParameterRe).slice(1), test.deepEqual(token.content.split(tagAliasParameterRe).slice(1),
rule.tags, "Tag mismatch for rule " + rule.name + "."); rule.tags, "Tag mismatch for rule " + rule.name + ".");
@ -882,7 +886,9 @@ module.exports.doc = function doc(test) {
var ruleLeft = rulesLeft.shift(); var ruleLeft = rulesLeft.shift();
test.ok(!ruleLeft, test.ok(!ruleLeft,
"Missing rule documentation for " + (ruleLeft || {}).name + "."); "Missing rule documentation for " + (ruleLeft || {}).name + ".");
if (rule) {
testTagsAliasesParams(); testTagsAliasesParams();
}
test.done(); test.done();
}); });
}; };