diff --git a/src/rules/no-bare-urls.js b/src/rules/no-bare-urls.js index 30f0d834..0f806688 100644 --- a/src/rules/no-bare-urls.js +++ b/src/rules/no-bare-urls.js @@ -41,6 +41,8 @@ //----------------------------------------------------------------------------- const htmlTagNamePattern = /^<(?[^!>][^/\s>]*)/u; +const linkTextSpecialCharacterPattern = /[[\]\\*_~`$&|]/gu; +const linkDestinationSpecialCharacterPattern = /[\\()&|]/gu; /** * Parses an HTML tag to extract its name and closing status @@ -168,7 +170,25 @@ export default /** @satisfies {NoBareUrlsRuleDefinition} */ ({ node: linkNode, messageId: "bareUrl", fix(fixer) { - return fixer.replaceText(linkNode, `<${text}>`); + let replacementText = `<${text}>`; + // GFM parses `www` autolinks with an `http://` URL. + if (url === `http://${text}`) { + const escapedLinkText = text.replace( + linkTextSpecialCharacterPattern, + "\\$&", + ); + const escapedLinkDestination = url.replace( + linkDestinationSpecialCharacterPattern, + "\\$&", + ); + + replacementText = `[${escapedLinkText}](${escapedLinkDestination})`; + } + + return fixer.replaceText( + linkNode, + replacementText, + ); }, }); } diff --git a/tests/rules/no-bare-urls.test.js b/tests/rules/no-bare-urls.test.js index df9d171b..0338de38 100644 --- a/tests/rules/no-bare-urls.test.js +++ b/tests/rules/no-bare-urls.test.js @@ -73,6 +73,171 @@ ruleTester.run("no-bare-urls", rule, { " Code https://example.com/code?type=indent code", ], invalid: [ + { + code: "Visit www.example.com for details.", + output: "Visit [www.example.com](http://www.example.com) for details.", + errors: [ + { + messageId: "bareUrl", + line: 1, + column: 7, + endLine: 1, + endColumn: 22, + }, + ], + }, + { + code: "www.google.com/search?q=(business))+ok", + output: "[www.google.com/search?q=(business))+ok](http://www.google.com/search?q=\\(business\\)\\)+ok)", + errors: [ + { + messageId: "bareUrl", + line: 1, + column: 1, + endLine: 1, + endColumn: 39, + }, + ], + }, + { + code: "www.example.com/a\\*b", + output: "[www.example.com/a\\\\\\*b](http://www.example.com/a\\\\*b)", + errors: [ + { + messageId: "bareUrl", + line: 1, + column: 1, + endLine: 1, + endColumn: 21, + }, + ], + }, + { + code: "www.example.com/a]b", + output: "[www.example.com/a\\]b](http://www.example.com/a]b)", + errors: [ + { + messageId: "bareUrl", + line: 1, + column: 1, + endLine: 1, + endColumn: 20, + }, + ], + }, + { + code: "www.example.com/a[b", + output: "[www.example.com/a\\[b](http://www.example.com/a[b)", + errors: [ + { + messageId: "bareUrl", + line: 1, + column: 1, + endLine: 1, + endColumn: 20, + }, + ], + }, + { + code: "www.example.com/a*b*c", + output: "[www.example.com/a\\*b\\*c](http://www.example.com/a*b*c)", + errors: [ + { + messageId: "bareUrl", + line: 1, + column: 1, + endLine: 1, + endColumn: 22, + }, + ], + }, + { + code: "www.example.com/a~b~c", + output: "[www.example.com/a\\~b\\~c](http://www.example.com/a~b~c)", + errors: [ + { + messageId: "bareUrl", + line: 1, + column: 1, + endLine: 1, + endColumn: 22, + }, + ], + }, + { + code: "www.example.com/a`b`c", + output: "[www.example.com/a\\`b\\`c](http://www.example.com/a`b`c)", + errors: [ + { + messageId: "bareUrl", + line: 1, + column: 1, + endLine: 1, + endColumn: 22, + }, + ], + }, + { + code: "www.example.com/$a$b$c", + output: "[www.example.com/\\$a\\$b\\$c](http://www.example.com/$a$b$c)", + languageOptions: { + math: true, + }, + errors: [ + { + messageId: "bareUrl", + line: 1, + column: 1, + endLine: 1, + endColumn: 23, + }, + ], + }, + { + code: dedent` + | URL | + | --- | + | www.example.com/a\|b |`, + output: dedent` + | URL | + | --- | + | [www.example.com/a\\\|b](http://www.example.com/a\\\|b) |`, + errors: [ + { + messageId: "bareUrl", + line: 3, + column: 3, + endLine: 3, + endColumn: 23, + }, + ], + }, + { + code: "www.example.com/a&b", + output: "[www.example.com/a\\&b](http://www.example.com/a\\&b)", + errors: [ + { + messageId: "bareUrl", + line: 1, + column: 1, + endLine: 1, + endColumn: 24, + }, + ], + }, + { + code: "www.example.com/a&b", + output: "[www.example.com/a\\&b](http://www.example.com/a\\&b)", + errors: [ + { + messageId: "bareUrl", + line: 1, + column: 1, + endLine: 1, + endColumn: 24, + }, + ], + }, { code: "https://www.example.com/", output: "",