@dolphinweex/himalaya
Version:
HTML to JSON parser
150 lines (134 loc) • 4.17 kB
JavaScript
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = parser;
exports.hasTerminalParent = hasTerminalParent;
exports.rewindStack = rewindStack;
exports.parse = parse;
var _compat = require('./compat');
function parser(tokens, options) {
var root = { tagName: null, children: [] };
var state = { tokens: tokens, options: options, cursor: 0, stack: [root] };
parse(state);
return root.children;
}
function hasTerminalParent(tagName, stack, terminals) {
var tagParents = terminals[tagName];
if (tagParents) {
var currentIndex = stack.length - 1;
while (currentIndex >= 0) {
var parentTagName = stack[currentIndex].tagName;
if (parentTagName === tagName) {
break;
}
if ((0, _compat.arrayIncludes)(tagParents, parentTagName)) {
return true;
}
currentIndex--;
}
}
return false;
}
function rewindStack(stack, newLength, childrenEndPosition, endPosition) {
stack[newLength].position.end = endPosition;
for (var i = newLength + 1, len = stack.length; i < len; i++) {
stack[i].position.end = childrenEndPosition;
}
stack.splice(newLength);
}
function parse(state) {
var tokens = state.tokens,
options = state.options;
var stack = state.stack;
var nodes = stack[stack.length - 1].children;
var len = tokens.length;
var cursor = state.cursor;
while (cursor < len) {
var token = tokens[cursor];
if (token.type !== 'tag-start') {
nodes.push(token);
cursor++;
continue;
}
var tagToken = tokens[++cursor];
cursor++;
var tagName = tagToken.content;
if (token.close) {
var index = stack.length;
var shouldRewind = false;
while (--index > -1) {
if (stack[index].tagName === tagName) {
shouldRewind = true;
break;
}
}
while (cursor < len) {
var endToken = tokens[cursor];
if (endToken.type !== 'tag-end') break;
cursor++;
}
if (shouldRewind) {
rewindStack(stack, index, token.position.start, tokens[cursor - 1].position.end);
break;
} else {
continue;
}
}
var isClosingTag = (0, _compat.arrayIncludes)(options.closingTags, tagName);
var shouldRewindToAutoClose = isClosingTag;
if (shouldRewindToAutoClose) {
var terminals = options.closingTagAncestorBreakers;
shouldRewindToAutoClose = !hasTerminalParent(tagName, stack, terminals);
}
if (shouldRewindToAutoClose) {
// rewind the stack to just above the previous
// closing tag of the same name
var currentIndex = stack.length - 1;
while (currentIndex > 0) {
if (tagName === stack[currentIndex].tagName) {
rewindStack(stack, currentIndex, token.position.start, token.position.start);
var previousIndex = currentIndex - 1;
nodes = stack[previousIndex].children;
break;
}
currentIndex = currentIndex - 1;
}
}
var attributes = [];
var attrToken = void 0;
while (cursor < len) {
attrToken = tokens[cursor];
if (attrToken.type === 'tag-end') break;
attributes.push(attrToken.content);
cursor++;
}
cursor++;
var children = [];
var position = {
start: token.position.start,
end: attrToken.position.end
};
var elementNode = {
type: 'element',
tagName: tagToken.content,
attributes: attributes,
children: children,
position: position
};
nodes.push(elementNode);
var hasChildren = !(attrToken.close || (0, _compat.arrayIncludes)(options.voidTags, tagName));
if (hasChildren) {
var size = stack.push({ tagName: tagName, children: children, position: position });
var innerState = { tokens: tokens, options: options, cursor: cursor, stack: stack };
parse(innerState);
cursor = innerState.cursor;
var rewoundInElement = stack.length === size;
if (rewoundInElement) {
elementNode.position.end = tokens[cursor - 1].position.end;
}
}
}
state.cursor = cursor;
}
//# sourceMappingURL=parser.js.map