diff --git a/src/rules/no-missing-link-fragments.js b/src/rules/no-missing-link-fragments.js index ebc29c8f..0455d642 100644 --- a/src/rules/no-missing-link-fragments.js +++ b/src/rules/no-missing-link-fragments.js @@ -28,7 +28,8 @@ import GithubSlugger from "github-slugger"; const githubLineReferencePattern = /^L\d+(?:C\d+)?(?:-L\d+(?:C\d+)?)?$/u; const customHeadingIdPattern = /\{#([^}\s]+)\}\s*$/u; const htmlCommentPattern = //gu; -const htmlIdNamePattern = /(?]+)\s(?:id|name)=["']([^"']+)["']/giu; +const htmlIdNamePattern = + /(?]+)\s(?:id|name)\s*=\s*["']?([^"'\s>]+)["']?/giu; /** * Checks if the fragment is a valid GitHub line reference diff --git a/tests/rules/no-missing-link-fragments.test.js b/tests/rules/no-missing-link-fragments.test.js index 4c44ac77..233582b3 100644 --- a/tests/rules/no-missing-link-fragments.test.js +++ b/tests/rules/no-missing-link-fragments.test.js @@ -101,6 +101,59 @@ ruleTester.run("no-missing-link-fragments", rule, { [Link](#old-style-6) `, + // HTML id/name attributes using unquoted values and spaces around equals sign + dedent` +

Bookmark

+

Old Style

+

Bookmark 2

+

Old Style 2

+

Bookmark 3

+

Old Style 3

+

Bookmark 4

+

Old Style 4

+
Bookmark 5
+
Old Style 5
+
Bookmark 6
+
Old Style 6
+
Bookmark 7
+
Old Style 7
+
Bookmark 8
+
Old Style 8
+
Bookmark 9
+
Old Style 9
+
Bookmark 10
+
Old Style 10
+
Bookmark 11
+
Old Style 11
+
Bookmark 12
+
Old Style 12
+ + [Link](#bookmark) + [Link](#old-style) + [Link](#bookmark-2) + [Link](#old-style-2) + [Link](#bookmark-3) + [Link](#old-style-3) + [Link](#bookmark-4) + [Link](#old-style-4) + [Link](#bookmark-5) + [Link](#old-style-5) + [Link](#bookmark-6) + [Link](#old-style-6) + [Link](#bookmark-7) + [Link](#old-style-7) + [Link](#bookmark-8) + [Link](#old-style-8) + [Link](#bookmark-9) + [Link](#old-style-9) + [Link](#bookmark-10) + [Link](#old-style-10) + [Link](#bookmark-11) + [Link](#old-style-11) + [Link](#bookmark-12) + [Link](#old-style-12) + `, + // Special #top link "[Link](#top)", @@ -497,6 +550,101 @@ ruleTester.run("no-missing-link-fragments", rule, { }, ], }, + + // Invalid: Link to non-existent ID with unquoted attributes + { + code: dedent` +

Bookmark

+ + [Link](#notfound) + `, + errors: [ + { + messageId: "invalidFragment", + data: { fragment: "notfound" }, + line: 3, + column: 1, + endLine: 3, + endColumn: 18, + }, + ], + }, + + // Invalid: Link to non-existent ID with spaced attributes + { + code: dedent` +

Bookmark 2

+ + [Link](#notfound) + `, + errors: [ + { + messageId: "invalidFragment", + data: { fragment: "notfound" }, + line: 3, + column: 1, + endLine: 3, + endColumn: 18, + }, + ], + }, + + // Invalid: Link to non-existent name attribute + { + code: dedent` +

Old Style 3

+ + [Link](#notfound) + `, + errors: [ + { + messageId: "invalidFragment", + data: { fragment: "notfound" }, + line: 3, + column: 1, + endLine: 3, + endColumn: 18, + }, + ], + }, + + // Invalid: Link to non-existent ID with quoted attributes + { + code: dedent` +

Bookmark 4

+ + [Link](#notfound) + `, + errors: [ + { + messageId: "invalidFragment", + data: { fragment: "notfound" }, + line: 3, + column: 1, + endLine: 3, + endColumn: 18, + }, + ], + }, + + // Invalid: Link to non-existent ID with spaced quoted attributes + { + code: dedent` +
Bookmark 5
+ + [Link](#notfound) + `, + errors: [ + { + messageId: "invalidFragment", + data: { fragment: "notfound" }, + line: 3, + column: 1, + endLine: 3, + endColumn: 18, + }, + ], + }, { code: dedent`