2023-01-12 21:20:54 -08:00
|
|
|
// @ts-check
|
|
|
|
|
2023-01-14 15:30:47 -08:00
|
|
|
"use strict";
|
2023-01-14 15:05:04 -08:00
|
|
|
|
|
|
|
// @ts-ignore
|
2023-02-23 22:14:44 -08:00
|
|
|
const {
|
2024-03-04 21:38:18 -08:00
|
|
|
directive, gfmAutolinkLiteral, gfmFootnote, gfmTable, math,
|
|
|
|
parse, postprocess, preprocess
|
2023-02-08 20:50:28 -08:00
|
|
|
// @ts-ignore
|
2023-02-23 22:14:44 -08:00
|
|
|
} = require("markdownlint-micromark");
|
2023-07-21 22:49:08 -07:00
|
|
|
const { newLineRe } = require("./shared.js");
|
2023-01-09 21:59:54 -08:00
|
|
|
|
2023-08-28 22:00:43 -07:00
|
|
|
const flatTokensSymbol = Symbol("flat-tokens");
|
|
|
|
|
2024-03-04 21:38:18 -08:00
|
|
|
/** @typedef {import("markdownlint-micromark").Event} Event */
|
|
|
|
/** @typedef {import("markdownlint-micromark").ParseOptions} ParseOptions */
|
|
|
|
/** @typedef {import("markdownlint-micromark").TokenType} TokenType */
|
2024-03-09 16:17:50 -08:00
|
|
|
/** @typedef {import("../lib/markdownlint.js").MicromarkToken} Token */
|
2023-01-09 21:59:54 -08:00
|
|
|
|
2023-09-02 12:07:14 -07:00
|
|
|
/**
|
|
|
|
* Returns whether a token is an htmlFlow type containing an HTML comment.
|
|
|
|
*
|
|
|
|
* @param {Token} token Micromark token.
|
|
|
|
* @returns {boolean} True iff token is htmlFlow containing a comment.
|
|
|
|
*/
|
|
|
|
function isHtmlFlowComment(token) {
|
|
|
|
const { text, type } = token;
|
|
|
|
if (
|
|
|
|
(type === "htmlFlow") &&
|
|
|
|
text.startsWith("<!--") &&
|
|
|
|
text.endsWith("-->")
|
|
|
|
) {
|
|
|
|
const comment = text.slice(4, -3);
|
|
|
|
return (
|
|
|
|
!comment.startsWith(">") &&
|
|
|
|
!comment.startsWith("->") &&
|
2023-10-07 20:18:43 -07:00
|
|
|
!comment.endsWith("-")
|
|
|
|
// The following condition from the CommonMark specification is commented
|
|
|
|
// to avoid parsing HTML comments that include "--" because that is NOT a
|
|
|
|
// condition of the HTML specification.
|
|
|
|
// https://spec.commonmark.org/0.30/#raw-html
|
|
|
|
// https://html.spec.whatwg.org/multipage/syntax.html#comments
|
|
|
|
// && !comment.includes("--")
|
2023-09-02 12:07:14 -07:00
|
|
|
);
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-01-09 21:59:54 -08:00
|
|
|
/**
|
2023-02-10 20:37:32 -08:00
|
|
|
* Parses a Markdown document and returns Micromark events.
|
2023-01-09 21:59:54 -08:00
|
|
|
*
|
|
|
|
* @param {string} markdown Markdown document.
|
2024-03-04 21:38:18 -08:00
|
|
|
* @param {ParseOptions} [micromarkOptions] Options for micromark.
|
2023-07-21 22:49:08 -07:00
|
|
|
* @param {boolean} [referencesDefined] Treat references as defined.
|
2024-03-04 21:38:18 -08:00
|
|
|
* @returns {Event[]} Micromark events.
|
2023-01-09 21:59:54 -08:00
|
|
|
*/
|
2023-07-21 22:49:08 -07:00
|
|
|
function getMicromarkEvents(
|
|
|
|
markdown,
|
|
|
|
micromarkOptions = {},
|
|
|
|
referencesDefined = true
|
|
|
|
) {
|
2023-01-29 20:36:53 -08:00
|
|
|
|
|
|
|
// Customize options object to add useful extensions
|
2023-07-21 22:49:08 -07:00
|
|
|
micromarkOptions.extensions = micromarkOptions.extensions || [];
|
|
|
|
micromarkOptions.extensions.push(
|
2024-01-01 22:12:42 -08:00
|
|
|
directive(),
|
2023-07-16 22:07:34 -07:00
|
|
|
gfmAutolinkLiteral(),
|
|
|
|
gfmFootnote(),
|
|
|
|
gfmTable(),
|
|
|
|
math()
|
|
|
|
);
|
2023-01-09 21:59:54 -08:00
|
|
|
|
|
|
|
// Use micromark to parse document into Events
|
|
|
|
const encoding = undefined;
|
|
|
|
const eol = true;
|
2023-07-21 22:49:08 -07:00
|
|
|
const parseContext = parse(micromarkOptions);
|
|
|
|
if (referencesDefined) {
|
2023-05-23 04:01:55 +00:00
|
|
|
// Customize ParseContext to treat all references as defined
|
|
|
|
parseContext.defined.includes = (searchElement) => searchElement.length > 0;
|
|
|
|
}
|
2023-01-09 21:59:54 -08:00
|
|
|
const chunks = preprocess()(markdown, encoding, eol);
|
2023-01-29 20:36:53 -08:00
|
|
|
const events = postprocess(parseContext.document().write(chunks));
|
2023-02-10 20:37:32 -08:00
|
|
|
return events;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Parses a Markdown document and returns (frozen) tokens.
|
|
|
|
*
|
|
|
|
* @param {string} markdown Markdown document.
|
2024-03-04 21:38:18 -08:00
|
|
|
* @param {ParseOptions} micromarkOptions Options for micromark.
|
2023-07-21 22:49:08 -07:00
|
|
|
* @param {boolean} referencesDefined Treat references as defined.
|
|
|
|
* @param {number} lineDelta Offset to apply to start/end line.
|
2023-10-19 23:01:31 -07:00
|
|
|
* @param {Token} [ancestor] Parent of top-most tokens.
|
2023-02-10 20:37:32 -08:00
|
|
|
* @returns {Token[]} Micromark tokens (frozen).
|
|
|
|
*/
|
2023-07-21 22:49:08 -07:00
|
|
|
function micromarkParseWithOffset(
|
|
|
|
markdown,
|
|
|
|
micromarkOptions,
|
|
|
|
referencesDefined,
|
2023-10-19 23:01:31 -07:00
|
|
|
lineDelta,
|
|
|
|
ancestor
|
2023-07-21 22:49:08 -07:00
|
|
|
) {
|
2023-02-10 20:37:32 -08:00
|
|
|
// Use micromark to parse document into Events
|
2023-07-21 22:49:08 -07:00
|
|
|
const events = getMicromarkEvents(
|
|
|
|
markdown, micromarkOptions, referencesDefined
|
|
|
|
);
|
2023-01-09 21:59:54 -08:00
|
|
|
|
|
|
|
// Create Token objects
|
|
|
|
const document = [];
|
2023-09-04 22:02:39 -07:00
|
|
|
let flatTokens = [];
|
2023-10-19 23:01:31 -07:00
|
|
|
/** @type {Token} */
|
2023-10-15 21:24:59 -07:00
|
|
|
const root = {
|
2024-02-29 23:05:27 -08:00
|
|
|
"type": "data",
|
2023-10-19 23:01:31 -07:00
|
|
|
"startLine": -1,
|
|
|
|
"startColumn": -1,
|
|
|
|
"endLine": -1,
|
|
|
|
"endColumn": -1,
|
|
|
|
"text": "ROOT",
|
|
|
|
"children": document,
|
|
|
|
"parent": null
|
2023-01-09 21:59:54 -08:00
|
|
|
};
|
2023-10-15 21:24:59 -07:00
|
|
|
const history = [ root ];
|
|
|
|
let current = root;
|
2024-03-04 21:38:18 -08:00
|
|
|
// eslint-disable-next-line jsdoc/valid-types
|
|
|
|
/** @type ParseOptions | null */
|
2023-07-21 22:49:08 -07:00
|
|
|
let reparseOptions = null;
|
|
|
|
let lines = null;
|
2023-09-02 12:07:14 -07:00
|
|
|
let skipHtmlFlowChildren = false;
|
2023-01-09 21:59:54 -08:00
|
|
|
for (const event of events) {
|
|
|
|
const [ kind, token, context ] = event;
|
2023-01-13 19:37:17 -08:00
|
|
|
const { type, start, end } = token;
|
2023-01-09 21:59:54 -08:00
|
|
|
const { "column": startColumn, "line": startLine } = start;
|
|
|
|
const { "column": endColumn, "line": endLine } = end;
|
2023-05-31 20:54:18 -07:00
|
|
|
const text = context.sliceSerialize(token);
|
2023-09-02 12:07:14 -07:00
|
|
|
if ((kind === "enter") && !skipHtmlFlowChildren) {
|
2023-01-09 21:59:54 -08:00
|
|
|
const previous = current;
|
|
|
|
history.push(previous);
|
|
|
|
current = {
|
|
|
|
type,
|
2023-07-21 22:49:08 -07:00
|
|
|
"startLine": startLine + lineDelta,
|
2023-01-09 21:59:54 -08:00
|
|
|
startColumn,
|
2023-07-21 22:49:08 -07:00
|
|
|
"endLine": endLine + lineDelta,
|
2023-01-09 21:59:54 -08:00
|
|
|
endColumn,
|
|
|
|
text,
|
2023-10-15 21:24:59 -07:00
|
|
|
"children": [],
|
2023-10-19 23:01:31 -07:00
|
|
|
"parent": ((previous === root) ? (ancestor || null) : previous)
|
2023-01-09 21:59:54 -08:00
|
|
|
};
|
2023-09-02 12:07:14 -07:00
|
|
|
previous.children.push(current);
|
|
|
|
flatTokens.push(current);
|
|
|
|
if ((current.type === "htmlFlow") && !isHtmlFlowComment(current)) {
|
|
|
|
skipHtmlFlowChildren = true;
|
2023-07-21 22:49:08 -07:00
|
|
|
if (!reparseOptions || !lines) {
|
|
|
|
reparseOptions = {
|
|
|
|
...micromarkOptions,
|
|
|
|
"extensions": [
|
|
|
|
{
|
|
|
|
"disable": {
|
|
|
|
"null": [ "codeIndented", "htmlFlow" ]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
]
|
|
|
|
};
|
|
|
|
lines = markdown.split(newLineRe);
|
|
|
|
}
|
|
|
|
const reparseMarkdown = lines
|
|
|
|
.slice(current.startLine - 1, current.endLine)
|
|
|
|
.join("\n");
|
2023-09-02 12:07:14 -07:00
|
|
|
const tokens = micromarkParseWithOffset(
|
2023-07-21 22:49:08 -07:00
|
|
|
reparseMarkdown,
|
|
|
|
reparseOptions,
|
|
|
|
referencesDefined,
|
2023-10-19 23:01:31 -07:00
|
|
|
current.startLine - 1,
|
|
|
|
current
|
2023-07-21 22:49:08 -07:00
|
|
|
);
|
2023-09-02 12:07:14 -07:00
|
|
|
current.children = tokens;
|
2023-09-04 22:02:39 -07:00
|
|
|
// Avoid stack overflow of Array.push(...spread)
|
|
|
|
// eslint-disable-next-line unicorn/prefer-spread
|
|
|
|
flatTokens = flatTokens.concat(tokens[flatTokensSymbol]);
|
2023-07-21 22:49:08 -07:00
|
|
|
}
|
2023-01-09 21:59:54 -08:00
|
|
|
} else if (kind === "exit") {
|
2023-09-02 12:07:14 -07:00
|
|
|
if (type === "htmlFlow") {
|
|
|
|
skipHtmlFlowChildren = false;
|
|
|
|
}
|
|
|
|
if (!skipHtmlFlowChildren) {
|
|
|
|
Object.freeze(current.children);
|
|
|
|
Object.freeze(current);
|
|
|
|
// @ts-ignore
|
|
|
|
current = history.pop();
|
|
|
|
}
|
2023-01-09 21:59:54 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return document
|
2023-08-28 22:00:43 -07:00
|
|
|
Object.defineProperty(document, flatTokensSymbol, { "value": flatTokens });
|
2023-01-15 21:41:22 -08:00
|
|
|
Object.freeze(document);
|
2023-01-09 21:59:54 -08:00
|
|
|
return document;
|
|
|
|
}
|
|
|
|
|
2023-07-21 22:49:08 -07:00
|
|
|
/**
|
|
|
|
* Parses a Markdown document and returns (frozen) tokens.
|
|
|
|
*
|
|
|
|
* @param {string} markdown Markdown document.
|
2024-03-04 21:38:18 -08:00
|
|
|
* @param {ParseOptions} [micromarkOptions] Options for micromark.
|
2023-07-21 22:49:08 -07:00
|
|
|
* @param {boolean} [referencesDefined] Treat references as defined.
|
|
|
|
* @returns {Token[]} Micromark tokens (frozen).
|
|
|
|
*/
|
|
|
|
function micromarkParse(
|
|
|
|
markdown,
|
|
|
|
micromarkOptions = {},
|
|
|
|
referencesDefined = true
|
|
|
|
) {
|
|
|
|
return micromarkParseWithOffset(
|
|
|
|
markdown,
|
|
|
|
micromarkOptions,
|
|
|
|
referencesDefined,
|
|
|
|
0
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2023-09-04 21:41:16 -07:00
|
|
|
/**
|
|
|
|
* @callback AllowedPredicate
|
|
|
|
* @param {Token} token Micromark token.
|
|
|
|
* @returns {boolean} True iff allowed.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @callback TransformPredicate
|
|
|
|
* @param {Token} token Micromark token.
|
|
|
|
* @returns {Token[]} Child tokens.
|
|
|
|
*/
|
|
|
|
|
2023-01-21 15:41:03 -08:00
|
|
|
/**
|
|
|
|
* Filter a list of Micromark tokens by predicate.
|
|
|
|
*
|
|
|
|
* @param {Token[]} tokens Micromark tokens.
|
2023-10-25 20:05:19 -07:00
|
|
|
* @param {AllowedPredicate} [allowed] Allowed token predicate.
|
2023-09-04 21:41:16 -07:00
|
|
|
* @param {TransformPredicate} [transformChildren] Transform predicate.
|
2023-01-21 15:41:03 -08:00
|
|
|
* @returns {Token[]} Filtered tokens.
|
|
|
|
*/
|
2023-03-14 21:03:07 -07:00
|
|
|
function filterByPredicate(tokens, allowed, transformChildren) {
|
2023-10-25 20:05:19 -07:00
|
|
|
allowed = allowed || (() => true);
|
2023-01-21 15:41:03 -08:00
|
|
|
const result = [];
|
2023-09-02 21:30:43 -07:00
|
|
|
const queue = [
|
|
|
|
{
|
|
|
|
"array": tokens,
|
|
|
|
"index": 0
|
2023-01-21 15:41:03 -08:00
|
|
|
}
|
2023-09-02 21:30:43 -07:00
|
|
|
];
|
|
|
|
while (queue.length > 0) {
|
|
|
|
const current = queue[queue.length - 1];
|
|
|
|
const { array, index } = current;
|
|
|
|
if (index < array.length) {
|
|
|
|
const token = array[current.index++];
|
|
|
|
if (allowed(token)) {
|
|
|
|
result.push(token);
|
|
|
|
}
|
|
|
|
const { children } = token;
|
|
|
|
if (children.length > 0) {
|
|
|
|
const transformed =
|
|
|
|
transformChildren ? transformChildren(token) : children;
|
|
|
|
queue.push(
|
|
|
|
{
|
|
|
|
"array": transformed,
|
|
|
|
"index": 0
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
queue.pop();
|
2023-03-10 22:22:54 -08:00
|
|
|
}
|
2023-01-21 15:41:03 -08:00
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Filter a list of Micromark tokens by type.
|
|
|
|
*
|
|
|
|
* @param {Token[]} tokens Micromark tokens.
|
2024-02-29 23:05:27 -08:00
|
|
|
* @param {TokenType[]} types Types to allow.
|
2023-01-21 15:41:03 -08:00
|
|
|
* @returns {Token[]} Filtered tokens.
|
|
|
|
*/
|
2023-10-21 22:03:11 -07:00
|
|
|
function filterByTypes(tokens, types) {
|
|
|
|
const predicate = (token) => types.includes(token.type);
|
2023-08-28 22:00:43 -07:00
|
|
|
const flatTokens = tokens[flatTokensSymbol];
|
|
|
|
if (flatTokens) {
|
|
|
|
return flatTokens.filter(predicate);
|
|
|
|
}
|
|
|
|
return filterByPredicate(tokens, predicate);
|
2023-02-05 16:58:06 -08:00
|
|
|
}
|
|
|
|
|
2023-08-08 22:56:47 -07:00
|
|
|
/**
|
|
|
|
* Gets the heading level of a Micromark heading tokan.
|
|
|
|
*
|
|
|
|
* @param {Token} heading Micromark heading token.
|
|
|
|
* @returns {number} Heading level.
|
|
|
|
*/
|
|
|
|
function getHeadingLevel(heading) {
|
|
|
|
const headingSequence = filterByTypes(
|
|
|
|
heading.children,
|
|
|
|
[ "atxHeadingSequence", "setextHeadingLineSequence" ]
|
|
|
|
);
|
|
|
|
let level = 1;
|
|
|
|
const { text } = headingSequence[0];
|
|
|
|
if (text[0] === "#") {
|
|
|
|
level = Math.min(text.length, 6);
|
|
|
|
} else if (text[0] === "-") {
|
|
|
|
level = 2;
|
|
|
|
}
|
|
|
|
return level;
|
|
|
|
}
|
|
|
|
|
2023-02-05 16:58:06 -08:00
|
|
|
/**
|
|
|
|
* Gets information about the tag in an HTML token.
|
|
|
|
*
|
|
|
|
* @param {Token} token Micromark token.
|
|
|
|
* @returns {Object | null} HTML tag information.
|
|
|
|
*/
|
|
|
|
function getHtmlTagInfo(token) {
|
|
|
|
const htmlTagNameRe = /^<([^!>][^/\s>]*)/;
|
|
|
|
if (token.type === "htmlText") {
|
|
|
|
const match = htmlTagNameRe.exec(token.text);
|
|
|
|
if (match) {
|
|
|
|
const name = match[1];
|
|
|
|
const close = name.startsWith("/");
|
|
|
|
return {
|
|
|
|
close,
|
|
|
|
"name": close ? name.slice(1) : name
|
2023-02-23 22:14:44 -08:00
|
|
|
};
|
2023-02-05 16:58:06 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
2023-01-21 15:41:03 -08:00
|
|
|
}
|
|
|
|
|
2023-10-21 22:03:11 -07:00
|
|
|
/**
|
|
|
|
* Gets the nearest parent of the specified type for a Micromark token.
|
|
|
|
*
|
|
|
|
* @param {Token} token Micromark token.
|
2024-02-29 23:05:27 -08:00
|
|
|
* @param {TokenType[]} types Types to allow.
|
2023-10-21 22:03:11 -07:00
|
|
|
* @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;
|
|
|
|
}
|
|
|
|
|
2023-01-29 20:36:53 -08:00
|
|
|
/**
|
2023-10-25 20:05:19 -07:00
|
|
|
* Get the text of the first match from a list of Micromark tokens by type.
|
2023-01-29 20:36:53 -08:00
|
|
|
*
|
|
|
|
* @param {Token[]} tokens Micromark tokens.
|
2024-02-29 23:05:27 -08:00
|
|
|
* @param {TokenType} type Type to match.
|
2023-01-29 20:36:53 -08:00
|
|
|
* @returns {string | null} Text of token.
|
|
|
|
*/
|
|
|
|
function getTokenTextByType(tokens, type) {
|
|
|
|
const filtered = tokens.filter((token) => token.type === type);
|
2023-10-25 20:05:19 -07:00
|
|
|
return (filtered.length > 0) ? filtered[0].text : null;
|
2023-01-29 20:36:53 -08:00
|
|
|
}
|
|
|
|
|
2023-10-21 22:03:11 -07:00
|
|
|
/**
|
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
|
2023-01-29 20:36:53 -08:00
|
|
|
/**
|
|
|
|
* Determines a list of Micromark tokens matches and returns a subset.
|
|
|
|
*
|
|
|
|
* @param {Token[]} tokens Micromark tokens.
|
|
|
|
* @param {string[]} matchTypes Types to match.
|
|
|
|
* @param {string[]} [resultTypes] Types to return.
|
2023-02-23 22:14:44 -08:00
|
|
|
* @returns {Token[] | null} Matching tokens.
|
2023-01-29 20:36:53 -08:00
|
|
|
*/
|
|
|
|
function matchAndGetTokensByType(tokens, matchTypes, resultTypes) {
|
|
|
|
if (tokens.length !== matchTypes.length) {
|
|
|
|
return null;
|
|
|
|
}
|
2023-02-25 16:24:24 -08:00
|
|
|
resultTypes = resultTypes || matchTypes;
|
2023-02-23 22:14:44 -08:00
|
|
|
const result = [];
|
|
|
|
// eslint-disable-next-line unicorn/no-for-loop
|
2023-01-29 20:36:53 -08:00
|
|
|
for (let i = 0; i < matchTypes.length; i++) {
|
|
|
|
if (tokens[i].type !== matchTypes[i]) {
|
|
|
|
return null;
|
|
|
|
} else if (resultTypes.includes(matchTypes[i])) {
|
2023-02-23 22:14:44 -08:00
|
|
|
result.push(tokens[i]);
|
2023-01-29 20:36:53 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2023-05-25 03:47:34 +00:00
|
|
|
/**
|
|
|
|
* Returns the specified token iff it is of the desired type.
|
|
|
|
*
|
|
|
|
* @param {Token} token Micromark token candidate.
|
2024-02-29 23:05:27 -08:00
|
|
|
* @param {TokenType} type Desired type.
|
2023-05-25 03:47:34 +00:00
|
|
|
* @returns {Token | null} Token instance.
|
|
|
|
*/
|
|
|
|
function tokenIfType(token, type) {
|
|
|
|
return (token && (token.type === type)) ? token : null;
|
|
|
|
}
|
|
|
|
|
2023-01-14 15:30:47 -08:00
|
|
|
module.exports = {
|
2023-02-05 16:58:06 -08:00
|
|
|
"parse": micromarkParse,
|
2023-01-21 15:41:03 -08:00
|
|
|
filterByPredicate,
|
|
|
|
filterByTypes,
|
2023-08-08 22:56:47 -07:00
|
|
|
getHeadingLevel,
|
2023-02-05 16:58:06 -08:00
|
|
|
getHtmlTagInfo,
|
2023-02-10 20:37:32 -08:00
|
|
|
getMicromarkEvents,
|
2023-10-21 22:03:11 -07:00
|
|
|
getTokenParentOfType,
|
2023-01-29 20:36:53 -08:00
|
|
|
getTokenTextByType,
|
2023-10-21 22:03:11 -07:00
|
|
|
inHtmlFlow,
|
2023-05-25 03:47:34 +00:00
|
|
|
matchAndGetTokensByType,
|
|
|
|
tokenIfType
|
2023-01-09 21:59:54 -08:00
|
|
|
};
|