diff --git a/demo/markdownlint-browser.js b/demo/markdownlint-browser.js index 71c25871..604e0481 100644 --- a/demo/markdownlint-browser.js +++ b/demo/markdownlint-browser.js @@ -51,7 +51,10 @@ module.exports.orderedListItemMarkerRe = /^[\s>]*0*(\d+)[.)]/; // Regular expression for all instances of emphasis markers var emphasisMarkersRe = /[_*]/g; // Regular expression for inline links and shortcut reference links -var linkRe = /\[(?:[^[\]]|\[[^\]]*\])*\](?:\(\S*\))?/g; +var linkRe = /(\[(?:[^[\]]|\[[^\]]*\])*\])(\(\S*\)|\[\S*\])?/g; +module.exports.linkRe = linkRe; +// Regular expression for link reference definition lines +module.exports.linkReferenceRe = /^ {0,3}\[[^\]]+]:\s.*$/; // readFile options for reading with the UTF-8 encoding module.exports.utf8Encoding = "utf8"; // All punctuation characters (normal and full-width) @@ -3745,7 +3748,7 @@ module.exports = { "use strict"; // @ts-check -var _a = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js"), addErrorDetailIf = _a.addErrorDetailIf, bareUrlRe = _a.bareUrlRe, escapeForRegExp = _a.escapeForRegExp, forEachLine = _a.forEachLine, newLineRe = _a.newLineRe, forEachInlineCodeSpan = _a.forEachInlineCodeSpan; +var _a = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js"), addErrorDetailIf = _a.addErrorDetailIf, bareUrlRe = _a.bareUrlRe, escapeForRegExp = _a.escapeForRegExp, forEachLine = _a.forEachLine, linkRe = _a.linkRe, linkReferenceRe = _a.linkReferenceRe, newLineRe = _a.newLineRe, forEachInlineCodeSpan = _a.forEachInlineCodeSpan; var lineMetadata = __webpack_require__(/*! ./cache */ "../lib/cache.js").lineMetadata; module.exports = { "names": ["MD044", "proper-names"], @@ -3758,6 +3761,23 @@ module.exports = { var codeBlocks = params.config.code_blocks; var includeCodeBlocks = (codeBlocks === undefined) ? true : !!codeBlocks; var exclusions = []; + forEachLine(lineMetadata(), function (line, lineIndex) { + if (linkReferenceRe.test(line)) { + exclusions.push([lineIndex, 0, line.length]); + } + else { + var match = null; + while ((match = bareUrlRe.exec(line)) !== null) { + exclusions.push([lineIndex, match.index, match[0].length]); + } + while ((match = linkRe.exec(line)) !== null) { + var text = match[1], destination = match[2]; + if (destination) { + exclusions.push([lineIndex, match.index + text.length, destination.length]); + } + } + } + }); if (!includeCodeBlocks) { forEachInlineCodeSpan(params.lines.join("\n"), function (code, lineIndex, columnIndex) { var codeLines = code.split(newLineRe); @@ -3770,21 +3790,20 @@ module.exports = { } var _loop_1 = function (name_1) { var escapedName = escapeForRegExp(name_1); - var startNamePattern = /^\W/.test(name_1) ? "" : "[^\\s([\"]*\\b_*"; - var endNamePattern = /\W$/.test(name_1) ? "" : "_*\\b[^\\s)\\]\"]*"; + var startNamePattern = /^\W/.test(name_1) ? "" : "\\b_*"; + var endNamePattern = /\W$/.test(name_1) ? "" : "_*\\b"; var namePattern = "(" + startNamePattern + ")(" + escapedName + ")" + endNamePattern; var nameRe = new RegExp(namePattern, "gi"); forEachLine(lineMetadata(), function (line, lineIndex, inCode, onFence) { if (includeCodeBlocks || (!inCode && !onFence)) { var match = null; var _loop_2 = function () { - var fullMatch = match[0], leftMatch = match[1], nameMatch = match[2]; + var leftMatch = match[1], nameMatch = match[2]; var index = match.index + leftMatch.length; var length_1 = nameMatch.length; - if ((fullMatch.search(bareUrlRe) === -1) && - exclusions.every(function (span) { return ((lineIndex !== span[0]) || - (index + length_1 < span[1]) || - (index > span[1] + span[2])); })) { + if (exclusions.every(function (span) { return ((lineIndex !== span[0]) || + (index + length_1 < span[1]) || + (index > span[1] + span[2])); })) { addErrorDetailIf(onError, lineIndex + 1, name_1, nameMatch, null, null, [index + 1, length_1], { "editColumn": index + 1, "deleteCount": length_1, diff --git a/helpers/helpers.js b/helpers/helpers.js index 52ca1470..5c6f58e6 100644 --- a/helpers/helpers.js +++ b/helpers/helpers.js @@ -29,7 +29,11 @@ module.exports.orderedListItemMarkerRe = /^[\s>]*0*(\d+)[.)]/; const emphasisMarkersRe = /[_*]/g; // Regular expression for inline links and shortcut reference links -const linkRe = /\[(?:[^[\]]|\[[^\]]*\])*\](?:\(\S*\))?/g; +const linkRe = /(\[(?:[^[\]]|\[[^\]]*\])*\])(\(\S*\)|\[\S*\])?/g; +module.exports.linkRe = linkRe; + +// Regular expression for link reference definition lines +module.exports.linkReferenceRe = /^ {0,3}\[[^\]]+]:\s.*$/; // readFile options for reading with the UTF-8 encoding module.exports.utf8Encoding = "utf8"; diff --git a/lib/md044.js b/lib/md044.js index d6f1187a..af3b1ce7 100644 --- a/lib/md044.js +++ b/lib/md044.js @@ -2,8 +2,8 @@ "use strict"; -const { addErrorDetailIf, bareUrlRe, escapeForRegExp, forEachLine, newLineRe, - forEachInlineCodeSpan } = require("../helpers"); +const { addErrorDetailIf, bareUrlRe, escapeForRegExp, forEachLine, linkRe, + linkReferenceRe, newLineRe, forEachInlineCodeSpan } = require("../helpers"); const { lineMetadata } = require("./cache"); module.exports = { @@ -17,6 +17,24 @@ module.exports = { const codeBlocks = params.config.code_blocks; const includeCodeBlocks = (codeBlocks === undefined) ? true : !!codeBlocks; const exclusions = []; + forEachLine(lineMetadata(), (line, lineIndex) => { + if (linkReferenceRe.test(line)) { + exclusions.push([ lineIndex, 0, line.length ]); + } else { + let match = null; + while ((match = bareUrlRe.exec(line)) !== null) { + exclusions.push([ lineIndex, match.index, match[0].length ]); + } + while ((match = linkRe.exec(line)) !== null) { + const [ , text, destination ] = match; + if (destination) { + exclusions.push( + [ lineIndex, match.index + text.length, destination.length ] + ); + } + } + } + }); if (!includeCodeBlocks) { forEachInlineCodeSpan( params.lines.join("\n"), @@ -34,8 +52,8 @@ module.exports = { } for (const name of names) { const escapedName = escapeForRegExp(name); - const startNamePattern = /^\W/.test(name) ? "" : "[^\\s([\"]*\\b_*"; - const endNamePattern = /\W$/.test(name) ? "" : "_*\\b[^\\s)\\]\"]*"; + const startNamePattern = /^\W/.test(name) ? "" : "\\b_*"; + const endNamePattern = /\W$/.test(name) ? "" : "_*\\b"; const namePattern = `(${startNamePattern})(${escapedName})${endNamePattern}`; const nameRe = new RegExp(namePattern, "gi"); @@ -43,11 +61,10 @@ module.exports = { if (includeCodeBlocks || (!inCode && !onFence)) { let match = null; while ((match = nameRe.exec(line)) !== null) { - const [ fullMatch, leftMatch, nameMatch ] = match; + const [ , leftMatch, nameMatch ] = match; const index = match.index + leftMatch.length; const length = nameMatch.length; if ( - (fullMatch.search(bareUrlRe) === -1) && exclusions.every((span) => ( (lineIndex !== span[0]) || (index + length < span[1]) || diff --git a/test/proper-names-links.md b/test/proper-names-links.md new file mode 100644 index 00000000..46d42a62 --- /dev/null +++ b/test/proper-names-links.md @@ -0,0 +1,43 @@ +# Proper names in links + +javascript {MD044} + +Learn about JavaScript here: https://example.com/javascript/about + +Learn about JavaScript here: + +Learn about [JavaScript](https://example.com/javascript/about). + +Learn about [JavaScript](wiki/javascript/about). + +Learn about [JavaScript](wiki/javascript). + +Learn about [JavaScript](javascript/about). + +Learn about [JavaScript](javascript). + +Learn about [JavaScript][js]. + +Learn about [JavaScript][example-js]. + +Learn about [JavaScript][javascript]. + +Learn about [javascript][js]. {MD044} + +Learn about [javascript][example-js]. {MD044} + +Learn about [javascript]. {MD044} + +[js]: https://example.com/javascript/about + [example-js]: javascript + [javascript]: https://example.com/js + [javascript]: https://example.com/js {MD044} + +