Refactor to remove micromark helper getTokenTextByType, update getDescendantsByType to allow sub-arrays.

This commit is contained in:
David Anson 2024-09-24 22:48:14 -07:00
parent 0f210d5c1a
commit 8cbbed8e79
7 changed files with 44 additions and 94 deletions

View file

@ -463,15 +463,10 @@ function getReferenceLinkImageData(tokens) {
if (definitions.has(reference)) { if (definitions.has(reference)) {
duplicateDefinitions.push([ reference, token.startLine - 1 ]); duplicateDefinitions.push([ reference, token.startLine - 1 ]);
} else { } else {
let destinationString = null;
const parent = const parent =
micromark.getTokenParentOfType(token, [ "definition" ]); micromark.getTokenParentOfType(token, [ "definition" ]);
if (parent) { const destinationString = parent &&
destinationString = micromark.getTokenTextByType( micromark.getDescendantsByType(parent, [ "definitionDestination", "definitionDestinationRaw", "definitionDestinationString" ])[0]?.text;
micromark.filterByPredicate(parent.children),
"definitionDestinationString"
);
}
definitions.set( definitions.set(
reference, reference,
[ token.startLine - 1, destinationString ] [ token.startLine - 1, destinationString ]
@ -1180,13 +1175,15 @@ function filterByTypes(tokens, types, htmlFlow) {
* Gets a list of nested Micromark token descendants by type path. * Gets a list of nested Micromark token descendants by type path.
* *
* @param {Token|Token[]} parent Micromark token parent or parents. * @param {Token|Token[]} parent Micromark token parent or parents.
* @param {TokenType[]} typePath Micromark token type path. * @param {(TokenType|TokenType[])[]} typePath Micromark token type path.
* @returns {Token[]} Micromark token descendants. * @returns {Token[]} Micromark token descendants.
*/ */
function getDescendantsByType(parent, typePath) { function getDescendantsByType(parent, typePath) {
let tokens = Array.isArray(parent) ? parent : [ parent ]; let tokens = Array.isArray(parent) ? parent : [ parent ];
for (const type of typePath) { for (const type of typePath) {
tokens = tokens.flatMap((t) => t.children).filter((t) => t.type === type); tokens = tokens
.flatMap((t) => t.children)
.filter((t) => Array.isArray(type) ? type.includes(t.type) : (type === t.type));
} }
return tokens; return tokens;
} }
@ -1317,22 +1314,6 @@ function getTokenParentOfType(token, types) {
return current; return current;
} }
/**
* Get the text of the first match from a list of Micromark tokens by type.
*
* @param {Token[]} tokens Micromark tokens.
* @param {TokenType} type Type to match.
* @returns {string | null} Text of token.
*/
function getTokenTextByType(tokens, type) {
for (const token of tokens) {
if (token.type === type) {
return token.text;
}
}
return null;
}
/** /**
* Set containing token types that do not contain content. * Set containing token types that do not contain content.
* *
@ -1361,7 +1342,6 @@ module.exports = {
getHtmlTagInfo, getHtmlTagInfo,
getMicromarkEvents, getMicromarkEvents,
getTokenParentOfType, getTokenParentOfType,
getTokenTextByType,
inHtmlFlow, inHtmlFlow,
isHtmlFlowComment, isHtmlFlowComment,
nonContentTokens nonContentTokens
@ -4711,7 +4691,7 @@ module.exports = {
const { addErrorDetailIf } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js"); const { addErrorDetailIf } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
const { getDescendantsByType, getTokenTextByType } = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs"); const { getDescendantsByType } = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
const { filterByTypesCached } = __webpack_require__(/*! ./cache */ "../lib/cache.js"); const { filterByTypesCached } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
const listStyleExamples = { const listStyleExamples = {
@ -4727,7 +4707,7 @@ const listStyleExamples = {
* @returns {number} List item value. * @returns {number} List item value.
*/ */
function getOrderedListItemValue(listItemPrefix) { function getOrderedListItemValue(listItemPrefix) {
return Number(getTokenTextByType(listItemPrefix.children, "listItemValue")); return Number(getDescendantsByType(listItemPrefix, [ "listItemValue" ])[0].text);
} }
// eslint-disable-next-line jsdoc/valid-types // eslint-disable-next-line jsdoc/valid-types
@ -5583,7 +5563,7 @@ module.exports = {
const { addError, addErrorContext } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js"); const { addError, addErrorContext } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
const { getDescendantsByType, getTokenTextByType } = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs"); const { getDescendantsByType } = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
const { filterByTypesCached } = __webpack_require__(/*! ./cache */ "../lib/cache.js"); const { filterByTypesCached } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
// eslint-disable-next-line jsdoc/valid-types // eslint-disable-next-line jsdoc/valid-types
@ -5600,14 +5580,14 @@ module.exports = {
const fencedCodes = filterByTypesCached([ "codeFenced" ]); const fencedCodes = filterByTypesCached([ "codeFenced" ]);
for (const fencedCode of fencedCodes) { for (const fencedCode of fencedCodes) {
const openingFence = getDescendantsByType(fencedCode, [ "codeFencedFence" ])[0]; const openingFence = getDescendantsByType(fencedCode, [ "codeFencedFence" ])[0];
const { children, startLine, text } = openingFence; const { startLine, text } = openingFence;
const info = getTokenTextByType(children, "codeFencedFenceInfo"); const info = getDescendantsByType(openingFence, [ "codeFencedFenceInfo" ])[0]?.text;
if (!info) { if (!info) {
addErrorContext(onError, startLine, text); addErrorContext(onError, startLine, text);
} else if ((allowed.length > 0) && !allowed.includes(info)) { } else if ((allowed.length > 0) && !allowed.includes(info)) {
addError(onError, startLine, `"${info}" is not allowed`); addError(onError, startLine, `"${info}" is not allowed`);
} }
if (languageOnly && getTokenTextByType(children, "codeFencedFenceMeta")) { if (languageOnly && getDescendantsByType(openingFence, [ "codeFencedFenceMeta" ]).length > 0) {
addError(onError, startLine, `Info string contains more than language: "${text}"`); addError(onError, startLine, `Info string contains more than language: "${text}"`);
} }
} }
@ -5699,11 +5679,7 @@ module.exports = {
const reference = getDescendantsByType(link, [ "reference" ]); const reference = getDescendantsByType(link, [ "reference" ]);
const resource = getDescendantsByType(link, [ "resource" ]); const resource = getDescendantsByType(link, [ "resource" ]);
const referenceString = getDescendantsByType(reference, [ "referenceString" ]); const referenceString = getDescendantsByType(reference, [ "referenceString" ]);
const resourceDestination = getDescendantsByType(resource, [ "resourceDestination" ]); const resourceDestinationString = getDescendantsByType(resource, [ "resourceDestination", [ "resourceDestinationLiteral", "resourceDestinationRaw" ], "resourceDestinationString" ]);
const resourceDestinationString = [
...getDescendantsByType(resourceDestination, [ "resourceDestinationRaw", "resourceDestinationString" ]),
...getDescendantsByType(resourceDestination, [ "resourceDestinationLiteral", "resourceDestinationString" ])
];
const hasLabelText = labelText.length > 0; const hasLabelText = labelText.length > 0;
const hasReference = reference.length > 0; const hasReference = reference.length > 0;
const hasResource = resource.length > 0; const hasResource = resource.length > 0;
@ -6583,7 +6559,7 @@ module.exports = {
const { addErrorContext, nextLinesRe } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js"); const { addErrorContext, nextLinesRe } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
const { filterByPredicate, getTokenTextByType } = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs"); const { getDescendantsByType } = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
const { getReferenceLinkImageData, filterByTypesCached } = __webpack_require__(/*! ./cache */ "../lib/cache.js"); const { getReferenceLinkImageData, filterByTypesCached } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
const backslashEscapeRe = /\\([!"#$%&'()*+,\-./:;<=>?@[\\\]^_`{|}~])/g; const backslashEscapeRe = /\\([!"#$%&'()*+,\-./:;<=>?@[\\\]^_`{|}~])/g;
@ -6625,24 +6601,23 @@ module.exports = {
let label = null; let label = null;
let destination = null; let destination = null;
const { const {
children, endColumn, endLine, startColumn, startLine, text, type endColumn, endLine, startColumn, startLine, text, type
} = link; } = link;
const image = (type === "image"); const image = (type === "image");
let isError = false; let isError = false;
if (type === "autolink") { if (type === "autolink") {
// link kind is an autolink // link kind is an autolink
destination = getTokenTextByType(children, "autolinkProtocol"); destination = getDescendantsByType(link, [ "autolinkProtocol" ])[0].text;
label = destination; label = destination;
isError = !autolink; isError = !autolink;
} else { } else {
// link type is "image" or "link" // link type is "image" or "link"
const descendents = filterByPredicate(children); label = getDescendantsByType(link, [ "label", "labelText" ])[0].text;
label = getTokenTextByType(descendents, "labelText");
destination = destination =
getTokenTextByType(descendents, "resourceDestinationString"); getDescendantsByType(link, [ "resource", "resourceDestination", [ "resourceDestinationLiteral", "resourceDestinationRaw" ], "resourceDestinationString" ])[0]?.text;
if (destination) { if (destination) {
// link kind is an inline link // link kind is an inline link
const title = getTokenTextByType(descendents, "resourceTitleString"); const title = getDescendantsByType(link, [ "resource", "resourceTitle", "resourceTitleString" ])[0]?.text;
isError = !inline || ( isError = !inline || (
!urlInline && !urlInline &&
autolink && autolink &&
@ -6653,9 +6628,9 @@ module.exports = {
); );
} 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 = getDescendantsByType(link, [ "reference" ]).length === 0;
const referenceString = getTokenTextByType(descendents, "referenceString"); const referenceString = getDescendantsByType(link, [ "reference", "referenceString" ])[0]?.text;
const isCollapsed = (referenceString === null); const isCollapsed = (referenceString === undefined);
const definition = definitions.get(referenceString || label); const definition = definitions.get(referenceString || label);
destination = definition && definition[1]; destination = definition && definition[1];
isError = destination && isError = destination &&

View file

@ -451,15 +451,10 @@ function getReferenceLinkImageData(tokens) {
if (definitions.has(reference)) { if (definitions.has(reference)) {
duplicateDefinitions.push([ reference, token.startLine - 1 ]); duplicateDefinitions.push([ reference, token.startLine - 1 ]);
} else { } else {
let destinationString = null;
const parent = const parent =
micromark.getTokenParentOfType(token, [ "definition" ]); micromark.getTokenParentOfType(token, [ "definition" ]);
if (parent) { const destinationString = parent &&
destinationString = micromark.getTokenTextByType( micromark.getDescendantsByType(parent, [ "definitionDestination", "definitionDestinationRaw", "definitionDestinationString" ])[0]?.text;
micromark.filterByPredicate(parent.children),
"definitionDestinationString"
);
}
definitions.set( definitions.set(
reference, reference,
[ token.startLine - 1, destinationString ] [ token.startLine - 1, destinationString ]

View file

@ -322,13 +322,15 @@ function filterByTypes(tokens, types, htmlFlow) {
* Gets a list of nested Micromark token descendants by type path. * Gets a list of nested Micromark token descendants by type path.
* *
* @param {Token|Token[]} parent Micromark token parent or parents. * @param {Token|Token[]} parent Micromark token parent or parents.
* @param {TokenType[]} typePath Micromark token type path. * @param {(TokenType|TokenType[])[]} typePath Micromark token type path.
* @returns {Token[]} Micromark token descendants. * @returns {Token[]} Micromark token descendants.
*/ */
function getDescendantsByType(parent, typePath) { function getDescendantsByType(parent, typePath) {
let tokens = Array.isArray(parent) ? parent : [ parent ]; let tokens = Array.isArray(parent) ? parent : [ parent ];
for (const type of typePath) { for (const type of typePath) {
tokens = tokens.flatMap((t) => t.children).filter((t) => t.type === type); tokens = tokens
.flatMap((t) => t.children)
.filter((t) => Array.isArray(type) ? type.includes(t.type) : (type === t.type));
} }
return tokens; return tokens;
} }
@ -459,22 +461,6 @@ function getTokenParentOfType(token, types) {
return current; return current;
} }
/**
* Get the text of the first match from a list of Micromark tokens by type.
*
* @param {Token[]} tokens Micromark tokens.
* @param {TokenType} type Type to match.
* @returns {string | null} Text of token.
*/
function getTokenTextByType(tokens, type) {
for (const token of tokens) {
if (token.type === type) {
return token.text;
}
}
return null;
}
/** /**
* Set containing token types that do not contain content. * Set containing token types that do not contain content.
* *
@ -503,7 +489,6 @@ module.exports = {
getHtmlTagInfo, getHtmlTagInfo,
getMicromarkEvents, getMicromarkEvents,
getTokenParentOfType, getTokenParentOfType,
getTokenTextByType,
inHtmlFlow, inHtmlFlow,
isHtmlFlowComment, isHtmlFlowComment,
nonContentTokens nonContentTokens

View file

@ -3,7 +3,7 @@
"use strict"; "use strict";
const { addErrorDetailIf } = require("../helpers"); const { addErrorDetailIf } = require("../helpers");
const { getDescendantsByType, getTokenTextByType } = require("../helpers/micromark.cjs"); const { getDescendantsByType } = require("../helpers/micromark.cjs");
const { filterByTypesCached } = require("./cache"); const { filterByTypesCached } = require("./cache");
const listStyleExamples = { const listStyleExamples = {
@ -19,7 +19,7 @@ const listStyleExamples = {
* @returns {number} List item value. * @returns {number} List item value.
*/ */
function getOrderedListItemValue(listItemPrefix) { function getOrderedListItemValue(listItemPrefix) {
return Number(getTokenTextByType(listItemPrefix.children, "listItemValue")); return Number(getDescendantsByType(listItemPrefix, [ "listItemValue" ])[0].text);
} }
// eslint-disable-next-line jsdoc/valid-types // eslint-disable-next-line jsdoc/valid-types

View file

@ -3,7 +3,7 @@
"use strict"; "use strict";
const { addError, addErrorContext } = require("../helpers"); const { addError, addErrorContext } = require("../helpers");
const { getDescendantsByType, getTokenTextByType } = require("../helpers/micromark.cjs"); const { getDescendantsByType } = require("../helpers/micromark.cjs");
const { filterByTypesCached } = require("./cache"); const { filterByTypesCached } = require("./cache");
// eslint-disable-next-line jsdoc/valid-types // eslint-disable-next-line jsdoc/valid-types
@ -20,14 +20,14 @@ module.exports = {
const fencedCodes = filterByTypesCached([ "codeFenced" ]); const fencedCodes = filterByTypesCached([ "codeFenced" ]);
for (const fencedCode of fencedCodes) { for (const fencedCode of fencedCodes) {
const openingFence = getDescendantsByType(fencedCode, [ "codeFencedFence" ])[0]; const openingFence = getDescendantsByType(fencedCode, [ "codeFencedFence" ])[0];
const { children, startLine, text } = openingFence; const { startLine, text } = openingFence;
const info = getTokenTextByType(children, "codeFencedFenceInfo"); const info = getDescendantsByType(openingFence, [ "codeFencedFenceInfo" ])[0]?.text;
if (!info) { if (!info) {
addErrorContext(onError, startLine, text); addErrorContext(onError, startLine, text);
} else if ((allowed.length > 0) && !allowed.includes(info)) { } else if ((allowed.length > 0) && !allowed.includes(info)) {
addError(onError, startLine, `"${info}" is not allowed`); addError(onError, startLine, `"${info}" is not allowed`);
} }
if (languageOnly && getTokenTextByType(children, "codeFencedFenceMeta")) { if (languageOnly && getDescendantsByType(openingFence, [ "codeFencedFenceMeta" ]).length > 0) {
addError(onError, startLine, `Info string contains more than language: "${text}"`); addError(onError, startLine, `Info string contains more than language: "${text}"`);
} }
} }

View file

@ -25,11 +25,7 @@ module.exports = {
const reference = getDescendantsByType(link, [ "reference" ]); const reference = getDescendantsByType(link, [ "reference" ]);
const resource = getDescendantsByType(link, [ "resource" ]); const resource = getDescendantsByType(link, [ "resource" ]);
const referenceString = getDescendantsByType(reference, [ "referenceString" ]); const referenceString = getDescendantsByType(reference, [ "referenceString" ]);
const resourceDestination = getDescendantsByType(resource, [ "resourceDestination" ]); const resourceDestinationString = getDescendantsByType(resource, [ "resourceDestination", [ "resourceDestinationLiteral", "resourceDestinationRaw" ], "resourceDestinationString" ]);
const resourceDestinationString = [
...getDescendantsByType(resourceDestination, [ "resourceDestinationRaw", "resourceDestinationString" ]),
...getDescendantsByType(resourceDestination, [ "resourceDestinationLiteral", "resourceDestinationString" ])
];
const hasLabelText = labelText.length > 0; const hasLabelText = labelText.length > 0;
const hasReference = reference.length > 0; const hasReference = reference.length > 0;
const hasResource = resource.length > 0; const hasResource = resource.length > 0;

View file

@ -3,7 +3,7 @@
"use strict"; "use strict";
const { addErrorContext, nextLinesRe } = require("../helpers"); const { addErrorContext, nextLinesRe } = require("../helpers");
const { filterByPredicate, getTokenTextByType } = require("../helpers/micromark.cjs"); const { getDescendantsByType } = require("../helpers/micromark.cjs");
const { getReferenceLinkImageData, filterByTypesCached } = require("./cache"); const { getReferenceLinkImageData, filterByTypesCached } = require("./cache");
const backslashEscapeRe = /\\([!"#$%&'()*+,\-./:;<=>?@[\\\]^_`{|}~])/g; const backslashEscapeRe = /\\([!"#$%&'()*+,\-./:;<=>?@[\\\]^_`{|}~])/g;
@ -45,24 +45,23 @@ module.exports = {
let label = null; let label = null;
let destination = null; let destination = null;
const { const {
children, endColumn, endLine, startColumn, startLine, text, type endColumn, endLine, startColumn, startLine, text, type
} = link; } = link;
const image = (type === "image"); const image = (type === "image");
let isError = false; let isError = false;
if (type === "autolink") { if (type === "autolink") {
// link kind is an autolink // link kind is an autolink
destination = getTokenTextByType(children, "autolinkProtocol"); destination = getDescendantsByType(link, [ "autolinkProtocol" ])[0].text;
label = destination; label = destination;
isError = !autolink; isError = !autolink;
} else { } else {
// link type is "image" or "link" // link type is "image" or "link"
const descendents = filterByPredicate(children); label = getDescendantsByType(link, [ "label", "labelText" ])[0].text;
label = getTokenTextByType(descendents, "labelText");
destination = destination =
getTokenTextByType(descendents, "resourceDestinationString"); getDescendantsByType(link, [ "resource", "resourceDestination", [ "resourceDestinationLiteral", "resourceDestinationRaw" ], "resourceDestinationString" ])[0]?.text;
if (destination) { if (destination) {
// link kind is an inline link // link kind is an inline link
const title = getTokenTextByType(descendents, "resourceTitleString"); const title = getDescendantsByType(link, [ "resource", "resourceTitle", "resourceTitleString" ])[0]?.text;
isError = !inline || ( isError = !inline || (
!urlInline && !urlInline &&
autolink && autolink &&
@ -73,9 +72,9 @@ module.exports = {
); );
} 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 = getDescendantsByType(link, [ "reference" ]).length === 0;
const referenceString = getTokenTextByType(descendents, "referenceString"); const referenceString = getDescendantsByType(link, [ "reference", "referenceString" ])[0]?.text;
const isCollapsed = (referenceString === null); const isCollapsed = (referenceString === undefined);
const definition = definitions.get(referenceString || label); const definition = definitions.get(referenceString || label);
destination = definition && definition[1]; destination = definition && definition[1];
isError = destination && isError = destination &&