Update MD054/link-image-style to add url_inline parameter (fixes #753).

This commit is contained in:
David Anson 2023-11-12 22:42:02 -08:00
parent 84333a5f08
commit b709a2f624
26 changed files with 347 additions and 39 deletions

View file

@ -5,8 +5,9 @@ atx
ATX ATX
atx_closed atx_closed
ATX-style ATX-style
Autolinks autolink
autolinks autolinks
Autolinks
axibase axibase
backtick backtick
backticks backticks
@ -15,18 +16,18 @@ blockquotes
Boostnote Boostnote
br br
br_spaces br_spaces
Changelog
Changelogs
changelog changelog
Changelog
changelogs changelogs
Changelogs
CLI CLI
coc-markdownlint coc-markdownlint
CodeQL CodeQL
CodiMD CodiMD
CommonMark CommonMark
config config
config.
Config Config
config.
CVE-\d+-\d+ CVE-\d+-\d+
docs-util docs-util
ES2015 ES2015
@ -76,19 +77,19 @@ npm
ol ol
ol_multi ol_multi
ol-prefix ol-prefix
Pandoc
pandoc pandoc
Pandoc
Params Params
parsers parsers
pre-commit pre-commit
Reactable Reactable
README README
Reimplement
reimplement reimplement
Reimplement
setext setext
setext-style
setext_with_atx setext_with_atx
setext_with_atx_closed setext_with_atx_closed
setext-style
single-h1 single-h1
sublist sublist
Super-Linter Super-Linter
@ -97,8 +98,8 @@ TOML
trimLeft trimLeft
trimRight trimRight
ul ul
ul-indent
ul_single ul_single
ul-indent
ul-start-left ul-start-left
ul-style ul-style
unhandled unhandled

View file

@ -6636,7 +6636,8 @@ module.exports = {
var full = config.full === undefined || !!config.full; var full = config.full === undefined || !!config.full;
var collapsed = config.collapsed === undefined || !!config.collapsed; var collapsed = config.collapsed === undefined || !!config.collapsed;
var shortcut = config.shortcut === undefined || !!config.shortcut; var shortcut = config.shortcut === undefined || !!config.shortcut;
if (autolink && inline && full && collapsed && shortcut) { var urlInline = config.url_inline === undefined || !!config.url_inline;
if (autolink && inline && full && collapsed && shortcut && urlInline) {
// Everything allowed, nothing to check // Everything allowed, nothing to check
return; return;
} }
@ -6671,7 +6672,8 @@ module.exports = {
destination = getTokenTextByType(descendents, "resourceDestinationString"); destination = getTokenTextByType(descendents, "resourceDestinationString");
if (destination) { if (destination) {
// link kind is an inline link // link kind is an inline link
isError = !inline; var title = getTokenTextByType(descendents, "resourceTitleString");
isError = !inline || !urlInline && autolink && !title && !image;
} else { } else {
// link kind is a full/collapsed/shortcut reference link // link kind is a full/collapsed/shortcut reference link
var isShortcut = !children.some(function (t) { var isShortcut = !children.some(function (t) {
@ -6690,13 +6692,16 @@ module.exports = {
if (startLine === endLine) { if (startLine === endLine) {
range = [startColumn, endColumn - startColumn]; range = [startColumn, endColumn - startColumn];
var insertText = null; var insertText = null;
if (inline && label) { var canInline = inline && label;
var canAutolink = autolink && !image && autolinkAble(destination);
if (canInline && (urlInline || !canAutolink)) {
// Most useful form // Most useful form
var prefix = image ? "!" : ""; var prefix = image ? "!" : "";
// @ts-ignore
var escapedLabel = label.replace(/[[\]]/g, "\\$&"); var escapedLabel = label.replace(/[[\]]/g, "\\$&");
var escapedDestination = destination.replace(/[()]/g, "\\$&"); var escapedDestination = destination.replace(/[()]/g, "\\$&");
insertText = "".concat(prefix, "[").concat(escapedLabel, "](").concat(escapedDestination, ")"); insertText = "".concat(prefix, "[").concat(escapedLabel, "](").concat(escapedDestination, ")");
} else if (autolink && !image && autolinkAble(destination)) { } else if (canAutolink) {
// Simplest form // Simplest form
insertText = "<".concat(removeBackslashEscapes(destination), ">"); insertText = "<".concat(removeBackslashEscapes(destination), ">");
} }

View file

@ -60,6 +60,20 @@ This rule does *not* fix scenarios that require converting a link or image to
the `full`, `collapsed`, or `shortcut` reference styles because that involves the `full`, `collapsed`, or `shortcut` reference styles because that involves
naming the reference and determining where to insert it in the document. naming the reference and determining where to insert it in the document.
Setting the `url_inline` parameter to `false` prevents the use of inline links
with the same absolute URL text/destination and no title because such links can
be converted to autolinks:
```markdown
[https://example.com](https://example.com)
```
To fix `url_inline` violations, use the simpler autolink syntax instead:
```markdown
<https://example.com>
```
Rationale: Consistent formatting makes it easier to understand a document. Rationale: Consistent formatting makes it easier to understand a document.
Autolinks are concise, but appear as URLs which can be long and confusing. Autolinks are concise, but appear as URLs which can be long and confusing.
Inline links and images can include descriptive text, but take up more space in Inline links and images can include descriptive text, but take up more space in

View file

@ -2240,6 +2240,7 @@ Parameters:
- `inline`: Allow inline links and images (`boolean`, default `true`) - `inline`: Allow inline links and images (`boolean`, default `true`)
- `shortcut`: Allow shortcut reference links and images (`boolean`, default - `shortcut`: Allow shortcut reference links and images (`boolean`, default
`true`) `true`)
- `url_inline`: Allow URLs as inline links (`boolean`, default `true`)
Fixable: Some violations can be fixed by tooling Fixable: Some violations can be fixed by tooling
@ -2305,6 +2306,20 @@ This rule does *not* fix scenarios that require converting a link or image to
the `full`, `collapsed`, or `shortcut` reference styles because that involves the `full`, `collapsed`, or `shortcut` reference styles because that involves
naming the reference and determining where to insert it in the document. naming the reference and determining where to insert it in the document.
Setting the `url_inline` parameter to `false` prevents the use of inline links
with the same absolute URL text/destination and no title because such links can
be converted to autolinks:
```markdown
[https://example.com](https://example.com)
```
To fix `url_inline` violations, use the simpler autolink syntax instead:
```markdown
<https://example.com>
```
Rationale: Consistent formatting makes it easier to understand a document. Rationale: Consistent formatting makes it easier to understand a document.
Autolinks are concise, but appear as URLs which can be long and confusing. Autolinks are concise, but appear as URLs which can be long and confusing.
Inline links and images can include descriptive text, but take up more space in Inline links and images can include descriptive text, but take up more space in

View file

@ -13,6 +13,7 @@ Parameters:
- `inline`: Allow inline links and images (`boolean`, default `true`) - `inline`: Allow inline links and images (`boolean`, default `true`)
- `shortcut`: Allow shortcut reference links and images (`boolean`, default - `shortcut`: Allow shortcut reference links and images (`boolean`, default
`true`) `true`)
- `url_inline`: Allow URLs as inline links (`boolean`, default `true`)
Fixable: Some violations can be fixed by tooling Fixable: Some violations can be fixed by tooling
@ -78,6 +79,20 @@ This rule does *not* fix scenarios that require converting a link or image to
the `full`, `collapsed`, or `shortcut` reference styles because that involves the `full`, `collapsed`, or `shortcut` reference styles because that involves
naming the reference and determining where to insert it in the document. naming the reference and determining where to insert it in the document.
Setting the `url_inline` parameter to `false` prevents the use of inline links
with the same absolute URL text/destination and no title because such links can
be converted to autolinks:
```markdown
[https://example.com](https://example.com)
```
To fix `url_inline` violations, use the simpler autolink syntax instead:
```markdown
<https://example.com>
```
Rationale: Consistent formatting makes it easier to understand a document. Rationale: Consistent formatting makes it easier to understand a document.
Autolinks are concise, but appear as URLs which can be long and confusing. Autolinks are concise, but appear as URLs which can be long and confusing.
Inline links and images can include descriptive text, but take up more space in Inline links and images can include descriptive text, but take up more space in

View file

@ -1020,6 +1020,10 @@ export interface Configuration {
* Allow shortcut reference links and images * Allow shortcut reference links and images
*/ */
shortcut?: boolean; shortcut?: boolean;
/**
* Allow URLs as inline links
*/
url_inline?: boolean;
}; };
/** /**
* MD054/link-image-style : Link and image style : https://github.com/DavidAnson/markdownlint/blob/v0.31.1/doc/md054.md * MD054/link-image-style : Link and image style : https://github.com/DavidAnson/markdownlint/blob/v0.31.1/doc/md054.md
@ -1047,6 +1051,10 @@ export interface Configuration {
* Allow shortcut reference links and images * Allow shortcut reference links and images
*/ */
shortcut?: boolean; shortcut?: boolean;
/**
* Allow URLs as inline links
*/
url_inline?: boolean;
}; };
/** /**
* headings : MD001, MD003, MD018, MD019, MD020, MD021, MD022, MD023, MD024, MD025, MD026, MD036, MD041, MD043 * headings : MD001, MD003, MD018, MD019, MD020, MD021, MD022, MD023, MD024, MD025, MD026, MD036, MD041, MD043

View file

@ -32,7 +32,8 @@ module.exports = {
const full = (config.full === undefined) || !!config.full; const full = (config.full === undefined) || !!config.full;
const collapsed = (config.collapsed === undefined) || !!config.collapsed; const collapsed = (config.collapsed === undefined) || !!config.collapsed;
const shortcut = (config.shortcut === undefined) || !!config.shortcut; const shortcut = (config.shortcut === undefined) || !!config.shortcut;
if (autolink && inline && full && collapsed && shortcut) { const urlInline = (config.url_inline === undefined) || !!config.url_inline;
if (autolink && inline && full && collapsed && shortcut && urlInline) {
// Everything allowed, nothing to check // Everything allowed, nothing to check
return; return;
} }
@ -62,7 +63,8 @@ module.exports = {
getTokenTextByType(descendents, "resourceDestinationString"); getTokenTextByType(descendents, "resourceDestinationString");
if (destination) { if (destination) {
// link kind is an inline link // link kind is an inline link
isError = !inline; const title = getTokenTextByType(descendents, "resourceTitleString");
isError = !inline || (!urlInline && autolink && !title && !image);
} else { } else {
// link kind is a full/collapsed/shortcut reference link // link kind is a full/collapsed/shortcut reference link
const isShortcut = !children.some((t) => t.type === "reference"); const isShortcut = !children.some((t) => t.type === "reference");
@ -80,13 +82,16 @@ module.exports = {
if (startLine === endLine) { if (startLine === endLine) {
range = [ startColumn, endColumn - startColumn ]; range = [ startColumn, endColumn - startColumn ];
let insertText = null; let insertText = null;
if (inline && label) { const canInline = (inline && label);
const canAutolink = (autolink && !image && autolinkAble(destination));
if (canInline && (urlInline || !canAutolink)) {
// Most useful form // Most useful form
const prefix = (image ? "!" : ""); const prefix = (image ? "!" : "");
// @ts-ignore
const escapedLabel = label.replace(/[[\]]/g, "\\$&"); const escapedLabel = label.replace(/[[\]]/g, "\\$&");
const escapedDestination = destination.replace(/[()]/g, "\\$&"); const escapedDestination = destination.replace(/[()]/g, "\\$&");
insertText = `${prefix}[${escapedLabel}](${escapedDestination})`; insertText = `${prefix}[${escapedLabel}](${escapedDestination})`;
} else if (autolink && !image && autolinkAble(destination)) { } else if (canAutolink) {
// Simplest form // Simplest form
insertText = `<${removeBackslashEscapes(destination)}>`; insertText = `<${removeBackslashEscapes(destination)}>`;
} }

View file

@ -290,6 +290,8 @@
// Allow collapsed reference links and images // Allow collapsed reference links and images
"collapsed": true, "collapsed": true,
// Allow shortcut reference links and images // Allow shortcut reference links and images
"shortcut": true "shortcut": true,
// Allow URLs as inline links
"url_inline": true
} }
} }

View file

@ -262,3 +262,5 @@ MD054:
collapsed: true collapsed: true
# Allow shortcut reference links and images # Allow shortcut reference links and images
shortcut: true shortcut: true
# Allow URLs as inline links
url_inline: true

View file

@ -523,7 +523,12 @@ for (const rule of rules) {
"description": "Allow shortcut reference links and images", "description": "Allow shortcut reference links and images",
"type": "boolean", "type": "boolean",
"default": true "default": true
} },
"url_inline": {
"description": "Allow URLs as inline links",
"type": "boolean",
"default": true
},
}; };
break; break;
default: default:

View file

@ -1587,6 +1587,11 @@
"description": "Allow shortcut reference links and images", "description": "Allow shortcut reference links and images",
"type": "boolean", "type": "boolean",
"default": true "default": true
},
"url_inline": {
"description": "Allow URLs as inline links",
"type": "boolean",
"default": true
} }
}, },
"additionalProperties": false "additionalProperties": false
@ -1623,6 +1628,11 @@
"description": "Allow shortcut reference links and images", "description": "Allow shortcut reference links and images",
"type": "boolean", "type": "boolean",
"default": true "default": true
},
"url_inline": {
"description": "Allow URLs as inline links",
"type": "boolean",
"default": true
} }
}, },
"additionalProperties": false "additionalProperties": false

View file

@ -1,4 +1,4 @@
# Link Style autolink_only # Link Style Autolink Only
Text [url](https://example.com) text {MD054} Text [url](https://example.com) text {MD054}

View file

@ -1,4 +1,4 @@
# Link Style autolink_or_inline # Link Style Autolink or Inline
Text [url](https://example.com) text Text [url](https://example.com) text

View file

@ -1,4 +1,4 @@
# Link Style autolink_or_reference # Link Style Autolink or Reference
Text [url](https://example.com) text {MD054} Text [url](https://example.com) text {MD054}

View file

@ -1,4 +1,4 @@
# Link Style reference_only # Link Style Collapsed Only
Text [url](https://example.com) text {MD054} Text [url](https://example.com) text {MD054}

View file

@ -1,4 +1,4 @@
# Link Style reference_only # Link Style Full Only
Text [url](https://example.com) text {MD054} Text [url](https://example.com) text {MD054}

View file

@ -1,4 +1,4 @@
# Link Style inline_only # Link Style Inline Only
Text [url](https://example.com) text Text [url](https://example.com) text

View file

@ -1,4 +1,4 @@
# Link Style inline_or_reference # Link Style Inline or Reference
Text [url](https://example.com) text Text [url](https://example.com) text

View file

@ -0,0 +1,37 @@
# Link Style No URL Inline Not Possible
Text [https://example.com](https://example.com) text
Text ![https://example.com](https://example.com) text
Text [https://example.com](<https://example.com>) text
Text ![https://example.com](<https://example.com>) text
Text [https://example.com](https://example.com/page "title") text
Text ![https://example.com](https://example.com/page "title") text
Text [https://example.com](https://example.com "title") text
Text ![https://example.com](https://example.com "title") text
Text [https://example.com][url] text
Text ![https://example.com][url] text
Text [https://example.com][url-title] text
Text ![https://example.com][url-title] text
Text <https://example.com> text {MD054}
[url]: https://example.com
[url-title]: https://example.com "title"
<!-- markdownlint-configure-file {
"link-image-style": {
"autolink": false,
"url_inline": false
}
} -->

View file

@ -0,0 +1,36 @@
# Link Style No URL Inline Possible
Text [https://example.com](https://example.com) text {MD054}
Text ![https://example.com](https://example.com) text
Text [https://example.com](<https://example.com>) text {MD054}
Text ![https://example.com](<https://example.com>) text
Text [https://example.com](https://example.com/page "title") text
Text ![https://example.com](https://example.com/page "title") text
Text [https://example.com](https://example.com "title") text
Text ![https://example.com](https://example.com "title") text
Text [https://example.com][url] text
Text ![https://example.com][url] text
Text [https://example.com][url-title] text
Text ![https://example.com][url-title] text
Text <https://example.com> text
[url]: https://example.com
[url-title]: https://example.com "title"
<!-- markdownlint-configure-file {
"link-image-style": {
"url_inline": false
}
} -->

View file

@ -1,4 +1,4 @@
# Link Style autolink_only # Link Style None
Text [url](https://example.com) text {MD054} Text [url](https://example.com) text {MD054}

View file

@ -1,4 +1,4 @@
# Link Style reference_only # Link Style Reference Only
Text [url](https://example.com) text {MD054} Text [url](https://example.com) text {MD054}

View file

@ -1,4 +1,4 @@
# Link Style reference_only # Link Style Shortcut Only
Text [url](https://example.com) text {MD054} Text [url](https://example.com) text {MD054}

View file

@ -915,7 +915,7 @@ test("readme", async(t) => {
}); });
test("validateJsonUsingConfigSchemaStrict", async(t) => { test("validateJsonUsingConfigSchemaStrict", async(t) => {
t.plan(169); t.plan(171);
const { addSchema, validate } = const { addSchema, validate } =
await import("@hyperjump/json-schema/draft-07"); await import("@hyperjump/json-schema/draft-07");
addSchema(configSchemaStrict, configSchemaStrictUri); addSchema(configSchemaStrict, configSchemaStrictUri);

View file

@ -24749,7 +24749,7 @@ Generated by [AVA](https://avajs.dev).
], ],
}, },
], ],
fixed: `# Link Style autolink_only␊ fixed: `# Link Style Autolink Only␊
Text <https://example.com> text {MD054}␊ Text <https://example.com> text {MD054}␊
@ -24999,7 +24999,7 @@ Generated by [AVA](https://avajs.dev).
], ],
}, },
], ],
fixed: `# Link Style autolink_or_inline␊ fixed: `# Link Style Autolink or Inline␊
Text [url](https://example.com) text␊ Text [url](https://example.com) text␊
@ -25386,7 +25386,7 @@ Generated by [AVA](https://avajs.dev).
], ],
}, },
], ],
fixed: `# Link Style autolink_or_reference␊ fixed: `# Link Style Autolink or Reference␊
Text <https://example.com> text {MD054}␊ Text <https://example.com> text {MD054}␊
@ -25898,7 +25898,7 @@ Generated by [AVA](https://avajs.dev).
], ],
}, },
], ],
fixed: `# Link Style reference_only␊ fixed: `# Link Style Collapsed Only␊
Text [url](https://example.com) text {MD054}␊ Text [url](https://example.com) text {MD054}␊
@ -26413,7 +26413,7 @@ Generated by [AVA](https://avajs.dev).
], ],
}, },
], ],
fixed: `# Link Style reference_only␊ fixed: `# Link Style Full Only␊
Text [url](https://example.com) text {MD054}␊ Text [url](https://example.com) text {MD054}␊
@ -26742,7 +26742,7 @@ Generated by [AVA](https://avajs.dev).
], ],
}, },
], ],
fixed: `# Link Style inline_only␊ fixed: `# Link Style Inline Only␊
Text [url](https://example.com) text␊ Text [url](https://example.com) text␊
@ -26912,7 +26912,7 @@ Generated by [AVA](https://avajs.dev).
], ],
}, },
], ],
fixed: `# Link Style inline_or_reference␊ fixed: `# Link Style Inline or Reference␊
Text [url](https://example.com) text␊ Text [url](https://example.com) text␊
@ -26991,6 +26991,159 @@ Generated by [AVA](https://avajs.dev).
`, `,
} }
## link-style-no-url-inline-not-possible.md
> Snapshot 1
{
errors: [
{
errorContext: '<https://example.com>',
errorDetail: null,
errorRange: [
6,
21,
],
fixInfo: {
deleteCount: 21,
editColumn: 6,
insertText: '[https://example.com](https://example.com)',
},
lineNumber: 27,
ruleDescription: 'Link and image style',
ruleInformation: 'https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/md054.md',
ruleNames: [
'MD054',
'link-image-style',
],
},
],
fixed: `# Link Style No URL Inline Not Possible␊
Text [https://example.com](https://example.com) text␊
Text ![https://example.com](https://example.com) text␊
Text [https://example.com](<https://example.com>) text␊
Text ![https://example.com](<https://example.com>) text␊
Text [https://example.com](https://example.com/page "title") text␊
Text ![https://example.com](https://example.com/page "title") text␊
Text [https://example.com](https://example.com "title") text␊
Text ![https://example.com](https://example.com "title") text␊
Text [https://example.com][url] text␊
Text ![https://example.com][url] text␊
Text [https://example.com][url-title] text␊
Text ![https://example.com][url-title] text␊
Text [https://example.com](https://example.com) text {MD054}␊
[url]: https://example.com␊
[url-title]: https://example.com "title"␊
<!-- markdownlint-configure-file {␊
"link-image-style": {␊
"autolink": false,␊
"url_inline": false␊
}␊
} -->␊
`,
}
## link-style-no-url-inline-possible.md
> Snapshot 1
{
errors: [
{
errorContext: '[https://example.com](https://...',
errorDetail: null,
errorRange: [
6,
42,
],
fixInfo: {
deleteCount: 42,
editColumn: 6,
insertText: '<https://example.com>',
},
lineNumber: 3,
ruleDescription: 'Link and image style',
ruleInformation: 'https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/md054.md',
ruleNames: [
'MD054',
'link-image-style',
],
},
{
errorContext: '[https://example.com](<https:/...',
errorDetail: null,
errorRange: [
6,
44,
],
fixInfo: {
deleteCount: 44,
editColumn: 6,
insertText: '<https://example.com>',
},
lineNumber: 7,
ruleDescription: 'Link and image style',
ruleInformation: 'https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/md054.md',
ruleNames: [
'MD054',
'link-image-style',
],
},
],
fixed: `# Link Style No URL Inline Possible␊
Text <https://example.com> text {MD054}␊
Text ![https://example.com](https://example.com) text␊
Text <https://example.com> text {MD054}␊
Text ![https://example.com](<https://example.com>) text␊
Text [https://example.com](https://example.com/page "title") text␊
Text ![https://example.com](https://example.com/page "title") text␊
Text [https://example.com](https://example.com "title") text␊
Text ![https://example.com](https://example.com "title") text␊
Text [https://example.com][url] text␊
Text ![https://example.com][url] text␊
Text [https://example.com][url-title] text␊
Text ![https://example.com][url-title] text␊
Text <https://example.com> text␊
[url]: https://example.com␊
[url-title]: https://example.com "title"␊
<!-- markdownlint-configure-file {␊
"link-image-style": {␊
"url_inline": false␊
}␊
} -->␊
`,
}
## link-style-none.md ## link-style-none.md
> Snapshot 1 > Snapshot 1
@ -27456,7 +27609,7 @@ Generated by [AVA](https://avajs.dev).
], ],
}, },
], ],
fixed: `# Link Style autolink_only fixed: `# Link Style None
Text [url](https://example.com) text {MD054}␊ Text [url](https://example.com) text {MD054}␊
@ -27877,7 +28030,7 @@ Generated by [AVA](https://avajs.dev).
], ],
}, },
], ],
fixed: `# Link Style reference_only␊ fixed: `# Link Style Reference Only␊
Text [url](https://example.com) text {MD054}␊ Text [url](https://example.com) text {MD054}␊
@ -28358,7 +28511,7 @@ Generated by [AVA](https://avajs.dev).
], ],
}, },
], ],
fixed: `# Link Style reference_only␊ fixed: `# Link Style Shortcut Only␊
Text [url](https://example.com) text {MD054}␊ Text [url](https://example.com) text {MD054}␊