function parse()

in packages/miniapp-runtime/src/dom-external/inner-html/parser.ts [188:278]


function parse(state: State) {
  const { tokens, stack } = state;
  let { cursor } = state;

  const len = tokens.length;

  let nodes = stack[stack.length - 1].children;

  while (cursor < len) {
    const token = tokens[cursor];
    if (token.type !== 'tag-start') {
      // comment or text
      nodes.push(token as ChildNode);
      cursor++;
      continue;
    }

    const tagToken = tokens[++cursor];
    cursor++;
    const tagName = tagToken.content!.toLowerCase();
    if (token.close) {
      let index = stack.length;
      let shouldRewind = false;
      while (--index > -1) {
        if (stack[index].tagName === tagName) {
          shouldRewind = true;
          break;
        }
      }
      while (cursor < len) {
        const endToken = tokens[cursor];
        if (endToken.type !== 'tag-end') break;
        cursor++;
      }
      if (shouldRewind) {
        stack.splice(index);
        break;
      } else {
        continue;
      }
    }

    const isClosingTag = options.html!.closingElements.has(tagName);
    let shouldRewindToAutoClose = isClosingTag;
    if (shouldRewindToAutoClose) {
      shouldRewindToAutoClose = !hasTerminalParent(tagName, stack);
    }

    if (shouldRewindToAutoClose) {
      let currentIndex = stack.length - 1;
      while (currentIndex > 0) {
        if (tagName === stack[currentIndex].tagName) {
          stack.splice(currentIndex);
          const previousIndex = currentIndex - 1;
          nodes = stack[previousIndex].children;
          break;
        }
        currentIndex = currentIndex - 1;
      }
    }

    const attributes: string[] = [];
    let attrToken: Token;
    while (cursor < len) {
      attrToken = tokens[cursor];
      if (attrToken.type === 'tag-end') break;
      attributes.push(attrToken.content!);
      cursor++;
    }

    cursor++;
    const children: Element[] = [];
    const element: Element = {
      type: 'element',
      tagName: tagToken.content!,
      attributes,
      children,
    };
    nodes.push(element);

    const hasChildren = !(attrToken!.close || options.html!.voidElements.has(tagName));
    if (hasChildren) {
      stack.push({ tagName, children } as any);
      const innerState: State = { tokens, cursor, stack };
      parse(innerState);
      cursor = innerState.cursor;
    }
  }

  state.cursor = cursor;
}