From 7f5dd9ab6bb08856ad4c80f44e6e594d1e82184d Mon Sep 17 00:00:00 2001 From: David Anson Date: Mon, 20 Jul 2015 22:06:48 -0700 Subject: [PATCH] Add MD041 with tests. --- README.md | 3 +- doc/Rules.md | 19 ++++++++ lib/rules.js | 23 ++++++++++ package.json | 2 + style/relaxed.json | 3 +- test/break-all-the-rules.json | 4 ++ test/break-all-the-rules.md | 2 +- test/first_line_top_level_header_atx.json | 4 ++ test/first_line_top_level_header_atx.md | 3 ++ test/first_line_top_level_header_setext.json | 4 ++ test/first_line_top_level_header_setext.md | 4 ++ test/markdownlint-test-default-config.json | 3 ++ test/markdownlint-test.js | 46 +++++++++++++------- test/no_first_line_header.json | 4 ++ test/no_first_line_header.md | 1 + test/no_first_line_top_level_header.json | 5 +++ test/no_first_line_top_level_header.md | 1 + 17 files changed, 113 insertions(+), 18 deletions(-) create mode 100644 test/break-all-the-rules.json create mode 100644 test/first_line_top_level_header_atx.json create mode 100644 test/first_line_top_level_header_atx.md create mode 100644 test/first_line_top_level_header_setext.json create mode 100644 test/first_line_top_level_header_setext.md create mode 100644 test/markdownlint-test-default-config.json create mode 100644 test/no_first_line_header.json create mode 100644 test/no_first_line_header.md create mode 100644 test/no_first_line_top_level_header.json create mode 100644 test/no_first_line_top_level_header.md diff --git a/README.md b/README.md index e10e0be7..1548750e 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,7 @@ playground for learning and exploring. * **MD038** - Spaces inside code span elements * **MD039** - Spaces inside link text * **MD040** - Fenced code blocks should have a language specified +* **MD041** - First line in file should be a top level header See [Rules.md](doc/Rules.md) for more details. @@ -90,7 +91,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 + MD024, MD025, MD026, MD036, MD041 * **hr** - MD035 * **html** - MD033 * **indentation** - MD005, MD006, MD007, MD027 diff --git a/doc/Rules.md b/doc/Rules.md index 7fc58729..587eb578 100644 --- a/doc/Rules.md +++ b/doc/Rules.md @@ -872,3 +872,22 @@ To fix this, add a language specifier to the code block: #!/bin/bash echo Hello world ``` + +## MD041 - First line in file should be a top level header + +Tags: headers + +This rule is triggered when the first line in the file isn't a top level (h1) +header: + + ``` + This is a file without a header + ``` + +To fix this, add a header to the top of your file: + + ``` + # File with header + + This is a file with a top level header + ``` diff --git a/lib/rules.js b/lib/rules.js index 14ee4c87..545f9c90 100644 --- a/lib/rules.js +++ b/lib/rules.js @@ -779,5 +779,28 @@ module.exports = [ } }); } + }, + + { + "name": "MD041", + "desc": "First line in file should be a top level header", + "tags": [ "headers" ], + "func": function MD041(params, errors) { + var firstHeader = null; + params.tokens.every(function forToken(token) { + if (token.type === "heading_open") { + firstHeader = token; + return false; + } else if (token.lineNumber > 1) { + return false; + } + return true; + }); + if (!firstHeader || + (firstHeader.lineNumber !== 1) || + (firstHeader.tag !== "h1")) { + errors.push(1); + } + } } ]; diff --git a/package.json b/package.json index 57ff4842..190cea7f 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,8 @@ "cpy": "^3.3.0", "eslint": "^0.23.0", "istanbul": "^0.3.15", + "lodash.assign": "^3.2.0", + "lodash.clone": "^3.0.2", "nodeunit": "^0.9.1", "q": "^1.4.0", "rimraf": "^2.4.0", diff --git a/style/relaxed.json b/style/relaxed.json index c340642c..f2e51faf 100644 --- a/style/relaxed.json +++ b/style/relaxed.json @@ -8,5 +8,6 @@ "MD007": false, "MD033": false, "MD034": false, - "MD040": false + "MD040": false, + "MD041": false } diff --git a/test/break-all-the-rules.json b/test/break-all-the-rules.json new file mode 100644 index 00000000..054e8566 --- /dev/null +++ b/test/break-all-the-rules.json @@ -0,0 +1,4 @@ +{ + "default": true, + "MD041": true +} diff --git a/test/break-all-the-rules.md b/test/break-all-the-rules.md index b7e44387..689fb2ae 100644 --- a/test/break-all-the-rules.md +++ b/test/break-all-the-rules.md @@ -1,4 +1,4 @@ -## Header 1 {MD002} +## Header 1 {MD002} {MD041} #### Header 2 {MD001} diff --git a/test/first_line_top_level_header_atx.json b/test/first_line_top_level_header_atx.json new file mode 100644 index 00000000..d6fc777e --- /dev/null +++ b/test/first_line_top_level_header_atx.json @@ -0,0 +1,4 @@ +{ + "default": false, + "MD041": true +} diff --git a/test/first_line_top_level_header_atx.md b/test/first_line_top_level_header_atx.md new file mode 100644 index 00000000..7845ec58 --- /dev/null +++ b/test/first_line_top_level_header_atx.md @@ -0,0 +1,3 @@ +# First line is a top level header + +This shouldn't trigger MD041 diff --git a/test/first_line_top_level_header_setext.json b/test/first_line_top_level_header_setext.json new file mode 100644 index 00000000..d6fc777e --- /dev/null +++ b/test/first_line_top_level_header_setext.json @@ -0,0 +1,4 @@ +{ + "default": false, + "MD041": true +} diff --git a/test/first_line_top_level_header_setext.md b/test/first_line_top_level_header_setext.md new file mode 100644 index 00000000..f783421d --- /dev/null +++ b/test/first_line_top_level_header_setext.md @@ -0,0 +1,4 @@ +First line top level header +=========================== + +This shouldn't trigger MD041 diff --git a/test/markdownlint-test-default-config.json b/test/markdownlint-test-default-config.json new file mode 100644 index 00000000..3eb7f83b --- /dev/null +++ b/test/markdownlint-test-default-config.json @@ -0,0 +1,3 @@ +{ + "MD041": false +} diff --git a/test/markdownlint-test.js b/test/markdownlint-test.js index 74b03bc5..fe2d473f 100644 --- a/test/markdownlint-test.js +++ b/test/markdownlint-test.js @@ -3,11 +3,14 @@ var fs = require("fs"); var path = require("path"); var md = require("markdown-it")(); +var assign = require("lodash.assign"); +var clone = require("lodash.clone"); var Q = require("q"); var markdownlint = require("../lib/markdownlint"); var shared = require("../lib/shared"); var rules = require("../lib/rules"); var polyfills = require("../demo/browser-polyfills"); +var defaultConfig = require("./markdownlint-test-default-config.json"); function createTestForFile(file) { return function testForFile(test) { @@ -27,9 +30,10 @@ function createTestForFile(file) { }) .then( function lintWithConfig(config) { + var mergedConfig = assign(clone(defaultConfig), config); return Q.nfcall(markdownlint, { "files": [ file ], - "config": config + "config": mergedConfig }); }); var expectedPromise = Q.nfcall(fs.readFile, file, shared.utf8Encoding) @@ -91,7 +95,8 @@ module.exports.resultFormatting = function resultFormatting(test) { "files": [ "./test/atx_header_spacing.md", "./test/first_header_bad_atx.md" - ] + ], + "config": defaultConfig }; markdownlint(options, function callback(err, actualResult) { test.ifError(err); @@ -129,7 +134,8 @@ module.exports.resultFormattingSync = function resultFormattingSync(test) { "files": [ "./test/atx_header_spacing.md", "./test/first_header_bad_atx.md" - ] + ], + "config": defaultConfig }; var actualResult = markdownlint.sync(options); var expectedResult = { @@ -167,7 +173,8 @@ module.exports.stringInputLineEndings = function stringInputLineEndings(test) { "lf": "One\nTwo\n#Three", "crlf": "One\r\nTwo\r\n#Three", "mixed": "One\rTwo\n#Three" - } + }, + "config": defaultConfig }; markdownlint(options, function callback(err, actualResult) { test.ifError(err); @@ -199,10 +206,12 @@ module.exports.defaultTrue = function defaultTrue(test) { "./test/atx_header_spacing.md": { "MD002": [ 3 ], "MD018": [ 1 ], - "MD019": [ 3, 5 ] + "MD019": [ 3, 5 ], + "MD041": [ 1 ] }, "./test/first_header_bad_atx.md": { - "MD002": [ 1 ] + "MD002": [ 1 ], + "MD041": [ 1 ] } }; test.deepEqual(actualResult, expectedResult, "Undetected issues."); @@ -247,10 +256,12 @@ module.exports.defaultUndefined = function defaultUndefined(test) { "./test/atx_header_spacing.md": { "MD002": [ 3 ], "MD018": [ 1 ], - "MD019": [ 3, 5 ] + "MD019": [ 3, 5 ], + "MD041": [ 1 ] }, "./test/first_header_bad_atx.md": { - "MD002": [ 1 ] + "MD002": [ 1 ], + "MD041": [ 1 ] } }; test.deepEqual(actualResult, expectedResult, "Undetected issues."); @@ -268,7 +279,8 @@ module.exports.disableRules = function disableRules(test) { "config": { "MD002": false, "default": true, - "MD019": false + "MD019": false, + "MD041": false } }; markdownlint(options, function callback(err, actualResult) { @@ -329,10 +341,12 @@ module.exports.disableTag = function disableTag(test) { test.ifError(err); var expectedResult = { "./test/atx_header_spacing.md": { - "MD002": [ 3 ] + "MD002": [ 3 ], + "MD041": [ 1 ] }, "./test/first_header_bad_atx.md": { - "MD002": [ 1 ] + "MD002": [ 1 ], + "MD041": [ 1 ] } }; test.deepEqual(actualResult, expectedResult, "Undetected issues."); @@ -421,7 +435,8 @@ module.exports.styleAll = function styleAll(test) { "MD037": [ 67 ], "MD038": [ 69 ], "MD039": [ 71 ], - "MD040": [ 73 ] + "MD040": [ 73 ], + "MD041": [ 1 ] } }; test.deepEqual(actualResult, expectedResult, "Undetected issues."); @@ -539,7 +554,8 @@ module.exports.missingStringValue = function missingStringValue(test) { "undefined": undefined, "null": null, "empty": "" - } + }, + "config": defaultConfig }, function callback(err, result) { test.ifError(err); var expectedResult = { @@ -553,7 +569,7 @@ module.exports.missingStringValue = function missingStringValue(test) { }; module.exports.readme = function readme(test) { - test.expect(95); + test.expect(97); var tagToRules = {}; rules.forEach(function forRule(rule) { rule.tags.forEach(function forTag(tag) { @@ -610,7 +626,7 @@ module.exports.readme = function readme(test) { }; module.exports.doc = function doc(test) { - test.expect(147); + test.expect(151); fs.readFile("doc/Rules.md", shared.utf8Encoding, function readFile(err, contents) { test.ifError(err); diff --git a/test/no_first_line_header.json b/test/no_first_line_header.json new file mode 100644 index 00000000..054e8566 --- /dev/null +++ b/test/no_first_line_header.json @@ -0,0 +1,4 @@ +{ + "default": true, + "MD041": true +} diff --git a/test/no_first_line_header.md b/test/no_first_line_header.md new file mode 100644 index 00000000..ef1745b0 --- /dev/null +++ b/test/no_first_line_header.md @@ -0,0 +1 @@ +This is a file without a top level header {MD041} diff --git a/test/no_first_line_top_level_header.json b/test/no_first_line_top_level_header.json new file mode 100644 index 00000000..f94c9582 --- /dev/null +++ b/test/no_first_line_top_level_header.json @@ -0,0 +1,5 @@ +{ + "default": true, + "MD002": false, + "MD041": true +} diff --git a/test/no_first_line_top_level_header.md b/test/no_first_line_top_level_header.md new file mode 100644 index 00000000..c0848eea --- /dev/null +++ b/test/no_first_line_top_level_header.md @@ -0,0 +1 @@ +## Second level header {MD041}