diff --git a/demo/markdownlint-browser.js b/demo/markdownlint-browser.js index 1c5c4e37..a30bea80 100644 --- a/demo/markdownlint-browser.js +++ b/demo/markdownlint-browser.js @@ -6245,7 +6245,7 @@ module.exports = [ -const { addError, addErrorDetailIf, getHtmlAttributeRe } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js"); +const { addError, getHtmlAttributeRe } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js"); const { filterByPredicate, filterByTypes, getHtmlTagInfo } = __webpack_require__(/*! ../helpers/micromark-helpers.cjs */ "../helpers/micromark-helpers.cjs"); const { filterByTypesCached } = __webpack_require__(/*! ./cache */ "../lib/cache.js"); @@ -6311,6 +6311,7 @@ module.exports = { "tags": [ "links" ], "parser": "micromark", "function": function MD051(params, onError) { + const ignoreCase = params.config.ignore_case || false; const fragments = new Map(); // Process headings @@ -6386,16 +6387,16 @@ module.exports = { if (mixedCaseKey) { // @ts-ignore (fixInfo || {}).insertText = mixedCaseKey; - addErrorDetailIf( - onError, - link.startLine, - mixedCaseKey, - text, - undefined, - context, - range, - fixInfo - ); + if (!ignoreCase && (mixedCaseKey !== text)) { + addError( + onError, + link.startLine, + `Expected: ${mixedCaseKey}; Actual: ${text}`, + context, + range, + fixInfo + ); + } } else { addError( onError, diff --git a/doc-build/md051.md b/doc-build/md051.md index c9d855a6..b741c908 100644 --- a/doc-build/md051.md +++ b/doc-build/md051.md @@ -16,9 +16,9 @@ generated name (see below): [Link](#heading-name) ``` -Link fragments may be handled case-sensitively, so this rule requires fragments -to exactly match the [GitHub heading algorithm][github-heading-algorithm]. -Therefore, the following example is reported as a violation: +For consistency, this rule requires fragments to exactly match the [GitHub +heading algorithm][github-heading-algorithm] which converts letters to +lowercase. Therefore, the following example is reported as a violation: ```markdown # Heading Name @@ -26,6 +26,10 @@ Therefore, the following example is reported as a violation: [Link](#Heading-Name) ``` +To ignore case when comparing fragments with heading names, the `ignore_case` +parameter can be set to `true`. In this configuration, the previous example is +not reported as a violation. + Alternatively, some platforms allow the syntax `{#named-anchor}` to be used within a heading to provide a specific name (consisting of only lower-case letters, numbers, `-`, and `_`): diff --git a/doc/Rules.md b/doc/Rules.md index 31a50516..ded40d8a 100644 --- a/doc/Rules.md +++ b/doc/Rules.md @@ -2100,6 +2100,10 @@ Tags: `links` Aliases: `link-fragments` +Parameters: + +- `ignore_case`: Ignore case of fragments (`boolean`, default `false`) + Fixable: Some violations can be fixed by tooling This rule is triggered when a link fragment does not match any of the fragments @@ -2120,9 +2124,9 @@ generated name (see below): [Link](#heading-name) ``` -Link fragments may be handled case-sensitively, so this rule requires fragments -to exactly match the [GitHub heading algorithm][github-heading-algorithm]. -Therefore, the following example is reported as a violation: +For consistency, this rule requires fragments to exactly match the [GitHub +heading algorithm][github-heading-algorithm] which converts letters to +lowercase. Therefore, the following example is reported as a violation: ```markdown # Heading Name @@ -2130,6 +2134,10 @@ Therefore, the following example is reported as a violation: [Link](#Heading-Name) ``` +To ignore case when comparing fragments with heading names, the `ignore_case` +parameter can be set to `true`. In this configuration, the previous example is +not reported as a violation. + Alternatively, some platforms allow the syntax `{#named-anchor}` to be used within a heading to provide a specific name (consisting of only lower-case letters, numbers, `-`, and `_`): diff --git a/doc/md051.md b/doc/md051.md index faaaccbe..947aba33 100644 --- a/doc/md051.md +++ b/doc/md051.md @@ -4,6 +4,10 @@ Tags: `links` Aliases: `link-fragments` +Parameters: + +- `ignore_case`: Ignore case of fragments (`boolean`, default `false`) + Fixable: Some violations can be fixed by tooling This rule is triggered when a link fragment does not match any of the fragments @@ -24,9 +28,9 @@ generated name (see below): [Link](#heading-name) ``` -Link fragments may be handled case-sensitively, so this rule requires fragments -to exactly match the [GitHub heading algorithm][github-heading-algorithm]. -Therefore, the following example is reported as a violation: +For consistency, this rule requires fragments to exactly match the [GitHub +heading algorithm][github-heading-algorithm] which converts letters to +lowercase. Therefore, the following example is reported as a violation: ```markdown # Heading Name @@ -34,6 +38,10 @@ Therefore, the following example is reported as a violation: [Link](#Heading-Name) ``` +To ignore case when comparing fragments with heading names, the `ignore_case` +parameter can be set to `true`. In this configuration, the previous example is +not reported as a violation. + Alternatively, some platforms allow the syntax `{#named-anchor}` to be used within a heading to provide a specific name (consisting of only lower-case letters, numbers, `-`, and `_`): diff --git a/lib/configuration-strict.d.ts b/lib/configuration-strict.d.ts index 8314ecbb..3a12ef7d 100644 --- a/lib/configuration-strict.d.ts +++ b/lib/configuration-strict.d.ts @@ -937,11 +937,25 @@ export interface ConfigurationStrict { /** * MD051/link-fragments : Link fragments should be valid : https://github.com/DavidAnson/markdownlint/blob/v0.35.0/doc/md051.md */ - MD051?: boolean; + MD051?: + | boolean + | { + /** + * Ignore case of fragments + */ + ignore_case?: boolean; + }; /** * MD051/link-fragments : Link fragments should be valid : https://github.com/DavidAnson/markdownlint/blob/v0.35.0/doc/md051.md */ - "link-fragments"?: boolean; + "link-fragments"?: + | boolean + | { + /** + * Ignore case of fragments + */ + ignore_case?: boolean; + }; /** * MD052/reference-links-images : Reference links and images should use a label that is defined : https://github.com/DavidAnson/markdownlint/blob/v0.35.0/doc/md052.md */ diff --git a/lib/md051.js b/lib/md051.js index 513e0eb8..781eb139 100644 --- a/lib/md051.js +++ b/lib/md051.js @@ -2,7 +2,7 @@ "use strict"; -const { addError, addErrorDetailIf, getHtmlAttributeRe } = require("../helpers"); +const { addError, getHtmlAttributeRe } = require("../helpers"); const { filterByPredicate, filterByTypes, getHtmlTagInfo } = require("../helpers/micromark-helpers.cjs"); const { filterByTypesCached } = require("./cache"); @@ -68,6 +68,7 @@ module.exports = { "tags": [ "links" ], "parser": "micromark", "function": function MD051(params, onError) { + const ignoreCase = params.config.ignore_case || false; const fragments = new Map(); // Process headings @@ -143,16 +144,16 @@ module.exports = { if (mixedCaseKey) { // @ts-ignore (fixInfo || {}).insertText = mixedCaseKey; - addErrorDetailIf( - onError, - link.startLine, - mixedCaseKey, - text, - undefined, - context, - range, - fixInfo - ); + if (!ignoreCase && (mixedCaseKey !== text)) { + addError( + onError, + link.startLine, + `Expected: ${mixedCaseKey}; Actual: ${text}`, + context, + range, + fixInfo + ); + } } else { addError( onError, diff --git a/schema/.markdownlint.jsonc b/schema/.markdownlint.jsonc index fa325344..6324e317 100644 --- a/schema/.markdownlint.jsonc +++ b/schema/.markdownlint.jsonc @@ -261,7 +261,10 @@ }, // MD051/link-fragments : Link fragments should be valid : https://github.com/DavidAnson/markdownlint/blob/v0.35.0/doc/md051.md - "MD051": true, + "MD051": { + // Ignore case of fragments + "ignore_case": false + }, // MD052/reference-links-images : Reference links and images should use a label that is defined : https://github.com/DavidAnson/markdownlint/blob/v0.35.0/doc/md052.md "MD052": { diff --git a/schema/.markdownlint.yaml b/schema/.markdownlint.yaml index 02b81d6f..6974e354 100644 --- a/schema/.markdownlint.yaml +++ b/schema/.markdownlint.yaml @@ -235,7 +235,9 @@ MD050: style: "consistent" # MD051/link-fragments : Link fragments should be valid : https://github.com/DavidAnson/markdownlint/blob/v0.35.0/doc/md051.md -MD051: true +MD051: + # Ignore case of fragments + ignore_case: false # MD052/reference-links-images : Reference links and images should use a label that is defined : https://github.com/DavidAnson/markdownlint/blob/v0.35.0/doc/md052.md MD052: diff --git a/schema/build-config-schema.js b/schema/build-config-schema.js index 51cd9988..b967ad1c 100644 --- a/schema/build-config-schema.js +++ b/schema/build-config-schema.js @@ -477,6 +477,15 @@ for (const rule of rules) { } }; break; + case "MD051": + scheme.properties = { + "ignore_case": { + "description": "Ignore case of fragments", + "type": "boolean", + "default": false + } + }; + break; case "MD052": scheme.properties = { "shortcut_syntax": { diff --git a/schema/markdownlint-config-schema-strict.json b/schema/markdownlint-config-schema-strict.json index 6621e96e..2aa1198b 100644 --- a/schema/markdownlint-config-schema-strict.json +++ b/schema/markdownlint-config-schema-strict.json @@ -1464,13 +1464,35 @@ }, "MD051": { "description": "MD051/link-fragments : Link fragments should be valid : https://github.com/DavidAnson/markdownlint/blob/v0.35.0/doc/md051.md", - "type": "boolean", - "default": true + "type": [ + "boolean", + "object" + ], + "default": true, + "properties": { + "ignore_case": { + "description": "Ignore case of fragments", + "type": "boolean", + "default": false + } + }, + "additionalProperties": false }, "link-fragments": { "description": "MD051/link-fragments : Link fragments should be valid : https://github.com/DavidAnson/markdownlint/blob/v0.35.0/doc/md051.md", - "type": "boolean", - "default": true + "type": [ + "boolean", + "object" + ], + "default": true, + "properties": { + "ignore_case": { + "description": "Ignore case of fragments", + "type": "boolean", + "default": false + } + }, + "additionalProperties": false }, "MD052": { "description": "MD052/reference-links-images : Reference links and images should use a label that is defined : https://github.com/DavidAnson/markdownlint/blob/v0.35.0/doc/md052.md", diff --git a/schema/markdownlint-config-schema.json b/schema/markdownlint-config-schema.json index cc057cb1..1586676e 100644 --- a/schema/markdownlint-config-schema.json +++ b/schema/markdownlint-config-schema.json @@ -1464,13 +1464,35 @@ }, "MD051": { "description": "MD051/link-fragments : Link fragments should be valid : https://github.com/DavidAnson/markdownlint/blob/v0.35.0/doc/md051.md", - "type": "boolean", - "default": true + "type": [ + "boolean", + "object" + ], + "default": true, + "properties": { + "ignore_case": { + "description": "Ignore case of fragments", + "type": "boolean", + "default": false + } + }, + "additionalProperties": false }, "link-fragments": { "description": "MD051/link-fragments : Link fragments should be valid : https://github.com/DavidAnson/markdownlint/blob/v0.35.0/doc/md051.md", - "type": "boolean", - "default": true + "type": [ + "boolean", + "object" + ], + "default": true, + "properties": { + "ignore_case": { + "description": "Ignore case of fragments", + "type": "boolean", + "default": false + } + }, + "additionalProperties": false }, "MD052": { "description": "MD052/reference-links-images : Reference links and images should use a label that is defined : https://github.com/DavidAnson/markdownlint/blob/v0.35.0/doc/md052.md", diff --git a/test/link-fragments-default-case.md b/test/link-fragments-default-case.md new file mode 100644 index 00000000..c8d4f509 --- /dev/null +++ b/test/link-fragments-default-case.md @@ -0,0 +1,23 @@ +# Link Fragments Default Case + +## Heading Name + +[Valid](#heading-name) + +[Invalid](#Heading-Name) {MD051} + +## Valid *Heading* With _Emphasis_ + +[Valid](#valid-heading-with-emphasis) + +[Invalid](#Valid-Heading-With-Emphasis) {MD051} + +## πŸš€ Valid Heading With Emoji + +[Valid](#-valid-heading-with-emoji) + +[Invalid](#-Valid-Heading-With-Emoji) {MD051} + + diff --git a/test/link-fragments-ignore-case.md b/test/link-fragments-ignore-case.md new file mode 100644 index 00000000..9be8298f --- /dev/null +++ b/test/link-fragments-ignore-case.md @@ -0,0 +1,26 @@ +# Link Fragments Ignore Case + +## Heading Name + +[Valid](#heading-name) + +[Valid](#Heading-Name) + +## Valid *Heading* With _Emphasis_ + +[Valid](#valid-heading-with-emphasis) + +[Valid](#Valid-Heading-With-Emphasis) + +## πŸš€ Valid Heading With Emoji + +[Valid](#-valid-heading-with-emoji) + +[Valid](#-Valid-Heading-With-Emoji) + + diff --git a/test/markdownlint-test.js b/test/markdownlint-test.js index da65b118..df907687 100644 --- a/test/markdownlint-test.js +++ b/test/markdownlint-test.js @@ -941,7 +941,7 @@ test("readme", async(t) => { }); test("validateJsonUsingConfigSchemaStrict", async(t) => { - t.plan(179); + t.plan(181); // @ts-ignore const ajv = new Ajv(ajvOptions); const validateSchemaStrict = ajv.compile(configSchemaStrict); diff --git a/test/snapshots/markdownlint-test-scenarios.js.md b/test/snapshots/markdownlint-test-scenarios.js.md index 8dc51d7a..146803a7 100644 --- a/test/snapshots/markdownlint-test-scenarios.js.md +++ b/test/snapshots/markdownlint-test-scenarios.js.md @@ -24546,6 +24546,134 @@ Generated by [AVA](https://avajs.dev). `, } +## link-fragments-default-case.md + +> Snapshot 1 + + { + errors: [ + { + errorContext: '[Invalid](#Heading-Name)', + errorDetail: 'Expected: #heading-name; Actual: #Heading-Name', + errorRange: [ + 1, + 24, + ], + fixInfo: { + deleteCount: 13, + editColumn: 11, + insertText: '#heading-name', + }, + lineNumber: 7, + ruleDescription: 'Link fragments should be valid', + ruleInformation: 'https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/md051.md', + ruleNames: [ + 'MD051', + 'link-fragments', + ], + }, + { + errorContext: '[Invalid](#Valid-Heading-With-Emphasis)', + errorDetail: 'Expected: #valid-heading-with-emphasis; Actual: #Valid-Heading-With-Emphasis', + errorRange: [ + 1, + 39, + ], + fixInfo: { + deleteCount: 28, + editColumn: 11, + insertText: '#valid-heading-with-emphasis', + }, + lineNumber: 13, + ruleDescription: 'Link fragments should be valid', + ruleInformation: 'https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/md051.md', + ruleNames: [ + 'MD051', + 'link-fragments', + ], + }, + { + errorContext: '[Invalid](#-Valid-Heading-With-Emoji)', + errorDetail: 'Expected: #-valid-heading-with-emoji; Actual: #-Valid-Heading-With-Emoji', + errorRange: [ + 1, + 37, + ], + fixInfo: { + deleteCount: 26, + editColumn: 11, + insertText: '#-valid-heading-with-emoji', + }, + lineNumber: 19, + ruleDescription: 'Link fragments should be valid', + ruleInformation: 'https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/md051.md', + ruleNames: [ + 'MD051', + 'link-fragments', + ], + }, + ], + fixed: `# Link Fragments Default Case␊ + ␊ + ## Heading Name␊ + ␊ + [Valid](#heading-name)␊ + ␊ + [Invalid](#heading-name) {MD051}␊ + ␊ + ## Valid *Heading* With _Emphasis_␊ + ␊ + [Valid](#valid-heading-with-emphasis)␊ + ␊ + [Invalid](#valid-heading-with-emphasis) {MD051}␊ + ␊ + ## πŸš€ Valid Heading With Emoji␊ + ␊ + [Valid](#-valid-heading-with-emoji)␊ + ␊ + [Invalid](#-valid-heading-with-emoji) {MD051}␊ + ␊ + ␊ + `, + } + +## link-fragments-ignore-case.md + +> Snapshot 1 + + { + errors: [], + fixed: `# Link Fragments Ignore Case␊ + ␊ + ## Heading Name␊ + ␊ + [Valid](#heading-name)␊ + ␊ + [Valid](#Heading-Name)␊ + ␊ + ## Valid *Heading* With _Emphasis_␊ + ␊ + [Valid](#valid-heading-with-emphasis)␊ + ␊ + [Valid](#Valid-Heading-With-Emphasis)␊ + ␊ + ## πŸš€ Valid Heading With Emoji␊ + ␊ + [Valid](#-valid-heading-with-emoji)␊ + ␊ + [Valid](#-Valid-Heading-With-Emoji)␊ + ␊ + ␊ + `, + } + ## link-fragments.md > Snapshot 1 diff --git a/test/snapshots/markdownlint-test-scenarios.js.snap b/test/snapshots/markdownlint-test-scenarios.js.snap index bff8c3f4..ef700255 100644 Binary files a/test/snapshots/markdownlint-test-scenarios.js.snap and b/test/snapshots/markdownlint-test-scenarios.js.snap differ