Update MD034/no-bare-urls to handle more scenarios, simplify slightly, replace blanket MD034 suppression for https://github.com/mdn/content with specific (valid) issues (refs #607).

This commit is contained in:
David Anson 2022-12-15 14:27:07 -08:00
parent 2e2937081e
commit d352d4ece1
6 changed files with 103 additions and 18 deletions

View file

@ -3756,7 +3756,7 @@ module.exports = {
"use strict"; "use strict";
// @ts-check // @ts-check
const { addErrorContext, urlRe, withinAnyRange } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js"); const { addErrorContext, filterTokens, urlRe, withinAnyRange } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
const { codeBlockAndSpanRanges, htmlElementRanges, referenceLinkImageData } = __webpack_require__(/*! ./cache */ "../lib/cache.js"); const { codeBlockAndSpanRanges, htmlElementRanges, referenceLinkImageData } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
const htmlLinkRe = /<a(?:|\s[^>]+)>[^<>]*<\/a\s*>/ig; const htmlLinkRe = /<a(?:|\s[^>]+)>[^<>]*<\/a\s*>/ig;
module.exports = { module.exports = {
@ -3769,6 +3769,11 @@ module.exports = {
...codeBlockAndSpanRanges(), ...codeBlockAndSpanRanges(),
...htmlElementRanges() ...htmlElementRanges()
]; ];
filterTokens(params, "html_block", (token) => {
for (let i = token.map[0]; i < token.map[1]; i++) {
codeExclusions.push([i, 0, lines[i].length]);
}
});
const { definitionLineIndices } = referenceLinkImageData(); const { definitionLineIndices } = referenceLinkImageData();
for (const [lineIndex, line] of lines.entries()) { for (const [lineIndex, line] of lines.entries()) {
if (definitionLineIndices[0] === lineIndex) { if (definitionLineIndices[0] === lineIndex) {
@ -3787,15 +3792,17 @@ module.exports = {
const prefix = line.slice(0, matchIndex); const prefix = line.slice(0, matchIndex);
const postfix = line.slice(matchIndex + bareUrlLength); const postfix = line.slice(matchIndex + bareUrlLength);
if ( if (
// Allow ](... to avoid reporting Markdown-style links // Allow <...> to avoid reporting non-bare links
!(/\]\(\s*$/.test(prefix)) && !(prefix.endsWith("<") && postfix.startsWith(">")) &&
// Allow <...> to avoid reporting non-bare links // Allow >...</ to avoid reporting <code>...</code>
!(prefix.endsWith("<") && /^[#)]?>/.test(postfix)) && !(prefix.endsWith(">") && postfix.startsWith("</")) &&
// Allow [...] to avoid MD011/no-reversed-links and nested links // Allow "..." and '...' to allow quoting a bare link
!(/\[[^\]]*$/.test(prefix) && /^[^[]*\]/.test(postfix)) &&
// Allow "..." and '...' for deliberately including a bare link
!(prefix.endsWith("\"") && postfix.startsWith("\"")) && !(prefix.endsWith("\"") && postfix.startsWith("\"")) &&
!(prefix.endsWith("'") && postfix.startsWith("'")) && !(prefix.endsWith("'") && postfix.startsWith("'")) &&
// Allow ](... to avoid reporting Markdown-style links
!(/\]\(\s*$/.test(prefix)) &&
// Allow [...] to avoid MD011/no-reversed-links and nested links
!(/\[[^\]]*$/.test(prefix) && /^[^[]*\]/.test(postfix)) &&
!withinAnyRange(lineExclusions, lineIndex, matchIndex, bareUrlLength) && !withinAnyRange(lineExclusions, lineIndex, matchIndex, bareUrlLength) &&
!withinAnyRange(codeExclusions, lineIndex, matchIndex, bareUrlLength)) { !withinAnyRange(codeExclusions, lineIndex, matchIndex, bareUrlLength)) {
const range = [ const range = [

View file

@ -2,7 +2,8 @@
"use strict"; "use strict";
const { addErrorContext, urlRe, withinAnyRange } = require("../helpers"); const { addErrorContext, filterTokens, urlRe, withinAnyRange } =
require("../helpers");
const { codeBlockAndSpanRanges, htmlElementRanges, referenceLinkImageData } = const { codeBlockAndSpanRanges, htmlElementRanges, referenceLinkImageData } =
require("./cache"); require("./cache");
@ -18,6 +19,11 @@ module.exports = {
...codeBlockAndSpanRanges(), ...codeBlockAndSpanRanges(),
...htmlElementRanges() ...htmlElementRanges()
]; ];
filterTokens(params, "html_block", (token) => {
for (let i = token.map[0]; i < token.map[1]; i++) {
codeExclusions.push([ i, 0, lines[i].length ]);
}
});
const { definitionLineIndices } = referenceLinkImageData(); const { definitionLineIndices } = referenceLinkImageData();
for (const [ lineIndex, line ] of lines.entries()) { for (const [ lineIndex, line ] of lines.entries()) {
if (definitionLineIndices[0] === lineIndex) { if (definitionLineIndices[0] === lineIndex) {
@ -35,15 +41,17 @@ module.exports = {
const prefix = line.slice(0, matchIndex); const prefix = line.slice(0, matchIndex);
const postfix = line.slice(matchIndex + bareUrlLength); const postfix = line.slice(matchIndex + bareUrlLength);
if ( if (
// Allow ](... to avoid reporting Markdown-style links
!(/\]\(\s*$/.test(prefix)) &&
// Allow <...> to avoid reporting non-bare links // Allow <...> to avoid reporting non-bare links
!(prefix.endsWith("<") && /^[#)]?>/.test(postfix)) && !(prefix.endsWith("<") && postfix.startsWith(">")) &&
// Allow [...] to avoid MD011/no-reversed-links and nested links // Allow >...</ to avoid reporting <code>...</code>
!(/\[[^\]]*$/.test(prefix) && /^[^[]*\]/.test(postfix)) && !(prefix.endsWith(">") && postfix.startsWith("</")) &&
// Allow "..." and '...' for deliberately including a bare link // Allow "..." and '...' to allow quoting a bare link
!(prefix.endsWith("\"") && postfix.startsWith("\"")) && !(prefix.endsWith("\"") && postfix.startsWith("\"")) &&
!(prefix.endsWith("'") && postfix.startsWith("'")) && !(prefix.endsWith("'") && postfix.startsWith("'")) &&
// Allow ](... to avoid reporting Markdown-style links
!(/\]\(\s*$/.test(prefix)) &&
// Allow [...] to avoid MD011/no-reversed-links and nested links
!(/\[[^\]]*$/.test(prefix) && /^[^[]*\]/.test(postfix)) &&
!withinAnyRange( !withinAnyRange(
lineExclusions, lineIndex, matchIndex, bareUrlLength lineExclusions, lineIndex, matchIndex, bareUrlLength
) && ) &&

View file

@ -32,7 +32,11 @@ As is <a href="https://example.com/info.htm">https://example.com/info.htm text</
This is not a bare [link]( https://example.com ). This is not a bare [link]( https://example.com ).
URLs in HTML are not bare: Nor is [link](https://example.com/path-with(parens)).
Or <https://example.com/path-with(parens)>.
URLs in HTML attributes are not bare:
<element-name first-attribute=" https://example.com/first " second-attribute=" https://example.com/second "> <element-name first-attribute=" https://example.com/first " second-attribute=" https://example.com/second ">
Text Text
@ -42,8 +46,27 @@ URLs in HTML are not bare:
first-attribute=" https://example.com/first " first-attribute=" https://example.com/first "
second-attribute=" https://example.com/second "></element-name> second-attribute=" https://example.com/second "></element-name>
URLs surrounded by HTML tags are not bare:
Not <code>https://example.com</code> bare.
Not <pre>https://example.com</pre> bare.
<p>
Not bare due to being in an HTML block:
https://example.com
<code>https://example.com</code>
<pre>https://example.com</pre>
</p>
URLs in link and image text are not bare: URLs in link and image text are not bare:
Text [link to https://example.com site](https://example.com) text. Text [link to https://example.com site](https://example.com) text.
Image ![for https://example.com site](https://example.com) text. Image ![for https://example.com site](https://example.com) text.
URLs may end with a dash: https://example.com#heading- {MD034}
... when explicit: <https://example.com#heading->
... when embedded: <code>https://example.com#heading-</code>

View file

@ -147,7 +147,11 @@ test("https://github.com/mdn/content", (t) => {
const rootDir = "./test-repos/mdn-content"; const rootDir = "./test-repos/mdn-content";
const globPatterns = [ join(rootDir, "**/*.md") ]; const globPatterns = [ join(rootDir, "**/*.md") ];
const configPath = join(rootDir, ".markdownlint-cli2.jsonc"); const configPath = join(rootDir, ".markdownlint-cli2.jsonc");
const ignoreRes = [ /^[^:]+: \d+: MD034\/.*$\r?\n?/gm ]; const ignoreRes = [
/test-repos\/mdn-content\/files\/en-us\/mdn\/community\/pull_requests\/index.md: \d+: MD034\/.*$\r?\n?/gm,
/test-repos\/mdn-content\/files\/en-us\/web\/api\/customelementregistry\/whendefined\/index.md: \d+: MD034\/.*$\r?\n?/gm,
/test-repos\/mdn-content\/files\/en-us\/web\/api\/css_painting_api\/guide\/index.md: \d+: MD034\/.*$\r?\n?/gm
];
return lintTestRepo(t, globPatterns, configPath, ignoreRes); return lintTestRepo(t, globPatterns, configPath, ignoreRes);
}); });

View file

@ -2966,6 +2966,26 @@ Generated by [AVA](https://avajs.dev).
'no-bare-urls', 'no-bare-urls',
], ],
}, },
{
errorContext: 'https://example.com#heading-',
errorDetail: null,
errorRange: [
27,
28,
],
fixInfo: {
deleteCount: 28,
editColumn: 27,
insertText: '<https://example.com#heading->',
},
lineNumber: 68,
ruleDescription: 'Bare URL used',
ruleInformation: 'https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/md034.md',
ruleNames: [
'MD034',
'no-bare-urls',
],
},
], ],
fixed: `# Detailed Results Bare URLs␊ fixed: `# Detailed Results Bare URLs␊
@ -3001,7 +3021,11 @@ Generated by [AVA](https://avajs.dev).
This is not a bare [link]( https://example.com ).␊ This is not a bare [link]( https://example.com ).␊
URLs in HTML are not bare:␊ Nor is [link](https://example.com/path-with(parens)).␊
Or <https://example.com/path-with(parens)>.␊
URLs in HTML attributes are not bare:␊
<element-name first-attribute=" https://example.com/first " second-attribute=" https://example.com/second "> <element-name first-attribute=" https://example.com/first " second-attribute=" https://example.com/second ">
Text␊ Text␊
@ -3011,11 +3035,30 @@ Generated by [AVA](https://avajs.dev).
first-attribute=" https://example.com/first "␊ first-attribute=" https://example.com/first "␊
second-attribute=" https://example.com/second "></element-name> second-attribute=" https://example.com/second "></element-name>
URLs surrounded by HTML tags are not bare:␊
Not <code>https://example.com</code> bare.␊
Not <pre>https://example.com</pre> bare.␊
<p>
Not bare due to being in an HTML block:␊
https://example.com␊
<code>https://example.com</code>
<pre>https://example.com</pre>
</p>
URLs in link and image text are not bare:␊ URLs in link and image text are not bare:␊
Text [link to https://example.com site](https://example.com) text.␊ Text [link to https://example.com site](https://example.com) text.␊
Image ![for https://example.com site](https://example.com) text.␊ Image ![for https://example.com site](https://example.com) text.␊
URLs may end with a dash: <https://example.com#heading-> {MD034}␊
... when explicit: <https://example.com#heading->
... when embedded: <code>https://example.com#heading-</code>
`, `,
} }