Adding new rule for enforcing new lines at the end of file, fixes #89

This commit is contained in:
KitoW 2019-04-05 12:36:12 +02:00
parent 827e1acb56
commit 12a51da282
39 changed files with 142 additions and 52 deletions

View file

@ -87,6 +87,7 @@ playground for learning and exploring.
* **[MD043](doc/Rules.md#md043)** *required-headings/required-headers* - Required heading structure * **[MD043](doc/Rules.md#md043)** *required-headings/required-headers* - Required heading structure
* **[MD044](doc/Rules.md#md044)** *proper-names* - Proper names should have the correct capitalization * **[MD044](doc/Rules.md#md044)** *proper-names* - Proper names should have the correct capitalization
* **[MD045](doc/Rules.md#md045)** *no-alt-text* - Images should have alternate text (alt text) * **[MD045](doc/Rules.md#md045)** *no-alt-text* - Images should have alternate text (alt text)
* **[MD046](doc/Rules.md#md046)** *new-line-eof* - New lines at the end of file
See [Rules.md](doc/Rules.md) for more details. See [Rules.md](doc/Rules.md) for more details.
@ -101,7 +102,7 @@ Tags group related rules and can be used to enable/disable multiple rules at onc
* **accessibility** - MD045 * **accessibility** - MD045
* **atx** - MD018, MD019 * **atx** - MD018, MD019
* **atx_closed** - MD020, MD021 * **atx_closed** - MD020, MD021
* **blank_lines** - MD012, MD022, MD031, MD032 * **blank_lines** - MD012, MD022, MD031, MD032, MD046
* **blockquote** - MD027, MD028 * **blockquote** - MD027, MD028
* **bullet** - MD004, MD005, MD006, MD007, MD032 * **bullet** - MD004, MD005, MD006, MD007, MD032
* **code** - MD014, MD031, MD038, MD040 * **code** - MD014, MD031, MD038, MD040

View file

@ -1544,3 +1544,30 @@ Or with reference syntax as:
Guidance for writing alternate text is available from the [W3C](https://www.w3.org/WAI/alt/), Guidance for writing alternate text is available from the [W3C](https://www.w3.org/WAI/alt/),
[Wikipedia](https://en.wikipedia.org/wiki/Alt_attribute), and [Wikipedia](https://en.wikipedia.org/wiki/Alt_attribute), and
[other locations](https://www.phase2technology.com/blog/no-more-excuses-definitive-guide-alt-text-field). [other locations](https://www.phase2technology.com/blog/no-more-excuses-definitive-guide-alt-text-field).
<a name="md045"></a>
## MD046 - New lines at the end of file
Tags: blank_lines
Aliases: new-line-eof
This rule is triggered when there is no new line at the end of the file.
Example that triggers the rule:
```markdown
# File ending without new line
This file ends without new line.
```
To fix the violation, add new line at the end of the file:
```markdown
# File ending with new line
This file ends with new line.
```

22
lib/md046.js Normal file
View file

@ -0,0 +1,22 @@
// @ts-check
"use strict";
const shared = require("./shared");
const { isBlankLine } = shared;
module.exports = {
"names": [ "MD046", "new-line-eof" ],
"description": "New lines at the end of file",
"tags": [ "blank_lines" ],
"function": function rule(params, onError) {
const lastLineNumber = params.lines.length;
const lastLine = params.lines[lastLineNumber - 1];
if (!isBlankLine(lastLine)) {
onError({
"lineNumber": lastLineNumber,
"detail": "file does not end with new line"
});
}
}
};

View file

@ -48,7 +48,8 @@ const rules = [
require("./md042"), require("./md042"),
require("./md043"), require("./md043"),
require("./md044"), require("./md044"),
require("./md045") require("./md045"),
require("./md046")
]; ];
rules.forEach((rule) => { rules.forEach((rule) => {
const name = rule.names[0].toLowerCase(); const name = rule.names[0].toLowerCase();

View file

@ -1255,6 +1255,16 @@
"type": "boolean", "type": "boolean",
"default": true "default": true
}, },
"MD046": {
"description": "MD046/new-line-eof - New lines at the end of file",
"type": "boolean",
"default": true
},
"new-line-eof": {
"description": "MD046/new-line-eof - New lines at the end of file",
"type": "boolean",
"default": true
},
"headings": { "headings": {
"description": "headings - MD001, MD002, MD003, MD018, MD019, MD020, MD021, MD022, MD023, MD024, MD025, MD026, MD036, MD041, MD043", "description": "headings - MD001, MD002, MD003, MD018, MD019, MD020, MD021, MD022, MD023, MD024, MD025, MD026, MD036, MD041, MD043",
"type": "boolean", "type": "boolean",

View file

@ -2,4 +2,4 @@
## Heading 2 {MD019} ## Heading 2 {MD019}
## Heading 3 {MD019} ## Heading 3 {MD019}

View file

@ -1,3 +1,3 @@
* Test X * Test X
* Test Y {MD007} * Test Y {MD007}
* Test Z {MD007} * Test Z {MD007}

View file

@ -11,4 +11,4 @@ Some text
Some more text Some more text
* Item {MD006} * Item {MD006}
* Item * Item

View file

@ -8,4 +8,4 @@ Some text {MD012:3}
with two blank lines in it with two blank lines in it
Some more text Some more text

View file

@ -1,3 +1,3 @@
* Item * Item
* Item * Item
* Item * Item

View file

@ -1,3 +1,3 @@
- Item - Item
- Item - Item
- Item - Item

View file

@ -1,3 +1,3 @@
+ Item + Item
+ Item + Item
+ Item + Item

View file

@ -10,4 +10,4 @@
- a - a
1. a 1. a

View file

@ -39,4 +39,4 @@ text
``` ```
code at end of file without newline code at end of file without newline
``` ``` {MD046}

View file

@ -1 +1 @@
## Heading ## Heading

View file

@ -1,2 +1,2 @@
Heading Heading
------- -------

View file

@ -1 +1 @@
# Heading # Heading

View file

@ -1,2 +1,2 @@
Heading Heading
======= =======

View file

@ -8,4 +8,4 @@
## Heading 3 ## Heading 3
{MD024:5} {MD024:7} {MD024:5} {MD024:7}

View file

@ -1,3 +1,3 @@
# Heading 1 # Heading 1
# Heading 2 {MD025} # Heading 2 {MD025}

View file

@ -2,4 +2,4 @@ Some introductory text
# Heading 1 # Heading 1
# Heading 2 # Heading 2

View file

@ -8,4 +8,4 @@
## Heading 5 {MD026}; ## Heading 5 {MD026};
## Heading 6 {MD026}? ## Heading 6 {MD026}?

View file

@ -4,4 +4,4 @@
## Heading 2 ## Heading 2
#### Heading 4 {MD001} #### Heading 4 {MD001}

View file

@ -2,4 +2,4 @@
## Heading 2 ## Heading 2
## Heading 3 ## Heading 3

View file

@ -12,4 +12,4 @@ Heading 4
Some text Some text
Heading 5 Heading 5
--------- ---------

View file

@ -1,4 +1,4 @@
* Item * Item
* Item {MD007} * Item {MD007}
* Item {MD005} * Item {MD005}
* Item {MD007} * Item {MD007}

View file

@ -1,3 +1,3 @@
* Item * Item
+ Item {MD004} + Item {MD004}
- Item {MD004} - Item {MD004}

View file

@ -1,3 +1,3 @@
- Item - Item
* Item {MD004} * Item {MD004}
+ Item {MD004} + Item {MD004}

View file

@ -1,3 +1,3 @@
+ Item + Item
* Item {MD004} * Item {MD004}
- Item {MD004} - Item {MD004}

View file

@ -72,4 +72,4 @@ code
text text
* list (on last line without newline) * list (on last line without newline) {MD046}

View file

@ -248,7 +248,7 @@ module.exports.resultFormattingV1 = function resultFormattingV1(test) {
const options = { const options = {
"strings": { "strings": {
"truncate": "truncate":
"# Multiple spaces inside hashes on closed atx style heading #" "# Multiple spaces inside hashes on closed atx style heading #\n"
}, },
"files": [ "files": [
"./test/atx_heading_spacing.md", "./test/atx_heading_spacing.md",
@ -350,7 +350,7 @@ module.exports.resultFormattingV2 = function resultFormattingV2(test) {
const options = { const options = {
"strings": { "strings": {
"truncate": "truncate":
"# Multiple spaces inside hashes on closed atx style heading #" "# Multiple spaces inside hashes on closed atx style heading #\n"
}, },
"files": [ "files": [
"./test/atx_heading_spacing.md", "./test/atx_heading_spacing.md",
@ -446,14 +446,14 @@ module.exports.stringInputLineEndings = function stringInputLineEndings(test) {
test.expect(2); test.expect(2);
const options = { const options = {
"strings": { "strings": {
"cr": "One\rTwo\r#Three", "cr": "One\rTwo\r#Three\n",
"lf": "One\nTwo\n#Three", "lf": "One\nTwo\n#Three\n",
"crlf": "One\r\nTwo\r\n#Three", "crlf": "One\r\nTwo\r\n#Three\n",
"mixed": "One\rTwo\n#Three", "mixed": "One\rTwo\n#Three\n",
"crnel": "One\r\u0085Two\r\u0085#Three", "crnel": "One\r\u0085Two\r\u0085#Three\n",
"snl": "One\u2424Two\u2424#Three", "snl": "One\u2424Two\u2424#Three\n",
"lsep": "One\u2028Two\u2028#Three", "lsep": "One\u2028Two\u2028#Three\n",
"nel": "One\u0085Two\u0085#Three" "nel": "One\u0085Two\u0085#Three\n"
}, },
"config": defaultConfig, "config": defaultConfig,
"resultVersion": 0 "resultVersion": 0
@ -913,7 +913,7 @@ module.exports.noInlineConfig = function noInlineConfig(test) {
"", "",
"<!-- markdownlint-enable-->", "<!-- markdownlint-enable-->",
"", "",
"\tTab" "\tTab\n"
].join("\n") ].join("\n")
}, },
"noInlineConfig": true, "noInlineConfig": true,
@ -1089,7 +1089,7 @@ module.exports.missingStringValue = function missingStringValue(test) {
}; };
module.exports.readme = function readme(test) { module.exports.readme = function readme(test) {
test.expect(109); test.expect(111);
const tagToRules = {}; const tagToRules = {};
rules.forEach(function forRule(rule) { rules.forEach(function forRule(rule) {
rule.tags.forEach(function forTag(tag) { rule.tags.forEach(function forTag(tag) {
@ -1155,7 +1155,7 @@ module.exports.readme = function readme(test) {
}; };
module.exports.doc = function doc(test) { module.exports.doc = function doc(test) {
test.expect(312); test.expect(319);
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);
@ -1883,7 +1883,7 @@ module.exports.configBadHybridSync = function configBadHybridSync(test) {
module.exports.allBuiltInRulesHaveValidUrl = module.exports.allBuiltInRulesHaveValidUrl =
function allBuiltInRulesHaveValidUrl(test) { function allBuiltInRulesHaveValidUrl(test) {
test.expect(123); test.expect(126);
rules.forEach(function forRule(rule) { rules.forEach(function forRule(rule) {
test.ok(rule.information); test.ok(rule.information);
test.ok(Object.getPrototypeOf(rule.information) === URL.prototype); test.ok(Object.getPrototypeOf(rule.information) === URL.prototype);
@ -2240,7 +2240,7 @@ module.exports.customRulesNpmPackage = function customRulesNpmPackage(test) {
const options = { const options = {
"customRules": [ require("./rules/npm") ], "customRules": [ require("./rules/npm") ],
"strings": { "strings": {
"string": "# Text\n\n---\n\nText" "string": "# Text\n\n---\n\nText\n"
}, },
"resultVersion": 0 "resultVersion": 0
}; };
@ -2539,7 +2539,7 @@ module.exports.customRulesOnErrorLazy = function customRulesOnErrorLazy(test) {
} }
], ],
"strings": { "strings": {
"string": "# Heading" "string": "# Heading\n"
} }
}; };
markdownlint(options, function callback(err, actualResult) { markdownlint(options, function callback(err, actualResult) {
@ -2626,7 +2626,7 @@ module.exports.markdownItPluginsSingle =
test.expect(2); test.expect(2);
markdownlint({ markdownlint({
"strings": { "strings": {
"string": "# Heading\n\nText [ link ](https://example.com)" "string": "# Heading\n\nText [ link ](https://example.com)\n"
}, },
"markdownItPlugins": [ "markdownItPlugins": [
[ [
@ -2651,7 +2651,7 @@ module.exports.markdownItPluginsMultiple =
test.expect(4); test.expect(4);
markdownlint({ markdownlint({
"strings": { "strings": {
"string": "# Heading\n\nText H~2~0 text 29^th^ text" "string": "# Heading\n\nText H~2~0 text 29^th^ text\n"
}, },
"markdownItPlugins": [ "markdownItPlugins": [
[ pluginSub ], [ pluginSub ],
@ -2704,7 +2704,7 @@ $$
1 1
$$$$ $$$$
2 2
$$` $$\n`
}, },
"markdownItPlugins": [ [ pluginKatex ] ], "markdownItPlugins": [ [ pluginKatex ] ],
"resultVersion": 0 "resultVersion": 0
@ -2719,3 +2719,25 @@ $$`
test.done(); test.done();
}); });
}; };
module.exports.newLineAtTheEndOfFile =
function newLineAtTheEndOfFile(test) {
test.expect(2);
markdownlint({
"files": "./test/new_line_EOF.md"
}, function callback(err, actual) {
test.ifError(err);
const expected = { "./test/new_line_EOF.md":
[
{ "lineNumber": 3,
"ruleNames": [ "MD046", "new-line-eof" ],
"ruleDescription": "New lines at the end of file",
"ruleInformation": `${homepage}/blob/v${version}/doc/Rules.md#md046`,
"errorDetail": "file does not end with new line",
"errorContext": null,
"errorRange": null }
] };
test.deepEqual(actual, expected, "Unexpected issues.");
test.done();
});
};

View file

@ -1 +1 @@
-- --

View file

@ -3,4 +3,4 @@
## Heading 2 {MD003} ## ## Heading 2 {MD003} ##
Heading 3 {MD003} Heading 3 {MD003}
----------------- -----------------

View file

@ -3,4 +3,4 @@
## Heading 2 {MD003} ## Heading 2 {MD003}
Heading 3 {MD003} Heading 3 {MD003}
----------------- -----------------

View file

@ -3,4 +3,4 @@ Heading 1
## Heading 2 {MD003} ## Heading 2 {MD003}
## Heading 3 {MD003} ## ## Heading 3 {MD003} ##

4
test/new_line_EOF.json Normal file
View file

@ -0,0 +1,4 @@
{
"default": true,
"MD046": false
}

3
test/new_line_EOF.md Normal file
View file

@ -0,0 +1,3 @@
# File ending without new line
This file ends without new line.

View file

@ -4,4 +4,4 @@ One
Two Two
--- ---
{MD043} {MD043} {MD046}

View file

@ -1,3 +1,3 @@
Some text {MD009} Some text {MD009}
Some more text {MD010} Some more text {MD010}
Some more text Some more text