mirror of
https://github.com/DavidAnson/markdownlint.git
synced 2025-09-22 05:40:48 +02:00
Update MD034/no-bare-urls to re-scan documents with potential violations using proper reference definition handling to avoid false positives (fixes #787).
This commit is contained in:
parent
054f208e9a
commit
488813f7f7
7 changed files with 136 additions and 102 deletions
|
@ -1374,10 +1374,12 @@ var _require = __webpack_require__(/*! markdownlint-micromark */ "markdownlint-m
|
||||||
*
|
*
|
||||||
* @param {string} markdown Markdown document.
|
* @param {string} markdown Markdown document.
|
||||||
* @param {Object} [options] Options for micromark.
|
* @param {Object} [options] Options for micromark.
|
||||||
|
* @param {boolean} [refsDefined] Whether to treat references as defined.
|
||||||
* @returns {Object[]} Micromark events.
|
* @returns {Object[]} Micromark events.
|
||||||
*/
|
*/
|
||||||
function getMicromarkEvents(markdown) {
|
function getMicromarkEvents(markdown) {
|
||||||
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
||||||
|
var refsDefined = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
|
||||||
// Customize options object to add useful extensions
|
// Customize options object to add useful extensions
|
||||||
options.extensions = options.extensions || [];
|
options.extensions = options.extensions || [];
|
||||||
options.extensions.push(gfmAutolinkLiteral, gfmFootnote(), gfmTable);
|
options.extensions.push(gfmAutolinkLiteral, gfmFootnote(), gfmTable);
|
||||||
|
@ -1386,10 +1388,12 @@ function getMicromarkEvents(markdown) {
|
||||||
var encoding = undefined;
|
var encoding = undefined;
|
||||||
var eol = true;
|
var eol = true;
|
||||||
var parseContext = parse(options);
|
var parseContext = parse(options);
|
||||||
|
if (refsDefined) {
|
||||||
// Customize ParseContext to treat all references as defined
|
// Customize ParseContext to treat all references as defined
|
||||||
parseContext.defined.includes = function (searchElement) {
|
parseContext.defined.includes = function (searchElement) {
|
||||||
return searchElement.length > 0;
|
return searchElement.length > 0;
|
||||||
};
|
};
|
||||||
|
}
|
||||||
var chunks = preprocess()(markdown, encoding, eol);
|
var chunks = preprocess()(markdown, encoding, eol);
|
||||||
var events = postprocess(parseContext.document().write(chunks));
|
var events = postprocess(parseContext.document().write(chunks));
|
||||||
return events;
|
return events;
|
||||||
|
@ -1400,12 +1404,14 @@ function getMicromarkEvents(markdown) {
|
||||||
*
|
*
|
||||||
* @param {string} markdown Markdown document.
|
* @param {string} markdown Markdown document.
|
||||||
* @param {Object} [options] Options for micromark.
|
* @param {Object} [options] Options for micromark.
|
||||||
|
* @param {boolean} [refsDefined] Whether to treat references as defined.
|
||||||
* @returns {Token[]} Micromark tokens (frozen).
|
* @returns {Token[]} Micromark tokens (frozen).
|
||||||
*/
|
*/
|
||||||
function micromarkParse(markdown) {
|
function micromarkParse(markdown) {
|
||||||
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
||||||
|
var refsDefined = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
|
||||||
// Use micromark to parse document into Events
|
// Use micromark to parse document into Events
|
||||||
var events = getMicromarkEvents(markdown, options);
|
var events = getMicromarkEvents(markdown, options, refsDefined);
|
||||||
|
|
||||||
// Create Token objects
|
// Create Token objects
|
||||||
var document = [];
|
var document = [];
|
||||||
|
@ -5008,13 +5014,15 @@ var _require = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js"),
|
||||||
addErrorContext = _require.addErrorContext;
|
addErrorContext = _require.addErrorContext;
|
||||||
var _require2 = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs"),
|
var _require2 = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs"),
|
||||||
filterByPredicate = _require2.filterByPredicate,
|
filterByPredicate = _require2.filterByPredicate,
|
||||||
getHtmlTagInfo = _require2.getHtmlTagInfo;
|
getHtmlTagInfo = _require2.getHtmlTagInfo,
|
||||||
|
parse = _require2.parse;
|
||||||
module.exports = {
|
module.exports = {
|
||||||
"names": ["MD034", "no-bare-urls"],
|
"names": ["MD034", "no-bare-urls"],
|
||||||
"description": "Bare URL used",
|
"description": "Bare URL used",
|
||||||
"tags": ["links", "url"],
|
"tags": ["links", "url"],
|
||||||
"function": function MD034(params, onError) {
|
"function": function MD034(params, onError) {
|
||||||
var literalAutolinks = filterByPredicate(params.parsers.micromark.tokens, function (token) {
|
var literalAutolinks = function literalAutolinks(tokens) {
|
||||||
|
return filterByPredicate(tokens, function (token) {
|
||||||
return token.type === "literalAutolink";
|
return token.type === "literalAutolink";
|
||||||
}, function (token) {
|
}, function (token) {
|
||||||
var children = token.children;
|
var children = token.children;
|
||||||
|
@ -5045,7 +5053,12 @@ module.exports = {
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
});
|
});
|
||||||
var _iterator = _createForOfIteratorHelper(literalAutolinks),
|
};
|
||||||
|
if (literalAutolinks(params.parsers.micromark.tokens).length > 0) {
|
||||||
|
// Re-parse with correct link/image reference definition handling
|
||||||
|
var document = params.lines.join("\n");
|
||||||
|
var tokens = parse(document, undefined, false);
|
||||||
|
var _iterator = _createForOfIteratorHelper(literalAutolinks(tokens)),
|
||||||
_step;
|
_step;
|
||||||
try {
|
try {
|
||||||
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
||||||
|
@ -5064,6 +5077,7 @@ module.exports = {
|
||||||
_iterator.f();
|
_iterator.f();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/***/ }),
|
/***/ }),
|
||||||
|
|
|
@ -26,9 +26,10 @@ const {
|
||||||
*
|
*
|
||||||
* @param {string} markdown Markdown document.
|
* @param {string} markdown Markdown document.
|
||||||
* @param {Object} [options] Options for micromark.
|
* @param {Object} [options] Options for micromark.
|
||||||
|
* @param {boolean} [refsDefined] Whether to treat references as defined.
|
||||||
* @returns {Object[]} Micromark events.
|
* @returns {Object[]} Micromark events.
|
||||||
*/
|
*/
|
||||||
function getMicromarkEvents(markdown, options = {}) {
|
function getMicromarkEvents(markdown, options = {}, refsDefined = true) {
|
||||||
|
|
||||||
// Customize options object to add useful extensions
|
// Customize options object to add useful extensions
|
||||||
options.extensions = options.extensions || [];
|
options.extensions = options.extensions || [];
|
||||||
|
@ -38,8 +39,10 @@ function getMicromarkEvents(markdown, options = {}) {
|
||||||
const encoding = undefined;
|
const encoding = undefined;
|
||||||
const eol = true;
|
const eol = true;
|
||||||
const parseContext = parse(options);
|
const parseContext = parse(options);
|
||||||
|
if (refsDefined) {
|
||||||
// Customize ParseContext to treat all references as defined
|
// Customize ParseContext to treat all references as defined
|
||||||
parseContext.defined.includes = (searchElement) => searchElement.length > 0;
|
parseContext.defined.includes = (searchElement) => searchElement.length > 0;
|
||||||
|
}
|
||||||
const chunks = preprocess()(markdown, encoding, eol);
|
const chunks = preprocess()(markdown, encoding, eol);
|
||||||
const events = postprocess(parseContext.document().write(chunks));
|
const events = postprocess(parseContext.document().write(chunks));
|
||||||
return events;
|
return events;
|
||||||
|
@ -50,12 +53,14 @@ function getMicromarkEvents(markdown, options = {}) {
|
||||||
*
|
*
|
||||||
* @param {string} markdown Markdown document.
|
* @param {string} markdown Markdown document.
|
||||||
* @param {Object} [options] Options for micromark.
|
* @param {Object} [options] Options for micromark.
|
||||||
|
* @param {boolean} [refsDefined] Whether to treat references as defined.
|
||||||
* @returns {Token[]} Micromark tokens (frozen).
|
* @returns {Token[]} Micromark tokens (frozen).
|
||||||
*/
|
*/
|
||||||
function micromarkParse(markdown, options = {}) {
|
function micromarkParse(markdown, options = {}, refsDefined = true) {
|
||||||
|
|
||||||
// Use micromark to parse document into Events
|
// Use micromark to parse document into Events
|
||||||
const events = getMicromarkEvents(markdown, options);
|
const events =
|
||||||
|
getMicromarkEvents(markdown, options, refsDefined);
|
||||||
|
|
||||||
// Create Token objects
|
// Create Token objects
|
||||||
const document = [];
|
const document = [];
|
||||||
|
|
17
lib/md034.js
17
lib/md034.js
|
@ -3,7 +3,7 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const { addErrorContext } = require("../helpers");
|
const { addErrorContext } = require("../helpers");
|
||||||
const { filterByPredicate, getHtmlTagInfo } =
|
const { filterByPredicate, getHtmlTagInfo, parse } =
|
||||||
require("../helpers/micromark.cjs");
|
require("../helpers/micromark.cjs");
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
@ -11,9 +11,9 @@ module.exports = {
|
||||||
"description": "Bare URL used",
|
"description": "Bare URL used",
|
||||||
"tags": [ "links", "url" ],
|
"tags": [ "links", "url" ],
|
||||||
"function": function MD034(params, onError) {
|
"function": function MD034(params, onError) {
|
||||||
const literalAutolinks =
|
const literalAutolinks = (tokens) => (
|
||||||
filterByPredicate(
|
filterByPredicate(
|
||||||
params.parsers.micromark.tokens,
|
tokens,
|
||||||
(token) => token.type === "literalAutolink",
|
(token) => token.type === "literalAutolink",
|
||||||
(token) => {
|
(token) => {
|
||||||
const { children } = token;
|
const { children } = token;
|
||||||
|
@ -43,8 +43,14 @@ module.exports = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
});
|
}
|
||||||
for (const token of literalAutolinks) {
|
)
|
||||||
|
);
|
||||||
|
if (literalAutolinks(params.parsers.micromark.tokens).length > 0) {
|
||||||
|
// Re-parse with correct link/image reference definition handling
|
||||||
|
const document = params.lines.join("\n");
|
||||||
|
const tokens = parse(document, undefined, false);
|
||||||
|
for (const token of literalAutolinks(tokens)) {
|
||||||
const range = [
|
const range = [
|
||||||
token.startColumn,
|
token.startColumn,
|
||||||
token.endColumn - token.startColumn
|
token.endColumn - token.startColumn
|
||||||
|
@ -65,4 +71,5 @@ module.exports = {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -83,8 +83,7 @@ Angle brackets work the same for email: <user@example.com>
|
||||||
|
|
||||||
Links bind to the innermost [link that [is-a-valid] link](https://example.com) {MD034}
|
Links bind to the innermost [link that [is-a-valid] link](https://example.com) {MD034}
|
||||||
|
|
||||||
But not if the [link [is-not-a-valid] link](https://example.com) {MD034}
|
But not if the [link [is-not-a-valid] link](https://example.com)
|
||||||
HOWEVER this scenario could have an invalid shortcut and IS reported
|
|
||||||
|
|
||||||
Escaping both inner square brackets avoids the unwanted report:
|
Escaping both inner square brackets avoids confusion:
|
||||||
[link \[is-not-a-valid\] link](https://example.com)
|
[link \[is-not-a-valid\] link](https://example.com)
|
||||||
|
|
|
@ -25,3 +25,8 @@ Duplicate links in tables should be handled:
|
||||||
| Link | Same Link | Violation |
|
| Link | Same Link | Violation |
|
||||||
|----------------------|----------------------|-----------|
|
|----------------------|----------------------|-----------|
|
||||||
| https://example.com/ | https://example.com/ | {MD034} |
|
| https://example.com/ | https://example.com/ | {MD034} |
|
||||||
|
|
||||||
|
This is not a bare URL: [text [undefined] text](https://example.com).
|
||||||
|
This is a bare URL: [text [defined] text](https://example.com). {MD034}
|
||||||
|
|
||||||
|
[defined]: https://example.com
|
||||||
|
|
|
@ -3247,26 +3247,6 @@ Generated by [AVA](https://avajs.dev).
|
||||||
'no-bare-urls',
|
'no-bare-urls',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
|
||||||
errorContext: 'https://example.com',
|
|
||||||
errorDetail: null,
|
|
||||||
errorRange: [
|
|
||||||
45,
|
|
||||||
19,
|
|
||||||
],
|
|
||||||
fixInfo: {
|
|
||||||
deleteCount: 19,
|
|
||||||
editColumn: 45,
|
|
||||||
insertText: '<https://example.com>',
|
|
||||||
},
|
|
||||||
lineNumber: 86,
|
|
||||||
ruleDescription: 'Bare URL used',
|
|
||||||
ruleInformation: 'https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/md034.md',
|
|
||||||
ruleNames: [
|
|
||||||
'MD034',
|
|
||||||
'no-bare-urls',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
fixed: `# Detailed Results Bare URLs␊
|
fixed: `# Detailed Results Bare URLs␊
|
||||||
␊
|
␊
|
||||||
|
@ -3353,10 +3333,9 @@ Generated by [AVA](https://avajs.dev).
|
||||||
␊
|
␊
|
||||||
Links bind to the innermost [link that [is-a-valid] link](<https://example.com>) {MD034}␊
|
Links bind to the innermost [link that [is-a-valid] link](<https://example.com>) {MD034}␊
|
||||||
␊
|
␊
|
||||||
But not if the [link [is-not-a-valid] link](<https://example.com>) {MD034}␊
|
But not if the [link [is-not-a-valid] link](https://example.com)␊
|
||||||
HOWEVER this scenario could have an invalid shortcut and IS reported␊
|
|
||||||
␊
|
␊
|
||||||
Escaping both inner square brackets avoids the unwanted report:␊
|
Escaping both inner square brackets avoids confusion:␊
|
||||||
[link \\[is-not-a-valid\\] link](https://example.com)␊
|
[link \\[is-not-a-valid\\] link](https://example.com)␊
|
||||||
`,
|
`,
|
||||||
}
|
}
|
||||||
|
@ -23985,6 +23964,26 @@ Generated by [AVA](https://avajs.dev).
|
||||||
'no-bare-urls',
|
'no-bare-urls',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
errorContext: 'https://example.com',
|
||||||
|
errorDetail: null,
|
||||||
|
errorRange: [
|
||||||
|
43,
|
||||||
|
19,
|
||||||
|
],
|
||||||
|
fixInfo: {
|
||||||
|
deleteCount: 19,
|
||||||
|
editColumn: 43,
|
||||||
|
insertText: '<https://example.com>',
|
||||||
|
},
|
||||||
|
lineNumber: 30,
|
||||||
|
ruleDescription: 'Bare URL used',
|
||||||
|
ruleInformation: 'https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/md034.md',
|
||||||
|
ruleNames: [
|
||||||
|
'MD034',
|
||||||
|
'no-bare-urls',
|
||||||
|
],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
fixed: `# Link test␊
|
fixed: `# Link test␊
|
||||||
␊
|
␊
|
||||||
|
@ -24013,6 +24012,11 @@ Generated by [AVA](https://avajs.dev).
|
||||||
| Link | Same Link | Violation |␊
|
| Link | Same Link | Violation |␊
|
||||||
|----------------------|----------------------|-----------|␊
|
|----------------------|----------------------|-----------|␊
|
||||||
| <https://example.com/> | <https://example.com/> | {MD034} |␊
|
| <https://example.com/> | <https://example.com/> | {MD034} |␊
|
||||||
|
␊
|
||||||
|
This is not a bare URL: [text [undefined] text](https://example.com).␊
|
||||||
|
This is a bare URL: [text [defined] text](<https://example.com>). {MD034}␊
|
||||||
|
␊
|
||||||
|
[defined]: https://example.com␊
|
||||||
`,
|
`,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue