UNPKG

@b-side/parser

Version:

HTML Parser used to parse HTML string and bind data to it.

121 lines 4.93 kB
"use strict"; 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