mirror of
https://github.com/DavidAnson/markdownlint.git
synced 2025-12-16 05:50:13 +01:00
Add support for named heading fragments as supported by some platforms (fixes #830).
This commit is contained in:
parent
3dcb66cac5
commit
7a794192ca
8 changed files with 295 additions and 18 deletions
|
|
@ -6225,6 +6225,7 @@ var _require = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js"),
|
||||||
// Regular expression for identifying HTML anchor names
|
// Regular expression for identifying HTML anchor names
|
||||||
var idRe = /[\t-\r \xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]id[\t-\r \xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]*=[\t-\r \xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]*["']?((?:(?![\t-\r "'>\xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uD800-\uDFFF\uFEFF])[\s\S]|[\uD800-\uDBFF][\uDC00-\uDFFF])+)/i;
|
var idRe = /[\t-\r \xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]id[\t-\r \xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]*=[\t-\r \xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]*["']?((?:(?![\t-\r "'>\xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uD800-\uDFFF\uFEFF])[\s\S]|[\uD800-\uDBFF][\uDC00-\uDFFF])+)/i;
|
||||||
var nameRe = /[\t-\r \xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]name[\t-\r \xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]*=[\t-\r \xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]*["']?((?:(?![\t-\r "'>\xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uD800-\uDFFF\uFEFF])[\s\S]|[\uD800-\uDBFF][\uDC00-\uDFFF])+)/i;
|
var nameRe = /[\t-\r \xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]name[\t-\r \xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]*=[\t-\r \xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]*["']?((?:(?![\t-\r "'>\xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uD800-\uDFFF\uFEFF])[\s\S]|[\uD800-\uDBFF][\uDC00-\uDFFF])+)/i;
|
||||||
|
var anchorRe = /\{(#[0-9a-z]+(?:[\x2D_][0-9a-z]+)*)\}/g;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts a Markdown heading into an HTML fragment according to the rules
|
* Converts a Markdown heading into an HTML fragment according to the rules
|
||||||
|
|
@ -6260,15 +6261,24 @@ module.exports = {
|
||||||
fragments.set("".concat(fragment, "-").concat(count), 0);
|
fragments.set("".concat(fragment, "-").concat(count), 0);
|
||||||
}
|
}
|
||||||
fragments.set(fragment, count + 1);
|
fragments.set(fragment, count + 1);
|
||||||
|
var match = null;
|
||||||
|
while ((match = anchorRe.exec(content)) !== null) {
|
||||||
|
var _match = match,
|
||||||
|
_match2 = _slicedToArray(_match, 2),
|
||||||
|
anchor = _match2[1];
|
||||||
|
if (!fragments.has(anchor)) {
|
||||||
|
fragments.set(anchor, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
// Process HTML anchors
|
// Process HTML anchors
|
||||||
var processHtmlToken = function processHtmlToken(token) {
|
var processHtmlToken = function processHtmlToken(token) {
|
||||||
var match = null;
|
var match = null;
|
||||||
while ((match = htmlElementRe.exec(token.content)) !== null) {
|
while ((match = htmlElementRe.exec(token.content)) !== null) {
|
||||||
var _match = match,
|
var _match3 = match,
|
||||||
_match2 = _slicedToArray(_match, 3),
|
_match4 = _slicedToArray(_match3, 3),
|
||||||
tag = _match2[0],
|
tag = _match4[0],
|
||||||
element = _match2[2];
|
element = _match4[2];
|
||||||
var anchorMatch = idRe.exec(tag) || element.toLowerCase() === "a" && nameRe.exec(tag);
|
var anchorMatch = idRe.exec(tag) || element.toLowerCase() === "a" && nameRe.exec(tag);
|
||||||
if (anchorMatch) {
|
if (anchorMatch) {
|
||||||
fragments.set("#".concat(anchorMatch[1]), 0);
|
fragments.set("#".concat(anchorMatch[1]), 0);
|
||||||
|
|
@ -6292,8 +6302,8 @@ module.exports = {
|
||||||
var fixInfo = null;
|
var fixInfo = null;
|
||||||
var match = line.match(new RegExp("\\[.*?\\]\\(".concat(escapeForRegExp(context), "\\)")));
|
var match = line.match(new RegExp("\\[.*?\\]\\(".concat(escapeForRegExp(context), "\\)")));
|
||||||
if (match) {
|
if (match) {
|
||||||
var _match3 = _slicedToArray(match, 1);
|
var _match5 = _slicedToArray(match, 1);
|
||||||
context = _match3[0];
|
context = _match5[0];
|
||||||
var index = match.index;
|
var index = match.index;
|
||||||
var length = context.length;
|
var length = context.length;
|
||||||
range = [index + 1, length];
|
range = [index + 1, length];
|
||||||
|
|
|
||||||
|
|
@ -2,17 +2,28 @@ This rule is triggered when a link fragment does not match any of the fragments
|
||||||
that are automatically generated for headings in a document:
|
that are automatically generated for headings in a document:
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
# Title
|
# Heading Name
|
||||||
|
|
||||||
[Link](#fragment)
|
[Link](#fragment)
|
||||||
```
|
```
|
||||||
|
|
||||||
To fix this issue, change the link fragment to reference an existing heading:
|
To fix this issue, change the link fragment to reference an existing heading's
|
||||||
|
generated name (see below):
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
# Title
|
# Heading Name
|
||||||
|
|
||||||
[Link](#title)
|
[Link](#heading-name)
|
||||||
|
```
|
||||||
|
|
||||||
|
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 `_`):
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# Heading Name {#custom-name}
|
||||||
|
|
||||||
|
[Link](#custom-name)
|
||||||
```
|
```
|
||||||
|
|
||||||
Alternatively, an HTML `a` tag with an `id` or a `name` attribute can be used to
|
Alternatively, an HTML `a` tag with an `id` or a `name` attribute can be used to
|
||||||
|
|
|
||||||
19
doc/Rules.md
19
doc/Rules.md
|
|
@ -2153,17 +2153,28 @@ This rule is triggered when a link fragment does not match any of the fragments
|
||||||
that are automatically generated for headings in a document:
|
that are automatically generated for headings in a document:
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
# Title
|
# Heading Name
|
||||||
|
|
||||||
[Link](#fragment)
|
[Link](#fragment)
|
||||||
```
|
```
|
||||||
|
|
||||||
To fix this issue, change the link fragment to reference an existing heading:
|
To fix this issue, change the link fragment to reference an existing heading's
|
||||||
|
generated name (see below):
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
# Title
|
# Heading Name
|
||||||
|
|
||||||
[Link](#title)
|
[Link](#heading-name)
|
||||||
|
```
|
||||||
|
|
||||||
|
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 `_`):
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# Heading Name {#custom-name}
|
||||||
|
|
||||||
|
[Link](#custom-name)
|
||||||
```
|
```
|
||||||
|
|
||||||
Alternatively, an HTML `a` tag with an `id` or a `name` attribute can be used to
|
Alternatively, an HTML `a` tag with an `id` or a `name` attribute can be used to
|
||||||
|
|
|
||||||
19
doc/md051.md
19
doc/md051.md
|
|
@ -10,17 +10,28 @@ This rule is triggered when a link fragment does not match any of the fragments
|
||||||
that are automatically generated for headings in a document:
|
that are automatically generated for headings in a document:
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
# Title
|
# Heading Name
|
||||||
|
|
||||||
[Link](#fragment)
|
[Link](#fragment)
|
||||||
```
|
```
|
||||||
|
|
||||||
To fix this issue, change the link fragment to reference an existing heading:
|
To fix this issue, change the link fragment to reference an existing heading's
|
||||||
|
generated name (see below):
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
# Title
|
# Heading Name
|
||||||
|
|
||||||
[Link](#title)
|
[Link](#heading-name)
|
||||||
|
```
|
||||||
|
|
||||||
|
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 `_`):
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# Heading Name {#custom-name}
|
||||||
|
|
||||||
|
[Link](#custom-name)
|
||||||
```
|
```
|
||||||
|
|
||||||
Alternatively, an HTML `a` tag with an `id` or a `name` attribute can be used to
|
Alternatively, an HTML `a` tag with an `id` or a `name` attribute can be used to
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ const { addError, addErrorDetailIf, escapeForRegExp, filterTokens,
|
||||||
// Regular expression for identifying HTML anchor names
|
// Regular expression for identifying HTML anchor names
|
||||||
const idRe = /\sid\s*=\s*['"]?([^'"\s>]+)/iu;
|
const idRe = /\sid\s*=\s*['"]?([^'"\s>]+)/iu;
|
||||||
const nameRe = /\sname\s*=\s*['"]?([^'"\s>]+)/iu;
|
const nameRe = /\sname\s*=\s*['"]?([^'"\s>]+)/iu;
|
||||||
|
const anchorRe = /\{(#[a-z\d]+(?:[-_][a-z\d]+)*)\}/gu;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts a Markdown heading into an HTML fragment according to the rules
|
* Converts a Markdown heading into an HTML fragment according to the rules
|
||||||
|
|
@ -50,6 +51,13 @@ module.exports = {
|
||||||
fragments.set(`${fragment}-${count}`, 0);
|
fragments.set(`${fragment}-${count}`, 0);
|
||||||
}
|
}
|
||||||
fragments.set(fragment, count + 1);
|
fragments.set(fragment, count + 1);
|
||||||
|
let match = null;
|
||||||
|
while ((match = anchorRe.exec(content)) !== null) {
|
||||||
|
const [ , anchor ] = match;
|
||||||
|
if (!fragments.has(anchor)) {
|
||||||
|
fragments.set(anchor, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
// Process HTML anchors
|
// Process HTML anchors
|
||||||
const processHtmlToken = (token) => {
|
const processHtmlToken = (token) => {
|
||||||
|
|
|
||||||
|
|
@ -171,6 +171,55 @@ Text
|
||||||
|
|
||||||
[mixedref]: #idLINK
|
[mixedref]: #idLINK
|
||||||
|
|
||||||
|
## Valid Named Fragments
|
||||||
|
|
||||||
|
[Valid](#named-fragment)
|
||||||
|
|
||||||
|
[Valid](#valid-heading-with-named-fragment-named-fragment)
|
||||||
|
|
||||||
|
[Valid](#another_fragment_123)
|
||||||
|
|
||||||
|
[Valid](#valid-heading-with-another-named-fragment-another_fragment_123)
|
||||||
|
|
||||||
|
[Valid](#closed-atx)
|
||||||
|
|
||||||
|
[Valid](#setext)
|
||||||
|
|
||||||
|
### Valid Heading with Named Fragment {#named-fragment}
|
||||||
|
|
||||||
|
### Valid Heading with Another Named Fragment {#another_fragment_123}
|
||||||
|
|
||||||
|
### Valid Closed ATX Heading with Named Fragment {#closed-atx} ###
|
||||||
|
|
||||||
|
Valid Setext Heading with Named Fragment {#setext}
|
||||||
|
--------------------------------------------------
|
||||||
|
|
||||||
|
## Invalid Named Fragments
|
||||||
|
|
||||||
|
### Invalid Heading with Named Fragment {#embedded space}
|
||||||
|
|
||||||
|
### Invalid Heading with Named Fragment {#hyphen--run}
|
||||||
|
|
||||||
|
### Invalid Heading with Named Fragment {#UpperCase}
|
||||||
|
|
||||||
|
{#named-fragment-outside-heading}
|
||||||
|
|
||||||
|
[Invalid](#embedded-space) {MD051}
|
||||||
|
|
||||||
|
[Invalid](#embedded_space) {MD051}
|
||||||
|
|
||||||
|
[Invalid](#embedded) {MD051}
|
||||||
|
|
||||||
|
[Invalid](#hyphen--run) {MD051}
|
||||||
|
|
||||||
|
[Invalid](#hyphen-run) {MD051}
|
||||||
|
|
||||||
|
[Invalid](#named-fragment-outside-heading) {MD051}
|
||||||
|
|
||||||
|
[Invalid](#UpperCase) {MD051}
|
||||||
|
|
||||||
|
[Invalid](#uppercase) {MD051}
|
||||||
|
|
||||||
<!-- markdownlint-configure-file {
|
<!-- markdownlint-configure-file {
|
||||||
"emphasis-style": false,
|
"emphasis-style": false,
|
||||||
"heading-style": false,
|
"heading-style": false,
|
||||||
|
|
|
||||||
|
|
@ -23709,6 +23709,134 @@ Generated by [AVA](https://avajs.dev).
|
||||||
'link-fragments',
|
'link-fragments',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
errorContext: '[Invalid](#embedded-space)',
|
||||||
|
errorDetail: null,
|
||||||
|
errorRange: [
|
||||||
|
1,
|
||||||
|
26,
|
||||||
|
],
|
||||||
|
fixInfo: null,
|
||||||
|
lineNumber: 207,
|
||||||
|
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](#embedded_space)',
|
||||||
|
errorDetail: null,
|
||||||
|
errorRange: [
|
||||||
|
1,
|
||||||
|
26,
|
||||||
|
],
|
||||||
|
fixInfo: null,
|
||||||
|
lineNumber: 209,
|
||||||
|
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](#embedded)',
|
||||||
|
errorDetail: null,
|
||||||
|
errorRange: [
|
||||||
|
1,
|
||||||
|
20,
|
||||||
|
],
|
||||||
|
fixInfo: null,
|
||||||
|
lineNumber: 211,
|
||||||
|
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](#hyphen--run)',
|
||||||
|
errorDetail: null,
|
||||||
|
errorRange: [
|
||||||
|
1,
|
||||||
|
23,
|
||||||
|
],
|
||||||
|
fixInfo: null,
|
||||||
|
lineNumber: 213,
|
||||||
|
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](#hyphen-run)',
|
||||||
|
errorDetail: null,
|
||||||
|
errorRange: [
|
||||||
|
1,
|
||||||
|
22,
|
||||||
|
],
|
||||||
|
fixInfo: null,
|
||||||
|
lineNumber: 215,
|
||||||
|
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](#named-fragment-outside-heading)',
|
||||||
|
errorDetail: null,
|
||||||
|
errorRange: [
|
||||||
|
1,
|
||||||
|
42,
|
||||||
|
],
|
||||||
|
fixInfo: null,
|
||||||
|
lineNumber: 217,
|
||||||
|
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](#UpperCase)',
|
||||||
|
errorDetail: null,
|
||||||
|
errorRange: [
|
||||||
|
1,
|
||||||
|
21,
|
||||||
|
],
|
||||||
|
fixInfo: null,
|
||||||
|
lineNumber: 219,
|
||||||
|
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](#uppercase)',
|
||||||
|
errorDetail: null,
|
||||||
|
errorRange: [
|
||||||
|
1,
|
||||||
|
21,
|
||||||
|
],
|
||||||
|
fixInfo: null,
|
||||||
|
lineNumber: 221,
|
||||||
|
ruleDescription: 'Link fragments should be valid',
|
||||||
|
ruleInformation: 'https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/md051.md',
|
||||||
|
ruleNames: [
|
||||||
|
'MD051',
|
||||||
|
'link-fragments',
|
||||||
|
],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
fixed: `# Valid/Invalid Link Fragments␊
|
fixed: `# Valid/Invalid Link Fragments␊
|
||||||
␊
|
␊
|
||||||
|
|
@ -23883,6 +24011,55 @@ Generated by [AVA](https://avajs.dev).
|
||||||
␊
|
␊
|
||||||
[mixedref]: #idLINK␊
|
[mixedref]: #idLINK␊
|
||||||
␊
|
␊
|
||||||
|
## Valid Named Fragments␊
|
||||||
|
␊
|
||||||
|
[Valid](#named-fragment)␊
|
||||||
|
␊
|
||||||
|
[Valid](#valid-heading-with-named-fragment-named-fragment)␊
|
||||||
|
␊
|
||||||
|
[Valid](#another_fragment_123)␊
|
||||||
|
␊
|
||||||
|
[Valid](#valid-heading-with-another-named-fragment-another_fragment_123)␊
|
||||||
|
␊
|
||||||
|
[Valid](#closed-atx)␊
|
||||||
|
␊
|
||||||
|
[Valid](#setext)␊
|
||||||
|
␊
|
||||||
|
### Valid Heading with Named Fragment {#named-fragment}␊
|
||||||
|
␊
|
||||||
|
### Valid Heading with Another Named Fragment {#another_fragment_123}␊
|
||||||
|
␊
|
||||||
|
### Valid Closed ATX Heading with Named Fragment {#closed-atx} ###␊
|
||||||
|
␊
|
||||||
|
Valid Setext Heading with Named Fragment {#setext}␊
|
||||||
|
--------------------------------------------------␊
|
||||||
|
␊
|
||||||
|
## Invalid Named Fragments␊
|
||||||
|
␊
|
||||||
|
### Invalid Heading with Named Fragment {#embedded space}␊
|
||||||
|
␊
|
||||||
|
### Invalid Heading with Named Fragment {#hyphen--run}␊
|
||||||
|
␊
|
||||||
|
### Invalid Heading with Named Fragment {#UpperCase}␊
|
||||||
|
␊
|
||||||
|
{#named-fragment-outside-heading}␊
|
||||||
|
␊
|
||||||
|
[Invalid](#embedded-space) {MD051}␊
|
||||||
|
␊
|
||||||
|
[Invalid](#embedded_space) {MD051}␊
|
||||||
|
␊
|
||||||
|
[Invalid](#embedded) {MD051}␊
|
||||||
|
␊
|
||||||
|
[Invalid](#hyphen--run) {MD051}␊
|
||||||
|
␊
|
||||||
|
[Invalid](#hyphen-run) {MD051}␊
|
||||||
|
␊
|
||||||
|
[Invalid](#named-fragment-outside-heading) {MD051}␊
|
||||||
|
␊
|
||||||
|
[Invalid](#UpperCase) {MD051}␊
|
||||||
|
␊
|
||||||
|
[Invalid](#uppercase) {MD051}␊
|
||||||
|
␊
|
||||||
<!-- markdownlint-configure-file {␊
|
<!-- markdownlint-configure-file {␊
|
||||||
"emphasis-style": false,␊
|
"emphasis-style": false,␊
|
||||||
"heading-style": false,␊
|
"heading-style": false,␊
|
||||||
|
|
|
||||||
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue