mirror of
https://github.com/DavidAnson/markdownlint.git
synced 2025-12-16 22:10:13 +01:00
Improve handling of nested tags and blocks by MD033/no-inline-html (fixes #179).
This commit is contained in:
parent
44fac78721
commit
4c7ffdd335
7 changed files with 372 additions and 18 deletions
|
|
@ -115,6 +115,19 @@ module.exports.escapeForRegExp = function escapeForRegExp(str) {
|
||||||
return str.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
|
return str.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Un-escapes Markdown content (simple algorithm; not a parser)
|
||||||
|
const escapedMarkdownRe = /\\./g;
|
||||||
|
module.exports.unescapeMarkdown =
|
||||||
|
function unescapeMarkdown(markdown, replacement) {
|
||||||
|
return markdown.replace(escapedMarkdownRe, (match) => {
|
||||||
|
const char = match[1];
|
||||||
|
if ("!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~".includes(char)) {
|
||||||
|
return replacement || char;
|
||||||
|
}
|
||||||
|
return match;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
// Returns the indent for a token
|
// Returns the indent for a token
|
||||||
function indentFor(token) {
|
function indentFor(token) {
|
||||||
const line = token.line.replace(/^[\s>]*(> |>)/, "");
|
const line = token.line.replace(/^[\s>]*(> |>)/, "");
|
||||||
|
|
|
||||||
38
lib/md033.js
38
lib/md033.js
|
|
@ -2,10 +2,12 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const { addError, filterTokens, forEachInlineChild, newLineRe,
|
const { addError, bareUrlRe, forEachLine, unescapeMarkdown } =
|
||||||
rangeFromRegExp } = require("../helpers");
|
require("../helpers");
|
||||||
|
const { lineMetadata } = require("./cache");
|
||||||
|
|
||||||
const htmlRe = /<[^>]*>/;
|
const htmlElementRe = /<(\w+)(?:[^>]*)?>/g;
|
||||||
|
const linkDestinationRe = /]\(\s*$/;
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
"names": [ "MD033", "no-inline-html" ],
|
"names": [ "MD033", "no-inline-html" ],
|
||||||
|
|
@ -14,21 +16,21 @@ module.exports = {
|
||||||
"function": function MD033(params, onError) {
|
"function": function MD033(params, onError) {
|
||||||
const allowedElements = (params.config.allowed_elements || [])
|
const allowedElements = (params.config.allowed_elements || [])
|
||||||
.map((element) => element.toLowerCase());
|
.map((element) => element.toLowerCase());
|
||||||
function forToken(token) {
|
forEachLine(lineMetadata(), (line, lineIndex, inCode) => {
|
||||||
token.content.split(newLineRe)
|
let match = null;
|
||||||
.forEach((line, offset) => {
|
// eslint-disable-next-line no-unmodified-loop-condition
|
||||||
const allowed = (line.match(/<[^/\s>!]*/g) || [])
|
while (!inCode && (match = htmlElementRe.exec(line))) {
|
||||||
.filter((element) => element.length > 1)
|
const [ tag, element ] = match;
|
||||||
.map((element) => element.slice(1).toLowerCase())
|
if (!allowedElements.includes(element.toLowerCase()) &&
|
||||||
.filter((element) => !allowedElements.includes(element));
|
!tag.endsWith("\\>") && !bareUrlRe.test(tag)) {
|
||||||
if (allowed.length) {
|
const prefix = line.substring(0, match.index);
|
||||||
addError(onError, token.lineNumber + offset,
|
if (!linkDestinationRe.test(prefix) &&
|
||||||
"Element: " + allowed[0], null,
|
!unescapeMarkdown(prefix + "<", "_").endsWith("_")) {
|
||||||
rangeFromRegExp(token.line, htmlRe));
|
addError(onError, lineIndex + 1, "Element: " + element,
|
||||||
|
null, [ match.index + 1, tag.length ]);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
filterTokens(params, "html_block", forToken);
|
});
|
||||||
forEachInlineChild(params, "html_inline", forToken);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
9
test/detailed-results-html-tags.json
Normal file
9
test/detailed-results-html-tags.json
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"default": true,
|
||||||
|
"MD033": {
|
||||||
|
"allowed_elements": [
|
||||||
|
"strong"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"MD046": false
|
||||||
|
}
|
||||||
81
test/detailed-results-html-tags.md
Normal file
81
test/detailed-results-html-tags.md
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
# Detailed HTML Results
|
||||||
|
|
||||||
|
Text
|
||||||
|
|
||||||
|
<em>Block block</em>
|
||||||
|
|
||||||
|
Text <em>inline inline</em> text
|
||||||
|
|
||||||
|
Text
|
||||||
|
|
||||||
|
<strong>Block block</strong>
|
||||||
|
|
||||||
|
Text <strong>inline inline</strong> text
|
||||||
|
|
||||||
|
Text
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Block
|
||||||
|
block <em>block</em> block
|
||||||
|
block
|
||||||
|
block <strong>block</strong> block
|
||||||
|
block
|
||||||
|
block <em>block</em> block <strong>block</strong> block
|
||||||
|
block <strong>block</strong> block <em>block</em> block
|
||||||
|
</p>
|
||||||
|
|
||||||
|
Text
|
||||||
|
|
||||||
|
<strong><em>Block</em> block</strong>
|
||||||
|
|
||||||
|
Text <strong><em>inline</em> inline</strong> text
|
||||||
|
|
||||||
|
Text
|
||||||
|
|
||||||
|
<em><strong>Block</strong> block</em>
|
||||||
|
|
||||||
|
Text <em><strong>inline</strong> inline</em> text
|
||||||
|
|
||||||
|
Text
|
||||||
|
|
||||||
|
Text <em>inline</em> text <strong>inline</strong> text <em>inline</em> text
|
||||||
|
|
||||||
|
Text <strong>inline</strong> text <em>inline</em> text <strong>inline</strong> text
|
||||||
|
|
||||||
|
Text
|
||||||
|
|
||||||
|
\<not>Block block\</not>
|
||||||
|
|
||||||
|
\\<problem>Block block\\</problem>
|
||||||
|
|
||||||
|
<not\>Block block</not\>
|
||||||
|
|
||||||
|
Text \<not>inline inline\</not> text
|
||||||
|
|
||||||
|
Text \\<problem>inline inline\\</problem> text
|
||||||
|
|
||||||
|
Text <not\>inline inline</not\> text
|
||||||
|
|
||||||
|
Text
|
||||||
|
|
||||||
|
> Text <em>inline inline</em> text
|
||||||
|
> text <strong>inline inline</strong> text
|
||||||
|
|
||||||
|
Text
|
||||||
|
|
||||||
|
Text <em>inline inline</em> text
|
||||||
|
text <strong>inline inline</strong> text
|
||||||
|
|
||||||
|
Text
|
||||||
|
|
||||||
|
```html
|
||||||
|
Text <em>inline inline</em> text
|
||||||
|
text <strong>inline inline</strong> text
|
||||||
|
```
|
||||||
|
|
||||||
|
Text
|
||||||
|
|
||||||
|
Text <a href="#anchor">inline</a> text
|
||||||
|
text <img src="src.png"/> text
|
||||||
|
|
||||||
|
Text
|
||||||
155
test/detailed-results-html-tags.results.json
Normal file
155
test/detailed-results-html-tags.results.json
Normal file
|
|
@ -0,0 +1,155 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"lineNumber": 5,
|
||||||
|
"ruleNames": [ "MD033", "no-inline-html" ],
|
||||||
|
"ruleDescription": "Inline HTML",
|
||||||
|
"ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md033",
|
||||||
|
"errorDetail": "Element: em",
|
||||||
|
"errorContext": null,
|
||||||
|
"errorRange": [ 1, 4 ]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lineNumber": 7,
|
||||||
|
"ruleNames": [ "MD033", "no-inline-html" ],
|
||||||
|
"ruleDescription": "Inline HTML",
|
||||||
|
"ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md033",
|
||||||
|
"errorDetail": "Element: em",
|
||||||
|
"errorContext": null,
|
||||||
|
"errorRange": [ 6, 4 ]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lineNumber": 17,
|
||||||
|
"ruleNames": [ "MD033", "no-inline-html" ],
|
||||||
|
"ruleDescription": "Inline HTML",
|
||||||
|
"ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md033",
|
||||||
|
"errorDetail": "Element: p",
|
||||||
|
"errorContext": null,
|
||||||
|
"errorRange": [ 1, 3 ]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lineNumber": 19,
|
||||||
|
"ruleNames": [ "MD033", "no-inline-html" ],
|
||||||
|
"ruleDescription": "Inline HTML",
|
||||||
|
"ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md033",
|
||||||
|
"errorDetail": "Element: em",
|
||||||
|
"errorContext": null,
|
||||||
|
"errorRange": [ 7, 4 ]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lineNumber": 23,
|
||||||
|
"ruleNames": [ "MD033", "no-inline-html" ],
|
||||||
|
"ruleDescription": "Inline HTML",
|
||||||
|
"ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md033",
|
||||||
|
"errorDetail": "Element: em",
|
||||||
|
"errorContext": null,
|
||||||
|
"errorRange": [ 7, 4 ]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lineNumber": 24,
|
||||||
|
"ruleNames": [ "MD033", "no-inline-html" ],
|
||||||
|
"ruleDescription": "Inline HTML",
|
||||||
|
"ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md033",
|
||||||
|
"errorDetail": "Element: em",
|
||||||
|
"errorContext": null,
|
||||||
|
"errorRange": [ 36, 4 ]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lineNumber": 29,
|
||||||
|
"ruleNames": [ "MD033", "no-inline-html" ],
|
||||||
|
"ruleDescription": "Inline HTML",
|
||||||
|
"ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md033",
|
||||||
|
"errorDetail": "Element: em",
|
||||||
|
"errorContext": null,
|
||||||
|
"errorRange": [ 9, 4 ]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lineNumber": 31,
|
||||||
|
"ruleNames": [ "MD033", "no-inline-html" ],
|
||||||
|
"ruleDescription": "Inline HTML",
|
||||||
|
"ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md033",
|
||||||
|
"errorDetail": "Element: em",
|
||||||
|
"errorContext": null,
|
||||||
|
"errorRange": [ 14, 4 ]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lineNumber": 35,
|
||||||
|
"ruleNames": [ "MD033", "no-inline-html" ],
|
||||||
|
"ruleDescription": "Inline HTML",
|
||||||
|
"ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md033",
|
||||||
|
"errorDetail": "Element: em",
|
||||||
|
"errorContext": null,
|
||||||
|
"errorRange": [ 1, 4 ]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lineNumber": 37,
|
||||||
|
"ruleNames": [ "MD033", "no-inline-html" ],
|
||||||
|
"ruleDescription": "Inline HTML",
|
||||||
|
"ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md033",
|
||||||
|
"errorDetail": "Element: em",
|
||||||
|
"errorContext": null,
|
||||||
|
"errorRange": [ 6, 4 ]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lineNumber": 41,
|
||||||
|
"ruleNames": [ "MD033", "no-inline-html" ],
|
||||||
|
"ruleDescription": "Inline HTML",
|
||||||
|
"ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md033",
|
||||||
|
"errorDetail": "Element: em",
|
||||||
|
"errorContext": null,
|
||||||
|
"errorRange": [ 6, 4 ]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lineNumber": 43,
|
||||||
|
"ruleNames": [ "MD033", "no-inline-html" ],
|
||||||
|
"ruleDescription": "Inline HTML",
|
||||||
|
"ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md033",
|
||||||
|
"errorDetail": "Element: em",
|
||||||
|
"errorContext": null,
|
||||||
|
"errorRange": [ 35, 4 ]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lineNumber": 49,
|
||||||
|
"ruleNames": [ "MD033", "no-inline-html" ],
|
||||||
|
"ruleDescription": "Inline HTML",
|
||||||
|
"ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md033",
|
||||||
|
"errorDetail": "Element: problem",
|
||||||
|
"errorContext": null,
|
||||||
|
"errorRange": [ 3, 9 ]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lineNumber": 55,
|
||||||
|
"ruleNames": [ "MD033", "no-inline-html" ],
|
||||||
|
"ruleDescription": "Inline HTML",
|
||||||
|
"ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md033",
|
||||||
|
"errorDetail": "Element: problem",
|
||||||
|
"errorContext": null,
|
||||||
|
"errorRange": [ 8, 9 ]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lineNumber": 61,
|
||||||
|
"ruleNames": [ "MD033", "no-inline-html" ],
|
||||||
|
"ruleDescription": "Inline HTML",
|
||||||
|
"ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md033",
|
||||||
|
"errorDetail": "Element: em",
|
||||||
|
"errorContext": null,
|
||||||
|
"errorRange": [ 8, 4 ]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lineNumber": 78,
|
||||||
|
"ruleNames": [ "MD033", "no-inline-html" ],
|
||||||
|
"ruleDescription": "Inline HTML",
|
||||||
|
"ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md033",
|
||||||
|
"errorDetail": "Element: a",
|
||||||
|
"errorContext": null,
|
||||||
|
"errorRange": [ 6, 18 ]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lineNumber": 79,
|
||||||
|
"ruleNames": [ "MD033", "no-inline-html" ],
|
||||||
|
"ruleDescription": "Inline HTML",
|
||||||
|
"ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md033",
|
||||||
|
"errorDetail": "Element: img",
|
||||||
|
"errorContext": null,
|
||||||
|
"errorRange": [ 6, 20 ]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
@ -6,24 +6,56 @@
|
||||||
|
|
||||||
[text](<>) {MD042}
|
[text](<>) {MD042}
|
||||||
|
|
||||||
|
[text]( <> ) {MD042}
|
||||||
|
|
||||||
|
[text](<> "title") {MD042}
|
||||||
|
|
||||||
|
[text]( <> "title" ) {MD042}
|
||||||
|
|
||||||
[text](#) {MD042}
|
[text](#) {MD042}
|
||||||
|
|
||||||
|
[text]( # ) {MD042}
|
||||||
|
|
||||||
|
[text](# "title") {MD042}
|
||||||
|
|
||||||
|
[text]( # "title" ) {MD042}
|
||||||
|
|
||||||
[text][frag] {MD042}
|
[text][frag] {MD042}
|
||||||
|
|
||||||
|
[text][ frag ] {MD042}
|
||||||
|
|
||||||
[frag]: #
|
[frag]: #
|
||||||
|
|
||||||
## Non-empty links
|
## Non-empty links
|
||||||
|
|
||||||
[text](link)
|
[text](link)
|
||||||
|
|
||||||
|
[text]( link )
|
||||||
|
|
||||||
[text](link "title")
|
[text](link "title")
|
||||||
|
|
||||||
|
[text]( link "title" )
|
||||||
|
|
||||||
[text](<link>)
|
[text](<link>)
|
||||||
|
|
||||||
|
[text]( <link> )
|
||||||
|
|
||||||
|
[text](<link> "title")
|
||||||
|
|
||||||
|
[text]( <link> "title" )
|
||||||
|
|
||||||
[text](#frag)
|
[text](#frag)
|
||||||
|
|
||||||
|
[text]( #frag )
|
||||||
|
|
||||||
|
[text](#frag "title")
|
||||||
|
|
||||||
|
[text]( #frag "title" )
|
||||||
|
|
||||||
[text][ref]
|
[text][ref]
|
||||||
|
|
||||||
|
[text][ ref ]
|
||||||
|
|
||||||
[ref]: link
|
[ref]: link
|
||||||
|
|
||||||
[text]
|
[text]
|
||||||
|
|
|
||||||
|
|
@ -1406,6 +1406,68 @@ function clearHtmlCommentTextEmbedded(test) {
|
||||||
test.done();
|
test.done();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
module.exports.unescapeMarkdown = function unescapeMarkdown(test) {
|
||||||
|
test.expect(7);
|
||||||
|
// Test cases from https://spec.commonmark.org/0.29/#backslash-escapes
|
||||||
|
const testCases = [
|
||||||
|
[
|
||||||
|
"\\!\\\"\\#\\$\\%\\&\\'\\(\\)\\*\\+\\,\\-\\.\\/\\:\\;" +
|
||||||
|
"\\<\\=\\>\\?\\@\\[\\\\\\]\\^\\_\\`\\{\\|\\}\\~",
|
||||||
|
"!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"\\→\\A\\a\\ \\3\\φ\\«",
|
||||||
|
"\\→\\A\\a\\ \\3\\φ\\«"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
`\\*not emphasized*
|
||||||
|
\\<br/> not a tag
|
||||||
|
\\[not a link](/foo)
|
||||||
|
\\\`not code\`
|
||||||
|
1\\. not a list
|
||||||
|
\\* not a list
|
||||||
|
\\# not a heading
|
||||||
|
\\[foo]: /url "not a reference"
|
||||||
|
\\ö not a character entity`,
|
||||||
|
`*not emphasized*
|
||||||
|
<br/> not a tag
|
||||||
|
[not a link](/foo)
|
||||||
|
\`not code\`
|
||||||
|
1. not a list
|
||||||
|
* not a list
|
||||||
|
# not a heading
|
||||||
|
[foo]: /url "not a reference"
|
||||||
|
ö not a character entity`
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"\\\\*emphasis*",
|
||||||
|
"\\*emphasis*"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
`foo\\
|
||||||
|
bar`,
|
||||||
|
`foo\\
|
||||||
|
bar`
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"Text \\<",
|
||||||
|
"Text _",
|
||||||
|
"_"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"Text \\\\<",
|
||||||
|
"Text _<",
|
||||||
|
"_"
|
||||||
|
]
|
||||||
|
];
|
||||||
|
testCases.forEach(function forTestCase(testCase) {
|
||||||
|
const [ markdown, expected, replacement ] = testCase;
|
||||||
|
const actual = helpers.unescapeMarkdown(markdown, replacement);
|
||||||
|
test.equal(actual, expected);
|
||||||
|
});
|
||||||
|
test.done();
|
||||||
|
};
|
||||||
|
|
||||||
module.exports.isBlankLine = function isBlankLine(test) {
|
module.exports.isBlankLine = function isBlankLine(test) {
|
||||||
test.expect(25);
|
test.expect(25);
|
||||||
const blankLines = [
|
const blankLines = [
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue