markdownlint
Advanced tools
Comparing version 0.21.1 to 0.22.0
@@ -46,2 +46,3 @@ # Contributing | ||
End commit messages with a period (`.`). | ||
Do not include `package-lock.json` in the pull request. | ||
Once accepted, the tag `fixed in next` will be added to the issue. | ||
@@ -48,0 +49,0 @@ When the commit is merged to the main branch during the release process, the issue will be closed automatically. |
@@ -344,2 +344,5 @@ # Rules | ||
Note: Trailing space is allowed in indented and fenced code blocks because some | ||
languages require it. | ||
The `br_spaces` parameter allows an exception to this rule for a specific number | ||
@@ -852,3 +855,3 @@ of trailing spaces, typically used to insert an explicit line break. The default | ||
Parameters: level, front_matter_title (number; default 1, string; default "^\s*title:") | ||
Parameters: level, front_matter_title (number; default 1, string; default "^\s*"?title"?\s*[:=]") | ||
@@ -900,3 +903,3 @@ This rule is triggered when a top level heading is in use (the first line of | ||
Parameters: punctuation (string; default ".,;:!?。,;:!?") | ||
Parameters: punctuation (string; default ".,;:!。,;:!") | ||
@@ -920,6 +923,11 @@ Fixable: Most violations can be fixed by tooling | ||
as punctuation at the end of a heading. For example, you can change it to | ||
`".,;:!"` to allow headings that end with a question mark, such as in an FAQ. | ||
`".,;:"` to allow headings that end with an exclamation point. Question mark is | ||
allowed by default because of how common it is in headings of FAQ-style documents. | ||
Setting the `punctuation` parameter to `""` allows all characters - and is | ||
equivalent to disabling the rule. | ||
Note: The trailing semicolon of | ||
[HTML entity references](https://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references) | ||
like `©`, `©`, and `©` is ignored by this rule. | ||
Rationale: Headings are not meant to be full sentences. More information: | ||
@@ -963,4 +971,2 @@ <https://cirosantilli.com/markdown-style-guide#punctuation-at-the-end-of-headers> | ||
Fixable: Most violations can be fixed by tooling | ||
This rule is triggered when two blockquote blocks are separated by nothing | ||
@@ -1573,3 +1579,3 @@ except for a blank line: | ||
Parameters: level, front_matter_title (number; default 1, string; default "^\s*title:") | ||
Parameters: level, front_matter_title (number; default 1, string; default "^\s*"?title"?\s*[:=]") | ||
@@ -1681,4 +1687,5 @@ This rule is intended to ensure documents have a title and is triggered when | ||
Use the special value `"*"` meaning "one or more unspecified headings" and set | ||
the `headings` parameter to: | ||
Use the special value `"*"` meaning "zero or more unspecified headings" or the | ||
special value `"+"` meaning "one or more unspecified headings" and set the | ||
`headings` parameter to: | ||
@@ -1685,0 +1692,0 @@ ```json |
@@ -15,3 +15,3 @@ // @ts-check | ||
// eslint-disable-next-line max-len | ||
/((^---\s*$[^]*?^---\s*$)|(^\+\+\+\s*$[^]*?^(\+\+\+|\.\.\.)\s*$))(\r\n|\r|\n|$)/m; | ||
/((^---\s*$[^]*?^---\s*$)|(^\+\+\+\s*$[^]*?^(\+\+\+|\.\.\.)\s*$)|(^\{\s*$[^]*?^\}\s*$))(\r\n|\r|\n|$)/m; | ||
@@ -21,3 +21,3 @@ // Regular expression for matching inline disable/enable comments | ||
// eslint-disable-next-line max-len | ||
/<!--\s*markdownlint-(?:(?:(disable|enable|capture|restore|disable-file|enable-file)((?:\s+[a-z0-9_-]+)*))|(?:(configure-file)\s+([\s\S]*?)))\s*-->/ig; | ||
/<!--\s*markdownlint-(?:(?:(disable|enable|capture|restore|disable-file|enable-file|disable-next-line)((?:\s+[a-z0-9_-]+)*))|(?:(configure-file)\s+([\s\S]*?)))\s*-->/ig; | ||
module.exports.inlineCommentRe = inlineCommentRe; | ||
@@ -40,4 +40,8 @@ | ||
// All punctuation characters (normal and full-width) | ||
module.exports.allPunctuation = ".,;:!?。,;:!?"; | ||
const allPunctuation = ".,;:!?。,;:!?"; | ||
module.exports.allPunctuation = allPunctuation; | ||
// All punctuation characters without question mark (normal and full-width) | ||
module.exports.allPunctuationNoQuestion = allPunctuation.replace(/[??]/gu, ""); | ||
// Returns true iff the input is a number | ||
@@ -277,2 +281,10 @@ module.exports.isNumber = function isNumber(obj) { | ||
params.tokens.forEach(function forToken(token) { | ||
if ( | ||
(token.type === "math_block") && | ||
(token.tag === "math") && | ||
token.map[1] | ||
) { | ||
// markdown-it-texmath package does not account for math_block_end | ||
token.map[1]++; | ||
} | ||
if ((token.type === "bullet_list_open") || | ||
@@ -501,3 +513,6 @@ (token.type === "ordered_list_open")) { | ||
const frontMatterTitleRe = | ||
new RegExp(String(frontMatterTitlePattern || "^\\s*title\\s*[:=]"), "i"); | ||
new RegExp( | ||
String(frontMatterTitlePattern || "^\\s*\"?title\"?\\s*[:=]"), | ||
"i" | ||
); | ||
return !ignoreFrontMatter && | ||
@@ -504,0 +519,0 @@ frontMatterLines.some((line) => frontMatterTitleRe.test(line)); |
{ | ||
"name": "markdownlint-rule-helpers", | ||
"version": "0.12.0", | ||
"version": "0.13.0", | ||
"description": "A collection of markdownlint helper functions for custom rules", | ||
@@ -5,0 +5,0 @@ "main": "helpers.js", |
@@ -54,3 +54,3 @@ export = markdownlint; | ||
*/ | ||
markdownItPlugins?: any[][]; | ||
markdownItPlugins?: Plugin[]; | ||
}; | ||
@@ -109,3 +109,3 @@ /** | ||
/** | ||
* markdown-it token objects. | ||
* Token objects from markdown-it. | ||
*/ | ||
@@ -267,3 +267,3 @@ tokens: MarkdownItToken[]; | ||
/** | ||
* markdown-it plugin. | ||
* A markdown-it plugin. | ||
*/ | ||
@@ -270,0 +270,0 @@ type Plugin = any[]; |
@@ -7,3 +7,3 @@ // @ts-check | ||
const path = require("path"); | ||
const util = require("util"); | ||
const { promisify } = require("util"); | ||
const markdownIt = require("markdown-it"); | ||
@@ -180,14 +180,23 @@ const rules = require("./rules"); | ||
function annotateTokens(tokens, lines) { | ||
let tbodyMap = null; | ||
let tableMap = null; | ||
tokens.forEach(function forToken(token) { | ||
// Handle missing maps for table body | ||
if (token.type === "tbody_open") { | ||
tbodyMap = token.map.slice(); | ||
} else if ((token.type === "tr_close") && tbodyMap) { | ||
tbodyMap[0]++; | ||
} else if (token.type === "tbody_close") { | ||
tbodyMap = null; | ||
// Handle missing maps for table head/body | ||
if ( | ||
(token.type === "thead_open") || | ||
(token.type === "tbody_open") | ||
) { | ||
tableMap = token.map.slice(); | ||
} else if ( | ||
(token.type === "tr_close") && | ||
tableMap | ||
) { | ||
tableMap[0]++; | ||
} else if ( | ||
(token.type === "thead_close") || | ||
(token.type === "tbody_close") | ||
) { | ||
tableMap = null; | ||
} | ||
if (tbodyMap && !token.map) { | ||
token.map = tbodyMap.slice(); | ||
if (tableMap && !token.map) { | ||
token.map = tableMap.slice(); | ||
} | ||
@@ -327,3 +336,3 @@ // Update token metadata | ||
const input = perLine ? lines : [ lines.join("\n") ]; | ||
input.forEach((line) => { | ||
input.forEach((line, lineIndex) => { | ||
if (!noInlineConfig) { | ||
@@ -334,3 +343,3 @@ let match = null; | ||
const parameter = match[2] || match[4]; | ||
forEachMatch(action, parameter); | ||
forEachMatch(action, parameter, lineIndex + 1); | ||
} | ||
@@ -358,3 +367,3 @@ } | ||
// eslint-disable-next-line jsdoc/require-jsdoc | ||
function applyEnableDisable(action, parameter) { | ||
function applyEnableDisable(action, parameter, state) { | ||
const enabled = (action.startsWith("ENABLE")); | ||
@@ -366,3 +375,3 @@ const items = parameter ? | ||
(aliasToRuleNames[nameUpper] || []).forEach((ruleName) => { | ||
enabledRules[ruleName] = enabled; | ||
state[ruleName] = enabled; | ||
}); | ||
@@ -374,3 +383,3 @@ }); | ||
if ((action === "ENABLE-FILE") || (action === "DISABLE-FILE")) { | ||
applyEnableDisable(action, parameter); | ||
applyEnableDisable(action, parameter, enabledRules); | ||
} | ||
@@ -386,3 +395,3 @@ } | ||
enabledRules = { ...enabledRules }; | ||
applyEnableDisable(action, parameter); | ||
applyEnableDisable(action, parameter, enabledRules); | ||
} | ||
@@ -392,4 +401,14 @@ } | ||
function updateLineState() { | ||
enabledRulesPerLineNumber.push(enabledRules); | ||
enabledRulesPerLineNumber.push({ ...enabledRules }); | ||
} | ||
// eslint-disable-next-line jsdoc/require-jsdoc | ||
function disableNextLine(action, parameter, lineNumber) { | ||
if (action === "DISABLE-NEXT-LINE") { | ||
applyEnableDisable( | ||
action, | ||
parameter, | ||
enabledRulesPerLineNumber[lineNumber + 1] || {} | ||
); | ||
} | ||
} | ||
// Handle inline comments | ||
@@ -407,2 +426,3 @@ handleInlineConfig(false, configureFile); | ||
handleInlineConfig(true, captureRestoreEnableDisable, updateLineState); | ||
handleInlineConfig(true, disableNextLine); | ||
// Return results | ||
@@ -452,4 +472,4 @@ return { | ||
* @param {string} name Identifier for the content. | ||
* @param {string} content Markdown content | ||
* @param {Object} md markdown-it instance. | ||
* @param {string} content Markdown content. | ||
* @param {Object} md Instance of markdown-it. | ||
* @param {Configuration} config Configuration object. | ||
@@ -665,3 +685,3 @@ * @param {RegExp} frontMatter Regular expression for front matter. | ||
* @param {string} file Path of file to lint. | ||
* @param {Object} md markdown-it instance. | ||
* @param {Object} md Instance of markdown-it. | ||
* @param {Configuration} config Configuration object. | ||
@@ -848,3 +868,3 @@ * @param {RegExp} frontMatter Regular expression for front matter. | ||
const markdownlintPromisify = util.promisify(markdownlint); | ||
const markdownlintPromisify = promisify(markdownlint); | ||
@@ -911,2 +931,28 @@ /** | ||
/** | ||
* Resolve referenced "extends" path in a configuration file | ||
* using path.resolve() with require.resolve() as a fallback. | ||
* | ||
* @param {string} configFile Configuration file name. | ||
* @param {string} referenceId Referenced identifier to resolve. | ||
* @returns {string} Resolved path to file. | ||
*/ | ||
function resolveConfigExtends(configFile, referenceId) { | ||
const configFileDirname = path.dirname(configFile); | ||
const resolvedExtendsFile = path.resolve(configFileDirname, referenceId); | ||
try { | ||
if (fs.statSync(resolvedExtendsFile).isFile()) { | ||
return resolvedExtendsFile; | ||
} | ||
} catch { | ||
// If not a file or fs.statSync throws, try require.resolve | ||
} | ||
try { | ||
return require.resolve(referenceId, { "paths": [ configFileDirname ] }); | ||
} catch { | ||
// If require.resolve throws, return resolvedExtendsFile | ||
} | ||
return resolvedExtendsFile; | ||
} | ||
/** | ||
* Read specified configuration file. | ||
@@ -941,4 +987,4 @@ * | ||
delete config.extends; | ||
const extendsFile = path.resolve(path.dirname(file), configExtends); | ||
return readConfig(extendsFile, parsers, (errr, extendsConfig) => { | ||
const resolvedExtends = resolveConfigExtends(file, configExtends); | ||
return readConfig(resolvedExtends, parsers, (errr, extendsConfig) => { | ||
if (errr) { | ||
@@ -957,3 +1003,3 @@ return callback(errr); | ||
const readConfigPromisify = util.promisify(readConfig); | ||
const readConfigPromisify = promisify(readConfig); | ||
@@ -992,7 +1038,5 @@ /** | ||
delete config.extends; | ||
const resolvedExtends = resolveConfigExtends(file, configExtends); | ||
return { | ||
...readConfigSync( | ||
path.resolve(path.dirname(file), configExtends), | ||
parsers | ||
), | ||
...readConfigSync(resolvedExtends, parsers), | ||
...config | ||
@@ -1040,3 +1084,3 @@ }; | ||
* @property {string} name File/string name. | ||
* @property {MarkdownItToken[]} tokens markdown-it token objects. | ||
* @property {MarkdownItToken[]} tokens Token objects from markdown-it. | ||
* @property {string[]} lines File/string lines. | ||
@@ -1124,3 +1168,3 @@ * @property {string[]} frontMatterLines Front matter lines. | ||
/** | ||
* markdown-it plugin. | ||
* A markdown-it plugin. | ||
* | ||
@@ -1127,0 +1171,0 @@ * @typedef {Array} Plugin |
@@ -50,8 +50,6 @@ // @ts-check | ||
const expected = (brSpaces < 2) ? 0 : brSpaces; | ||
let inFencedCode = 0; | ||
forEachLine(lineMetadata(), (line, lineIndex, inCode, onFence) => { | ||
inFencedCode += onFence; | ||
forEachLine(lineMetadata(), (line, lineIndex, inCode) => { | ||
const lineNumber = lineIndex + 1; | ||
const trailingSpaces = line.length - line.trimRight().length; | ||
if ((!inCode || inFencedCode) && trailingSpaces && | ||
if (trailingSpaces && !inCode && | ||
!includesSorted(listItemLineNumbers, lineNumber)) { | ||
@@ -58,0 +56,0 @@ if ((expected !== trailingSpaces) || |
@@ -5,5 +5,7 @@ // @ts-check | ||
const { addError, allPunctuation, escapeForRegExp, forEachHeading } = | ||
const { addError, allPunctuationNoQuestion, escapeForRegExp, forEachHeading } = | ||
require("../helpers"); | ||
const endOfLineHtmlEntityRe = /&#?[0-9a-zA-Z]+;$/; | ||
module.exports = { | ||
@@ -15,4 +17,5 @@ "names": [ "MD026", "no-trailing-punctuation" ], | ||
let punctuation = params.config.punctuation; | ||
punctuation = | ||
String((punctuation === undefined) ? allPunctuation : punctuation); | ||
punctuation = String( | ||
(punctuation === undefined) ? allPunctuationNoQuestion : punctuation | ||
); | ||
const trailingPunctuationRe = | ||
@@ -24,3 +27,3 @@ new RegExp("\\s*[" + escapeForRegExp(punctuation) + "]+$"); | ||
const match = trailingPunctuationRe.exec(trimmedLine); | ||
if (match) { | ||
if (match && !endOfLineHtmlEntityRe.test(trimmedLine)) { | ||
const fullMatch = match[0]; | ||
@@ -27,0 +30,0 @@ const column = match.index + 1; |
@@ -21,11 +21,3 @@ // @ts-check | ||
lineNumber++) { | ||
addError( | ||
onError, | ||
lineNumber, | ||
null, | ||
null, | ||
null, | ||
{ | ||
"deleteCount": -1 | ||
}); | ||
addError(onError, lineNumber); | ||
} | ||
@@ -32,0 +24,0 @@ } |
@@ -16,17 +16,24 @@ // @ts-check | ||
const levels = {}; | ||
[ 1, 2, 3, 4, 5, 6 ].forEach(function forLevel(level) { | ||
[ 1, 2, 3, 4, 5, 6 ].forEach((level) => { | ||
levels["h" + level] = "######".substr(-level); | ||
}); | ||
let i = 0; | ||
let optional = false; | ||
let errorCount = 0; | ||
forEachHeading(params, function forHeading(heading, content) { | ||
if (!errorCount) { | ||
let matchAny = false; | ||
let hasError = false; | ||
let anyHeadings = false; | ||
// eslint-disable-next-line func-style | ||
const getExpected = () => requiredHeadings[i++] || "[None]"; | ||
forEachHeading(params, (heading, content) => { | ||
if (!hasError) { | ||
anyHeadings = true; | ||
const actual = levels[heading.tag] + " " + content; | ||
const expected = requiredHeadings[i++] || "[None]"; | ||
let expected = getExpected(); | ||
if (expected === "*") { | ||
optional = true; | ||
matchAny = true; | ||
expected = getExpected(); | ||
} else if (expected === "+") { | ||
matchAny = true; | ||
} else if (expected.toLowerCase() === actual.toLowerCase()) { | ||
optional = false; | ||
} else if (optional) { | ||
matchAny = false; | ||
} else if (matchAny) { | ||
i--; | ||
@@ -36,7 +43,11 @@ } else { | ||
expected, actual); | ||
errorCount++; | ||
hasError = true; | ||
} | ||
} | ||
}); | ||
if ((i < requiredHeadings.length) && !errorCount) { | ||
if ( | ||
!hasError && | ||
(i < requiredHeadings.length) && | ||
(anyHeadings || !requiredHeadings.every((heading) => heading === "*")) | ||
) { | ||
addErrorContext(onError, params.lines.length, | ||
@@ -43,0 +54,0 @@ requiredHeadings[i]); |
{ | ||
"name": "markdownlint", | ||
"version": "0.21.1", | ||
"version": "0.22.0", | ||
"description": "A Node.js style checker and lint tool for Markdown/CommonMark files.", | ||
@@ -36,13 +36,13 @@ "main": "lib/markdownlint.js", | ||
"dependencies": { | ||
"markdown-it": "11.0.0" | ||
"markdown-it": "12.0.2" | ||
}, | ||
"devDependencies": { | ||
"@types/node": "~14.6.4", | ||
"browserify": "~16.5.2", | ||
"c8": "~7.3.0", | ||
"@types/node": "~14.14.9", | ||
"browserify": "~17.0.0", | ||
"c8": "~7.3.5", | ||
"cpy-cli": "~3.1.1", | ||
"eslint": "~7.8.1", | ||
"eslint-plugin-jsdoc": "~30.3.1", | ||
"eslint": "~7.14.0", | ||
"eslint-plugin-jsdoc": "~30.7.8", | ||
"eslint-plugin-node": "~11.1.0", | ||
"eslint-plugin-unicorn": "~21.0.0", | ||
"eslint-plugin-unicorn": "~23.0.0", | ||
"globby": "~11.0.1", | ||
@@ -55,3 +55,3 @@ "js-yaml": "~3.14.0", | ||
"markdown-it-texmath": "~0.8.0", | ||
"markdownlint-rule-helpers": "~0.11.0", | ||
"markdownlint-rule-helpers": "~0.12.0", | ||
"rimraf": "~3.0.2", | ||
@@ -63,4 +63,4 @@ "strip-json-comments": "~3.1.1", | ||
"tv4": "~1.3.0", | ||
"typescript": "~4.0.2", | ||
"uglify-js": "~3.10.3" | ||
"typescript": "~4.1.2", | ||
"uglify-js": "~3.12.0" | ||
}, | ||
@@ -67,0 +67,0 @@ "keywords": [ |
@@ -158,4 +158,6 @@ # markdownlint | ||
* Enable all rules: `<!-- markdownlint-enable -->` | ||
* Disable all rules for the next line only: `<!-- markdownlint-disable-next-line -->` | ||
* Disable one or more rules by name: `<!-- markdownlint-disable MD001 MD005 -->` | ||
* Enable one or more rules by name: `<!-- markdownlint-enable MD001 MD005 -->` | ||
* Disable one or more rules by name for the next line only: `<!-- markdownlint-disable-next-line MD001 MD005 -->` | ||
* Capture the current rule configuration: `<!-- markdownlint-capture -->` | ||
@@ -167,2 +169,9 @@ * Restore the captured rule configuration: `<!-- markdownlint-restore -->` | ||
```markdown | ||
<!-- markdownlint-disable-next-line no-space-in-emphasis --> | ||
deliberate space * in * emphasis | ||
``` | ||
Or: | ||
```markdown | ||
<!-- markdownlint-disable no-space-in-emphasis --> | ||
@@ -434,7 +443,8 @@ deliberate space * in * emphasis | ||
```js | ||
/((^---\s*$[^]*?^---\s*$)|(^\+\+\+\s*$[^]*?^(\+\+\+|\.\.\.)\s*$))(\r\n|\r|\n|$)/m | ||
/((^---\s*$[^]*?^---\s*$)|(^\+\+\+\s*$[^]*?^(\+\+\+|\.\.\.)\s*$)|(^\{\s*$[^]*?^\}\s*$))(\r\n|\r|\n|$)/m | ||
``` | ||
Ignores [YAML](https://en.wikipedia.org/wiki/YAML) and | ||
[TOML](https://en.wikipedia.org/wiki/TOML) such as: | ||
Ignores [YAML](https://en.wikipedia.org/wiki/YAML), | ||
[TOML](https://en.wikipedia.org/wiki/TOML), and | ||
[JSON](https://en.wikipedia.org/wiki/JSON) front matter such as: | ||
@@ -912,2 +922,5 @@ ```text | ||
* 0.21.1 - Improve MD011/MD031, export `getVersion` API. | ||
* 0.22.0 - Allow `extends` in config to reference installed packages by name, add | ||
`markdownlint-disable-next-line` inline comment, support JSON front matter, improve | ||
MD009/MD026/MD028/MD043, update dependencies (including `markdown-it` to v12). | ||
@@ -914,0 +927,0 @@ [npm-image]: https://img.shields.io/npm/v/markdownlint.svg |
@@ -225,2 +225,10 @@ // @ts-check | ||
case "MD026": | ||
scheme.properties = { | ||
"punctuation": { | ||
"description": "Punctuation characters", | ||
"type": "string", | ||
"default": ".,;:!。,;:!" | ||
} | ||
}; | ||
break; | ||
case "MD036": | ||
@@ -227,0 +235,0 @@ scheme.properties = { |
@@ -766,3 +766,3 @@ { | ||
"type": "string", | ||
"default": ".,;:!?。,;:!?" | ||
"default": ".,;:!。,;:!" | ||
} | ||
@@ -783,3 +783,3 @@ }, | ||
"type": "string", | ||
"default": ".,;:!?。,;:!?" | ||
"default": ".,;:!。,;:!" | ||
} | ||
@@ -786,0 +786,0 @@ }, |
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 2 instances in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
520707
12008
929
10
3
+ Addedargparse@2.0.1(transitive)
+ Addedmarkdown-it@12.0.2(transitive)
- Removedargparse@1.0.10(transitive)
- Removedmarkdown-it@11.0.0(transitive)
- Removedsprintf-js@1.0.3(transitive)
Updatedmarkdown-it@12.0.2