@b-side/parser
Version:
HTML Parser used to parse HTML string and bind data to it.
121 lines • 4.93 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Parser = void 0;
const element_1 = require("./element");
const utils_1 = require("./utils");
var Events;
(function (Events) {
Events["attributes"] = "attributes";
Events["end"] = "end";
Events["expression"] = "expression";
Events["style"] = "style";
Events["text"] = "text";
Events["tag"] = "newTag";
})(Events || (Events = {}));
const regexTagContent = new RegExp(/\/?>|([a-zA-Z\-:]+)?=?(["'`(]{1}|{{)|[a-zA-Z\-:]+/);
function* RegexIterator(string, regex, endDelimiter) {
let indexBefore = 0;
let indexAfter = 0;
while (indexBefore < string.length) {
const match = string.substring(indexBefore).match(regex);
if (!match || match.index === undefined) {
break;
}
indexBefore += match.index;
indexAfter = indexBefore + match[0].length;
if (endDelimiter && match[0].match(endDelimiter)) {
return { indexBefore, indexAfter, match: [match[0]] };
}
else {
indexBefore = (yield { indexBefore, indexAfter, match: [...match] }) || indexAfter;
}
}
}
class Parser {
constructor(string) {
this.string = string;
this.callbacks = {};
}
on(type, callback) {
if (!this.callbacks[type]) {
this.callbacks[type] = [];
}
this.callbacks[type]?.push(callback);
}
async notify(type, data, options) {
for (const callback of this.callbacks[type] || []) {
const result = await callback(data, options);
if (result !== undefined) {
data = result;
}
}
return data;
}
async discoverChilds(parent, string) {
const name = parent ? parent.nodeName : '';
const regexChildContent = new RegExp(`<([a-zA-Z0-9-]+)|</${name}>|<!--|{{`);
const iterator = RegexIterator(string, regexChildContent, `</${name}>`);
let index = 0;
let result;
do {
result = iterator.next(index);
const [wholeMatch, nodeName] = result.value?.match || [];
const text = string.substring(index, result.value?.indexBefore);
parent.appendText(text);
index = result.value?.indexAfter || index;
if (result.done) {
break;
}
if (wholeMatch === '<!--' || wholeMatch === '{{') {
const [value, newindex] = (0, utils_1.FindEndOfStringDelimiter)(string.substring(index), wholeMatch);
if (wholeMatch === '{{') {
parent.appendExpression(await this.notify(Events.expression, value, parent));
}
index += newindex;
}
else if (nodeName) {
const [element, newindex, isTagClosed] = await this.discoverElement(nodeName, string.substring(index));
parent.append(element);
index += newindex;
if (!isTagClosed) {
const [, newindex] = await this.discoverChilds(element, string.substring(index));
index += newindex;
}
}
} while (!result.done);
return [parent, index];
}
async discoverElement(nodeName, string) {
const attributes = new Map();
const iterator = RegexIterator(string, regexTagContent, '/>|>');
let index = 0;
let result;
do {
result = iterator.next(index);
const [wholeMatch, attributeNameWithValue, valueDelimiter] = result.value?.match || [];
index = result.value?.indexAfter || index;
if (result.done) {
break;
}
const isExpression = valueDelimiter === '(' || valueDelimiter === '{{';
if (attributeNameWithValue || isExpression) {
const [value, newIndex] = (0, utils_1.FindEndOfStringDelimiter)(string.substring(index), valueDelimiter);
attributes.set(attributeNameWithValue || `@__${attributes.size}__@`, isExpression ? { expression: value } : { value });
index += newIndex;
}
else if (wholeMatch) {
attributes.set(wholeMatch, {});
}
} while (!result.done);
const isTagClosed = result.value?.match[0] === '/>' || element_1.SelfClosingTags.includes(nodeName);
const element = (0, element_1.CreateElement)(await this.notify(Events.tag, nodeName, attributes), string.substring(0, index), attributes);
return [element, index, isTagClosed];
}
async toElements() {
const [useElement] = await this.discoverChilds((0, element_1.CreateElement)('fragment'), this.string);
await this.notify(Events.end, useElement);
return useElement;
}
}
exports.Parser = Parser;
//# sourceMappingURL=parser.js.map