in x-parser.js [961:1087]
static parse(strings, onToken) {
const stringsLength = strings.length;
const tagNames = [null];
let tagName = null;
let stringsIndex = 0;
let string = null;
let stringLength = null;
let stringIndex = null;
let nextStringIndex = null;
let value = XParser.#initial; // Values are stateful regular expressions.
try {
while (stringsIndex < stringsLength) {
XParser.#validateRawString(strings.raw[stringsIndex]);
string = strings[stringsIndex];
if (stringsIndex > 0) {
switch (value) {
case XParser.#initial:
case XParser.#boundContent:
case XParser.#text:
case XParser.#startTagClose:
case XParser.#endTag:
if (tagName === 'textarea') {
// The textarea tag only accepts text, we restrict interpolation
// there. See note on “replaceable character data” in the
// following reference document:
// https://w3c.github.io/html-reference/syntax.html#text-syntax
const sloppyStartInterpolation = value !== XParser.#startTagClose;
XParser.#sendBoundTextTokens(onToken, stringsIndex - 1, string, stringIndex, sloppyStartInterpolation);
} else {
XParser.#sendBoundContentTokens(onToken, stringsIndex - 1, string, stringIndex);
}
value = XParser.#boundContent;
break;
}
}
stringLength = string.length;
stringIndex = 0;
while (stringIndex < stringLength) {
// The string will be empty if we have a template like this `${…}${…}`.
// See related logic at the end of the inner loop;
if (string.length > 0) {
const nextValue = XParser.#validTransition(string, stringIndex, value);
if (!nextValue) {
XParser.#throwTransitionError(strings, stringsIndex, string, stringIndex, value);
}
value = nextValue;
nextStringIndex = value.lastIndex;
}
// When we transition into certain values, we need to take action.
switch (value) {
case XParser.#text:
XParser.#sendTextTokens(onToken, stringsIndex, string, stringIndex, nextStringIndex);
break;
case XParser.#comment:
XParser.#sendCommentTokens(onToken, stringsIndex, string, stringIndex, nextStringIndex);
break;
case XParser.#startTagOpen:
tagName = XParser.#sendStartTagOpenTokens(onToken, stringsIndex, string, stringIndex, nextStringIndex);
tagNames.push(tagName);
break;
case XParser.#startTagSpace:
XParser.#sendStartTagSpaceTokens(onToken, stringsIndex, string, stringIndex, nextStringIndex);
break;
case XParser.#danglingQuote:
XParser.#sendDanglingQuoteTokens(onToken, stringsIndex, string, stringIndex, nextStringIndex);
break;
case XParser.#boolean:
XParser.#sendBooleanTokens(onToken, tagName, stringsIndex, string, stringIndex, nextStringIndex);
break;
case XParser.#attribute:
XParser.#sendAttributeTokens(onToken, tagName, stringsIndex, string, stringIndex, nextStringIndex);
break;
case XParser.#boundBoolean:
XParser.#sendBoundBooleanTokens(onToken, stringsIndex, string, stringIndex, nextStringIndex);
break;
case XParser.#boundDefined:
XParser.#sendBoundDefinedTokens(onToken, stringsIndex, string, stringIndex, nextStringIndex);
break;
case XParser.#boundAttribute:
XParser.#sendBoundAttributeTokens(onToken, stringsIndex, string, stringIndex, nextStringIndex);
break;
case XParser.#boundProperty:
XParser.#sendBoundPropertyTokens(onToken, stringsIndex, string, stringIndex, nextStringIndex);
break;
case XParser.#startTagClose:
if (XParser.#voidHtmlElements.has(tagName)) {
XParser.#sendVoidElementTokens(onToken, stringsIndex, stringIndex, nextStringIndex);
tagNames.pop();
tagName = tagNames[tagNames.length - 1];
} else if (tagName === 'textarea' && XParser.#startTagClose.lastIndex !== string.length) {
// If successful, move cursor through textarea element end tag.
nextStringIndex = XParser.#sendTextareaTokens(onToken, stringsIndex, string, stringIndex, nextStringIndex);
value = XParser.#endTag;
value.lastIndex = nextStringIndex;
tagNames.pop();
tagName = tagNames[tagNames.length - 1];
} else {
XParser.#sendStartTagCloseTokens(onToken, stringsIndex, stringIndex, nextStringIndex);
}
break;
case XParser.#endTag: {
XParser.#sendEndTagTokens(onToken, tagName, strings, stringsIndex, string, stringIndex, nextStringIndex);
tagNames.pop();
tagName = tagNames[tagNames.length - 1];
break;
}
}
stringIndex = nextStringIndex; // Update out pointer from our pattern match.
nextStringIndex = null;
}
stringsIndex++;
}
XParser.#validateExit(value, tagName);
} catch (error) {
// Roughly match the conventions for “onToken”.
const index = stringsIndex;
const start = stringIndex;
const end = nextStringIndex;
error[XParser.#errorContextKey] = { index, start, end };
throw error;
}
}