Add front_matter_title parameter to MD025/single-title/single-h1 (refs #169).

This commit is contained in:
David Anson 2019-03-16 20:21:57 -07:00
parent 050cbbba82
commit 61d6311a3e
14 changed files with 124 additions and 32 deletions

View file

@ -66,7 +66,7 @@ playground for learning and exploring.
* **[MD022](doc/Rules.md#md022)** *blanks-around-headings/blanks-around-headers* - Headings should be surrounded by blank lines * **[MD022](doc/Rules.md#md022)** *blanks-around-headings/blanks-around-headers* - Headings should be surrounded by blank lines
* **[MD023](doc/Rules.md#md023)** *heading-start-left/header-start-left* - Headings must start at the beginning of the line * **[MD023](doc/Rules.md#md023)** *heading-start-left/header-start-left* - Headings must start at the beginning of the line
* **[MD024](doc/Rules.md#md024)** *no-duplicate-heading/no-duplicate-header* - Multiple headings with the same content * **[MD024](doc/Rules.md#md024)** *no-duplicate-heading/no-duplicate-header* - Multiple headings with the same content
* **[MD025](doc/Rules.md#md025)** *single-h1* - Multiple top level headings in the same document * **[MD025](doc/Rules.md#md025)** *single-title/single-h1* - Multiple top level headings in the same document
* **[MD026](doc/Rules.md#md026)** *no-trailing-punctuation* - Trailing punctuation in heading * **[MD026](doc/Rules.md#md026)** *no-trailing-punctuation* - Trailing punctuation in heading
* **[MD027](doc/Rules.md#md027)** *no-multiple-space-blockquote* - Multiple spaces after blockquote symbol * **[MD027](doc/Rules.md#md027)** *no-multiple-space-blockquote* - Multiple spaces after blockquote symbol
* **[MD028](doc/Rules.md#md028)** *no-blanks-blockquote* - Blank line inside blockquote * **[MD028](doc/Rules.md#md028)** *no-blanks-blockquote* - Blank line inside blockquote

View file

@ -730,9 +730,9 @@ in change logs):
Tags: headings, headers Tags: headings, headers
Aliases: single-h1 Aliases: single-title, single-h1
Parameters: level (number; default 1) Parameters: level, front_matter_title (number; default 1, string; default "^\s*title:")
This rule is triggered when a top level heading is in use (the first line of This rule is triggered when a top level heading is in use (the first line of
the file is an h1 heading), and more than one h1 heading is in use in the the file is an h1 heading), and more than one h1 heading is in use in the
@ -764,6 +764,13 @@ should be contained within this heading.
Note: The `level` parameter can be used to change the top level (ex: to h2) in Note: The `level` parameter can be used to change the top level (ex: to h2) in
cases where an h1 is added externally. cases where an h1 is added externally.
If [YAML](https://en.wikipedia.org/wiki/YAML) front matter is present and contains
a `title` property (commonly used with blog posts), this rule treats that as a top
level heading and will report a violation for any subsequent top level headings.
To use a different property name in front matter, specify the text of a regular
expression via the `front_matter_title` parameter. To disable the use of front
matter by this rule, specify `""` for `front_matter_title`.
<a name="md026"></a> <a name="md026"></a>
## MD026 - Trailing punctuation in heading ## MD026 - Trailing punctuation in heading

View file

@ -5,16 +5,21 @@
const shared = require("./shared"); const shared = require("./shared");
module.exports = { module.exports = {
"names": [ "MD025", "single-h1" ], "names": [ "MD025", "single-title", "single-h1" ],
"description": "Multiple top level headings in the same document", "description": "Multiple top level headings in the same document",
"tags": [ "headings", "headers" ], "tags": [ "headings", "headers" ],
"function": function MD025(params, onError) { "function": function MD025(params, onError) {
const level = params.config.level || 1; const level = params.config.level || 1;
const tag = "h" + level; const tag = "h" + level;
const foundFrontMatterTitle =
shared.frontMatterHasTitle(
params.frontMatterLines,
params.config.front_matter_title
);
let hasTopLevelHeading = false; let hasTopLevelHeading = false;
shared.filterTokens(params, "heading_open", function forToken(token) { shared.filterTokens(params, "heading_open", function forToken(token) {
if (token.tag === tag) { if (token.tag === tag) {
if (hasTopLevelHeading) { if (hasTopLevelHeading || foundFrontMatterTitle) {
shared.addErrorContext(onError, token.lineNumber, shared.addErrorContext(onError, token.lineNumber,
token.line.trim()); token.line.trim());
} else if (token.lineNumber === 1) { } else if (token.lineNumber === 1) {

View file

@ -11,13 +11,11 @@ module.exports = {
"function": function MD041(params, onError) { "function": function MD041(params, onError) {
const level = params.config.level || 1; const level = params.config.level || 1;
const tag = "h" + level; const tag = "h" + level;
const frontMatterTitle = params.config.front_matter_title; const foundFrontMatterTitle =
const ignoreFrontMatter = shared.frontMatterHasTitle(
(frontMatterTitle !== undefined) && !frontMatterTitle; params.frontMatterLines,
const frontMatterTitleRe = params.config.front_matter_title
new RegExp(frontMatterTitle || "^\\s*title\\s*[:=]", "i"); );
const foundFrontMatterTitle = !ignoreFrontMatter &&
params.frontMatterLines.some((line) => frontMatterTitleRe.test(line));
if (!foundFrontMatterTitle) { if (!foundFrontMatterTitle) {
params.tokens.every((token) => { params.tokens.every((token) => {
if (token.type === "html_block") { if (token.type === "html_block") {

View file

@ -334,18 +334,18 @@ module.exports.addErrorDetailIf = function addErrorDetailIf(
// Adds an error object with context via the onError callback // Adds an error object with context via the onError callback
module.exports.addErrorContext = module.exports.addErrorContext =
function addErrorContext(onError, lineNumber, context, left, right, range) { function addErrorContext(onError, lineNumber, context, left, right, range) {
if (context.length <= 30) { if (context.length <= 30) {
// Nothing to do // Nothing to do
} else if (left && right) { } else if (left && right) {
context = context.substr(0, 15) + "..." + context.substr(-15); context = context.substr(0, 15) + "..." + context.substr(-15);
} else if (right) { } else if (right) {
context = "..." + context.substr(-30); context = "..." + context.substr(-30);
} else { } else {
context = context.substr(0, 30) + "..."; context = context.substr(0, 30) + "...";
} }
addError(onError, lineNumber, null, context, range); addError(onError, lineNumber, null, context, range);
}; };
// Returns a range object for a line by applying a RegExp // Returns a range object for a line by applying a RegExp
module.exports.rangeFromRegExp = function rangeFromRegExp(line, regexp) { module.exports.rangeFromRegExp = function rangeFromRegExp(line, regexp) {
@ -362,3 +362,14 @@ module.exports.rangeFromRegExp = function rangeFromRegExp(line, regexp) {
} }
return range; return range;
}; };
// Determines if the front matter includes a title
module.exports.frontMatterHasTitle =
function frontMatterHasTitle(frontMatterLines, frontMatterTitlePattern) {
const ignoreFrontMatter =
(frontMatterTitlePattern !== undefined) && !frontMatterTitlePattern;
const frontMatterTitleRe =
new RegExp(frontMatterTitlePattern || "^\\s*title\\s*[:=]", "i");
return !ignoreFrontMatter &&
frontMatterLines.some((line) => frontMatterTitleRe.test(line));
};

View file

@ -40,7 +40,6 @@ rules.forEach(function forRule(rule) {
let custom = true; let custom = true;
switch (rule.names[0]) { switch (rule.names[0]) {
case "MD002": case "MD002":
case "MD025":
scheme.properties = { scheme.properties = {
"level": { "level": {
"description": "Heading level", "description": "Heading level",
@ -236,6 +235,7 @@ rules.forEach(function forRule(rule) {
} }
}; };
break; break;
case "MD025":
case "MD041": case "MD041":
scheme.properties = { scheme.properties = {
"level": { "level": {

View file

@ -577,7 +577,7 @@
"additionalProperties": false "additionalProperties": false
}, },
"MD025": { "MD025": {
"description": "MD025/single-h1 - Multiple top level headings in the same document", "description": "MD025/single-title/single-h1 - Multiple top level headings in the same document",
"type": [ "type": [
"boolean", "boolean",
"object" "object"
@ -588,12 +588,38 @@
"description": "Heading level", "description": "Heading level",
"type": "integer", "type": "integer",
"default": 1 "default": 1
},
"front_matter_title": {
"description": "RegExp for matching title in front matter",
"type": "string",
"default": "^\\s*title\\s*[:=]"
}
},
"additionalProperties": false
},
"single-title": {
"description": "MD025/single-title/single-h1 - Multiple top level headings in the same document",
"type": [
"boolean",
"object"
],
"default": true,
"properties": {
"level": {
"description": "Heading level",
"type": "integer",
"default": 1
},
"front_matter_title": {
"description": "RegExp for matching title in front matter",
"type": "string",
"default": "^\\s*title\\s*[:=]"
} }
}, },
"additionalProperties": false "additionalProperties": false
}, },
"single-h1": { "single-h1": {
"description": "MD025/single-h1 - Multiple top level headings in the same document", "description": "MD025/single-title/single-h1 - Multiple top level headings in the same document",
"type": [ "type": [
"boolean", "boolean",
"object" "object"
@ -604,6 +630,11 @@
"description": "Heading level", "description": "Heading level",
"type": "integer", "type": "integer",
"default": 1 "default": 1
},
"front_matter_title": {
"description": "RegExp for matching title in front matter",
"type": "string",
"default": "^\\s*title\\s*[:=]"
} }
}, },
"additionalProperties": false "additionalProperties": false
@ -955,7 +986,28 @@
"default": true "default": true
}, },
"MD041": { "MD041": {
"description": "MD041/first-line-h1 - First line in file should be a top level heading", "description": "MD041/first-line-heading/first-line-h1 - First line in file should be a top level heading",
"type": [
"boolean",
"object"
],
"default": true,
"properties": {
"level": {
"description": "Heading level",
"type": "integer",
"default": 1
},
"front_matter_title": {
"description": "RegExp for matching title in front matter",
"type": "string",
"default": "^\\s*title\\s*[:=]"
}
},
"additionalProperties": false
},
"first-line-heading": {
"description": "MD041/first-line-heading/first-line-h1 - First line in file should be a top level heading",
"type": [ "type": [
"boolean", "boolean",
"object" "object"
@ -976,7 +1028,7 @@
"additionalProperties": false "additionalProperties": false
}, },
"first-line-h1": { "first-line-h1": {
"description": "MD041/first-line-h1 - First line in file should be a top level heading", "description": "MD041/first-line-heading/first-line-h1 - First line in file should be a top level heading",
"type": [ "type": [
"boolean", "boolean",
"object" "object"

View file

@ -28,7 +28,7 @@
}, },
{ {
"lineNumber": 4, "lineNumber": 4,
"ruleNames": [ "MD025", "single-h1" ], "ruleNames": [ "MD025", "single-title", "single-h1" ],
"ruleDescription": "Multiple top level headings in the same document", "ruleDescription": "Multiple top level headings in the same document",
"ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md025", "ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md025",
"errorDetail": null, "errorDetail": null,

View file

@ -0,0 +1,7 @@
{
"default": true,
"MD013": false,
"MD025": {
"front_matter_title": "^\\s*alternate="
}
}

View file

@ -0,0 +1,6 @@
---
alternate="Welcome to Jekyll!"
---
# Top level heading {MD025}
Front matter from [Jekyll documentation](https://jekyllrb.com/docs/posts/#a-typical-post).

View file

@ -4,6 +4,6 @@ title: "Welcome to Jekyll!"
date: 2015-11-17 16:16:01 -0600 date: 2015-11-17 16:16:01 -0600
categories: jekyll update categories: jekyll update
--- ---
# Top level heading # Top level heading {MD025}
Front matter from [Jekyll documentation](https://jekyllrb.com/docs/posts/#a-typical-post). Front matter from [Jekyll documentation](https://jekyllrb.com/docs/posts/#a-typical-post).

View file

@ -1,4 +1,7 @@
{ {
"default": true, "default": true,
"MD025": {
"front_matter_title": ""
},
"MD041": true "MD041": true
} }

View file

@ -1,4 +1,7 @@
{ {
"default": true, "default": true,
"MD025": {
"front_matter_title": ""
},
"MD041": true "MD041": true
} }

View file

@ -9,7 +9,7 @@ tags:
url: https://example.com url: https://example.com
excerpt: Hello World! Vestibulum imperdiet adipiscing arcu, quis aliquam dolor condimentum dapibus. Aliquam fermentum leo aliquet quam volutpat et molestie mauris mattis. Suspendisse semper consequat velit in suscipit. excerpt: Hello World! Vestibulum imperdiet adipiscing arcu, quis aliquam dolor condimentum dapibus. Aliquam fermentum leo aliquet quam volutpat et molestie mauris mattis. Suspendisse semper consequat velit in suscipit.
--- ---
# heading1 # heading1 {MD025}
This is just a sample post. This is just a sample post.