buildNodes static method
Converts the given markdownNodes to a list of Nodes.
This also handles html blocks inside the parsed markdown content.
Implementation
static List<Node> buildNodes(Iterable<md.Node> markdownNodes) {
final root = ElementNode('_', {}, []);
final List<ElementNode> stack = [root];
List<Node> currentNodes = root.children!;
for (final node in markdownNodes) {
if (node is HtmlText) {
final tokenizer = html.HtmlTokenizer(node.text, lowercaseElementName: false);
while (tokenizer.moveNext()) {
final token = tokenizer.current;
if (token.kind == html.TokenKind.parseError) {
final error = (token as html.ParseErrorToken).data;
if (error == 'expected-tag-name-but-got-question-mark') {
// This error happens with processing instructions like <?some-instruction ... ?>
// We can safely ignore it, since the next token will be a comment containing the instruction.
continue;
} else {
throw AssertionError('Unexpected parse error: ${token.data}');
}
}
if (token.kind == html.TokenKind.startTag) {
final tag = (token as html.StartTagToken).name ?? '';
final attributes = token.data.map((k, v) => MapEntry(k.toString(), v));
final element = ElementNode(tag, attributes, []);
currentNodes.add(element);
final selfClosing = token.selfClosing || const DomValidator().isSelfClosing(token.name ?? '');
if (!selfClosing) {
stack.add(element);
currentNodes = element.children!;
}
} else if (token.kind == html.TokenKind.endTag) {
if (stack.last.tag != (token as html.EndTagToken).name) {
// If the end tag does not match the last opened tag, we ignore it.
continue;
}
stack.removeLast();
currentNodes = stack.last.children!;
} else if (token.kind == html.TokenKind.characters || token.kind == html.TokenKind.spaceCharacters) {
currentNodes.add(TextNode((token as html.StringToken).data));
} else if (token.kind == html.TokenKind.comment) {
var data = (token as html.CommentToken).data;
if (data.startsWith('?') && data.endsWith('?')) {
data = data.substring(1, data.length - 1);
}
currentNodes.add(TextNode('<!--$data-->', raw: true));
} else if (token.kind == html.TokenKind.doctype) {
// Ignore doctype tokens.
continue;
}
}
} else if (node is md.Text) {
currentNodes.addAll(HtmlParser.buildNodes(html.parseFragment(node.text).nodes));
} else if (node is md.Element) {
final children = buildNodes(node.children ?? []);
currentNodes.add(
ElementNode(node.tag, {'id': ?node.generatedId, ...node.attributes}, children),
);
}
}
return root.children!;
}