tag-soup
Version:
The fastest pure JS SAX/DOM XML/HTML parser.
85 lines (84 loc) • 3.39 kB
JavaScript
import { createSaxParser } from './createSaxParser';
/**
* Creates a new stateful DOM parser.
*
* @template Node The type of object that describes a node in the DOM tree.
* @template ContainerNode The type of object that describes an element or a document in the DOM tree.
*
* @param handler The handler that provides factories and callbacks that produce the DOM tree.
* @param options The parser options.
* @returns The new parser that produces a DOM tree during parsing.
*/
export function createDomParser(handler, options) {
var nodes = [];
var saxParser = createSaxParser(createSaxHandler(nodes, handler, function (node) { return nodes.push(node); }), options);
var write = function (sourceChunk) {
saxParser.write(sourceChunk);
return nodes;
};
var parse = function (source) {
saxParser.parse(source);
var result = nodes;
reset();
return result;
};
var reset = function () {
saxParser.reset();
nodes = [];
};
return {
write: write,
parse: parse,
reset: reset,
};
}
function createSaxHandler(nodes, handler, pushRootNode) {
var elementFactory = handler.element, containerEndCallback = handler.containerEnd, appendChildCallback = handler.appendChild, textFactory = handler.text, documentFactory = handler.document, commentFactory = handler.comment, processingInstructionFactory = handler.processingInstruction, cdataFactory = handler.cdata, sourceEndCallback = handler.sourceEnd, resetCallback = handler.reset;
var ancestors = { length: 0 };
if (typeof elementFactory !== 'function') {
throw new Error('Missing element factory');
}
if (typeof appendChildCallback !== 'function') {
throw new Error('Missing appendChild callback');
}
var pushNode = function (node) {
if (ancestors.length !== 0) {
appendChildCallback(ancestors[ancestors.length - 1], node);
}
else {
pushRootNode(node);
}
};
var createDataTokenCallback = function (dataFactory) {
return dataFactory != null ? function (token) { return pushNode(dataFactory(token)); } : undefined;
};
return {
startTag: function (token) {
var node = elementFactory(token);
pushNode(node);
if (!token.selfClosing) {
ancestors[ancestors.length++] = node;
}
},
endTag: function (token) {
--ancestors.length;
containerEndCallback === null || containerEndCallback === void 0 ? void 0 : containerEndCallback(ancestors[ancestors.length], token);
},
doctype: function (token) {
if (documentFactory && nodes.length === 0) {
var node = documentFactory(token);
pushNode(node);
ancestors[ancestors.length++] = node;
}
},
text: createDataTokenCallback(textFactory),
processingInstruction: createDataTokenCallback(processingInstructionFactory),
cdata: createDataTokenCallback(cdataFactory),
comment: createDataTokenCallback(commentFactory),
sourceEnd: sourceEndCallback,
reset: function () {
ancestors.length = 0;
resetCallback === null || resetCallback === void 0 ? void 0 : resetCallback();
},
};
}