diff --git a/.eslintrc.json b/.eslintrc.json
index 46e07bb95..705789e7b 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -7,6 +7,7 @@
"eslint:all",
"plugin:jsdoc/recommended",
"plugin:n/recommended",
+ "plugin:regexp/recommended",
"plugin:unicorn/all"
],
"ignorePatterns": [
@@ -200,6 +201,11 @@
"n/prefer-promises/dns": "error",
"n/prefer-promises/fs": "error",
+ "regexp/no-empty-alternative": "off",
+ "regexp/no-super-linear-backtracking": "off",
+ "regexp/no-unused-capturing-group": "off",
+ "regexp/no-useless-assertions": "off",
+
"unicorn/better-regex": "off",
"unicorn/consistent-function-scoping": "off",
"unicorn/filename-case": "off",
diff --git a/demo/markdownlint-browser.js b/demo/markdownlint-browser.js
index 10ffb46b5..b32aa7eab 100644
--- a/demo/markdownlint-browser.js
+++ b/demo/markdownlint-browser.js
@@ -37,14 +37,14 @@ module.exports.newLineRe = newLineRe;
// Regular expression for matching common front matter (YAML and TOML)
module.exports.frontMatterRe =
// eslint-disable-next-line max-len
- /((^---\s*$[^]*?^---\s*$)|(^\+\+\+\s*$[^]*?^(\+\+\+|\.\.\.)\s*$)|(^\{\s*$[^]*?^\}\s*$))(\r\n|\r|\n|$)/m;
+ /((^---\s*$[\s\S]*?^---\s*$)|(^\+\+\+\s*$[\s\S]*?^(\+\+\+|\.\.\.)\s*$)|(^\{\s*$[\s\S]*?^\}\s*$))(\r\n|\r|\n|$)/m;
// Regular expression for matching the start of inline disable/enable comments
const inlineCommentStartRe =
// eslint-disable-next-line max-len
-/()/ig;
+/()/gi;
module.exports.inlineCommentStartRe = inlineCommentStartRe;
// Regular expression for matching HTML elements
-const htmlElementRe = /<(([A-Za-z][A-Za-z0-9-]*)(?:\s[^`>]*)?)\/?>/g;
+const htmlElementRe = /<(([A-Za-z][A-Za-z\d-]*)(?:\s[^`>]*)?)\/?>/g;
module.exports.htmlElementRe = htmlElementRe;
// Regular expressions for range matching
module.exports.listItemMarkerRe = /^([\s>]*)(?:[*+-]|\d+[.)])\s+/;
@@ -55,9 +55,9 @@ const emphasisMarkersRe = /[_*]/g;
const blockquotePrefixRe = /^[>\s]*/;
module.exports.blockquotePrefixRe = blockquotePrefixRe;
// Regular expression for reference links (full, collapsed, and shortcut)
-const referenceLinkRe = /!?\\?\[((?:\[[^\]\0]*]|[^[\]\0])*)](?:(?:\[([^\]\0]*)\])|([^(])|$)/g;
+const referenceLinkRe = /!?\\?\[((?:\[[^\]\0]*\]|[^[\]\0])*)\](?:\[([^\]\0]*)\]|([^(])|$)/g;
// Regular expression for link reference definitions
-const linkReferenceDefinitionRe = /^ {0,3}\[([^\]]*[^\\])]:/;
+const linkReferenceDefinitionRe = /^ {0,3}\[([^\]]*[^\\])\]:/;
module.exports.linkReferenceDefinitionRe = linkReferenceDefinitionRe;
// All punctuation characters (normal and full-width)
const allPunctuation = ".,;:!?。,;:!?";
@@ -2902,7 +2902,7 @@ module.exports = {
const { addError, forEachLine, withinAnyRange } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
const { codeBlockAndSpanRanges, lineMetadata } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
-const reversedLinkRe = /(^|[^\\])\(([^()]+)\)\[([^\]^][^\]]*)](?!\()/g;
+const reversedLinkRe = /(^|[^\\])\(([^()]+)\)\[([^\]^][^\]]*)\](?!\()/g;
module.exports = {
"names": ["MD011", "no-reversed-links"],
"description": "Reversed link syntax",
@@ -3163,7 +3163,7 @@ module.exports = {
filterTokens(params, "heading_open", (token) => {
if (headingStyleFor(token) === "atx") {
const { line, lineNumber } = token;
- const match = /^(#+)([ \t]{2,})(?:\S)/.exec(line);
+ const match = /^(#+)([ \t]{2,})\S/.exec(line);
if (match) {
const [, { "length": hashLength }, { "length": spacesLength }] = match;
addErrorContext(onError, lineNumber, line.trim(), null, null, [1, hashLength + spacesLength + 1], {
@@ -3346,7 +3346,7 @@ module.exports = {
// @ts-check
const { addErrorContext, filterTokens } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
-const spaceBeforeHeadingRe = /^((?:\s+)|(?:[>\s]+\s\s))[^>\s]/;
+const spaceBeforeHeadingRe = /^(\s+|[>\s]+\s\s)[^>\s]/;
module.exports = {
"names": ["MD023", "heading-start-left", "header-start-left"],
"description": "Headings must start at the beginning of the line",
@@ -3465,7 +3465,7 @@ module.exports = {
// @ts-check
const { addError, allPunctuationNoQuestion, escapeForRegExp, forEachHeading } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
-const endOfLineHtmlEntityRe = /?[0-9a-zA-Z]+;$/;
+const endOfLineHtmlEntityRe = /?[\da-zA-Z]+;$/;
module.exports = {
"names": ["MD026", "no-trailing-punctuation"],
"description": "Trailing punctuation in heading",
@@ -3798,11 +3798,11 @@ module.exports = {
const { addError, forEachLine, htmlElementRe, withinAnyRange, unescapeMarkdown } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
const { codeBlockAndSpanRanges, lineMetadata, referenceLinkImageData } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
-const linkDestinationRe = /]\(\s*$/;
+const linkDestinationRe = /\]\(\s*$/;
// See https://spec.commonmark.org/0.29/#autolinks
const emailAddressRe =
// eslint-disable-next-line max-len
-/^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
+/^[\w.!#$%&'*+/=?^`{|}~-]+@[a-zA-Z\d](?:[a-zA-Z\d-]{0,61}[a-zA-Z\d])?(?:\.[a-zA-Z\d](?:[a-zA-Z\d-]{0,61}[a-zA-Z\d])?)*$/;
module.exports = {
"names": ["MD033", "no-inline-html"],
"description": "Inline HTML",
@@ -3858,7 +3858,7 @@ module.exports = {
const { addErrorContext, filterTokens, funcExpExec, urlFe, withinAnyRange } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
const { codeBlockAndSpanRanges, htmlElementRanges, referenceLinkImageData } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
-const htmlLinkRe = /]+)>[^<>]*<\/a\s*>/ig;
+const htmlLinkRe = /]+)>[^<>]*<\/a\s*>/gi;
module.exports = {
"names": ["MD034", "no-bare-urls"],
"description": "Bare URL used",
@@ -3945,7 +3945,7 @@ module.exports = {
filterTokens(params, "hr", (token) => {
const { line, lineNumber } = token;
let { markup } = token;
- const match = line.match(/[_*\-\s\t]+$/);
+ const match = line.match(/[_*\-\s]+$/);
if (match) {
markup = match[0].trim();
}
@@ -4036,8 +4036,8 @@ module.exports = {
const { addErrorContext, emphasisMarkersInContent, forEachLine, isBlankLine, withinAnyRange } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
const { htmlElementRanges, lineMetadata } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
-const emphasisRe = /(^|[^\\]|\\\\)(?:(\*\*?\*?)|(__?_?))/g;
-const embeddedUnderscoreRe = /([A-Za-z0-9])_([A-Za-z0-9])/g;
+const emphasisRe = /(^|[^\\]|\\\\)(?:(\*{1,3})|(_{1,3}))/g;
+const embeddedUnderscoreRe = /([A-Za-z\d])_([A-Za-z\d])/g;
const asteriskListItemMarkerRe = /^([\s>]*)\*(\s+)/;
const leftSpaceRe = /^\s+/;
const rightSpaceRe = /\s+$/;
@@ -4271,7 +4271,7 @@ module.exports = {
// @ts-check
const { addErrorContext, filterTokens } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
-const spaceInLinkRe = /\[(?:\s+(?:[^\]]*?)\s*|(?:[^\]]*?)\s+)](?=((?:\([^)]*\))|(?:\[[^\]]*\])))/;
+const spaceInLinkRe = /\[(?:\s[^\]]*|[^\]]*?\s)\](?=(\([^)]*\)|\[[^\]]*\]))/;
module.exports = {
"names": ["MD039", "no-space-in-links"],
"description": "Spaces inside link text",
diff --git a/helpers/helpers.js b/helpers/helpers.js
index 3db2a98ff..5f1560fcd 100644
--- a/helpers/helpers.js
+++ b/helpers/helpers.js
@@ -10,16 +10,16 @@ module.exports.newLineRe = newLineRe;
// Regular expression for matching common front matter (YAML and TOML)
module.exports.frontMatterRe =
// eslint-disable-next-line max-len
- /((^---\s*$[^]*?^---\s*$)|(^\+\+\+\s*$[^]*?^(\+\+\+|\.\.\.)\s*$)|(^\{\s*$[^]*?^\}\s*$))(\r\n|\r|\n|$)/m;
+ /((^---\s*$[\s\S]*?^---\s*$)|(^\+\+\+\s*$[\s\S]*?^(\+\+\+|\.\.\.)\s*$)|(^\{\s*$[\s\S]*?^\}\s*$))(\r\n|\r|\n|$)/m;
// Regular expression for matching the start of inline disable/enable comments
const inlineCommentStartRe =
// eslint-disable-next-line max-len
- /()/ig;
+ /()/gi;
module.exports.inlineCommentStartRe = inlineCommentStartRe;
// Regular expression for matching HTML elements
-const htmlElementRe = /<(([A-Za-z][A-Za-z0-9-]*)(?:\s[^`>]*)?)\/?>/g;
+const htmlElementRe = /<(([A-Za-z][A-Za-z\d-]*)(?:\s[^`>]*)?)\/?>/g;
module.exports.htmlElementRe = htmlElementRe;
// Regular expressions for range matching
@@ -35,10 +35,10 @@ module.exports.blockquotePrefixRe = blockquotePrefixRe;
// Regular expression for reference links (full, collapsed, and shortcut)
const referenceLinkRe =
- /!?\\?\[((?:\[[^\]\0]*]|[^[\]\0])*)](?:(?:\[([^\]\0]*)\])|([^(])|$)/g;
+ /!?\\?\[((?:\[[^\]\0]*\]|[^[\]\0])*)\](?:\[([^\]\0]*)\]|([^(])|$)/g;
// Regular expression for link reference definitions
-const linkReferenceDefinitionRe = /^ {0,3}\[([^\]]*[^\\])]:/;
+const linkReferenceDefinitionRe = /^ {0,3}\[([^\]]*[^\\])\]:/;
module.exports.linkReferenceDefinitionRe = linkReferenceDefinitionRe;
// All punctuation characters (normal and full-width)
diff --git a/lib/md011.js b/lib/md011.js
index 401c7d097..090db726c 100644
--- a/lib/md011.js
+++ b/lib/md011.js
@@ -6,7 +6,7 @@ const { addError, forEachLine, withinAnyRange } = require("../helpers");
const { codeBlockAndSpanRanges, lineMetadata } = require("./cache");
const reversedLinkRe =
- /(^|[^\\])\(([^()]+)\)\[([^\]^][^\]]*)](?!\()/g;
+ /(^|[^\\])\(([^()]+)\)\[([^\]^][^\]]*)\](?!\()/g;
module.exports = {
"names": [ "MD011", "no-reversed-links" ],
diff --git a/lib/md019.js b/lib/md019.js
index bb5b2b265..b7d343551 100644
--- a/lib/md019.js
+++ b/lib/md019.js
@@ -13,7 +13,7 @@ module.exports = {
filterTokens(params, "heading_open", (token) => {
if (headingStyleFor(token) === "atx") {
const { line, lineNumber } = token;
- const match = /^(#+)([ \t]{2,})(?:\S)/.exec(line);
+ const match = /^(#+)([ \t]{2,})\S/.exec(line);
if (match) {
const [
,
diff --git a/lib/md023.js b/lib/md023.js
index c7173c172..ee7ace277 100644
--- a/lib/md023.js
+++ b/lib/md023.js
@@ -4,7 +4,7 @@
const { addErrorContext, filterTokens } = require("../helpers");
-const spaceBeforeHeadingRe = /^((?:\s+)|(?:[>\s]+\s\s))[^>\s]/;
+const spaceBeforeHeadingRe = /^(\s+|[>\s]+\s\s)[^>\s]/;
module.exports = {
"names": [ "MD023", "heading-start-left", "header-start-left" ],
diff --git a/lib/md026.js b/lib/md026.js
index 0a517d790..6ab1f6457 100644
--- a/lib/md026.js
+++ b/lib/md026.js
@@ -5,7 +5,7 @@
const { addError, allPunctuationNoQuestion, escapeForRegExp, forEachHeading } =
require("../helpers");
-const endOfLineHtmlEntityRe = /?[0-9a-zA-Z]+;$/;
+const endOfLineHtmlEntityRe = /?[\da-zA-Z]+;$/;
module.exports = {
"names": [ "MD026", "no-trailing-punctuation" ],
diff --git a/lib/md033.js b/lib/md033.js
index 1e427edd7..c3ffd3680 100644
--- a/lib/md033.js
+++ b/lib/md033.js
@@ -8,11 +8,11 @@ const {
const { codeBlockAndSpanRanges, lineMetadata, referenceLinkImageData } =
require("./cache");
-const linkDestinationRe = /]\(\s*$/;
+const linkDestinationRe = /\]\(\s*$/;
// See https://spec.commonmark.org/0.29/#autolinks
const emailAddressRe =
// eslint-disable-next-line max-len
- /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
+ /^[\w.!#$%&'*+/=?^`{|}~-]+@[a-zA-Z\d](?:[a-zA-Z\d-]{0,61}[a-zA-Z\d])?(?:\.[a-zA-Z\d](?:[a-zA-Z\d-]{0,61}[a-zA-Z\d])?)*$/;
module.exports = {
"names": [ "MD033", "no-inline-html" ],
diff --git a/lib/md034.js b/lib/md034.js
index b479642ab..51e9bc247 100644
--- a/lib/md034.js
+++ b/lib/md034.js
@@ -7,7 +7,7 @@ const { addErrorContext, filterTokens, funcExpExec, urlFe, withinAnyRange } =
const { codeBlockAndSpanRanges, htmlElementRanges, referenceLinkImageData } =
require("./cache");
-const htmlLinkRe = /]+)>[^<>]*<\/a\s*>/ig;
+const htmlLinkRe = /]+)>[^<>]*<\/a\s*>/gi;
module.exports = {
"names": [ "MD034", "no-bare-urls" ],
diff --git a/lib/md035.js b/lib/md035.js
index e6afb5442..b8dbb5d30 100644
--- a/lib/md035.js
+++ b/lib/md035.js
@@ -13,7 +13,7 @@ module.exports = {
filterTokens(params, "hr", (token) => {
const { line, lineNumber } = token;
let { markup } = token;
- const match = line.match(/[_*\-\s\t]+$/);
+ const match = line.match(/[_*\-\s]+$/);
if (match) {
markup = match[0].trim();
}
diff --git a/lib/md037.js b/lib/md037.js
index cc1f401e4..ec6bb6461 100644
--- a/lib/md037.js
+++ b/lib/md037.js
@@ -6,8 +6,8 @@ const { addErrorContext, emphasisMarkersInContent, forEachLine, isBlankLine,
withinAnyRange } = require("../helpers");
const { htmlElementRanges, lineMetadata } = require("./cache");
-const emphasisRe = /(^|[^\\]|\\\\)(?:(\*\*?\*?)|(__?_?))/g;
-const embeddedUnderscoreRe = /([A-Za-z0-9])_([A-Za-z0-9])/g;
+const emphasisRe = /(^|[^\\]|\\\\)(?:(\*{1,3})|(_{1,3}))/g;
+const embeddedUnderscoreRe = /([A-Za-z\d])_([A-Za-z\d])/g;
const asteriskListItemMarkerRe = /^([\s>]*)\*(\s+)/;
const leftSpaceRe = /^\s+/;
const rightSpaceRe = /\s+$/;
diff --git a/lib/md039.js b/lib/md039.js
index 8362eb26d..1cfe71acd 100644
--- a/lib/md039.js
+++ b/lib/md039.js
@@ -5,7 +5,7 @@
const { addErrorContext, filterTokens } = require("../helpers");
const spaceInLinkRe =
- /\[(?:\s+(?:[^\]]*?)\s*|(?:[^\]]*?)\s+)](?=((?:\([^)]*\))|(?:\[[^\]]*\])))/;
+ /\[(?:\s[^\]]*|[^\]]*?\s)\](?=(\([^)]*\)|\[[^\]]*\]))/;
module.exports = {
"names": [ "MD039", "no-space-in-links" ],
diff --git a/package.json b/package.json
index b769a1cb9..486467c12 100644
--- a/package.json
+++ b/package.json
@@ -70,6 +70,7 @@
"eslint-plugin-es": "4.1.0",
"eslint-plugin-jsdoc": "39.6.4",
"eslint-plugin-n": "15.6.0",
+ "eslint-plugin-regexp": "1.11.0",
"eslint-plugin-unicorn": "45.0.2",
"globby": "13.1.3",
"js-yaml": "4.1.0",
diff --git a/test/markdownlint-test.js b/test/markdownlint-test.js
index 24a3e4391..93b863b91 100644
--- a/test/markdownlint-test.js
+++ b/test/markdownlint-test.js
@@ -568,7 +568,7 @@ test("customFrontMatter", (t) => new Promise((resolve) => {
"strings": {
"content": "\n\t\n\n# Heading\n"
},
- "frontMatter": /[^]*<\/head>/,
+ "frontMatter": /[\s\S]*<\/head>/,
"config": {
"default": false,
"MD010": true
diff --git a/test/rules/lint-javascript.js b/test/rules/lint-javascript.js
index c80f15580..10612e761 100644
--- a/test/rules/lint-javascript.js
+++ b/test/rules/lint-javascript.js
@@ -17,7 +17,7 @@ const languageJavaScript = /js|javascript/i;
function cleanJsdocRulesFromEslintConfig(config) {
const cleanedConfig = { ...config };
for (const rule in config.rules) {
- if (/^(es|jsdoc|n|unicorn)\//.test(rule)) {
+ if (/^(es|jsdoc|n|regexp|unicorn)\//.test(rule)) {
delete cleanedConfig.rules[rule];
}
}