Exclude htmlFlow content by default for filterByTypes, opt in as needed.

This commit is contained in:
David Anson 2024-06-09 17:09:03 -07:00
parent ea9659841e
commit 12502f6571
18 changed files with 206 additions and 129 deletions

View file

@ -1240,13 +1240,40 @@ const {
} = __webpack_require__(/*! markdownlint-micromark */ "markdownlint-micromark"); } = __webpack_require__(/*! markdownlint-micromark */ "markdownlint-micromark");
const { newLineRe } = __webpack_require__(/*! ./shared.js */ "../helpers/shared.js"); const { newLineRe } = __webpack_require__(/*! ./shared.js */ "../helpers/shared.js");
const flatTokensSymbol = Symbol("flat-tokens"); const flatTokensWithHtmlFlowSymbol = Symbol("flat-tokens-with-html-flow");
const flatTokensWithoutHtmlFlowSymbol = Symbol("flat-tokens-without-html-flow");
/** @typedef {import("markdownlint-micromark").Event} Event */ /** @typedef {import("markdownlint-micromark").Event} Event */
/** @typedef {import("markdownlint-micromark").ParseOptions} ParseOptions */ /** @typedef {import("markdownlint-micromark").ParseOptions} ParseOptions */
/** @typedef {import("markdownlint-micromark").TokenType} TokenType */ /** @typedef {import("markdownlint-micromark").TokenType} TokenType */
/** @typedef {import("../lib/markdownlint.js").MicromarkToken} Token */ /** @typedef {import("../lib/markdownlint.js").MicromarkToken} Token */
/**
* Gets the nearest parent of the specified type for a Micromark token.
*
* @param {Token} token Micromark token.
* @param {TokenType[]} types Types to allow.
* @returns {Token | null} Parent token.
*/
function getTokenParentOfType(token, types) {
/** @type {Token | null} */
let current = token;
while ((current = current.parent) && !types.includes(current.type)) {
// Empty
}
return current;
}
/**
* Determines if a Micromark token has an htmlFlow-type parent.
*
* @param {Token} token Micromark token.
* @returns {boolean} True iff the token has an htmlFlow-type parent.
*/
function inHtmlFlow(token) {
return getTokenParentOfType(token, [ "htmlFlow" ]) !== null;
}
/** /**
* Returns whether a token is an htmlFlow type containing an HTML comment. * Returns whether a token is an htmlFlow type containing an HTML comment.
* *
@ -1337,7 +1364,8 @@ function micromarkParseWithOffset(
// Create Token objects // Create Token objects
const document = []; const document = [];
let flatTokens = []; let flatTokensWithHtmlFlow = [];
const flatTokensWithoutHtmlFlow = [];
/** @type {Token} */ /** @type {Token} */
const root = { const root = {
"type": "data", "type": "data",
@ -1376,7 +1404,8 @@ function micromarkParseWithOffset(
"parent": ((previous === root) ? (ancestor || null) : previous) "parent": ((previous === root) ? (ancestor || null) : previous)
}; };
previous.children.push(current); previous.children.push(current);
flatTokens.push(current); flatTokensWithHtmlFlow.push(current);
flatTokensWithoutHtmlFlow.push(current);
if ((current.type === "htmlFlow") && !isHtmlFlowComment(current)) { if ((current.type === "htmlFlow") && !isHtmlFlowComment(current)) {
skipHtmlFlowChildren = true; skipHtmlFlowChildren = true;
if (!reparseOptions || !lines) { if (!reparseOptions || !lines) {
@ -1405,7 +1434,7 @@ function micromarkParseWithOffset(
current.children = tokens; current.children = tokens;
// Avoid stack overflow of Array.push(...spread) // Avoid stack overflow of Array.push(...spread)
// eslint-disable-next-line unicorn/prefer-spread // eslint-disable-next-line unicorn/prefer-spread
flatTokens = flatTokens.concat(tokens[flatTokensSymbol]); flatTokensWithHtmlFlow = flatTokensWithHtmlFlow.concat(tokens[flatTokensWithHtmlFlowSymbol]);
} }
} else if (kind === "exit") { } else if (kind === "exit") {
if (type === "htmlFlow") { if (type === "htmlFlow") {
@ -1421,7 +1450,8 @@ function micromarkParseWithOffset(
} }
// Return document // Return document
Object.defineProperty(document, flatTokensSymbol, { "value": flatTokens }); Object.defineProperty(document, flatTokensWithHtmlFlowSymbol, { "value": flatTokensWithHtmlFlow });
Object.defineProperty(document, flatTokensWithoutHtmlFlowSymbol, { "value": flatTokensWithoutHtmlFlow });
Object.freeze(document); Object.freeze(document);
return document; return document;
} }
@ -1507,15 +1537,24 @@ function filterByPredicate(tokens, allowed, transformChildren) {
* *
* @param {Token[]} tokens Micromark tokens. * @param {Token[]} tokens Micromark tokens.
* @param {TokenType[]} types Types to allow. * @param {TokenType[]} types Types to allow.
* @param {boolean} [htmlFlow] Whether to include htmlFlow content.
* @returns {Token[]} Filtered tokens. * @returns {Token[]} Filtered tokens.
*/ */
function filterByTypes(tokens, types) { function filterByTypes(tokens, types, htmlFlow) {
const predicate = (token) => types.includes(token.type); const predicate = (token) => types.includes(token.type);
const flatTokens = tokens[flatTokensSymbol]; const flatTokens = tokens[
htmlFlow ?
flatTokensWithHtmlFlowSymbol :
flatTokensWithoutHtmlFlowSymbol
];
if (flatTokens) { if (flatTokens) {
return flatTokens.filter(predicate); return flatTokens.filter(predicate);
} }
return filterByPredicate(tokens, predicate); const result = filterByPredicate(tokens, predicate);
if (htmlFlow) {
return result;
}
return result.filter((token) => !inHtmlFlow(token));
} }
/** /**
@ -1589,22 +1628,6 @@ function getHtmlTagInfo(token) {
return null; return null;
} }
/**
* Gets the nearest parent of the specified type for a Micromark token.
*
* @param {Token} token Micromark token.
* @param {TokenType[]} types Types to allow.
* @returns {Token | null} Parent token.
*/
function getTokenParentOfType(token, types) {
/** @type {Token | null} */
let current = token;
while ((current = current.parent) && !types.includes(current.type)) {
// Empty
}
return current;
}
/** /**
* Get the text of the first match from a list of Micromark tokens by type. * Get the text of the first match from a list of Micromark tokens by type.
* *
@ -1617,16 +1640,6 @@ function getTokenTextByType(tokens, type) {
return (filtered.length > 0) ? filtered[0].text : null; return (filtered.length > 0) ? filtered[0].text : null;
} }
/**
* Determines if a Micromark token has an htmlFlow-type parent.
*
* @param {Token} token Micromark token.
* @returns {boolean} True iff the token has an htmlFlow-type parent.
*/
function inHtmlFlow(token) {
return getTokenParentOfType(token, [ "htmlFlow" ]) !== null;
}
/** /**
* Determines a list of Micromark tokens matches and returns a subset. * Determines a list of Micromark tokens matches and returns a subset.
* *
@ -3374,7 +3387,7 @@ module.exports = {
const { addErrorDetailIf } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js"); const { addErrorDetailIf } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
const { filterByTypes, getHeadingLevel, getHeadingStyle, inHtmlFlow } = const { filterByTypes, getHeadingLevel, getHeadingStyle } =
__webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs"); __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
// eslint-disable-next-line jsdoc/valid-types // eslint-disable-next-line jsdoc/valid-types
@ -3395,7 +3408,7 @@ module.exports = {
if (style === "consistent") { if (style === "consistent") {
style = styleForToken; style = styleForToken;
} }
if ((styleForToken !== style) && !inHtmlFlow(heading)) { if (styleForToken !== style) {
const h12 = getHeadingLevel(heading) <= 2; const h12 = getHeadingLevel(heading) <= 2;
const setextWithAtx = const setextWithAtx =
(style === "setext_with_atx") && (style === "setext_with_atx") &&
@ -3529,7 +3542,7 @@ module.exports = {
const { addError, addErrorDetailIf } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js"); const { addError, addErrorDetailIf } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
const { filterByTypes, inHtmlFlow } = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs"); const { filterByTypes } = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
// eslint-disable-next-line jsdoc/valid-types // eslint-disable-next-line jsdoc/valid-types
/** @type import("./markdownlint").Rule */ /** @type import("./markdownlint").Rule */
@ -3547,7 +3560,7 @@ module.exports = {
const lists = filterByTypes( const lists = filterByTypes(
micromarkTokens, micromarkTokens,
[ "listOrdered", "listUnordered" ] [ "listOrdered", "listUnordered" ]
).filter((list) => !inHtmlFlow(list)); );
for (const list of lists) { for (const list of lists) {
const expectedIndent = list.startColumn - 1; const expectedIndent = list.startColumn - 1;
let expectedEnd = 0; let expectedEnd = 0;
@ -3564,8 +3577,8 @@ module.exports = {
lineNumber, lineNumber,
expectedIndent, expectedIndent,
actualIndent, actualIndent,
null, undefined,
null, undefined,
range range
// No fixInfo; MD007 handles this scenario better // No fixInfo; MD007 handles this scenario better
); );
@ -3621,8 +3634,7 @@ module.exports = {
const { addErrorDetailIf } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js"); const { addErrorDetailIf } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
const { filterByTypes, getTokenParentOfType, inHtmlFlow } = const { filterByTypes, getTokenParentOfType } = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
__webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
// eslint-disable-next-line jsdoc/valid-types // eslint-disable-next-line jsdoc/valid-types
/** @type import("markdownlint-micromark").TokenType[] */ /** @type import("markdownlint-micromark").TokenType[] */
@ -3675,7 +3687,7 @@ module.exports = {
if (nesting >= 0) { if (nesting >= 0) {
unorderedListNesting.set(token, nesting); unorderedListNesting.set(token, nesting);
} }
} else if (!inHtmlFlow(token)) { } else {
// listItemPrefix // listItemPrefix
const nesting = unorderedListNesting.get(parent); const nesting = unorderedListNesting.get(parent);
if (nesting !== undefined) { if (nesting !== undefined) {
@ -4438,8 +4450,7 @@ module.exports = {
const { addErrorDetailIf, blockquotePrefixRe, isBlankLine } = const { addErrorDetailIf, blockquotePrefixRe, isBlankLine } =
__webpack_require__(/*! ../helpers */ "../helpers/helpers.js"); __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
const { filterByTypes, getHeadingLevel, inHtmlFlow } = const { filterByTypes, getHeadingLevel } = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
__webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
const defaultLines = 1; const defaultLines = 1;
@ -4484,7 +4495,7 @@ module.exports = {
const headings = filterByTypes( const headings = filterByTypes(
micromarkTokens, micromarkTokens,
[ "atxHeading", "setextHeading" ] [ "atxHeading", "setextHeading" ]
).filter((heading) => !inHtmlFlow(heading)); );
for (const heading of headings) { for (const heading of headings) {
const { startLine, endLine } = heading; const { startLine, endLine } = heading;
const line = lines[startLine - 1].trim(); const line = lines[startLine - 1].trim();
@ -4507,7 +4518,7 @@ module.exports = {
actualAbove, actualAbove,
"Above", "Above",
line, line,
null, undefined,
{ {
"insertText": getBlockQuote( "insertText": getBlockQuote(
lines[startLine - 2], lines[startLine - 2],
@ -4535,7 +4546,7 @@ module.exports = {
actualBelow, actualBelow,
"Below", "Below",
line, line,
null, undefined,
{ {
"lineNumber": endLine + 1, "lineNumber": endLine + 1,
"insertText": getBlockQuote( "insertText": getBlockQuote(
@ -4672,8 +4683,7 @@ module.exports = {
const { addErrorContext, frontMatterHasTitle } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js"); const { addErrorContext, frontMatterHasTitle } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
const { filterByTypes, getHeadingLevel, inHtmlFlow } = const { filterByTypes, getHeadingLevel } = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
__webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
// eslint-disable-next-line jsdoc/valid-types // eslint-disable-next-line jsdoc/valid-types
/** @type import("./markdownlint").Rule */ /** @type import("./markdownlint").Rule */
@ -4696,7 +4706,7 @@ module.exports = {
); );
for (const heading of headings) { for (const heading of headings) {
const headingLevel = getHeadingLevel(heading); const headingLevel = getHeadingLevel(heading);
if ((headingLevel === level) && !inHtmlFlow(heading)) { if (headingLevel === level) {
if (hasTopLevelHeading || foundFrontMatterTitle) { if (hasTopLevelHeading || foundFrontMatterTitle) {
const headingTexts = filterByTypes( const headingTexts = filterByTypes(
heading.children, heading.children,
@ -5223,7 +5233,7 @@ module.exports = {
const micromarkTokens = const micromarkTokens =
// @ts-ignore // @ts-ignore
params.parsers.micromark.tokens; params.parsers.micromark.tokens;
for (const token of filterByTypes(micromarkTokens, [ "htmlText" ])) { for (const token of filterByTypes(micromarkTokens, [ "htmlText" ], true)) {
const htmlTagInfo = getHtmlTagInfo(token); const htmlTagInfo = getHtmlTagInfo(token);
if ( if (
htmlTagInfo && htmlTagInfo &&
@ -5352,8 +5362,8 @@ module.exports = {
onError, onError,
token.startLine, token.startLine,
token.text, token.text,
null, undefined,
null, undefined,
range, range,
fixInfo fixInfo
); );
@ -5590,8 +5600,7 @@ module.exports = {
const { addErrorContext } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js"); const { addErrorContext } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
const { filterByTypes, inHtmlFlow, tokenIfType } = const { filterByTypes, tokenIfType } = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
__webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
const leftSpaceRe = /^\s(?:[^`]|$)/; const leftSpaceRe = /^\s(?:[^`]|$)/;
const rightSpaceRe = /[^`]\s$/; const rightSpaceRe = /[^`]\s$/;
@ -5619,8 +5628,7 @@ module.exports = {
const micromarkTokens = const micromarkTokens =
// @ts-ignore // @ts-ignore
params.parsers.micromark.tokens; params.parsers.micromark.tokens;
const codeTexts = filterByTypes(micromarkTokens, [ "codeText" ]) const codeTexts = filterByTypes(micromarkTokens, [ "codeText" ]);
.filter((codeText) => !inHtmlFlow(codeText));
for (const codeText of codeTexts) { for (const codeText of codeTexts) {
const { children } = codeText; const { children } = codeText;
const first = 0; const first = 0;
@ -5862,7 +5870,7 @@ module.exports = {
if ((token.type === "atxHeading") || (token.type === "setextHeading")) { if ((token.type === "atxHeading") || (token.type === "setextHeading")) {
isError = (getHeadingLevel(token) !== level); isError = (getHeadingLevel(token) !== level);
} else if (token.type === "htmlFlow") { } else if (token.type === "htmlFlow") {
const htmlTexts = filterByTypes(token.children, [ "htmlText" ]); const htmlTexts = filterByTypes(token.children, [ "htmlText" ], true);
const tagInfo = (htmlTexts.length > 0) && getHtmlTagInfo(htmlTexts[0]); const tagInfo = (htmlTexts.length > 0) && getHtmlTagInfo(htmlTexts[0]);
isError = !tagInfo || (tagInfo.name.toLowerCase() !== `h${level}`); isError = !tagInfo || (tagInfo.name.toLowerCase() !== `h${level}`);
} }
@ -6195,7 +6203,7 @@ module.exports = {
} }
// Process HTML images // Process HTML images
const htmlTexts = filterByTypes(micromarkTokens, [ "htmlText" ]); const htmlTexts = filterByTypes(micromarkTokens, [ "htmlText" ], true);
for (const htmlText of htmlTexts) { for (const htmlText of htmlTexts) {
const { startColumn, startLine, text } = htmlText; const { startColumn, startLine, text } = htmlText;
const htmlTagInfo = getHtmlTagInfo(htmlText); const htmlTagInfo = getHtmlTagInfo(htmlText);
@ -6596,7 +6604,7 @@ module.exports = {
} }
// Process HTML anchors // Process HTML anchors
for (const token of filterByTypes(micromarkTokens, [ "htmlText" ])) { for (const token of filterByTypes(micromarkTokens, [ "htmlText" ], true)) {
const htmlTagInfo = getHtmlTagInfo(token); const htmlTagInfo = getHtmlTagInfo(token);
if (htmlTagInfo && !htmlTagInfo.close) { if (htmlTagInfo && !htmlTagInfo.close) {
const anchorMatch = idRe.exec(token.text) || const anchorMatch = idRe.exec(token.text) ||

View file

@ -10,13 +10,40 @@ const {
} = require("markdownlint-micromark"); } = require("markdownlint-micromark");
const { newLineRe } = require("./shared.js"); const { newLineRe } = require("./shared.js");
const flatTokensSymbol = Symbol("flat-tokens"); const flatTokensWithHtmlFlowSymbol = Symbol("flat-tokens-with-html-flow");
const flatTokensWithoutHtmlFlowSymbol = Symbol("flat-tokens-without-html-flow");
/** @typedef {import("markdownlint-micromark").Event} Event */ /** @typedef {import("markdownlint-micromark").Event} Event */
/** @typedef {import("markdownlint-micromark").ParseOptions} ParseOptions */ /** @typedef {import("markdownlint-micromark").ParseOptions} ParseOptions */
/** @typedef {import("markdownlint-micromark").TokenType} TokenType */ /** @typedef {import("markdownlint-micromark").TokenType} TokenType */
/** @typedef {import("../lib/markdownlint.js").MicromarkToken} Token */ /** @typedef {import("../lib/markdownlint.js").MicromarkToken} Token */
/**
* Gets the nearest parent of the specified type for a Micromark token.
*
* @param {Token} token Micromark token.
* @param {TokenType[]} types Types to allow.
* @returns {Token | null} Parent token.
*/
function getTokenParentOfType(token, types) {
/** @type {Token | null} */
let current = token;
while ((current = current.parent) && !types.includes(current.type)) {
// Empty
}
return current;
}
/**
* Determines if a Micromark token has an htmlFlow-type parent.
*
* @param {Token} token Micromark token.
* @returns {boolean} True iff the token has an htmlFlow-type parent.
*/
function inHtmlFlow(token) {
return getTokenParentOfType(token, [ "htmlFlow" ]) !== null;
}
/** /**
* Returns whether a token is an htmlFlow type containing an HTML comment. * Returns whether a token is an htmlFlow type containing an HTML comment.
* *
@ -107,7 +134,8 @@ function micromarkParseWithOffset(
// Create Token objects // Create Token objects
const document = []; const document = [];
let flatTokens = []; let flatTokensWithHtmlFlow = [];
const flatTokensWithoutHtmlFlow = [];
/** @type {Token} */ /** @type {Token} */
const root = { const root = {
"type": "data", "type": "data",
@ -146,7 +174,8 @@ function micromarkParseWithOffset(
"parent": ((previous === root) ? (ancestor || null) : previous) "parent": ((previous === root) ? (ancestor || null) : previous)
}; };
previous.children.push(current); previous.children.push(current);
flatTokens.push(current); flatTokensWithHtmlFlow.push(current);
flatTokensWithoutHtmlFlow.push(current);
if ((current.type === "htmlFlow") && !isHtmlFlowComment(current)) { if ((current.type === "htmlFlow") && !isHtmlFlowComment(current)) {
skipHtmlFlowChildren = true; skipHtmlFlowChildren = true;
if (!reparseOptions || !lines) { if (!reparseOptions || !lines) {
@ -175,7 +204,7 @@ function micromarkParseWithOffset(
current.children = tokens; current.children = tokens;
// Avoid stack overflow of Array.push(...spread) // Avoid stack overflow of Array.push(...spread)
// eslint-disable-next-line unicorn/prefer-spread // eslint-disable-next-line unicorn/prefer-spread
flatTokens = flatTokens.concat(tokens[flatTokensSymbol]); flatTokensWithHtmlFlow = flatTokensWithHtmlFlow.concat(tokens[flatTokensWithHtmlFlowSymbol]);
} }
} else if (kind === "exit") { } else if (kind === "exit") {
if (type === "htmlFlow") { if (type === "htmlFlow") {
@ -191,7 +220,8 @@ function micromarkParseWithOffset(
} }
// Return document // Return document
Object.defineProperty(document, flatTokensSymbol, { "value": flatTokens }); Object.defineProperty(document, flatTokensWithHtmlFlowSymbol, { "value": flatTokensWithHtmlFlow });
Object.defineProperty(document, flatTokensWithoutHtmlFlowSymbol, { "value": flatTokensWithoutHtmlFlow });
Object.freeze(document); Object.freeze(document);
return document; return document;
} }
@ -277,15 +307,24 @@ function filterByPredicate(tokens, allowed, transformChildren) {
* *
* @param {Token[]} tokens Micromark tokens. * @param {Token[]} tokens Micromark tokens.
* @param {TokenType[]} types Types to allow. * @param {TokenType[]} types Types to allow.
* @param {boolean} [htmlFlow] Whether to include htmlFlow content.
* @returns {Token[]} Filtered tokens. * @returns {Token[]} Filtered tokens.
*/ */
function filterByTypes(tokens, types) { function filterByTypes(tokens, types, htmlFlow) {
const predicate = (token) => types.includes(token.type); const predicate = (token) => types.includes(token.type);
const flatTokens = tokens[flatTokensSymbol]; const flatTokens = tokens[
htmlFlow ?
flatTokensWithHtmlFlowSymbol :
flatTokensWithoutHtmlFlowSymbol
];
if (flatTokens) { if (flatTokens) {
return flatTokens.filter(predicate); return flatTokens.filter(predicate);
} }
return filterByPredicate(tokens, predicate); const result = filterByPredicate(tokens, predicate);
if (htmlFlow) {
return result;
}
return result.filter((token) => !inHtmlFlow(token));
} }
/** /**
@ -359,22 +398,6 @@ function getHtmlTagInfo(token) {
return null; return null;
} }
/**
* Gets the nearest parent of the specified type for a Micromark token.
*
* @param {Token} token Micromark token.
* @param {TokenType[]} types Types to allow.
* @returns {Token | null} Parent token.
*/
function getTokenParentOfType(token, types) {
/** @type {Token | null} */
let current = token;
while ((current = current.parent) && !types.includes(current.type)) {
// Empty
}
return current;
}
/** /**
* Get the text of the first match from a list of Micromark tokens by type. * Get the text of the first match from a list of Micromark tokens by type.
* *
@ -387,16 +410,6 @@ function getTokenTextByType(tokens, type) {
return (filtered.length > 0) ? filtered[0].text : null; return (filtered.length > 0) ? filtered[0].text : null;
} }
/**
* Determines if a Micromark token has an htmlFlow-type parent.
*
* @param {Token} token Micromark token.
* @returns {boolean} True iff the token has an htmlFlow-type parent.
*/
function inHtmlFlow(token) {
return getTokenParentOfType(token, [ "htmlFlow" ]) !== null;
}
/** /**
* Determines a list of Micromark tokens matches and returns a subset. * Determines a list of Micromark tokens matches and returns a subset.
* *

View file

@ -3,7 +3,7 @@
"use strict"; "use strict";
const { addErrorDetailIf } = require("../helpers"); const { addErrorDetailIf } = require("../helpers");
const { filterByTypes, getHeadingLevel, getHeadingStyle, inHtmlFlow } = const { filterByTypes, getHeadingLevel, getHeadingStyle } =
require("../helpers/micromark.cjs"); require("../helpers/micromark.cjs");
// eslint-disable-next-line jsdoc/valid-types // eslint-disable-next-line jsdoc/valid-types
@ -24,7 +24,7 @@ module.exports = {
if (style === "consistent") { if (style === "consistent") {
style = styleForToken; style = styleForToken;
} }
if ((styleForToken !== style) && !inHtmlFlow(heading)) { if (styleForToken !== style) {
const h12 = getHeadingLevel(heading) <= 2; const h12 = getHeadingLevel(heading) <= 2;
const setextWithAtx = const setextWithAtx =
(style === "setext_with_atx") && (style === "setext_with_atx") &&

View file

@ -3,7 +3,7 @@
"use strict"; "use strict";
const { addError, addErrorDetailIf } = require("../helpers"); const { addError, addErrorDetailIf } = require("../helpers");
const { filterByTypes, inHtmlFlow } = require("../helpers/micromark.cjs"); const { filterByTypes } = require("../helpers/micromark.cjs");
// eslint-disable-next-line jsdoc/valid-types // eslint-disable-next-line jsdoc/valid-types
/** @type import("./markdownlint").Rule */ /** @type import("./markdownlint").Rule */
@ -21,7 +21,7 @@ module.exports = {
const lists = filterByTypes( const lists = filterByTypes(
micromarkTokens, micromarkTokens,
[ "listOrdered", "listUnordered" ] [ "listOrdered", "listUnordered" ]
).filter((list) => !inHtmlFlow(list)); );
for (const list of lists) { for (const list of lists) {
const expectedIndent = list.startColumn - 1; const expectedIndent = list.startColumn - 1;
let expectedEnd = 0; let expectedEnd = 0;
@ -38,8 +38,8 @@ module.exports = {
lineNumber, lineNumber,
expectedIndent, expectedIndent,
actualIndent, actualIndent,
null, undefined,
null, undefined,
range range
// No fixInfo; MD007 handles this scenario better // No fixInfo; MD007 handles this scenario better
); );

View file

@ -3,8 +3,7 @@
"use strict"; "use strict";
const { addErrorDetailIf } = require("../helpers"); const { addErrorDetailIf } = require("../helpers");
const { filterByTypes, getTokenParentOfType, inHtmlFlow } = const { filterByTypes, getTokenParentOfType } = require("../helpers/micromark.cjs");
require("../helpers/micromark.cjs");
// eslint-disable-next-line jsdoc/valid-types // eslint-disable-next-line jsdoc/valid-types
/** @type import("markdownlint-micromark").TokenType[] */ /** @type import("markdownlint-micromark").TokenType[] */
@ -57,7 +56,7 @@ module.exports = {
if (nesting >= 0) { if (nesting >= 0) {
unorderedListNesting.set(token, nesting); unorderedListNesting.set(token, nesting);
} }
} else if (!inHtmlFlow(token)) { } else {
// listItemPrefix // listItemPrefix
const nesting = unorderedListNesting.get(parent); const nesting = unorderedListNesting.get(parent);
if (nesting !== undefined) { if (nesting !== undefined) {

View file

@ -4,8 +4,7 @@
const { addErrorDetailIf, blockquotePrefixRe, isBlankLine } = const { addErrorDetailIf, blockquotePrefixRe, isBlankLine } =
require("../helpers"); require("../helpers");
const { filterByTypes, getHeadingLevel, inHtmlFlow } = const { filterByTypes, getHeadingLevel } = require("../helpers/micromark.cjs");
require("../helpers/micromark.cjs");
const defaultLines = 1; const defaultLines = 1;
@ -50,7 +49,7 @@ module.exports = {
const headings = filterByTypes( const headings = filterByTypes(
micromarkTokens, micromarkTokens,
[ "atxHeading", "setextHeading" ] [ "atxHeading", "setextHeading" ]
).filter((heading) => !inHtmlFlow(heading)); );
for (const heading of headings) { for (const heading of headings) {
const { startLine, endLine } = heading; const { startLine, endLine } = heading;
const line = lines[startLine - 1].trim(); const line = lines[startLine - 1].trim();
@ -73,7 +72,7 @@ module.exports = {
actualAbove, actualAbove,
"Above", "Above",
line, line,
null, undefined,
{ {
"insertText": getBlockQuote( "insertText": getBlockQuote(
lines[startLine - 2], lines[startLine - 2],
@ -101,7 +100,7 @@ module.exports = {
actualBelow, actualBelow,
"Below", "Below",
line, line,
null, undefined,
{ {
"lineNumber": endLine + 1, "lineNumber": endLine + 1,
"insertText": getBlockQuote( "insertText": getBlockQuote(

View file

@ -3,8 +3,7 @@
"use strict"; "use strict";
const { addErrorContext, frontMatterHasTitle } = require("../helpers"); const { addErrorContext, frontMatterHasTitle } = require("../helpers");
const { filterByTypes, getHeadingLevel, inHtmlFlow } = const { filterByTypes, getHeadingLevel } = require("../helpers/micromark.cjs");
require("../helpers/micromark.cjs");
// eslint-disable-next-line jsdoc/valid-types // eslint-disable-next-line jsdoc/valid-types
/** @type import("./markdownlint").Rule */ /** @type import("./markdownlint").Rule */
@ -27,7 +26,7 @@ module.exports = {
); );
for (const heading of headings) { for (const heading of headings) {
const headingLevel = getHeadingLevel(heading); const headingLevel = getHeadingLevel(heading);
if ((headingLevel === level) && !inHtmlFlow(heading)) { if (headingLevel === level) {
if (hasTopLevelHeading || foundFrontMatterTitle) { if (hasTopLevelHeading || foundFrontMatterTitle) {
const headingTexts = filterByTypes( const headingTexts = filterByTypes(
heading.children, heading.children,

View file

@ -22,7 +22,7 @@ module.exports = {
const micromarkTokens = const micromarkTokens =
// @ts-ignore // @ts-ignore
params.parsers.micromark.tokens; params.parsers.micromark.tokens;
for (const token of filterByTypes(micromarkTokens, [ "htmlText" ])) { for (const token of filterByTypes(micromarkTokens, [ "htmlText" ], true)) {
const htmlTagInfo = getHtmlTagInfo(token); const htmlTagInfo = getHtmlTagInfo(token);
if ( if (
htmlTagInfo && htmlTagInfo &&

View file

@ -94,8 +94,8 @@ module.exports = {
onError, onError,
token.startLine, token.startLine,
token.text, token.text,
null, undefined,
null, undefined,
range, range,
fixInfo fixInfo
); );

View file

@ -3,8 +3,7 @@
"use strict"; "use strict";
const { addErrorContext } = require("../helpers"); const { addErrorContext } = require("../helpers");
const { filterByTypes, inHtmlFlow, tokenIfType } = const { filterByTypes, tokenIfType } = require("../helpers/micromark.cjs");
require("../helpers/micromark.cjs");
const leftSpaceRe = /^\s(?:[^`]|$)/; const leftSpaceRe = /^\s(?:[^`]|$)/;
const rightSpaceRe = /[^`]\s$/; const rightSpaceRe = /[^`]\s$/;
@ -32,8 +31,7 @@ module.exports = {
const micromarkTokens = const micromarkTokens =
// @ts-ignore // @ts-ignore
params.parsers.micromark.tokens; params.parsers.micromark.tokens;
const codeTexts = filterByTypes(micromarkTokens, [ "codeText" ]) const codeTexts = filterByTypes(micromarkTokens, [ "codeText" ]);
.filter((codeText) => !inHtmlFlow(codeText));
for (const codeText of codeTexts) { for (const codeText of codeTexts) {
const { children } = codeText; const { children } = codeText;
const first = 0; const first = 0;

View file

@ -23,7 +23,7 @@ module.exports = {
if ((token.type === "atxHeading") || (token.type === "setextHeading")) { if ((token.type === "atxHeading") || (token.type === "setextHeading")) {
isError = (getHeadingLevel(token) !== level); isError = (getHeadingLevel(token) !== level);
} else if (token.type === "htmlFlow") { } else if (token.type === "htmlFlow") {
const htmlTexts = filterByTypes(token.children, [ "htmlText" ]); const htmlTexts = filterByTypes(token.children, [ "htmlText" ], true);
const tagInfo = (htmlTexts.length > 0) && getHtmlTagInfo(htmlTexts[0]); const tagInfo = (htmlTexts.length > 0) && getHtmlTagInfo(htmlTexts[0]);
isError = !tagInfo || (tagInfo.name.toLowerCase() !== `h${level}`); isError = !tagInfo || (tagInfo.name.toLowerCase() !== `h${level}`);
} }

View file

@ -40,7 +40,7 @@ module.exports = {
} }
// Process HTML images // Process HTML images
const htmlTexts = filterByTypes(micromarkTokens, [ "htmlText" ]); const htmlTexts = filterByTypes(micromarkTokens, [ "htmlText" ], true);
for (const htmlText of htmlTexts) { for (const htmlText of htmlTexts) {
const { startColumn, startLine, text } = htmlText; const { startColumn, startLine, text } = htmlText;
const htmlTagInfo = getHtmlTagInfo(htmlText); const htmlTagInfo = getHtmlTagInfo(htmlText);

View file

@ -97,7 +97,7 @@ module.exports = {
} }
// Process HTML anchors // Process HTML anchors
for (const token of filterByTypes(micromarkTokens, [ "htmlText" ])) { for (const token of filterByTypes(micromarkTokens, [ "htmlText" ], true)) {
const htmlTagInfo = getHtmlTagInfo(token); const htmlTagInfo = getHtmlTagInfo(token);
if (htmlTagInfo && !htmlTagInfo.close) { if (htmlTagInfo && !htmlTagInfo.close) {
const anchorMatch = idRe.exec(token.text) || const anchorMatch = idRe.exec(token.text) ||

View file

@ -5,3 +5,9 @@
## Heading 2 ## Heading 2
#### Heading 4 {MD001} #### Heading 4 {MD001}
<p>
###### Not heading
</p>
<!-- markdownlint-disable-file no-inline-html -->

View file

@ -63,15 +63,40 @@ test("getMicromarkEvents/filterByPredicate", async(t) => {
t.deepEqual(tokenTypes, eventTypes); t.deepEqual(tokenTypes, eventTypes);
}); });
test("filterByTypes", async(t) => { test("filterByTypes, htmlFlow false", async(t) => {
t.plan(8); t.plan(7);
const filtered = filterByTypes( const tokens = await testTokens;
await testTokens, // eslint-disable-next-line jsdoc/valid-types
[ "atxHeadingText", "codeText", "htmlText", "setextHeadingText" ] /** @type import("../micromark/micromark.cjs").TokenType[] */
); const types = [ "atxHeadingText", "codeText", "htmlText", "setextHeadingText" ];
const filtered = filterByTypes(tokens, types);
// Using flat tokens
for (const token of filtered) { for (const token of filtered) {
t.true(token.type.endsWith("Text")); t.true(token.type.endsWith("Text"));
} }
// Not using flat tokens
t.deepEqual(
filtered,
filterByTypes([ ...tokens], types)
)
});
test("filterByTypes, htmlFlow true", async(t) => {
t.plan(9);
const tokens = await testTokens;
// eslint-disable-next-line jsdoc/valid-types
/** @type import("../micromark/micromark.cjs").TokenType[] */
const types = [ "atxHeadingText", "codeText", "htmlText", "setextHeadingText" ];
// Using flat tokens
const filtered = filterByTypes(tokens, types, true);
for (const token of filtered) {
t.true(token.type.endsWith("Text"));
}
// Not using flat tokens
t.deepEqual(
filtered,
filterByTypes([ ...tokens], types, true)
)
}); });
test("filterByPredicate/filterByTypes", async(t) => { test("filterByPredicate/filterByTypes", async(t) => {
@ -79,6 +104,6 @@ test("filterByPredicate/filterByTypes", async(t) => {
const tokens = await testTokens; const tokens = await testTokens;
const byPredicate = filterByPredicate(tokens, () => true); const byPredicate = filterByPredicate(tokens, () => true);
const allTypes = new Set(byPredicate.map(((token) => token.type))); const allTypes = new Set(byPredicate.map(((token) => token.type)));
const byTypes = filterByTypes(tokens, [ ...allTypes.values() ]); const byTypes = filterByTypes(tokens, [ ...allTypes.values() ], true);
t.deepEqual(byPredicate, byTypes); t.deepEqual(byPredicate, byTypes);
}); });

View file

@ -64,6 +64,10 @@ Uppercase image tag with alt attribute set
Uppercase image tag with no alt set <IMG SRC="cat.png" /> {MD045} Uppercase image tag with no alt set <IMG SRC="cat.png" /> {MD045}
<p>
<img src="image.png" /> {MD045}
</p>
<!-- markdownlint-restore no-inline-html --> <!-- markdownlint-restore no-inline-html -->
[notitle]: image.jpg [notitle]: image.jpg

View file

@ -16101,6 +16101,12 @@ Generated by [AVA](https://avajs.dev).
## Heading 2␊ ## Heading 2␊
#### Heading 4 {MD001}␊ #### Heading 4 {MD001}␊
<p>
###### Not heading␊
</p>
<!-- markdownlint-disable-file no-inline-html -->
`, `,
} }
@ -39577,6 +39583,22 @@ Generated by [AVA](https://avajs.dev).
'no-alt-text', 'no-alt-text',
], ],
}, },
{
errorContext: null,
errorDetail: null,
errorRange: [
3,
23,
],
fixInfo: null,
lineNumber: 68,
ruleDescription: 'Images should have alternate text (alt text)',
ruleInformation: 'https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/md045.md',
ruleNames: [
'MD045',
'no-alt-text',
],
},
], ],
fixed: `# Images with and without alternate text␊ fixed: `# Images with and without alternate text␊
@ -39644,6 +39666,10 @@ Generated by [AVA](https://avajs.dev).
Uppercase image tag with no alt set <IMG SRC="cat.png" /> {MD045}␊ Uppercase image tag with no alt set <IMG SRC="cat.png" /> {MD045}␊
<p>
<img src="image.png" /> {MD045}␊
</p>
<!-- markdownlint-restore no-inline-html --> <!-- markdownlint-restore no-inline-html -->
[notitle]: image.jpg␊ [notitle]: image.jpg␊