mirror of
https://github.com/DavidAnson/markdownlint.git
synced 2025-09-21 21:30:47 +02:00
Expose shared.js helper code for custom rule authors (fixes #134).
This commit is contained in:
parent
f614f3e1ce
commit
7e980401b8
52 changed files with 283 additions and 184 deletions
|
@ -53,6 +53,8 @@ A rule is implemented as an `Object` with four required properties:
|
|||
- `context` is an optional `String` with relevant text surrounding the error location.
|
||||
- `range` is an optional `Array` with two `Number` values identifying the 1-based column and length of the error.
|
||||
|
||||
The collection of helper functions shared by the built-in rules is available for use by custom rules in the [markdownlint-rule-helpers package](https://www.npmjs.com/package/markdownlint-rule-helpers).
|
||||
|
||||
## Examples
|
||||
|
||||
- [Simple rules used by the project's test cases](../test/rules)
|
||||
|
|
21
helpers/LICENSE
Normal file
21
helpers/LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015-2019 David Anson
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
50
helpers/README.md
Normal file
50
helpers/README.md
Normal file
|
@ -0,0 +1,50 @@
|
|||
# markdownlint-rule-helpers
|
||||
|
||||
> A collection of `markdownlint` helper functions for custom rules
|
||||
|
||||
## Overview
|
||||
|
||||
The [Markdown](https://en.wikipedia.org/wiki/Markdown) linter
|
||||
[`markdownlint`](https://github.com/DavidAnson/markdownlint) offers a variety of built-in validation
|
||||
[rules](https://github.com/DavidAnson/markdownlint/blob/master/doc/Rules.md) and supports the
|
||||
creation of [custom rules](https://github.com/DavidAnson/markdownlint/blob/master/doc/CustomRules.md).
|
||||
The internal rules share various helper functions; this package exposes those for reuse by custom rules.
|
||||
|
||||
## API
|
||||
|
||||
_Undocumented_ - This package exports the internal functions as-is. The APIs were not originally meant
|
||||
to be public, are not officially supported, and may change from release to release. There are brief
|
||||
descriptive comments above each function, but no [JSDoc](https://en.m.wikipedia.org/wiki/JSDoc)
|
||||
annotations. That said, some of what's here will be useful to custom rule authors and may avoid
|
||||
duplicating code.
|
||||
|
||||
## Example
|
||||
|
||||
```js
|
||||
const { forEachLine, getLineMetadata } = require("markdownlint-rule-helpers");
|
||||
|
||||
module.exports = {
|
||||
"names": [ "every-n-lines" ],
|
||||
"description": "Rule that reports an error every N lines",
|
||||
"tags": [ "test" ],
|
||||
"function": (params, onError) => {
|
||||
const n = params.config.n || 2;
|
||||
forEachLine(getLineMetadata(params), (line, lineIndex) => {
|
||||
const lineNumber = lineIndex + 1;
|
||||
if ((lineNumber % n) === 0) {
|
||||
onError({
|
||||
"lineNumber": lineNumber,
|
||||
"detail": "Line number " + lineNumber
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
See also: [`markdownlint` built-in rule implementations](https://github.com/DavidAnson/markdownlint/tree/master/lib).
|
||||
|
||||
## Tests
|
||||
|
||||
_None_ - The entire body of code is tested to 100% coverage by the core `markdownlint` project,
|
||||
so there are no additional tests here.
|
18
helpers/package.json
Normal file
18
helpers/package.json
Normal file
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"name": "markdownlint-rule-helpers",
|
||||
"version": "0.1.0",
|
||||
"description": "A collection of markdownlint helper functions for custom rules",
|
||||
"main": "helpers.js",
|
||||
"author": "David Anson (https://dlaa.me/)",
|
||||
"license": "MIT",
|
||||
"homepage": "https://github.com/DavidAnson/markdownlint",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/DavidAnson/markdownlint.git"
|
||||
},
|
||||
"bugs": "https://github.com/DavidAnson/markdownlint/issues",
|
||||
"keywords": [
|
||||
"markdownlint",
|
||||
"markdownlint-rule"
|
||||
]
|
||||
}
|
|
@ -7,7 +7,7 @@ const path = require("path");
|
|||
const { URL } = require("url");
|
||||
const markdownIt = require("markdown-it");
|
||||
const rules = require("./rules");
|
||||
const shared = require("./shared");
|
||||
const helpers = require("../helpers");
|
||||
const cache = require("./cache");
|
||||
|
||||
const deprecatedRuleNames = [ "MD002" ];
|
||||
|
@ -31,7 +31,7 @@ function validateRuleList(ruleList) {
|
|||
const value = rule[property];
|
||||
if (!result &&
|
||||
(!value || !Array.isArray(value) || (value.length === 0) ||
|
||||
!value.every(shared.isString) || value.some(shared.isEmptyString))) {
|
||||
!value.every(helpers.isString) || value.some(helpers.isEmptyString))) {
|
||||
result = newError(property);
|
||||
}
|
||||
});
|
||||
|
@ -134,7 +134,7 @@ function removeFrontMatter(content, frontMatter) {
|
|||
if (frontMatterMatch && !frontMatterMatch.index) {
|
||||
const contentMatched = frontMatterMatch[0];
|
||||
content = content.slice(contentMatched.length);
|
||||
frontMatterLines = contentMatched.split(shared.newLineRe);
|
||||
frontMatterLines = contentMatched.split(helpers.newLineRe);
|
||||
if (frontMatterLines.length &&
|
||||
(frontMatterLines[frontMatterLines.length - 1] === "")) {
|
||||
frontMatterLines.length--;
|
||||
|
@ -269,12 +269,12 @@ function getEnabledRulesPerLineNumber(
|
|||
const enabledRulesPerLineNumber = new Array(1 + frontMatterLines.length);
|
||||
lines.forEach(function forLine(line) {
|
||||
if (!noInlineConfig) {
|
||||
let match = shared.inlineCommentRe.exec(line);
|
||||
let match = helpers.inlineCommentRe.exec(line);
|
||||
if (match) {
|
||||
enabledRules = shared.clone(enabledRules);
|
||||
enabledRules = helpers.clone(enabledRules);
|
||||
while (match) {
|
||||
forMatch(match);
|
||||
match = shared.inlineCommentRe.exec(line);
|
||||
match = helpers.inlineCommentRe.exec(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -310,10 +310,10 @@ function lintContent(
|
|||
const removeFrontMatterResult = removeFrontMatter(content, frontMatter);
|
||||
const frontMatterLines = removeFrontMatterResult.frontMatterLines;
|
||||
// Ignore the content of HTML comments
|
||||
content = shared.clearHtmlCommentText(removeFrontMatterResult.content);
|
||||
content = helpers.clearHtmlCommentText(removeFrontMatterResult.content);
|
||||
// Parse content into tokens and lines
|
||||
const tokens = md.parse(content, {});
|
||||
const lines = content.split(shared.newLineRe);
|
||||
const lines = content.split(helpers.newLineRe);
|
||||
annotateTokens(tokens, lines);
|
||||
const aliasToRuleNames = mapAliasToRuleNames(ruleList);
|
||||
const effectiveConfig =
|
||||
|
@ -328,8 +328,8 @@ function lintContent(
|
|||
lines,
|
||||
frontMatterLines
|
||||
};
|
||||
cache.lineMetadata(shared.getLineMetadata(params));
|
||||
cache.flattenedLists(shared.flattenLists(params));
|
||||
cache.lineMetadata(helpers.getLineMetadata(params));
|
||||
cache.flattenedLists(helpers.flattenLists(params));
|
||||
// Function to run for each rule
|
||||
const result = (resultVersion === 0) ? {} : [];
|
||||
function forRule(rule) {
|
||||
|
@ -345,22 +345,22 @@ function lintContent(
|
|||
function onError(errorInfo) {
|
||||
if (!errorInfo ||
|
||||
!errorInfo.lineNumber ||
|
||||
!shared.isNumber(errorInfo.lineNumber)) {
|
||||
!helpers.isNumber(errorInfo.lineNumber)) {
|
||||
throwError("lineNumber");
|
||||
}
|
||||
if (errorInfo.detail &&
|
||||
!shared.isString(errorInfo.detail)) {
|
||||
!helpers.isString(errorInfo.detail)) {
|
||||
throwError("detail");
|
||||
}
|
||||
if (errorInfo.context &&
|
||||
!shared.isString(errorInfo.context)) {
|
||||
!helpers.isString(errorInfo.context)) {
|
||||
throwError("context");
|
||||
}
|
||||
if (errorInfo.range &&
|
||||
(!Array.isArray(errorInfo.range) ||
|
||||
(errorInfo.range.length !== 2) ||
|
||||
!shared.isNumber(errorInfo.range[0]) ||
|
||||
!shared.isNumber(errorInfo.range[1]))) {
|
||||
!helpers.isNumber(errorInfo.range[0]) ||
|
||||
!helpers.isNumber(errorInfo.range[1]))) {
|
||||
throwError("range");
|
||||
}
|
||||
errors.push({
|
||||
|
@ -440,9 +440,9 @@ function lintFile(
|
|||
}
|
||||
// Make a/synchronous call to read file
|
||||
if (synchronous) {
|
||||
lintContentWrapper(null, fs.readFileSync(file, shared.utf8Encoding));
|
||||
lintContentWrapper(null, fs.readFileSync(file, helpers.utf8Encoding));
|
||||
} else {
|
||||
fs.readFile(file, shared.utf8Encoding, lintContentWrapper);
|
||||
fs.readFile(file, helpers.utf8Encoding, lintContentWrapper);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -466,7 +466,7 @@ function lintInput(options, synchronous, callback) {
|
|||
const stringsKeys = Object.keys(strings);
|
||||
const config = options.config || { "default": true };
|
||||
const frontMatter = (options.frontMatter === undefined) ?
|
||||
shared.frontMatterRe : options.frontMatter;
|
||||
helpers.frontMatterRe : options.frontMatter;
|
||||
const noInlineConfig = !!options.noInlineConfig;
|
||||
const resultVersion = (options.resultVersion === undefined) ?
|
||||
2 : options.resultVersion;
|
||||
|
@ -590,7 +590,7 @@ function readConfig(file, parsers, callback) {
|
|||
parsers = null;
|
||||
}
|
||||
// Read file
|
||||
fs.readFile(file, shared.utf8Encoding, (err, content) => {
|
||||
fs.readFile(file, helpers.utf8Encoding, (err, content) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
@ -608,7 +608,7 @@ function readConfig(file, parsers, callback) {
|
|||
if (errr) {
|
||||
return callback(errr);
|
||||
}
|
||||
return callback(null, shared.assign(extendsConfig, config));
|
||||
return callback(null, helpers.assign(extendsConfig, config));
|
||||
});
|
||||
}
|
||||
return callback(null, config);
|
||||
|
@ -624,7 +624,7 @@ function readConfig(file, parsers, callback) {
|
|||
*/
|
||||
function readConfigSync(file, parsers) {
|
||||
// Read file
|
||||
const content = fs.readFileSync(file, shared.utf8Encoding);
|
||||
const content = fs.readFileSync(file, helpers.utf8Encoding);
|
||||
// Try to parse file
|
||||
const { config, message } = parseConfiguration(file, content, parsers);
|
||||
if (!config) {
|
||||
|
@ -634,7 +634,7 @@ function readConfigSync(file, parsers) {
|
|||
const configExtends = config.extends;
|
||||
if (configExtends) {
|
||||
delete config.extends;
|
||||
return shared.assign(
|
||||
return helpers.assign(
|
||||
readConfigSync(path.resolve(path.dirname(file), configExtends), parsers),
|
||||
config);
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const shared = require("./shared");
|
||||
const { addErrorDetailIf, filterTokens } = require("../helpers");
|
||||
|
||||
module.exports = {
|
||||
"names": [ "MD001", "heading-increment", "header-increment" ],
|
||||
|
@ -10,10 +10,10 @@ module.exports = {
|
|||
"tags": [ "headings", "headers" ],
|
||||
"function": function MD001(params, onError) {
|
||||
let prevLevel = 0;
|
||||
shared.filterTokens(params, "heading_open", function forToken(token) {
|
||||
filterTokens(params, "heading_open", function forToken(token) {
|
||||
const level = parseInt(token.tag.slice(1), 10);
|
||||
if (prevLevel && (level > prevLevel)) {
|
||||
shared.addErrorDetailIf(onError, token.lineNumber,
|
||||
addErrorDetailIf(onError, token.lineNumber,
|
||||
"h" + (prevLevel + 1), "h" + level);
|
||||
}
|
||||
prevLevel = level;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const shared = require("./shared");
|
||||
const { addErrorDetailIf } = require("../helpers");
|
||||
|
||||
module.exports = {
|
||||
"names": [ "MD002", "first-heading-h1", "first-header-h1" ],
|
||||
|
@ -13,7 +13,7 @@ module.exports = {
|
|||
const tag = "h" + level;
|
||||
params.tokens.every(function forToken(token) {
|
||||
if (token.type === "heading_open") {
|
||||
shared.addErrorDetailIf(onError, token.lineNumber, tag, token.tag);
|
||||
addErrorDetailIf(onError, token.lineNumber, tag, token.tag);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const shared = require("./shared");
|
||||
const { addErrorDetailIf, filterTokens, headingStyleFor } =
|
||||
require("../helpers");
|
||||
|
||||
module.exports = {
|
||||
"names": [ "MD003", "heading-style", "header-style" ],
|
||||
|
@ -10,8 +11,8 @@ module.exports = {
|
|||
"tags": [ "headings", "headers" ],
|
||||
"function": function MD003(params, onError) {
|
||||
let style = params.config.style || "consistent";
|
||||
shared.filterTokens(params, "heading_open", function forToken(token) {
|
||||
const styleForToken = shared.headingStyleFor(token);
|
||||
filterTokens(params, "heading_open", function forToken(token) {
|
||||
const styleForToken = headingStyleFor(token);
|
||||
if (style === "consistent") {
|
||||
style = styleForToken;
|
||||
}
|
||||
|
@ -32,7 +33,7 @@ module.exports = {
|
|||
} else if (style === "setext_with_atx_closed") {
|
||||
expected = h12 ? "setext" : "atx_closed";
|
||||
}
|
||||
shared.addErrorDetailIf(onError, token.lineNumber,
|
||||
addErrorDetailIf(onError, token.lineNumber,
|
||||
expected, styleForToken);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"use strict";
|
||||
|
||||
const { addErrorDetailIf, listItemMarkerRe,
|
||||
rangeFromRegExp } = require("./shared");
|
||||
rangeFromRegExp } = require("../helpers");
|
||||
const { flattenedLists } = require("./cache");
|
||||
|
||||
// Returns the unordered list style for a list item token
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"use strict";
|
||||
|
||||
const { addError, addErrorDetailIf, indentFor, listItemMarkerRe,
|
||||
orderedListItemMarkerRe, rangeFromRegExp } = require("./shared");
|
||||
orderedListItemMarkerRe, rangeFromRegExp } = require("../helpers");
|
||||
const { flattenedLists } = require("./cache");
|
||||
|
||||
module.exports = {
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"use strict";
|
||||
|
||||
const { addErrorDetailIf, listItemMarkerRe, rangeFromRegExp } =
|
||||
require("./shared");
|
||||
require("../helpers");
|
||||
const { flattenedLists } = require("./cache");
|
||||
|
||||
module.exports = {
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"use strict";
|
||||
|
||||
const { addErrorDetailIf, listItemMarkerRe, rangeFromRegExp } =
|
||||
require("./shared");
|
||||
require("../helpers");
|
||||
const { flattenedLists } = require("./cache");
|
||||
|
||||
module.exports = {
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"use strict";
|
||||
|
||||
const { addError, filterTokens, forEachLine, includesSorted, rangeFromRegExp,
|
||||
trimRight } = require("./shared");
|
||||
trimRight } = require("../helpers");
|
||||
const { lineMetadata } = require("./cache");
|
||||
|
||||
const trailingSpaceRe = /\s+$/;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const { addError, forEachLine, rangeFromRegExp } = require("./shared");
|
||||
const { addError, forEachLine, rangeFromRegExp } = require("../helpers");
|
||||
const { lineMetadata } = require("./cache");
|
||||
|
||||
const tabRe = /\t+/;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const shared = require("./shared");
|
||||
const { addError, forEachInlineChild, rangeFromRegExp } = require("../helpers");
|
||||
|
||||
const reversedLinkRe = /\([^)]+\)\[[^\]^][^\]]*]/;
|
||||
|
||||
|
@ -11,11 +11,11 @@ module.exports = {
|
|||
"description": "Reversed link syntax",
|
||||
"tags": [ "links" ],
|
||||
"function": function MD011(params, onError) {
|
||||
shared.forEachInlineChild(params, "text", function forToken(token) {
|
||||
forEachInlineChild(params, "text", function forToken(token) {
|
||||
const match = reversedLinkRe.exec(token.content);
|
||||
if (match) {
|
||||
shared.addError(onError, token.lineNumber, match[0], null,
|
||||
shared.rangeFromRegExp(token.line, reversedLinkRe));
|
||||
addError(onError, token.lineNumber, match[0], null,
|
||||
rangeFromRegExp(token.line, reversedLinkRe));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const { addErrorDetailIf, forEachLine } = require("./shared");
|
||||
const { addErrorDetailIf, forEachLine } = require("../helpers");
|
||||
const { lineMetadata } = require("./cache");
|
||||
|
||||
module.exports = {
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"use strict";
|
||||
|
||||
const { addErrorDetailIf, filterTokens, forEachHeading, forEachLine,
|
||||
includesSorted, rangeFromRegExp } = require("./shared");
|
||||
includesSorted, rangeFromRegExp } = require("../helpers");
|
||||
const { lineMetadata } = require("./cache");
|
||||
|
||||
const longLineRePrefix = "^(.{";
|
||||
|
|
13
lib/md014.js
13
lib/md014.js
|
@ -2,7 +2,8 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const shared = require("./shared");
|
||||
const { addErrorContext, filterTokens, newLineRe, rangeFromRegExp } =
|
||||
require("../helpers");
|
||||
|
||||
const dollarCommandRe = /^(\s*)(\$\s)/;
|
||||
|
||||
|
@ -12,15 +13,15 @@ module.exports = {
|
|||
"tags": [ "code" ],
|
||||
"function": function MD014(params, onError) {
|
||||
[ "code_block", "fence" ].forEach(function forType(type) {
|
||||
shared.filterTokens(params, type, function forToken(token) {
|
||||
filterTokens(params, type, function forToken(token) {
|
||||
let allBlank = true;
|
||||
if (token.content && token.content.split(shared.newLineRe)
|
||||
if (token.content && token.content.split(newLineRe)
|
||||
.every(function forLine(line) {
|
||||
return !line || (allBlank = false) || dollarCommandRe.test(line);
|
||||
}) && !allBlank) {
|
||||
shared.addErrorContext(onError, token.lineNumber,
|
||||
token.content.split(shared.newLineRe)[0].trim(), null, null,
|
||||
shared.rangeFromRegExp(token.line, dollarCommandRe));
|
||||
addErrorContext(onError, token.lineNumber,
|
||||
token.content.split(newLineRe)[0].trim(), null, null,
|
||||
rangeFromRegExp(token.line, dollarCommandRe));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"use strict";
|
||||
|
||||
const { addErrorContext, atxHeadingSpaceRe, forEachLine,
|
||||
rangeFromRegExp } = require("./shared");
|
||||
rangeFromRegExp } = require("../helpers");
|
||||
const { lineMetadata } = require("./cache");
|
||||
|
||||
module.exports = {
|
||||
|
|
11
lib/md019.js
11
lib/md019.js
|
@ -2,19 +2,20 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const shared = require("./shared");
|
||||
const { addErrorContext, atxHeadingSpaceRe, filterTokens, headingStyleFor,
|
||||
rangeFromRegExp } = require("../helpers");
|
||||
|
||||
module.exports = {
|
||||
"names": [ "MD019", "no-multiple-space-atx" ],
|
||||
"description": "Multiple spaces after hash on atx style heading",
|
||||
"tags": [ "headings", "headers", "atx", "spaces" ],
|
||||
"function": function MD019(params, onError) {
|
||||
shared.filterTokens(params, "heading_open", function forToken(token) {
|
||||
if ((shared.headingStyleFor(token) === "atx") &&
|
||||
filterTokens(params, "heading_open", function forToken(token) {
|
||||
if ((headingStyleFor(token) === "atx") &&
|
||||
/^#+\s\s/.test(token.line)) {
|
||||
shared.addErrorContext(onError, token.lineNumber, token.line.trim(),
|
||||
addErrorContext(onError, token.lineNumber, token.line.trim(),
|
||||
null, null,
|
||||
shared.rangeFromRegExp(token.line, shared.atxHeadingSpaceRe));
|
||||
rangeFromRegExp(token.line, atxHeadingSpaceRe));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const { addErrorContext, forEachLine, rangeFromRegExp } = require("./shared");
|
||||
const { addErrorContext, forEachLine, rangeFromRegExp } = require("../helpers");
|
||||
const { lineMetadata } = require("./cache");
|
||||
|
||||
const atxClosedHeadingNoSpaceRe = /(?:^#+[^#\s])|(?:[^#\s]#+\s*$)/;
|
||||
|
|
11
lib/md021.js
11
lib/md021.js
|
@ -2,7 +2,8 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const shared = require("./shared");
|
||||
const { addErrorContext, filterTokens, headingStyleFor, rangeFromRegExp } =
|
||||
require("../helpers");
|
||||
|
||||
const atxClosedHeadingSpaceRe = /(?:^#+\s\s+?\S)|(?:\S\s\s+?#+\s*$)/;
|
||||
|
||||
|
@ -11,14 +12,14 @@ module.exports = {
|
|||
"description": "Multiple spaces inside hashes on closed atx style heading",
|
||||
"tags": [ "headings", "headers", "atx_closed", "spaces" ],
|
||||
"function": function MD021(params, onError) {
|
||||
shared.filterTokens(params, "heading_open", function forToken(token) {
|
||||
if (shared.headingStyleFor(token) === "atx_closed") {
|
||||
filterTokens(params, "heading_open", function forToken(token) {
|
||||
if (headingStyleFor(token) === "atx_closed") {
|
||||
const left = /^#+\s\s/.test(token.line);
|
||||
const right = /\s\s#+$/.test(token.line);
|
||||
if (left || right) {
|
||||
shared.addErrorContext(onError, token.lineNumber, token.line.trim(),
|
||||
addErrorContext(onError, token.lineNumber, token.line.trim(),
|
||||
left, right,
|
||||
shared.rangeFromRegExp(token.line, atxClosedHeadingSpaceRe));
|
||||
rangeFromRegExp(token.line, atxClosedHeadingSpaceRe));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -2,8 +2,7 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const shared = require("./shared");
|
||||
const { addErrorDetailIf, filterTokens, isBlankLine } = shared;
|
||||
const { addErrorDetailIf, filterTokens, isBlankLine } = require("../helpers");
|
||||
|
||||
module.exports = {
|
||||
"names": [ "MD022", "blanks-around-headings", "blanks-around-headers" ],
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const shared = require("./shared");
|
||||
const { addErrorContext, filterTokens, rangeFromRegExp } =
|
||||
require("../helpers");
|
||||
|
||||
const spaceBeforeHeadingRe = /^((?:\s+)|(?:[>\s]+\s\s))[^>\s]/;
|
||||
|
||||
|
@ -11,10 +12,10 @@ module.exports = {
|
|||
"description": "Headings must start at the beginning of the line",
|
||||
"tags": [ "headings", "headers", "spaces" ],
|
||||
"function": function MD023(params, onError) {
|
||||
shared.filterTokens(params, "heading_open", function forToken(token) {
|
||||
filterTokens(params, "heading_open", function forToken(token) {
|
||||
if (spaceBeforeHeadingRe.test(token.line)) {
|
||||
shared.addErrorContext(onError, token.lineNumber, token.line, null,
|
||||
null, shared.rangeFromRegExp(token.line, spaceBeforeHeadingRe));
|
||||
addErrorContext(onError, token.lineNumber, token.line, null,
|
||||
null, rangeFromRegExp(token.line, spaceBeforeHeadingRe));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -2,8 +2,7 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const shared = require("./shared");
|
||||
const { addErrorContext, forEachHeading } = shared;
|
||||
const { addErrorContext, forEachHeading } = require("../helpers");
|
||||
|
||||
module.exports = {
|
||||
"names": [ "MD024", "no-duplicate-heading", "no-duplicate-header" ],
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const shared = require("./shared");
|
||||
const { addErrorContext, filterTokens, frontMatterHasTitle } =
|
||||
require("../helpers");
|
||||
|
||||
module.exports = {
|
||||
"names": [ "MD025", "single-title", "single-h1" ],
|
||||
|
@ -12,15 +13,15 @@ module.exports = {
|
|||
const level = params.config.level || 1;
|
||||
const tag = "h" + level;
|
||||
const foundFrontMatterTitle =
|
||||
shared.frontMatterHasTitle(
|
||||
frontMatterHasTitle(
|
||||
params.frontMatterLines,
|
||||
params.config.front_matter_title
|
||||
);
|
||||
let hasTopLevelHeading = false;
|
||||
shared.filterTokens(params, "heading_open", function forToken(token) {
|
||||
filterTokens(params, "heading_open", function forToken(token) {
|
||||
if (token.tag === tag) {
|
||||
if (hasTopLevelHeading || foundFrontMatterTitle) {
|
||||
shared.addErrorContext(onError, token.lineNumber,
|
||||
addErrorContext(onError, token.lineNumber,
|
||||
token.line.trim());
|
||||
} else if (token.lineNumber === 1) {
|
||||
hasTopLevelHeading = true;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const shared = require("./shared");
|
||||
const { addError, forEachHeading, rangeFromRegExp } = require("../helpers");
|
||||
|
||||
module.exports = {
|
||||
"names": [ "MD026", "no-trailing-punctuation" ],
|
||||
|
@ -11,12 +11,12 @@ module.exports = {
|
|||
"function": function MD026(params, onError) {
|
||||
const punctuation = params.config.punctuation || ".,;:!?";
|
||||
const trailingPunctuationRe = new RegExp("[" + punctuation + "]$");
|
||||
shared.forEachHeading(params, function forHeading(heading, content) {
|
||||
forEachHeading(params, function forHeading(heading, content) {
|
||||
const match = trailingPunctuationRe.exec(content);
|
||||
if (match) {
|
||||
shared.addError(onError, heading.lineNumber,
|
||||
addError(onError, heading.lineNumber,
|
||||
"Punctuation: '" + match[0] + "'", null,
|
||||
shared.rangeFromRegExp(heading.line, trailingPunctuationRe));
|
||||
rangeFromRegExp(heading.line, trailingPunctuationRe));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
12
lib/md027.js
12
lib/md027.js
|
@ -2,7 +2,7 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const shared = require("./shared");
|
||||
const { addErrorContext, newLineRe, rangeFromRegExp } = require("../helpers");
|
||||
|
||||
const spaceAfterBlockQuote = /^\s*(?:>\s+)+\S/;
|
||||
|
||||
|
@ -27,15 +27,15 @@ module.exports = {
|
|||
/^(\s*>)+\s\s+>/.test(token.line) :
|
||||
/^(\s*>)+\s\s/.test(token.line);
|
||||
if (multipleSpaces) {
|
||||
shared.addErrorContext(onError, token.lineNumber, token.line, null,
|
||||
null, shared.rangeFromRegExp(token.line, spaceAfterBlockQuote));
|
||||
addErrorContext(onError, token.lineNumber, token.line, null,
|
||||
null, rangeFromRegExp(token.line, spaceAfterBlockQuote));
|
||||
}
|
||||
token.content.split(shared.newLineRe)
|
||||
token.content.split(newLineRe)
|
||||
.forEach(function forLine(line, offset) {
|
||||
if (/^\s/.test(line)) {
|
||||
shared.addErrorContext(onError, token.lineNumber + offset,
|
||||
addErrorContext(onError, token.lineNumber + offset,
|
||||
"> " + line, null, null,
|
||||
shared.rangeFromRegExp(line, spaceAfterBlockQuote));
|
||||
rangeFromRegExp(line, spaceAfterBlockQuote));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const shared = require("./shared");
|
||||
const { addError } = require("../helpers");
|
||||
|
||||
module.exports = {
|
||||
"names": [ "MD028", "no-blanks-blockquote" ],
|
||||
|
@ -13,7 +13,7 @@ module.exports = {
|
|||
params.tokens.forEach(function forToken(token) {
|
||||
if ((token.type === "blockquote_open") &&
|
||||
(prevToken.type === "blockquote_close")) {
|
||||
shared.addError(onError, token.lineNumber - 1);
|
||||
addError(onError, token.lineNumber - 1);
|
||||
}
|
||||
prevToken = token;
|
||||
});
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"use strict";
|
||||
|
||||
const { addErrorDetailIf, listItemMarkerRe, orderedListItemMarkerRe,
|
||||
rangeFromRegExp } = require("./shared");
|
||||
rangeFromRegExp } = require("../helpers");
|
||||
const { flattenedLists } = require("./cache");
|
||||
|
||||
const listStyleExamples = {
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"use strict";
|
||||
|
||||
const { addErrorDetailIf, listItemMarkerRe, rangeFromRegExp } =
|
||||
require("./shared");
|
||||
require("../helpers");
|
||||
const { flattenedLists } = require("./cache");
|
||||
|
||||
module.exports = {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const { addErrorContext, forEachLine, isBlankLine } = require("./shared");
|
||||
const { addErrorContext, forEachLine, isBlankLine } = require("../helpers");
|
||||
const { lineMetadata } = require("./cache");
|
||||
|
||||
module.exports = {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const { addErrorContext, isBlankLine } = require("./shared");
|
||||
const { addErrorContext, isBlankLine } = require("../helpers");
|
||||
const { flattenedLists } = require("./cache");
|
||||
|
||||
module.exports = {
|
||||
|
|
|
@ -2,10 +2,8 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const shared = require("./shared");
|
||||
const {
|
||||
addError, filterTokens, forEachInlineChild, newLineRe, rangeFromRegExp
|
||||
} = shared;
|
||||
const { addError, filterTokens, forEachInlineChild, newLineRe,
|
||||
rangeFromRegExp } = require("../helpers");
|
||||
|
||||
const htmlRe = /<[^>]*>/;
|
||||
|
||||
|
|
11
lib/md034.js
11
lib/md034.js
|
@ -2,14 +2,15 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const shared = require("./shared");
|
||||
const { addErrorContext, bareUrlRe, filterTokens, rangeFromRegExp } =
|
||||
require("../helpers");
|
||||
|
||||
module.exports = {
|
||||
"names": [ "MD034", "no-bare-urls" ],
|
||||
"description": "Bare URL used",
|
||||
"tags": [ "links", "url" ],
|
||||
"function": function MD034(params, onError) {
|
||||
shared.filterTokens(params, "inline", function forToken(token) {
|
||||
filterTokens(params, "inline", function forToken(token) {
|
||||
let inLink = false;
|
||||
token.children.forEach(function forChild(child) {
|
||||
let match = null;
|
||||
|
@ -19,9 +20,9 @@ module.exports = {
|
|||
inLink = false;
|
||||
} else if ((child.type === "text") &&
|
||||
!inLink &&
|
||||
(match = shared.bareUrlRe.exec(child.content))) {
|
||||
shared.addErrorContext(onError, child.lineNumber, match[0], null,
|
||||
null, shared.rangeFromRegExp(child.line, shared.bareUrlRe));
|
||||
(match = bareUrlRe.exec(child.content))) {
|
||||
addErrorContext(onError, child.lineNumber, match[0], null,
|
||||
null, rangeFromRegExp(child.line, bareUrlRe));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const shared = require("./shared");
|
||||
const { addErrorDetailIf, filterTokens } = require("../helpers");
|
||||
|
||||
module.exports = {
|
||||
"names": [ "MD035", "hr-style" ],
|
||||
|
@ -10,12 +10,12 @@ module.exports = {
|
|||
"tags": [ "hr" ],
|
||||
"function": function MD035(params, onError) {
|
||||
let style = params.config.style || "consistent";
|
||||
shared.filterTokens(params, "hr", function forToken(token) {
|
||||
filterTokens(params, "hr", function forToken(token) {
|
||||
const lineTrim = token.line.trim();
|
||||
if (style === "consistent") {
|
||||
style = lineTrim;
|
||||
}
|
||||
shared.addErrorDetailIf(onError, token.lineNumber, style, lineTrim);
|
||||
addErrorDetailIf(onError, token.lineNumber, style, lineTrim);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const shared = require("./shared");
|
||||
const { addErrorContext } = require("../helpers");
|
||||
|
||||
module.exports = {
|
||||
"names": [ "MD036", "no-emphasis-as-heading", "no-emphasis-as-header" ],
|
||||
|
@ -24,7 +24,7 @@ module.exports = {
|
|||
(children[0].type === "em_open")) &&
|
||||
(children[1].type === "text") &&
|
||||
!re.test(children[1].content)) {
|
||||
shared.addErrorContext(onError, t.lineNumber,
|
||||
addErrorContext(onError, t.lineNumber,
|
||||
children[1].content);
|
||||
}
|
||||
return base;
|
||||
|
|
|
@ -2,8 +2,7 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const shared = require("./shared");
|
||||
const { addErrorContext, forEachInlineChild } = shared;
|
||||
const { addErrorContext, forEachInlineChild } = require("../helpers");
|
||||
|
||||
module.exports = {
|
||||
"names": [ "MD037", "no-space-in-emphasis" ],
|
||||
|
|
11
lib/md038.js
11
lib/md038.js
|
@ -2,7 +2,8 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const shared = require("./shared");
|
||||
const { addErrorContext, filterTokens, forEachInlineCodeSpan, newLineRe } =
|
||||
require("../helpers");
|
||||
|
||||
const startRe = /^\s([^`]|$)/;
|
||||
const endRe = /[^`]\s$/;
|
||||
|
@ -12,16 +13,16 @@ module.exports = {
|
|||
"description": "Spaces inside code span elements",
|
||||
"tags": [ "whitespace", "code" ],
|
||||
"function": function MD038(params, onError) {
|
||||
shared.filterTokens(params, "inline", (token) => {
|
||||
filterTokens(params, "inline", (token) => {
|
||||
if (token.children.some((child) => child.type === "code_inline")) {
|
||||
const tokenLines = params.lines.slice(token.map[0], token.map[1]);
|
||||
shared.forEachInlineCodeSpan(
|
||||
forEachInlineCodeSpan(
|
||||
tokenLines.join("\n"),
|
||||
(code, lineIndex, columnIndex, tickCount) => {
|
||||
let rangeIndex = columnIndex - tickCount;
|
||||
let rangeLength = code.length + (2 * tickCount);
|
||||
let rangeLineOffset = 0;
|
||||
const codeLines = code.split(shared.newLineRe);
|
||||
const codeLines = code.split(newLineRe);
|
||||
const left = startRe.test(code);
|
||||
const right = !left && endRe.test(code);
|
||||
if (right && (codeLines.length > 1)) {
|
||||
|
@ -34,7 +35,7 @@ module.exports = {
|
|||
}
|
||||
const context = tokenLines[lineIndex + rangeLineOffset]
|
||||
.substring(rangeIndex, rangeIndex + rangeLength);
|
||||
shared.addErrorContext(
|
||||
addErrorContext(
|
||||
onError, token.lineNumber + lineIndex + rangeLineOffset,
|
||||
context, left, right, [ rangeIndex + 1, rangeLength ]);
|
||||
}
|
||||
|
|
13
lib/md039.js
13
lib/md039.js
|
@ -2,7 +2,8 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const shared = require("./shared");
|
||||
const { addErrorContext, filterTokens, rangeFromRegExp, trimLeft, trimRight } =
|
||||
require("../helpers");
|
||||
|
||||
const spaceInLinkRe = /\[(?:\s+(?:[^\]]*?)\s*|(?:[^\]]*?)\s+)](?=\(\S*\))/;
|
||||
|
||||
|
@ -11,7 +12,7 @@ module.exports = {
|
|||
"description": "Spaces inside link text",
|
||||
"tags": [ "whitespace", "links" ],
|
||||
"function": function MD039(params, onError) {
|
||||
shared.filterTokens(params, "inline", function forToken(token) {
|
||||
filterTokens(params, "inline", function forToken(token) {
|
||||
let inLink = false;
|
||||
let linkText = "";
|
||||
token.children.forEach(function forChild(child) {
|
||||
|
@ -20,12 +21,12 @@ module.exports = {
|
|||
linkText = "";
|
||||
} else if (child.type === "link_close") {
|
||||
inLink = false;
|
||||
const left = shared.trimLeft(linkText).length !== linkText.length;
|
||||
const right = shared.trimRight(linkText).length !== linkText.length;
|
||||
const left = trimLeft(linkText).length !== linkText.length;
|
||||
const right = trimRight(linkText).length !== linkText.length;
|
||||
if (left || right) {
|
||||
shared.addErrorContext(onError, token.lineNumber,
|
||||
addErrorContext(onError, token.lineNumber,
|
||||
"[" + linkText + "]", left, right,
|
||||
shared.rangeFromRegExp(token.line, spaceInLinkRe));
|
||||
rangeFromRegExp(token.line, spaceInLinkRe));
|
||||
}
|
||||
} else if (inLink) {
|
||||
linkText += child.content;
|
||||
|
|
|
@ -2,16 +2,16 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const shared = require("./shared");
|
||||
const { addErrorContext, filterTokens } = require("../helpers");
|
||||
|
||||
module.exports = {
|
||||
"names": [ "MD040", "fenced-code-language" ],
|
||||
"description": "Fenced code blocks should have a language specified",
|
||||
"tags": [ "code", "language" ],
|
||||
"function": function MD040(params, onError) {
|
||||
shared.filterTokens(params, "fence", function forToken(token) {
|
||||
filterTokens(params, "fence", function forToken(token) {
|
||||
if (!token.info.trim()) {
|
||||
shared.addErrorContext(onError, token.lineNumber, token.line);
|
||||
addErrorContext(onError, token.lineNumber, token.line);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const shared = require("./shared");
|
||||
const { addErrorContext, frontMatterHasTitle } = require("../helpers");
|
||||
|
||||
module.exports = {
|
||||
"names": [ "MD041", "first-line-heading", "first-line-h1" ],
|
||||
|
@ -12,7 +12,7 @@ module.exports = {
|
|||
const level = params.config.level || 1;
|
||||
const tag = "h" + level;
|
||||
const foundFrontMatterTitle =
|
||||
shared.frontMatterHasTitle(
|
||||
frontMatterHasTitle(
|
||||
params.frontMatterLines,
|
||||
params.config.front_matter_title
|
||||
);
|
||||
|
@ -22,7 +22,7 @@ module.exports = {
|
|||
return true;
|
||||
}
|
||||
if ((token.type !== "heading_open") || (token.tag !== tag)) {
|
||||
shared.addErrorContext(onError, token.lineNumber, token.line);
|
||||
addErrorContext(onError, token.lineNumber, token.line);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const shared = require("./shared");
|
||||
const { addErrorContext, filterTokens, rangeFromRegExp } =
|
||||
require("../helpers");
|
||||
|
||||
const emptyLinkRe = /\[[^\]]*](?:\((?:#?|(?:<>))\))/;
|
||||
|
||||
|
@ -11,7 +12,7 @@ module.exports = {
|
|||
"description": "No empty links",
|
||||
"tags": [ "links" ],
|
||||
"function": function MD042(params, onError) {
|
||||
shared.filterTokens(params, "inline", function forToken(token) {
|
||||
filterTokens(params, "inline", function forToken(token) {
|
||||
let inLink = false;
|
||||
let linkText = "";
|
||||
let emptyLink = false;
|
||||
|
@ -27,9 +28,9 @@ module.exports = {
|
|||
} else if (child.type === "link_close") {
|
||||
inLink = false;
|
||||
if (emptyLink) {
|
||||
shared.addErrorContext(onError, child.lineNumber,
|
||||
addErrorContext(onError, child.lineNumber,
|
||||
"[" + linkText + "]()", null, null,
|
||||
shared.rangeFromRegExp(child.line, emptyLinkRe));
|
||||
rangeFromRegExp(child.line, emptyLinkRe));
|
||||
}
|
||||
} else if (inLink) {
|
||||
linkText += child.content;
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const shared = require("./shared");
|
||||
const { addErrorContext, addErrorDetailIf, forEachHeading } =
|
||||
require("../helpers");
|
||||
|
||||
module.exports = {
|
||||
"names": [ "MD043", "required-headings", "required-headers" ],
|
||||
|
@ -18,7 +19,7 @@ module.exports = {
|
|||
let i = 0;
|
||||
let optional = false;
|
||||
let errorCount = 0;
|
||||
shared.forEachHeading(params, function forHeading(heading, content) {
|
||||
forEachHeading(params, function forHeading(heading, content) {
|
||||
if (!errorCount) {
|
||||
const actual = levels[heading.tag] + " " + content;
|
||||
const expected = requiredHeadings[i++] || "[None]";
|
||||
|
@ -29,14 +30,14 @@ module.exports = {
|
|||
} else if (optional) {
|
||||
i--;
|
||||
} else {
|
||||
shared.addErrorDetailIf(onError, heading.lineNumber,
|
||||
addErrorDetailIf(onError, heading.lineNumber,
|
||||
expected, actual);
|
||||
errorCount++;
|
||||
}
|
||||
}
|
||||
});
|
||||
if ((i < requiredHeadings.length) && !errorCount) {
|
||||
shared.addErrorContext(onError, params.lines.length,
|
||||
addErrorContext(onError, params.lines.length,
|
||||
requiredHeadings[i]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,9 +2,8 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const shared = require("./shared");
|
||||
const { addErrorDetailIf, bareUrlRe, escapeForRegExp, filterTokens,
|
||||
forEachInlineChild, newLineRe } = shared;
|
||||
forEachInlineChild, newLineRe } = require("../helpers");
|
||||
|
||||
module.exports = {
|
||||
"names": [ "MD044", "proper-names" ],
|
||||
|
|
|
@ -2,16 +2,16 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const shared = require("./shared");
|
||||
const { addError, forEachInlineChild } = require("../helpers");
|
||||
|
||||
module.exports = {
|
||||
"names": [ "MD045", "no-alt-text" ],
|
||||
"description": "Images should have alternate text (alt text)",
|
||||
"tags": [ "accessibility", "images" ],
|
||||
"function": function MD045(params, onError) {
|
||||
shared.forEachInlineChild(params, "image", function forToken(token) {
|
||||
forEachInlineChild(params, "image", function forToken(token) {
|
||||
if (token.content === "") {
|
||||
shared.addError(onError, token.lineNumber);
|
||||
addError(onError, token.lineNumber);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
"test-cover": "istanbul cover node_modules/nodeunit/bin/nodeunit test/markdownlint-test.js",
|
||||
"test-extra": "nodeunit test/markdownlint-test-extra.js",
|
||||
"debug": "node debug node_modules/nodeunit/bin/nodeunit",
|
||||
"lint": "eslint lib test schema && eslint --env browser --global markdownit --global markdownlint --rule \"no-unused-vars: 0, no-extend-native: 0, max-statements: 0, no-console: 0, no-var: 0\" demo && eslint --rule \"no-console: 0, no-invalid-this: 0, no-shadow: 0, object-property-newline: 0\" example",
|
||||
"lint": "eslint lib helpers test schema && eslint --env browser --global markdownit --global markdownlint --rule \"no-unused-vars: 0, no-extend-native: 0, max-statements: 0, no-console: 0, no-var: 0\" demo && eslint --rule \"no-console: 0, no-invalid-this: 0, no-shadow: 0, object-property-newline: 0\" example",
|
||||
"build-config-schema": "node schema/build-config-schema.js",
|
||||
"build-demo": "cpy node_modules/markdown-it/dist/markdown-it.min.js demo && cd demo && rimraf markdownlint-browser.* && cpy file-header.js . --rename=markdownlint-browser.js && tsc --allowJs --resolveJsonModule --outDir ../lib-es3 ../lib/markdownlint.js && browserify ../lib-es3/lib/markdownlint.js --standalone markdownlint >> markdownlint-browser.js && uglifyjs markdownlint-browser.js --compress --mangle --comments --output markdownlint-browser.min.js",
|
||||
"build-example": "npm install --no-save --ignore-scripts grunt grunt-cli gulp through2",
|
||||
|
|
|
@ -4,7 +4,7 @@ const fs = require("fs");
|
|||
const path = require("path");
|
||||
const glob = require("glob");
|
||||
const markdownlint = require("../lib/markdownlint");
|
||||
const shared = require("../lib/shared");
|
||||
const { newLineRe, utf8Encoding } = require("../helpers");
|
||||
|
||||
module.exports.typeTestFiles = function typeTestFiles(test) {
|
||||
// Simulates typing each test file to validate handling of partial input
|
||||
|
@ -15,7 +15,7 @@ module.exports.typeTestFiles = function typeTestFiles(test) {
|
|||
},
|
||||
"resultVersion": 0
|
||||
});
|
||||
const contentLineCount = content.split(shared.newLineRe).length;
|
||||
const contentLineCount = content.split(newLineRe).length;
|
||||
Object.keys(results.content).forEach(function forKey(ruleName) {
|
||||
results.content[ruleName].forEach(function forLine(line) {
|
||||
test.ok((line >= 1) && (line <= contentLineCount),
|
||||
|
@ -28,7 +28,7 @@ module.exports.typeTestFiles = function typeTestFiles(test) {
|
|||
files.forEach(function forFile(file) {
|
||||
if (/\.md$/.test(file)) {
|
||||
let content = fs.readFileSync(
|
||||
path.join("./test", file), shared.utf8Encoding);
|
||||
path.join("./test", file), utf8Encoding);
|
||||
while (content) {
|
||||
validate(file, content);
|
||||
content = content.slice(0, -1);
|
||||
|
|
|
@ -11,7 +11,7 @@ const pluginSup = require("markdown-it-sup");
|
|||
const tv4 = require("tv4");
|
||||
const packageJson = require("../package.json");
|
||||
const markdownlint = require("../lib/markdownlint");
|
||||
const shared = require("../lib/shared");
|
||||
const helpers = require("../helpers");
|
||||
const rules = require("../lib/rules");
|
||||
const customRules = require("./rules/rules.js");
|
||||
const defaultConfig = require("./markdownlint-test-default-config.json");
|
||||
|
@ -39,7 +39,7 @@ function createTestForFile(file) {
|
|||
const actualPromise = promisify(fs.stat, configFile)
|
||||
.then(
|
||||
function configFileExists() {
|
||||
return promisify(fs.readFile, configFile, shared.utf8Encoding)
|
||||
return promisify(fs.readFile, configFile, helpers.utf8Encoding)
|
||||
.then(JSON.parse);
|
||||
},
|
||||
function noConfigFile() {
|
||||
|
@ -48,7 +48,7 @@ function createTestForFile(file) {
|
|||
.then(
|
||||
function lintWithConfig(config) {
|
||||
const mergedConfig =
|
||||
shared.assign(shared.clone(defaultConfig), config);
|
||||
helpers.assign(helpers.clone(defaultConfig), config);
|
||||
return promisify(markdownlint, {
|
||||
"files": [ file ],
|
||||
"config": mergedConfig,
|
||||
|
@ -56,7 +56,7 @@ function createTestForFile(file) {
|
|||
});
|
||||
});
|
||||
const expectedPromise = detailedResults ?
|
||||
promisify(fs.readFile, resultsFile, shared.utf8Encoding)
|
||||
promisify(fs.readFile, resultsFile, helpers.utf8Encoding)
|
||||
.then(
|
||||
function fileContents(contents) {
|
||||
const errorObjects = JSON.parse(contents);
|
||||
|
@ -68,10 +68,10 @@ function createTestForFile(file) {
|
|||
});
|
||||
return errorObjects;
|
||||
}) :
|
||||
promisify(fs.readFile, file, shared.utf8Encoding)
|
||||
promisify(fs.readFile, file, helpers.utf8Encoding)
|
||||
.then(
|
||||
function fileContents(contents) {
|
||||
const lines = contents.split(shared.newLineRe);
|
||||
const lines = contents.split(helpers.newLineRe);
|
||||
const results = {};
|
||||
lines.forEach(function forLine(line, lineNum) {
|
||||
const regex = /\{(MD\d+)(?::(\d+))?\}/g;
|
||||
|
@ -114,7 +114,8 @@ module.exports.projectFiles = function projectFiles(test) {
|
|||
const options = {
|
||||
"files": [
|
||||
"README.md",
|
||||
"CONTRIBUTING.md"
|
||||
"CONTRIBUTING.md",
|
||||
"helpers/README.md"
|
||||
],
|
||||
"noInlineConfig": true,
|
||||
"config": {
|
||||
|
@ -126,7 +127,8 @@ module.exports.projectFiles = function projectFiles(test) {
|
|||
test.ifError(err);
|
||||
const expected = {
|
||||
"README.md": [],
|
||||
"CONTRIBUTING.md": []
|
||||
"CONTRIBUTING.md": [],
|
||||
"helpers/README.md": []
|
||||
};
|
||||
test.deepEqual(actual, expected, "Issue(s) with project files.");
|
||||
test.done();
|
||||
|
@ -1098,7 +1100,7 @@ module.exports.readme = function readme(test) {
|
|||
tagToRules[tag] = tagRules;
|
||||
});
|
||||
});
|
||||
fs.readFile("README.md", shared.utf8Encoding,
|
||||
fs.readFile("README.md", helpers.utf8Encoding,
|
||||
function readFile(err, contents) {
|
||||
test.ifError(err);
|
||||
const rulesLeft = rules.slice();
|
||||
|
@ -1156,7 +1158,7 @@ module.exports.readme = function readme(test) {
|
|||
|
||||
module.exports.doc = function doc(test) {
|
||||
test.expect(312);
|
||||
fs.readFile("doc/Rules.md", shared.utf8Encoding,
|
||||
fs.readFile("doc/Rules.md", helpers.utf8Encoding,
|
||||
function readFile(err, contents) {
|
||||
test.ifError(err);
|
||||
const rulesLeft = rules.slice();
|
||||
|
@ -1323,7 +1325,7 @@ function clearHtmlCommentTextValid(test) {
|
|||
"<!--",
|
||||
" \\"
|
||||
];
|
||||
const actual = shared.clearHtmlCommentText(validComments.join("\n"));
|
||||
const actual = helpers.clearHtmlCommentText(validComments.join("\n"));
|
||||
const expected = validResult.join("\n");
|
||||
test.equal(actual, expected);
|
||||
test.done();
|
||||
|
@ -1350,7 +1352,7 @@ function clearHtmlCommentTextInvalid(test) {
|
|||
"<!--text--->",
|
||||
"<!--te--xt-->"
|
||||
];
|
||||
const actual = shared.clearHtmlCommentText(invalidComments.join("\n"));
|
||||
const actual = helpers.clearHtmlCommentText(invalidComments.join("\n"));
|
||||
const expected = invalidComments.join("\n");
|
||||
test.equal(actual, expected);
|
||||
test.done();
|
||||
|
@ -1371,7 +1373,7 @@ function clearHtmlCommentTextNonGreedy(test) {
|
|||
"<!-- --> -->",
|
||||
"<!----> -->"
|
||||
];
|
||||
const actual = shared.clearHtmlCommentText(nonGreedyComments.join("\n"));
|
||||
const actual = helpers.clearHtmlCommentText(nonGreedyComments.join("\n"));
|
||||
const expected = nonGreedyResult.join("\n");
|
||||
test.equal(actual, expected);
|
||||
test.done();
|
||||
|
@ -1394,7 +1396,7 @@ function clearHtmlCommentTextEmbedded(test) {
|
|||
"text<!-- markdownlint-disable MD010 -->text",
|
||||
"text<!-- -->text"
|
||||
];
|
||||
const actual = shared.clearHtmlCommentText(embeddedComments.join("\n"));
|
||||
const actual = helpers.clearHtmlCommentText(embeddedComments.join("\n"));
|
||||
const expected = embeddedResult.join("\n");
|
||||
test.equal(actual, expected);
|
||||
test.done();
|
||||
|
@ -1421,7 +1423,7 @@ module.exports.isBlankLine = function isBlankLine(test) {
|
|||
"> <!--text-->",
|
||||
">><!--text-->"
|
||||
];
|
||||
blankLines.forEach((line) => test.ok(shared.isBlankLine(line), line));
|
||||
blankLines.forEach((line) => test.ok(helpers.isBlankLine(line), line));
|
||||
const nonBlankLines = [
|
||||
"text",
|
||||
" text ",
|
||||
|
@ -1432,7 +1434,7 @@ module.exports.isBlankLine = function isBlankLine(test) {
|
|||
"<!--",
|
||||
"-->"
|
||||
];
|
||||
nonBlankLines.forEach((line) => test.ok(!shared.isBlankLine(line), line));
|
||||
nonBlankLines.forEach((line) => test.ok(!helpers.isBlankLine(line), line));
|
||||
test.done();
|
||||
};
|
||||
|
||||
|
@ -1449,7 +1451,7 @@ module.exports.includesSorted = function includesSorted(test) {
|
|||
];
|
||||
inputs.forEach((input) => {
|
||||
for (let i = 0; i <= 21; i++) {
|
||||
test.equal(shared.includesSorted(input, i), input.includes(i));
|
||||
test.equal(helpers.includesSorted(input, i), input.includes(i));
|
||||
}
|
||||
});
|
||||
test.done();
|
||||
|
@ -1475,9 +1477,9 @@ module.exports.trimLeftRight = function trimLeftRight(test) {
|
|||
];
|
||||
test.expect(inputs.length * 2);
|
||||
inputs.forEach(function forInput(input) {
|
||||
test.equal(shared.trimLeft(input), input.trimLeft(),
|
||||
test.equal(helpers.trimLeft(input), input.trimLeft(),
|
||||
"trimLeft incorrect for '" + input + "'");
|
||||
test.equal(shared.trimRight(input), input.trimRight(),
|
||||
test.equal(helpers.trimRight(input), input.trimRight(),
|
||||
"trimRight incorrect for '" + input + "'");
|
||||
});
|
||||
test.done();
|
||||
|
@ -1571,7 +1573,7 @@ module.exports.forEachInlineCodeSpan = function forEachInlineCodeSpan(test) {
|
|||
];
|
||||
testCases.forEach((testCase) => {
|
||||
const [ input, expecteds ] = testCase;
|
||||
shared.forEachInlineCodeSpan(input, (code, line, column, ticks) => {
|
||||
helpers.forEachInlineCodeSpan(input, (code, line, column, ticks) => {
|
||||
const [ expectedCode, expectedLine, expectedColumn, expectedTicks ] =
|
||||
expecteds.shift();
|
||||
test.equal(code, expectedCode, input);
|
||||
|
@ -1611,9 +1613,9 @@ module.exports.configMultiple = function configMultiple(test) {
|
|||
markdownlint.readConfig("./test/config/config-grandparent.json",
|
||||
function callback(err, actual) {
|
||||
test.ifError(err);
|
||||
const expected = shared.assign(
|
||||
shared.assign(
|
||||
shared.assign({}, require("./config/config-child.json")),
|
||||
const expected = helpers.assign(
|
||||
helpers.assign(
|
||||
helpers.assign({}, require("./config/config-child.json")),
|
||||
require("./config/config-parent.json")),
|
||||
require("./config/config-grandparent.json"));
|
||||
delete expected.extends;
|
||||
|
@ -1689,9 +1691,9 @@ module.exports.configMultipleYaml = function configMultipleYaml(test) {
|
|||
[ require("js-yaml").safeLoad ],
|
||||
function callback(err, actual) {
|
||||
test.ifError(err);
|
||||
const expected = shared.assign(
|
||||
shared.assign(
|
||||
shared.assign({}, require("./config/config-child.json")),
|
||||
const expected = helpers.assign(
|
||||
helpers.assign(
|
||||
helpers.assign({}, require("./config/config-child.json")),
|
||||
require("./config/config-parent.json")),
|
||||
require("./config/config-grandparent.json"));
|
||||
delete expected.extends;
|
||||
|
@ -1707,9 +1709,9 @@ module.exports.configMultipleHybrid = function configMultipleHybrid(test) {
|
|||
[ JSON.parse, require("toml").parse, require("js-yaml").safeLoad ],
|
||||
function callback(err, actual) {
|
||||
test.ifError(err);
|
||||
const expected = shared.assign(
|
||||
shared.assign(
|
||||
shared.assign({}, require("./config/config-child.json")),
|
||||
const expected = helpers.assign(
|
||||
helpers.assign(
|
||||
helpers.assign({}, require("./config/config-child.json")),
|
||||
require("./config/config-parent.json")),
|
||||
require("./config/config-grandparent.json"));
|
||||
delete expected.extends;
|
||||
|
@ -1756,9 +1758,9 @@ module.exports.configMultipleSync = function configMultipleSync(test) {
|
|||
test.expect(1);
|
||||
const actual =
|
||||
markdownlint.readConfigSync("./test/config/config-grandparent.json");
|
||||
const expected = shared.assign(
|
||||
shared.assign(
|
||||
shared.assign({}, require("./config/config-child.json")),
|
||||
const expected = helpers.assign(
|
||||
helpers.assign(
|
||||
helpers.assign({}, require("./config/config-child.json")),
|
||||
require("./config/config-parent.json")),
|
||||
require("./config/config-grandparent.json"));
|
||||
delete expected.extends;
|
||||
|
@ -1837,9 +1839,9 @@ module.exports.configMultipleYamlSync = function configMultipleYamlSync(test) {
|
|||
test.expect(1);
|
||||
const actual = markdownlint.readConfigSync(
|
||||
"./test/config/config-grandparent.yaml", [ require("js-yaml").safeLoad ]);
|
||||
const expected = shared.assign(
|
||||
shared.assign(
|
||||
shared.assign({}, require("./config/config-child.json")),
|
||||
const expected = helpers.assign(
|
||||
helpers.assign(
|
||||
helpers.assign({}, require("./config/config-child.json")),
|
||||
require("./config/config-parent.json")),
|
||||
require("./config/config-grandparent.json"));
|
||||
delete expected.extends;
|
||||
|
@ -1853,9 +1855,9 @@ function configMultipleHybridSync(test) {
|
|||
const actual = markdownlint.readConfigSync(
|
||||
"./test/config/config-grandparent-hybrid.yaml",
|
||||
[ JSON.parse, require("toml").parse, require("js-yaml").safeLoad ]);
|
||||
const expected = shared.assign(
|
||||
shared.assign(
|
||||
shared.assign({}, require("./config/config-child.json")),
|
||||
const expected = helpers.assign(
|
||||
helpers.assign(
|
||||
helpers.assign({}, require("./config/config-child.json")),
|
||||
require("./config/config-parent.json")),
|
||||
require("./config/config-grandparent.json"));
|
||||
delete expected.extends;
|
||||
|
@ -2266,7 +2268,7 @@ module.exports.customRulesBadProperty = function customRulesBadProperty(test) {
|
|||
].forEach(function forProperty(property) {
|
||||
const propertyName = property[0];
|
||||
property[1].forEach(function forPropertyValue(propertyValue) {
|
||||
const badRule = shared.clone(customRules.anyBlockquote);
|
||||
const badRule = helpers.clone(customRules.anyBlockquote);
|
||||
badRule[propertyName] = propertyValue;
|
||||
const options = {
|
||||
"customRules": [ badRule ]
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
"use strict";
|
||||
|
||||
const { URL } = require("url");
|
||||
const { filterTokens } = require("../../helpers");
|
||||
|
||||
module.exports = {
|
||||
"names": [ "any-blockquote" ],
|
||||
|
@ -12,10 +13,8 @@ module.exports = {
|
|||
"/blob/master/test/rules/any-blockquote.js"
|
||||
),
|
||||
"tags": [ "test" ],
|
||||
"function": function rule(params, onError) {
|
||||
params.tokens.filter(function filterToken(token) {
|
||||
return token.type === "blockquote_open";
|
||||
}).forEach(function forToken(blockquote) {
|
||||
"function": (params, onError) => {
|
||||
filterTokens(params, "blockquote_open", (blockquote) => {
|
||||
const lines = blockquote.map[1] - blockquote.map[0];
|
||||
onError({
|
||||
"lineNumber": blockquote.lineNumber,
|
||||
|
|
|
@ -2,13 +2,15 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const { forEachLine, getLineMetadata } = require("../../helpers");
|
||||
|
||||
module.exports = {
|
||||
"names": [ "every-n-lines" ],
|
||||
"description": "Rule that reports an error every N lines",
|
||||
"tags": [ "test" ],
|
||||
"function": function rule(params, onError) {
|
||||
"function": (params, onError) => {
|
||||
const n = params.config.n || 2;
|
||||
params.lines.forEach(function forLine(line, lineIndex) {
|
||||
forEachLine(getLineMetadata(params), (line, lineIndex) => {
|
||||
const lineNumber = lineIndex + 1;
|
||||
if ((lineNumber % n) === 0) {
|
||||
onError({
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue