Socket
Socket
Sign inDemoInstall

@bbob/parser

Package Overview
Dependencies
Maintainers
1
Versions
53
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@bbob/parser - npm Package Compare versions

Comparing version 2.9.0 to 3.0.0

1664

dist/index.js
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.BbobParser = {}));
})(this, (function (exports) { 'use strict';
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@bbob/plugin-helper')) :
typeof define === 'function' && define.amd ? define(['exports', '@bbob/plugin-helper'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.BbobParser = {}, global.pluginHelper));
})(this, (function (exports, pluginHelper) { 'use strict';
const N = '\n';
const TAB = '\t';
const EQ = '=';
const QUOTEMARK = '"';
const SPACE = ' ';
const OPEN_BRAKET = '[';
const CLOSE_BRAKET = ']';
const SLASH = '/';
const BACKSLASH = '\\';
// type, value, line, row,
const TOKEN_TYPE_ID = 'type'; // 0;
const TOKEN_VALUE_ID = 'value'; // 1;
const TOKEN_COLUMN_ID = 'row'; // 2;
const TOKEN_LINE_ID = 'line'; // 3;
const TOKEN_TYPE_WORD = 1; // 'word';
const TOKEN_TYPE_TAG = 2; // 'tag';
const TOKEN_TYPE_ATTR_NAME = 3; // 'attr-name';
const TOKEN_TYPE_ATTR_VALUE = 4; // 'attr-value';
const TOKEN_TYPE_SPACE = 5; // 'space';
const TOKEN_TYPE_NEW_LINE = 6; // 'new-line';
/**
* @param {Token} token
* @returns {string}
*/ const getTokenValue = (token)=>{
if (token && typeof token[TOKEN_VALUE_ID] !== 'undefined') {
return token[TOKEN_VALUE_ID];
}
return '';
};
/**
* @param {Token}token
* @returns {number}
*/ const getTokenLine = (token)=>token && token[TOKEN_LINE_ID] || 0;
const getTokenColumn = (token)=>token && token[TOKEN_COLUMN_ID] || 0;
/**
* @param {Token} token
* @returns {boolean}
*/ const isTextToken = (token)=>{
if (token && typeof token[TOKEN_TYPE_ID] !== 'undefined') {
return token[TOKEN_TYPE_ID] === TOKEN_TYPE_SPACE || token[TOKEN_TYPE_ID] === TOKEN_TYPE_NEW_LINE || token[TOKEN_TYPE_ID] === TOKEN_TYPE_WORD;
}
return false;
};
/**
* @param {Token} token
* @returns {boolean}
*/ const isTagToken = (token)=>{
if (token && typeof token[TOKEN_TYPE_ID] !== 'undefined') {
return token[TOKEN_TYPE_ID] === TOKEN_TYPE_TAG;
}
return false;
};
const isTagEnd = (token)=>getTokenValue(token).charCodeAt(0) === pluginHelper.SLASH.charCodeAt(0);
const isTagStart = (token)=>!isTagEnd(token);
const isAttrNameToken = (token)=>{
if (token && typeof token[TOKEN_TYPE_ID] !== 'undefined') {
return token[TOKEN_TYPE_ID] === TOKEN_TYPE_ATTR_NAME;
}
return false;
};
/**
* @param {Token} token
* @returns {boolean}
*/ const isAttrValueToken = (token)=>{
if (token && typeof token[TOKEN_TYPE_ID] !== 'undefined') {
return token[TOKEN_TYPE_ID] === TOKEN_TYPE_ATTR_VALUE;
}
return false;
};
const getTagName = (token)=>{
const value = getTokenValue(token);
return isTagEnd(token) ? value.slice(1) : value;
};
const convertTagToText = (token)=>{
let text = pluginHelper.OPEN_BRAKET;
text += getTokenValue(token);
text += pluginHelper.CLOSE_BRAKET;
return text;
};
class Token {
isEmpty() {
// eslint-disable-next-line no-restricted-globals
return isNaN(this[TOKEN_TYPE_ID]);
}
isText() {
return isTextToken(this);
}
isTag() {
return isTagToken(this);
}
isAttrName() {
return isAttrNameToken(this);
}
isAttrValue() {
return isAttrValueToken(this);
}
isStart() {
return isTagStart(this);
}
isEnd() {
return isTagEnd(this);
}
getName() {
return getTagName(this);
}
getValue() {
return getTokenValue(this);
}
getLine() {
return getTokenLine(this);
}
getColumn() {
return getTokenColumn(this);
}
toString() {
return convertTagToText(this);
}
/**
* @param {String} type
* @param {String} value
* @param line
* @param row
*/ constructor(type, value, line, row){
this[TOKEN_TYPE_ID] = Number(type);
this[TOKEN_VALUE_ID] = String(value);
this[TOKEN_LINE_ID] = Number(line);
this[TOKEN_COLUMN_ID] = Number(row);
}
}
const TYPE_WORD = TOKEN_TYPE_WORD;
const TYPE_TAG = TOKEN_TYPE_TAG;
const TYPE_ATTR_NAME = TOKEN_TYPE_ATTR_NAME;
const TYPE_ATTR_VALUE = TOKEN_TYPE_ATTR_VALUE;
const TYPE_SPACE = TOKEN_TYPE_SPACE;
const TYPE_NEW_LINE = TOKEN_TYPE_NEW_LINE;
const isTagNode = (el)=>typeof el === 'object' && !!el.tag;
const isStringNode = (el)=>typeof el === 'string';
const keysReduce = (obj, reduce, def)=>Object.keys(obj).reduce(reduce, def);
const getNodeLength = (node)=>{
if (isTagNode(node)) {
return node.content.reduce((count, contentNode)=>count + getNodeLength(contentNode), 0);
}
if (isStringNode(node)) {
return node.length;
}
return 0;
};
/**
* Appends value to Tag Node
* @param {TagNode} node
* @param value
*/ const appendToNode = (node, value)=>{
node.content.push(value);
};
/**
* Replaces " to &qquot;
* @param {String} value
*/ const escapeHTML = (value)=>value.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#039;')// eslint-disable-next-line no-script-url
.replace(/(javascript|data|vbscript):/gi, '$1%3A');
/**
* Acept name and value and return valid html5 attribute string
* @param {String} name
* @param {String} value
* @return {string}
*/ const attrValue = (name, value)=>{
const type = typeof value;
const types = {
boolean: ()=>value ? `${name}` : '',
number: ()=>`${name}="${value}"`,
string: ()=>`${name}="${escapeHTML(value)}"`,
object: ()=>`${name}="${escapeHTML(JSON.stringify(value))}"`
};
return types[type] ? types[type]() : '';
};
/**
* Transforms attrs to html params string
* @param values
*/ const attrsToString = (values)=>{
// To avoid some malformed attributes
if (values == null) {
return '';
}
return keysReduce(values, (arr, key)=>[
...arr,
attrValue(key, values[key])
], [
''
]).join(' ');
};
/**
* Gets value from
* @example
* getUniqAttr({ 'foo': true, 'bar': bar' }) => 'bar'
* @param attrs
* @returns {string}
*/ const getUniqAttr = (attrs)=>keysReduce(attrs, (res, key)=>attrs[key] === key ? attrs[key] : null, null);
function CharGrabber(source, options) {
const cursor = {
pos: 0,
len: source.length
};
const substrUntilChar = (char)=>{
const { pos } = cursor;
const idx = source.indexOf(char, pos);
return idx >= 0 ? source.substring(pos, idx) : '';
};
const includes = (val)=>source.indexOf(val, cursor.pos) >= 0;
const hasNext = ()=>cursor.len > cursor.pos;
const isLast = ()=>cursor.pos === cursor.len;
const skip = (num = 1, silent)=>{
cursor.pos += num;
if (options && options.onSkip && !silent) {
options.onSkip();
}
};
const rest = ()=>source.substring(cursor.pos);
const grabN = (num = 0)=>source.substring(cursor.pos, cursor.pos + num);
const curr = ()=>source[cursor.pos];
const prev = ()=>{
const prevPos = cursor.pos - 1;
return typeof source[prevPos] !== 'undefined' ? source[prevPos] : null;
};
const next = ()=>{
const nextPos = cursor.pos + 1;
return nextPos <= source.length - 1 ? source[nextPos] : null;
};
const grabWhile = (cond, silent)=>{
let start = 0;
if (hasNext()) {
start = cursor.pos;
while(hasNext() && cond(curr())){
skip(1, silent);
}
}
return source.substring(start, cursor.pos);
};
/**
* @type {skip}
*/ this.skip = skip;
/**
* @returns {Boolean}
*/ this.hasNext = hasNext;
/**
* @returns {String}
*/ this.getCurr = curr;
/**
* @returns {String}
*/ this.getRest = rest;
/**
* @returns {String}
*/ this.getNext = next;
/**
* @returns {String}
*/ this.getPrev = prev;
/**
* @returns {Boolean}
*/ this.isLast = isLast;
/**
* @returns {Boolean}
*/ this.includes = includes;
/**
* @param {Function} cond
* @param {Boolean} silent
* @return {String}
*/ this.grabWhile = grabWhile;
/**
* @param {Number} num
* @return {String}
*/ this.grabN = grabN;
/**
* Grabs rest of string until it find a char
* @param {String} char
* @return {String}
*/ this.substrUntilChar = substrUntilChar;
}
/**
* Creates a grabber wrapper for source string, that helps to iterate over string char by char
* @param {String} source
* @param {Object} options
* @param {Function} options.onSkip
* @return CharGrabber
*/ const createCharGrabber = (source, options)=>new CharGrabber(source, options);
/**
* Trims string from start and end by char
* @example
* trimChar('*hello*', '*') ==> 'hello'
* @param {String} str
* @param {String} charToRemove
* @returns {String}
*/ const trimChar = (str, charToRemove)=>{
while(str.charAt(0) === charToRemove){
// eslint-disable-next-line no-param-reassign
str = str.substring(1);
}
while(str.charAt(str.length - 1) === charToRemove){
// eslint-disable-next-line no-param-reassign
str = str.substring(0, str.length - 1);
}
return str;
};
/**
* Unquotes \" to "
* @param str
* @return {String}
*/ const unquote = (str)=>str.replace(pluginHelper.BACKSLASH + pluginHelper.QUOTEMARK, pluginHelper.QUOTEMARK);
function NodeList(values = []) {
const nodes = values;
const getLast = ()=>Array.isArray(nodes) && nodes.length > 0 && typeof nodes[nodes.length - 1] !== 'undefined' ? nodes[nodes.length - 1] : null;
const flushLast = ()=>nodes.length ? nodes.pop() : false;
const push = (value)=>nodes.push(value);
const toArray = ()=>nodes;
this.push = push;
this.toArray = toArray;
this.getLast = getLast;
this.flushLast = flushLast;
}
/**
*
* @param values
* @return {NodeList}
*/ const createList = (values = [])=>new NodeList(values);
const getTagAttrs = (tag, params)=>{
const uniqAattr = getUniqAttr(params);
if (uniqAattr) {
const tagAttr = attrValue(tag, uniqAattr);
const attrs = {
...params
};
delete attrs[uniqAattr];
const attrsStr = attrsToString(attrs);
return `${tagAttr}${attrsStr}`;
}
return `${tag}${attrsToString(params)}`;
};
class TagNode {
attr(name, value) {
if (typeof value !== 'undefined') {
this.attrs[name] = value;
}
return this.attrs[name];
}
append(value) {
return appendToNode(this, value);
}
get length() {
return getNodeLength(this);
}
toTagStart({ openTag =OPEN_BRAKET , closeTag =CLOSE_BRAKET } = {}) {
const tagAttrs = getTagAttrs(this.tag, this.attrs);
return `${openTag}${tagAttrs}${closeTag}`;
}
toTagEnd({ openTag =OPEN_BRAKET , closeTag =CLOSE_BRAKET } = {}) {
return `${openTag}${SLASH}${this.tag}${closeTag}`;
}
toTagNode() {
return new TagNode(this.tag.toLowerCase(), this.attrs, this.content);
}
toString({ openTag =OPEN_BRAKET , closeTag =CLOSE_BRAKET } = {}) {
const isEmpty = this.content.length === 0;
const content = this.content.reduce((r, node)=>r + node.toString({
openTag,
closeTag
}), '');
const tagStart = this.toTagStart({
openTag,
closeTag
});
if (isEmpty) {
return tagStart;
}
return `${tagStart}${content}${this.toTagEnd({
openTag,
closeTag
})}`;
}
constructor(tag, attrs, content){
this.tag = tag;
this.attrs = attrs;
this.content = Array.isArray(content) ? content : [
content
];
}
}
TagNode.create = (tag, attrs = {}, content = [])=>new TagNode(tag, attrs, content);
TagNode.isOf = (node, type)=>node.tag === type;
// for cases <!-- -->
const EM = '!';
/**
* Creates a Token entity class
* @param {Number} type
* @param {String} value
* @param {Number} r line number
* @param {Number} cl char number in line
*/ const createToken = (type, value, r = 0, cl = 0)=>new Token(type, value, r, cl);
/**
* @typedef {Object} Lexer
* @property {Function} tokenize
* @property {Function} isTokenNested
*/ /**
* @param {String} buffer
* @param {Object} options
* @param {Function} options.onToken
* @param {String} options.openTag
* @param {String} options.closeTag
* @param {Boolean} options.enableEscapeTags
* @return {Lexer}
*/ function createLexer(buffer, options = {}) {
const STATE_WORD = 0;
const STATE_TAG = 1;
const STATE_TAG_ATTRS = 2;
const TAG_STATE_NAME = 0;
const TAG_STATE_ATTR = 1;
const TAG_STATE_VALUE = 2;
let row = 0;
let col = 0;
let tokenIndex = -1;
let stateMode = STATE_WORD;
let tagMode = TAG_STATE_NAME;
let contextFreeTag = '';
const tokens = new Array(Math.floor(buffer.length));
const openTag = options.openTag || pluginHelper.OPEN_BRAKET;
const closeTag = options.closeTag || pluginHelper.CLOSE_BRAKET;
const escapeTags = !!options.enableEscapeTags;
const contextFreeTags = options.contextFreeTags || [];
const onToken = options.onToken || (()=>{});
const RESERVED_CHARS = [
closeTag,
openTag,
pluginHelper.QUOTEMARK,
pluginHelper.BACKSLASH,
pluginHelper.SPACE,
pluginHelper.TAB,
pluginHelper.EQ,
pluginHelper.N,
EM
];
const NOT_CHAR_TOKENS = [
openTag,
pluginHelper.SPACE,
pluginHelper.TAB,
pluginHelper.N
];
const WHITESPACES = [
pluginHelper.SPACE,
pluginHelper.TAB
];
const SPECIAL_CHARS = [
pluginHelper.EQ,
pluginHelper.SPACE,
pluginHelper.TAB
];
const isCharReserved = (char)=>RESERVED_CHARS.indexOf(char) >= 0;
const isNewLine = (char)=>char === pluginHelper.N;
const isWhiteSpace = (char)=>WHITESPACES.indexOf(char) >= 0;
const isCharToken = (char)=>NOT_CHAR_TOKENS.indexOf(char) === -1;
const isSpecialChar = (char)=>SPECIAL_CHARS.indexOf(char) >= 0;
const isEscapableChar = (char)=>char === openTag || char === closeTag || char === pluginHelper.BACKSLASH;
const isEscapeChar = (char)=>char === pluginHelper.BACKSLASH;
const onSkip = ()=>{
col++;
};
const unq = (val)=>unquote(trimChar(val, pluginHelper.QUOTEMARK));
const checkContextFreeMode = (name, isClosingTag)=>{
if (contextFreeTag !== '' && isClosingTag) {
contextFreeTag = '';
}
if (contextFreeTag === '' && contextFreeTags.includes(name)) {
contextFreeTag = name;
}
};
const chars = createCharGrabber(buffer, {
onSkip
});
/**
* Emits newly created token to subscriber
* @param {Number} type
* @param {String} value
*/ function emitToken(type, value) {
const token = createToken(type, value, row, col);
onToken(token);
tokenIndex += 1;
tokens[tokenIndex] = token;
}
function nextTagState(tagChars, isSingleValueTag) {
if (tagMode === TAG_STATE_ATTR) {
const validAttrName = (char)=>!(char === pluginHelper.EQ || isWhiteSpace(char));
const name = tagChars.grabWhile(validAttrName);
const isEnd = tagChars.isLast();
const isValue = tagChars.getCurr() !== pluginHelper.EQ;
tagChars.skip();
if (isEnd || isValue) {
emitToken(TYPE_ATTR_VALUE, unq(name));
} else {
emitToken(TYPE_ATTR_NAME, name);
}
if (isEnd) {
return TAG_STATE_NAME;
}
if (isValue) {
return TAG_STATE_ATTR;
}
return TAG_STATE_VALUE;
}
if (tagMode === TAG_STATE_VALUE) {
let stateSpecial = false;
const validAttrValue = (char)=>{
// const isEQ = char === EQ;
const isQM = char === pluginHelper.QUOTEMARK;
const prevChar = tagChars.getPrev();
const nextChar = tagChars.getNext();
const isPrevSLASH = prevChar === pluginHelper.BACKSLASH;
const isNextEQ = nextChar === pluginHelper.EQ;
const isWS = isWhiteSpace(char);
// const isPrevWS = isWhiteSpace(prevChar);
const isNextWS = isWhiteSpace(nextChar);
if (stateSpecial && isSpecialChar(char)) {
return true;
}
if (isQM && !isPrevSLASH) {
stateSpecial = !stateSpecial;
if (!stateSpecial && !(isNextEQ || isNextWS)) {
return false;
}
}
if (!isSingleValueTag) {
return isWS === false;
// return (isEQ || isWS) === false;
}
return true;
};
const name1 = tagChars.grabWhile(validAttrValue);
tagChars.skip();
emitToken(TYPE_ATTR_VALUE, unq(name1));
if (tagChars.isLast()) {
return TAG_STATE_NAME;
}
return TAG_STATE_ATTR;
}
const validName = (char)=>!(char === pluginHelper.EQ || isWhiteSpace(char) || tagChars.isLast());
const name2 = tagChars.grabWhile(validName);
emitToken(TYPE_TAG, name2);
checkContextFreeMode(name2);
tagChars.skip();
// in cases when we has [url=someval]GET[/url] and we dont need to parse all
if (isSingleValueTag) {
return TAG_STATE_VALUE;
}
const hasEQ = tagChars.includes(pluginHelper.EQ);
return hasEQ ? TAG_STATE_ATTR : TAG_STATE_VALUE;
}
function stateTag() {
const currChar = chars.getCurr();
const nextChar = chars.getNext();
chars.skip();
// detect case where we have '[My word [tag][/tag]' or we have '[My last line word'
const substr = chars.substrUntilChar(closeTag);
const hasInvalidChars = substr.length === 0 || substr.indexOf(openTag) >= 0;
if (isCharReserved(nextChar) || hasInvalidChars || chars.isLast()) {
emitToken(TYPE_WORD, currChar);
return STATE_WORD;
}
// [myTag ]
const isNoAttrsInTag = substr.indexOf(pluginHelper.EQ) === -1;
// [/myTag]
const isClosingTag = substr[0] === pluginHelper.SLASH;
if (isNoAttrsInTag || isClosingTag) {
const name = chars.grabWhile((char)=>char !== closeTag);
chars.skip(); // skip closeTag
emitToken(TYPE_TAG, name);
checkContextFreeMode(name, isClosingTag);
return STATE_WORD;
}
return STATE_TAG_ATTRS;
}
function stateAttrs() {
const silent = true;
const tagStr = chars.grabWhile((char)=>char !== closeTag, silent);
const tagGrabber = createCharGrabber(tagStr, {
onSkip
});
const hasSpace = tagGrabber.includes(pluginHelper.SPACE);
tagMode = TAG_STATE_NAME;
while(tagGrabber.hasNext()){
tagMode = nextTagState(tagGrabber, !hasSpace);
}
chars.skip(); // skip closeTag
return STATE_WORD;
}
function stateWord() {
if (isNewLine(chars.getCurr())) {
emitToken(TYPE_NEW_LINE, chars.getCurr());
chars.skip();
col = 0;
row++;
return STATE_WORD;
}
if (isWhiteSpace(chars.getCurr())) {
const word = chars.grabWhile(isWhiteSpace);
emitToken(TYPE_SPACE, word);
return STATE_WORD;
}
if (chars.getCurr() === openTag) {
if (contextFreeTag) {
const fullTagLen = openTag.length + pluginHelper.SLASH.length + contextFreeTag.length;
const fullTagName = `${openTag}${pluginHelper.SLASH}${contextFreeTag}`;
const foundTag = chars.grabN(fullTagLen);
const isEndContextFreeMode = foundTag === fullTagName;
if (isEndContextFreeMode) {
return STATE_TAG;
}
} else if (chars.includes(closeTag)) {
return STATE_TAG;
}
emitToken(TYPE_WORD, chars.getCurr());
chars.skip();
return STATE_WORD;
}
if (escapeTags) {
if (isEscapeChar(chars.getCurr())) {
const currChar = chars.getCurr();
const nextChar = chars.getNext();
chars.skip(); // skip the \ without emitting anything
if (isEscapableChar(nextChar)) {
chars.skip(); // skip past the [, ] or \ as well
emitToken(TYPE_WORD, nextChar);
return STATE_WORD;
}
emitToken(TYPE_WORD, currChar);
return STATE_WORD;
}
const isChar = (char)=>isCharToken(char) && !isEscapeChar(char);
const word1 = chars.grabWhile(isChar);
emitToken(TYPE_WORD, word1);
return STATE_WORD;
}
const word2 = chars.grabWhile(isCharToken);
emitToken(TYPE_WORD, word2);
return STATE_WORD;
}
function tokenize() {
stateMode = STATE_WORD;
while(chars.hasNext()){
switch(stateMode){
case STATE_TAG:
stateMode = stateTag();
break;
case STATE_TAG_ATTRS:
stateMode = stateAttrs();
break;
case STATE_WORD:
default:
stateMode = stateWord();
break;
}
}
tokens.length = tokenIndex + 1;
return tokens;
}
function isTokenNested(token) {
const value = openTag + pluginHelper.SLASH + token.getValue();
// potential bottleneck
return buffer.indexOf(value) > -1;
}
return {
tokenize,
isTokenNested
};
}
// type, value, line, row,
const TOKEN_TYPE_ID = 'type'; // 0;
const TOKEN_VALUE_ID = 'value'; // 1;
const TOKEN_COLUMN_ID = 'row'; // 2;
const TOKEN_LINE_ID = 'line'; // 3;
const TOKEN_TYPE_WORD = 1; // 'word';
const TOKEN_TYPE_TAG = 2; // 'tag';
const TOKEN_TYPE_ATTR_NAME = 3; // 'attr-name';
const TOKEN_TYPE_ATTR_VALUE = 4; // 'attr-value';
const TOKEN_TYPE_SPACE = 5; // 'space';
const TOKEN_TYPE_NEW_LINE = 6; // 'new-line';
/**
* @param {Token} token
* @returns {string}
*/ const getTokenValue = (token)=>{
if (token && typeof token[TOKEN_VALUE_ID] !== 'undefined') {
return token[TOKEN_VALUE_ID];
}
return '';
};
/**
* @param {Token}token
* @returns {number}
*/ const getTokenLine = (token)=>token && token[TOKEN_LINE_ID] || 0;
const getTokenColumn = (token)=>token && token[TOKEN_COLUMN_ID] || 0;
/**
* @param {Token} token
* @returns {boolean}
*/ const isTextToken = (token)=>{
if (token && typeof token[TOKEN_TYPE_ID] !== 'undefined') {
return token[TOKEN_TYPE_ID] === TOKEN_TYPE_SPACE || token[TOKEN_TYPE_ID] === TOKEN_TYPE_NEW_LINE || token[TOKEN_TYPE_ID] === TOKEN_TYPE_WORD;
}
return false;
};
/**
* @param {Token} token
* @returns {boolean}
*/ const isTagToken = (token)=>{
if (token && typeof token[TOKEN_TYPE_ID] !== 'undefined') {
return token[TOKEN_TYPE_ID] === TOKEN_TYPE_TAG;
}
return false;
};
const isTagEnd = (token)=>getTokenValue(token).charCodeAt(0) === SLASH.charCodeAt(0);
const isTagStart = (token)=>!isTagEnd(token);
const isAttrNameToken = (token)=>{
if (token && typeof token[TOKEN_TYPE_ID] !== 'undefined') {
return token[TOKEN_TYPE_ID] === TOKEN_TYPE_ATTR_NAME;
}
return false;
};
/**
* @param {Token} token
* @returns {boolean}
*/ const isAttrValueToken = (token)=>{
if (token && typeof token[TOKEN_TYPE_ID] !== 'undefined') {
return token[TOKEN_TYPE_ID] === TOKEN_TYPE_ATTR_VALUE;
}
return false;
};
const getTagName = (token)=>{
const value = getTokenValue(token);
return isTagEnd(token) ? value.slice(1) : value;
};
const convertTagToText = (token)=>{
let text = OPEN_BRAKET;
text += getTokenValue(token);
text += CLOSE_BRAKET;
return text;
};
class Token {
isEmpty() {
// eslint-disable-next-line no-restricted-globals
return isNaN(this[TOKEN_TYPE_ID]);
}
isText() {
return isTextToken(this);
}
isTag() {
return isTagToken(this);
}
isAttrName() {
return isAttrNameToken(this);
}
isAttrValue() {
return isAttrValueToken(this);
}
isStart() {
return isTagStart(this);
}
isEnd() {
return isTagEnd(this);
}
getName() {
return getTagName(this);
}
getValue() {
return getTokenValue(this);
}
getLine() {
return getTokenLine(this);
}
getColumn() {
return getTokenColumn(this);
}
toString() {
return convertTagToText(this);
}
/**
* @param {String} type
* @param {String} value
* @param line
* @param row
*/ constructor(type, value, line, row){
this[TOKEN_TYPE_ID] = Number(type);
this[TOKEN_VALUE_ID] = String(value);
this[TOKEN_LINE_ID] = Number(line);
this[TOKEN_COLUMN_ID] = Number(row);
}
}
const TYPE_WORD = TOKEN_TYPE_WORD;
const TYPE_TAG = TOKEN_TYPE_TAG;
const TYPE_ATTR_NAME = TOKEN_TYPE_ATTR_NAME;
const TYPE_ATTR_VALUE = TOKEN_TYPE_ATTR_VALUE;
const TYPE_SPACE = TOKEN_TYPE_SPACE;
const TYPE_NEW_LINE = TOKEN_TYPE_NEW_LINE;
/**
* @public
* @param {String} input
* @param {Object} opts
* @param {Function} opts.createTokenizer
* @param {Array<string>} opts.onlyAllowTags
* @param {Array<string>} opts.contextFreeTags
* @param {Boolean} opts.enableEscapeTags
* @param {String} opts.openTag
* @param {String} opts.closeTag
* @return {Array}
*/ const parse = (input, opts = {})=>{
const options = opts;
const openTag = options.openTag || pluginHelper.OPEN_BRAKET;
const closeTag = options.closeTag || pluginHelper.CLOSE_BRAKET;
let tokenizer = null;
/**
* Result AST of nodes
* @private
* @type {NodeList}
*/ const nodes = createList();
/**
* Temp buffer of nodes that's nested to another node
* @private
* @type {NodeList}
*/ const nestedNodes = createList();
/**
* Temp buffer of nodes [tag..]...[/tag]
* @private
* @type {NodeList}
*/ const tagNodes = createList();
/**
* Temp buffer of tag attributes
* @private
* @type {NodeList}
*/ const tagNodesAttrName = createList();
/**
* Cache for nested tags checks
*/ const nestedTagsMap = new Set();
/**
*
* @param token
* @returns {boolean}
*/ const isTokenNested = (token)=>{
const value = token.getValue();
if (!nestedTagsMap.has(value) && tokenizer.isTokenNested && tokenizer.isTokenNested(token)) {
nestedTagsMap.add(value);
return true;
}
return nestedTagsMap.has(value);
};
/**
* @param tagName
* @returns {boolean}
*/ const isTagNested = (tagName)=>Boolean(nestedTagsMap.has(tagName));
/**
* @private
* @param {String} value
* @return {boolean}
*/ const isAllowedTag = (value)=>{
if (options.onlyAllowTags && options.onlyAllowTags.length) {
return options.onlyAllowTags.indexOf(value) >= 0;
}
return true;
};
/**
* Flushes temp tag nodes and its attributes buffers
* @private
* @return {Array}
*/ const flushTagNodes = ()=>{
if (tagNodes.flushLast()) {
tagNodesAttrName.flushLast();
}
};
/**
* @private
* @return {Array}
*/ const getNodes = ()=>{
const lastNestedNode = nestedNodes.getLast();
if (lastNestedNode && Array.isArray(lastNestedNode.content)) {
return lastNestedNode.content;
}
return nodes.toArray();
};
/**
* @private
* @param {string|TagNode} node
*/ const appendNodes = (node)=>{
const items = getNodes();
if (Array.isArray(items)) {
if (pluginHelper.isTagNode(node)) {
if (isAllowedTag(node.tag)) {
items.push(node.toTagNode());
} else {
items.push(node.toTagStart({
openTag,
closeTag
}));
if (node.content.length) {
node.content.forEach((item)=>{
items.push(item);
});
items.push(node.toTagEnd({
openTag,
closeTag
}));
}
}
} else {
items.push(node);
}
}
};
/**
* @private
* @param {Token} token
*/ const handleTagStart = (token)=>{
flushTagNodes();
const tagNode = pluginHelper.TagNode.create(token.getValue());
const isNested = isTokenNested(token);
tagNodes.push(tagNode);
if (isNested) {
nestedNodes.push(tagNode);
} else {
appendNodes(tagNode);
}
};
/**
* @private
* @param {Token} token
*/ const handleTagEnd = (token)=>{
flushTagNodes();
const lastNestedNode = nestedNodes.flushLast();
if (lastNestedNode) {
appendNodes(lastNestedNode);
} else if (typeof options.onError === 'function') {
const tag = token.getValue();
const line = token.getLine();
const column = token.getColumn();
options.onError({
message: `Inconsistent tag '${tag}' on line ${line} and column ${column}`,
tagName: tag,
lineNumber: line,
columnNumber: column
});
}
};
/**
* @private
* @param {Token} token
*/ const handleTag = (token)=>{
// [tag]
if (token.isStart()) {
handleTagStart(token);
}
// [/tag]
if (token.isEnd()) {
handleTagEnd(token);
}
};
/**
* @private
* @param {Token} token
*/ const handleNode = (token)=>{
/**
* @type {TagNode}
*/ const lastTagNode = tagNodes.getLast();
const tokenValue = token.getValue();
const isNested = isTagNested(token);
if (lastTagNode) {
if (token.isAttrName()) {
tagNodesAttrName.push(tokenValue);
lastTagNode.attr(tagNodesAttrName.getLast(), '');
} else if (token.isAttrValue()) {
const attrName = tagNodesAttrName.getLast();
if (attrName) {
lastTagNode.attr(attrName, tokenValue);
tagNodesAttrName.flushLast();
} else {
lastTagNode.attr(tokenValue, tokenValue);
}
} else if (token.isText()) {
if (isNested) {
lastTagNode.append(tokenValue);
} else {
appendNodes(tokenValue);
}
} else if (token.isTag()) {
// if tag is not allowed, just past it as is
appendNodes(token.toString());
}
} else if (token.isText()) {
appendNodes(tokenValue);
} else if (token.isTag()) {
// if tag is not allowed, just past it as is
appendNodes(token.toString());
}
};
/**
* @private
* @param {Token} token
*/ const onToken = (token)=>{
if (token.isTag()) {
handleTag(token);
} else {
handleNode(token);
}
};
tokenizer = (opts.createTokenizer ? opts.createTokenizer : createLexer)(input, {
onToken,
openTag,
closeTag,
onlyAllowTags: options.onlyAllowTags,
contextFreeTags: options.contextFreeTags,
enableEscapeTags: options.enableEscapeTags
});
// eslint-disable-next-line no-unused-vars
tokenizer.tokenize();
return nodes.toArray();
};
function CharGrabber(source, options) {
const cursor = {
pos: 0,
len: source.length
};
const substrUntilChar = (char)=>{
const { pos } = cursor;
const idx = source.indexOf(char, pos);
return idx >= 0 ? source.substring(pos, idx) : '';
};
const includes = (val)=>source.indexOf(val, cursor.pos) >= 0;
const hasNext = ()=>cursor.len > cursor.pos;
const isLast = ()=>cursor.pos === cursor.len;
const skip = (num = 1, silent)=>{
cursor.pos += num;
if (options && options.onSkip && !silent) {
options.onSkip();
}
};
const rest = ()=>source.substring(cursor.pos);
const grabN = (num = 0)=>source.substring(cursor.pos, cursor.pos + num);
const curr = ()=>source[cursor.pos];
const prev = ()=>{
const prevPos = cursor.pos - 1;
return typeof source[prevPos] !== 'undefined' ? source[prevPos] : null;
};
const next = ()=>{
const nextPos = cursor.pos + 1;
return nextPos <= source.length - 1 ? source[nextPos] : null;
};
const grabWhile = (cond, silent)=>{
let start = 0;
if (hasNext()) {
start = cursor.pos;
while(hasNext() && cond(curr())){
skip(1, silent);
}
}
return source.substring(start, cursor.pos);
};
/**
* @type {skip}
*/ this.skip = skip;
/**
* @returns {Boolean}
*/ this.hasNext = hasNext;
/**
* @returns {String}
*/ this.getCurr = curr;
/**
* @returns {String}
*/ this.getRest = rest;
/**
* @returns {String}
*/ this.getNext = next;
/**
* @returns {String}
*/ this.getPrev = prev;
/**
* @returns {Boolean}
*/ this.isLast = isLast;
/**
* @returns {Boolean}
*/ this.includes = includes;
/**
* @param {Function} cond
* @param {Boolean} silent
* @return {String}
*/ this.grabWhile = grabWhile;
/**
* @param {Number} num
* @return {String}
*/ this.grabN = grabN;
/**
* Grabs rest of string until it find a char
* @param {String} char
* @return {String}
*/ this.substrUntilChar = substrUntilChar;
}
/**
* Creates a grabber wrapper for source string, that helps to iterate over string char by char
* @param {String} source
* @param {Object} options
* @param {Function} options.onSkip
* @return CharGrabber
*/ const createCharGrabber = (source, options)=>new CharGrabber(source, options);
/**
* Trims string from start and end by char
* @example
* trimChar('*hello*', '*') ==> 'hello'
* @param {String} str
* @param {String} charToRemove
* @returns {String}
*/ const trimChar = (str, charToRemove)=>{
while(str.charAt(0) === charToRemove){
// eslint-disable-next-line no-param-reassign
str = str.substring(1);
}
while(str.charAt(str.length - 1) === charToRemove){
// eslint-disable-next-line no-param-reassign
str = str.substring(0, str.length - 1);
}
return str;
};
/**
* Unquotes \" to "
* @param str
* @return {String}
*/ const unquote = (str)=>str.replace(BACKSLASH + QUOTEMARK, QUOTEMARK);
function NodeList(values = []) {
const nodes = values;
const getLast = ()=>Array.isArray(nodes) && nodes.length > 0 && typeof nodes[nodes.length - 1] !== 'undefined' ? nodes[nodes.length - 1] : null;
const flushLast = ()=>nodes.length ? nodes.pop() : false;
const push = (value)=>nodes.push(value);
const toArray = ()=>nodes;
this.push = push;
this.toArray = toArray;
this.getLast = getLast;
this.flushLast = flushLast;
}
/**
*
* @param values
* @return {NodeList}
*/ const createList = (values = [])=>new NodeList(values);
Object.defineProperty(exports, 'TagNode', {
enumerable: true,
get: function () { return pluginHelper.TagNode; }
});
exports.default = parse;
exports.parse = parse;
// for cases <!-- -->
const EM = '!';
/**
* Creates a Token entity class
* @param {Number} type
* @param {String} value
* @param {Number} r line number
* @param {Number} cl char number in line
*/ const createToken = (type, value, r = 0, cl = 0)=>new Token(type, value, r, cl);
/**
* @typedef {Object} Lexer
* @property {Function} tokenize
* @property {Function} isTokenNested
*/ /**
* @param {String} buffer
* @param {Object} options
* @param {Function} options.onToken
* @param {String} options.openTag
* @param {String} options.closeTag
* @param {Boolean} options.enableEscapeTags
* @return {Lexer}
*/ function createLexer(buffer, options = {}) {
const STATE_WORD = 0;
const STATE_TAG = 1;
const STATE_TAG_ATTRS = 2;
const TAG_STATE_NAME = 0;
const TAG_STATE_ATTR = 1;
const TAG_STATE_VALUE = 2;
let row = 0;
let col = 0;
let tokenIndex = -1;
let stateMode = STATE_WORD;
let tagMode = TAG_STATE_NAME;
let contextFreeTag = '';
const tokens = new Array(Math.floor(buffer.length));
const openTag = options.openTag || OPEN_BRAKET;
const closeTag = options.closeTag || CLOSE_BRAKET;
const escapeTags = !!options.enableEscapeTags;
const contextFreeTags = options.contextFreeTags || [];
const onToken = options.onToken || (()=>{});
const RESERVED_CHARS = [
closeTag,
openTag,
QUOTEMARK,
BACKSLASH,
SPACE,
TAB,
EQ,
N,
EM
];
const NOT_CHAR_TOKENS = [
openTag,
SPACE,
TAB,
N
];
const WHITESPACES = [
SPACE,
TAB
];
const SPECIAL_CHARS = [
EQ,
SPACE,
TAB
];
const isCharReserved = (char)=>RESERVED_CHARS.indexOf(char) >= 0;
const isNewLine = (char)=>char === N;
const isWhiteSpace = (char)=>WHITESPACES.indexOf(char) >= 0;
const isCharToken = (char)=>NOT_CHAR_TOKENS.indexOf(char) === -1;
const isSpecialChar = (char)=>SPECIAL_CHARS.indexOf(char) >= 0;
const isEscapableChar = (char)=>char === openTag || char === closeTag || char === BACKSLASH;
const isEscapeChar = (char)=>char === BACKSLASH;
const onSkip = ()=>{
col++;
};
const unq = (val)=>unquote(trimChar(val, QUOTEMARK));
const checkContextFreeMode = (name, isClosingTag)=>{
if (contextFreeTag !== '' && isClosingTag) {
contextFreeTag = '';
}
if (contextFreeTag === '' && contextFreeTags.includes(name)) {
contextFreeTag = name;
}
};
const chars = createCharGrabber(buffer, {
onSkip
});
/**
* Emits newly created token to subscriber
* @param {Number} type
* @param {String} value
*/ function emitToken(type, value) {
const token = createToken(type, value, row, col);
onToken(token);
tokenIndex += 1;
tokens[tokenIndex] = token;
}
function nextTagState(tagChars, isSingleValueTag) {
if (tagMode === TAG_STATE_ATTR) {
const validAttrName = (char)=>!(char === EQ || isWhiteSpace(char));
const name = tagChars.grabWhile(validAttrName);
const isEnd = tagChars.isLast();
const isValue = tagChars.getCurr() !== EQ;
tagChars.skip();
if (isEnd || isValue) {
emitToken(TYPE_ATTR_VALUE, unq(name));
} else {
emitToken(TYPE_ATTR_NAME, name);
}
if (isEnd) {
return TAG_STATE_NAME;
}
if (isValue) {
return TAG_STATE_ATTR;
}
return TAG_STATE_VALUE;
}
if (tagMode === TAG_STATE_VALUE) {
let stateSpecial = false;
const validAttrValue = (char)=>{
// const isEQ = char === EQ;
const isQM = char === QUOTEMARK;
const prevChar = tagChars.getPrev();
const nextChar = tagChars.getNext();
const isPrevSLASH = prevChar === BACKSLASH;
const isNextEQ = nextChar === EQ;
const isWS = isWhiteSpace(char);
// const isPrevWS = isWhiteSpace(prevChar);
const isNextWS = isWhiteSpace(nextChar);
if (stateSpecial && isSpecialChar(char)) {
return true;
}
if (isQM && !isPrevSLASH) {
stateSpecial = !stateSpecial;
if (!stateSpecial && !(isNextEQ || isNextWS)) {
return false;
}
}
if (!isSingleValueTag) {
return isWS === false;
// return (isEQ || isWS) === false;
}
return true;
};
const name1 = tagChars.grabWhile(validAttrValue);
tagChars.skip();
emitToken(TYPE_ATTR_VALUE, unq(name1));
if (tagChars.isLast()) {
return TAG_STATE_NAME;
}
return TAG_STATE_ATTR;
}
const validName = (char)=>!(char === EQ || isWhiteSpace(char) || tagChars.isLast());
const name2 = tagChars.grabWhile(validName);
emitToken(TYPE_TAG, name2);
checkContextFreeMode(name2);
tagChars.skip();
// in cases when we has [url=someval]GET[/url] and we dont need to parse all
if (isSingleValueTag) {
return TAG_STATE_VALUE;
}
const hasEQ = tagChars.includes(EQ);
return hasEQ ? TAG_STATE_ATTR : TAG_STATE_VALUE;
}
function stateTag() {
const currChar = chars.getCurr();
const nextChar = chars.getNext();
chars.skip();
// detect case where we have '[My word [tag][/tag]' or we have '[My last line word'
const substr = chars.substrUntilChar(closeTag);
const hasInvalidChars = substr.length === 0 || substr.indexOf(openTag) >= 0;
if (isCharReserved(nextChar) || hasInvalidChars || chars.isLast()) {
emitToken(TYPE_WORD, currChar);
return STATE_WORD;
}
// [myTag ]
const isNoAttrsInTag = substr.indexOf(EQ) === -1;
// [/myTag]
const isClosingTag = substr[0] === SLASH;
if (isNoAttrsInTag || isClosingTag) {
const name = chars.grabWhile((char)=>char !== closeTag);
chars.skip(); // skip closeTag
emitToken(TYPE_TAG, name);
checkContextFreeMode(name, isClosingTag);
return STATE_WORD;
}
return STATE_TAG_ATTRS;
}
function stateAttrs() {
const silent = true;
const tagStr = chars.grabWhile((char)=>char !== closeTag, silent);
const tagGrabber = createCharGrabber(tagStr, {
onSkip
});
const hasSpace = tagGrabber.includes(SPACE);
tagMode = TAG_STATE_NAME;
while(tagGrabber.hasNext()){
tagMode = nextTagState(tagGrabber, !hasSpace);
}
chars.skip(); // skip closeTag
return STATE_WORD;
}
function stateWord() {
if (isNewLine(chars.getCurr())) {
emitToken(TYPE_NEW_LINE, chars.getCurr());
chars.skip();
col = 0;
row++;
return STATE_WORD;
}
if (isWhiteSpace(chars.getCurr())) {
const word = chars.grabWhile(isWhiteSpace);
emitToken(TYPE_SPACE, word);
return STATE_WORD;
}
if (chars.getCurr() === openTag) {
if (contextFreeTag) {
const fullTagLen = openTag.length + SLASH.length + contextFreeTag.length;
const fullTagName = `${openTag}${SLASH}${contextFreeTag}`;
const foundTag = chars.grabN(fullTagLen);
const isEndContextFreeMode = foundTag === fullTagName;
if (isEndContextFreeMode) {
return STATE_TAG;
}
} else if (chars.includes(closeTag)) {
return STATE_TAG;
}
emitToken(TYPE_WORD, chars.getCurr());
chars.skip();
return STATE_WORD;
}
if (escapeTags) {
if (isEscapeChar(chars.getCurr())) {
const currChar = chars.getCurr();
const nextChar = chars.getNext();
chars.skip(); // skip the \ without emitting anything
if (isEscapableChar(nextChar)) {
chars.skip(); // skip past the [, ] or \ as well
emitToken(TYPE_WORD, nextChar);
return STATE_WORD;
}
emitToken(TYPE_WORD, currChar);
return STATE_WORD;
}
const isChar = (char)=>isCharToken(char) && !isEscapeChar(char);
const word1 = chars.grabWhile(isChar);
emitToken(TYPE_WORD, word1);
return STATE_WORD;
}
const word2 = chars.grabWhile(isCharToken);
emitToken(TYPE_WORD, word2);
return STATE_WORD;
}
function tokenize() {
stateMode = STATE_WORD;
while(chars.hasNext()){
switch(stateMode){
case STATE_TAG:
stateMode = stateTag();
break;
case STATE_TAG_ATTRS:
stateMode = stateAttrs();
break;
case STATE_WORD:
default:
stateMode = stateWord();
break;
}
}
tokens.length = tokenIndex + 1;
return tokens;
}
function isTokenNested(token) {
const value = openTag + SLASH + token.getValue();
// potential bottleneck
return buffer.indexOf(value) > -1;
}
return {
tokenize,
isTokenNested
};
}
Object.defineProperty(exports, '__esModule', { value: true });
/**
* @public
* @param {String} input
* @param {Object} opts
* @param {Function} opts.createTokenizer
* @param {Array<string>} opts.onlyAllowTags
* @param {Array<string>} opts.contextFreeTags
* @param {Boolean} opts.enableEscapeTags
* @param {String} opts.openTag
* @param {String} opts.closeTag
* @return {Array}
*/ const parse = (input, opts = {})=>{
const options = opts;
const openTag = options.openTag || OPEN_BRAKET;
const closeTag = options.closeTag || CLOSE_BRAKET;
let tokenizer = null;
/**
* Result AST of nodes
* @private
* @type {NodeList}
*/ const nodes = createList();
/**
* Temp buffer of nodes that's nested to another node
* @private
* @type {NodeList}
*/ const nestedNodes = createList();
/**
* Temp buffer of nodes [tag..]...[/tag]
* @private
* @type {NodeList}
*/ const tagNodes = createList();
/**
* Temp buffer of tag attributes
* @private
* @type {NodeList}
*/ const tagNodesAttrName = createList();
/**
* Cache for nested tags checks
*/ const nestedTagsMap = new Set();
/**
*
* @param token
* @returns {boolean}
*/ const isTokenNested = (token)=>{
const value = token.getValue();
if (!nestedTagsMap.has(value) && tokenizer.isTokenNested && tokenizer.isTokenNested(token)) {
nestedTagsMap.add(value);
return true;
}
return nestedTagsMap.has(value);
};
/**
* @param tagName
* @returns {boolean}
*/ const isTagNested = (tagName)=>Boolean(nestedTagsMap.has(tagName));
/**
* @private
* @param {String} value
* @return {boolean}
*/ const isAllowedTag = (value)=>{
if (options.onlyAllowTags && options.onlyAllowTags.length) {
return options.onlyAllowTags.indexOf(value) >= 0;
}
return true;
};
/**
* Flushes temp tag nodes and its attributes buffers
* @private
* @return {Array}
*/ const flushTagNodes = ()=>{
if (tagNodes.flushLast()) {
tagNodesAttrName.flushLast();
}
};
/**
* @private
* @return {Array}
*/ const getNodes = ()=>{
const lastNestedNode = nestedNodes.getLast();
if (lastNestedNode && Array.isArray(lastNestedNode.content)) {
return lastNestedNode.content;
}
return nodes.toArray();
};
/**
* @private
* @param {string|TagNode} node
*/ const appendNodes = (node)=>{
const items = getNodes();
if (Array.isArray(items)) {
if (isTagNode(node)) {
if (isAllowedTag(node.tag)) {
items.push(node.toTagNode());
} else {
items.push(node.toTagStart({
openTag,
closeTag
}));
if (node.content.length) {
node.content.forEach((item)=>{
items.push(item);
});
items.push(node.toTagEnd({
openTag,
closeTag
}));
}
}
} else {
items.push(node);
}
}
};
/**
* @private
* @param {Token} token
*/ const handleTagStart = (token)=>{
flushTagNodes();
const tagNode = TagNode.create(token.getValue());
const isNested = isTokenNested(token);
tagNodes.push(tagNode);
if (isNested) {
nestedNodes.push(tagNode);
} else {
appendNodes(tagNode);
}
};
/**
* @private
* @param {Token} token
*/ const handleTagEnd = (token)=>{
flushTagNodes();
const lastNestedNode = nestedNodes.flushLast();
if (lastNestedNode) {
appendNodes(lastNestedNode);
} else if (typeof options.onError === 'function') {
const tag = token.getValue();
const line = token.getLine();
const column = token.getColumn();
options.onError({
message: `Inconsistent tag '${tag}' on line ${line} and column ${column}`,
tagName: tag,
lineNumber: line,
columnNumber: column
});
}
};
/**
* @private
* @param {Token} token
*/ const handleTag = (token)=>{
// [tag]
if (token.isStart()) {
handleTagStart(token);
}
// [/tag]
if (token.isEnd()) {
handleTagEnd(token);
}
};
/**
* @private
* @param {Token} token
*/ const handleNode = (token)=>{
/**
* @type {TagNode}
*/ const lastTagNode = tagNodes.getLast();
const tokenValue = token.getValue();
const isNested = isTagNested(token);
if (lastTagNode) {
if (token.isAttrName()) {
tagNodesAttrName.push(tokenValue);
lastTagNode.attr(tagNodesAttrName.getLast(), '');
} else if (token.isAttrValue()) {
const attrName = tagNodesAttrName.getLast();
if (attrName) {
lastTagNode.attr(attrName, tokenValue);
tagNodesAttrName.flushLast();
} else {
lastTagNode.attr(tokenValue, tokenValue);
}
} else if (token.isText()) {
if (isNested) {
lastTagNode.append(tokenValue);
} else {
appendNodes(tokenValue);
}
} else if (token.isTag()) {
// if tag is not allowed, just past it as is
appendNodes(token.toString());
}
} else if (token.isText()) {
appendNodes(tokenValue);
} else if (token.isTag()) {
// if tag is not allowed, just past it as is
appendNodes(token.toString());
}
};
/**
* @private
* @param {Token} token
*/ const onToken = (token)=>{
if (token.isTag()) {
handleTag(token);
} else {
handleNode(token);
}
};
tokenizer = (opts.createTokenizer ? opts.createTokenizer : createLexer)(input, {
onToken,
openTag,
closeTag,
onlyAllowTags: options.onlyAllowTags,
contextFreeTags: options.contextFreeTags,
enableEscapeTags: options.enableEscapeTags
});
// eslint-disable-next-line no-unused-vars
tokenizer.tokenize();
return nodes.toArray();
};
exports.TagNode = TagNode;
exports.default = parse;
exports.parse = parse;
Object.defineProperty(exports, '__esModule', { value: true });
}));

@@ -1,1 +0,1 @@

!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).BbobParser={})}(this,function(t){"use strict";let e=t=>"object"==typeof t&&!!t.tag,r=t=>"string"==typeof t,n=(t,e,r)=>Object.keys(t).reduce(e,r),s=t=>e(t)?t.content.reduce((t,e)=>t+s(e),0):r(t)?t.length:0,i=(t,e)=>{t.content.push(e)},l=t=>t.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#039;").replace(/(javascript|data|vbscript):/gi,"$1%3A"),a=(t,e)=>{let r=typeof e,n={boolean:()=>e?""+t:"",number:()=>`${t}="${e}"`,string:()=>`${t}="${l(e)}"`,object:()=>`${t}="${l(JSON.stringify(e))}"`};return n[r]?n[r]():""},o=t=>null==t?"":n(t,(e,r)=>[...e,a(r,t[r])],[""]).join(" "),u=t=>n(t,(e,r)=>t[r]===r?t[r]:null,null),g=(t,e)=>{let r=u(e);if(r){let n=a(t,r),s={...e};delete s[r];let i=o(s);return`${n}${i}`}return`${t}${o(e)}`};class h{attr(t,e){return void 0!==e&&(this.attrs[t]=e),this.attrs[t]}append(t){return i(this,t)}get length(){return s(this)}toTagStart({openTag:t="[",closeTag:e="]"}={}){let r=g(this.tag,this.attrs);return`${t}${r}${e}`}toTagEnd({openTag:t="[",closeTag:e="]"}={}){return`${t}/${this.tag}${e}`}toTagNode(){return new h(this.tag.toLowerCase(),this.attrs,this.content)}toString({openTag:t="[",closeTag:e="]"}={}){let r=0===this.content.length,n=this.content.reduce((r,n)=>r+n.toString({openTag:t,closeTag:e}),""),s=this.toTagStart({openTag:t,closeTag:e});return r?s:`${s}${n}${this.toTagEnd({openTag:t,closeTag:e})}`}constructor(t,e,r){this.tag=t,this.attrs=e,this.content=Array.isArray(r)?r:[r]}}h.create=(t,e={},r=[])=>new h(t,e,r),h.isOf=(t,e)=>t.tag===e;let c="type",f="value",p="line",d=t=>t&&void 0!==t[f]?t[f]:"",b=t=>t&&t[p]||0,T=t=>t&&t.row||0,y=t=>!!t&&void 0!==t[c]&&(5===t[c]||6===t[c]||1===t[c]),N=t=>!!t&&void 0!==t[c]&&2===t[c],x=t=>47===d(t).charCodeAt(0),A=t=>!x(t),$=t=>!!t&&void 0!==t[c]&&3===t[c],k=t=>!!t&&void 0!==t[c]&&4===t[c],m=t=>{let e=d(t);return x(t)?e.slice(1):e},L=t=>d(t)+"]";class C{isEmpty(){return isNaN(this[c])}isText(){return y(this)}isTag(){return N(this)}isAttrName(){return $(this)}isAttrValue(){return k(this)}isStart(){return A(this)}isEnd(){return x(this)}getName(){return m(this)}getValue(){return d(this)}getLine(){return b(this)}getColumn(){return T(this)}toString(){return L(this)}constructor(t,e,r,n){this[c]=Number(t),this[f]=String(e),this[p]=Number(r),this.row=Number(n)}}function w(t,e){let r={pos:0,len:t.length},n=e=>{let{pos:n}=r,s=t.indexOf(e,n);return s>=0?t.substring(n,s):""},s=e=>t.indexOf(e,r.pos)>=0,i=()=>r.len>r.pos,l=(t=1,n)=>{r.pos+=t,e&&e.onSkip&&!n&&e.onSkip()},a=()=>t.substring(r.pos),o=(e=0)=>t.substring(r.pos,r.pos+e),u=()=>t[r.pos],g=()=>{let e=r.pos-1;return void 0!==t[e]?t[e]:null},h=()=>{let e=r.pos+1;return e<=t.length-1?t[e]:null},c=(e,n)=>{let s=0;if(i())for(s=r.pos;i()&&e(u());)l(1,n);return t.substring(s,r.pos)};this.skip=l,this.hasNext=i,this.getCurr=u,this.getRest=a,this.getNext=h,this.getPrev=g,this.isLast=()=>r.pos===r.len,this.includes=s,this.grabWhile=c,this.grabN=o,this.substrUntilChar=n}let S=(t,e)=>new w(t,e),v=(t,e)=>{for(;t.charAt(0)===e;)t=t.substring(1);for(;t.charAt(t.length-1)===e;)t=t.substring(0,t.length-1);return t},O=t=>t.replace('\\"','"');function E(t=[]){let e=()=>Array.isArray(t)&&t.length>0&&void 0!==t[t.length-1]?t[t.length-1]:null,r=()=>!!t.length&&t.pop(),n=e=>t.push(e);this.push=n,this.toArray=()=>t,this.getLast=e,this.flushLast=r}let W=(t=[])=>new E(t),V=(t,e,r=0,n=0)=>new C(t,e,r,n);function j(t,e={}){let r=0,n=0,s=-1,i=0,l=0,a="",o=Array(Math.floor(t.length)),u=e.openTag||"[",g=e.closeTag||"]",h=!!e.enableEscapeTags,c=e.contextFreeTags||[],f=e.onToken||(()=>{}),p=[g,u,'"',"\\"," "," ","=","\n","!"],d=[u," "," ","\n"],b=[" "," "],T=["="," "," "],y=t=>p.indexOf(t)>=0,N=t=>"\n"===t,x=t=>b.indexOf(t)>=0,A=t=>-1===d.indexOf(t),$=t=>T.indexOf(t)>=0,k=t=>t===u||t===g||"\\"===t,m=t=>"\\"===t,L=()=>{n++},C=t=>O(v(t,'"')),w=(t,e)=>{""!==a&&e&&(a=""),""===a&&c.includes(t)&&(a=t)},E=S(t,{onSkip:L});function W(t,e){let i=V(t,e,r,n);f(i),o[s+=1]=i}return{tokenize:function(){for(i=0;E.hasNext();)switch(i){case 1:i=function(){let t=E.getCurr(),e=E.getNext();E.skip();let r=E.substrUntilChar(g),n=0===r.length||r.indexOf(u)>=0;if(y(e)||n||E.isLast())return W(1,t),0;let s=-1===r.indexOf("="),i="/"===r[0];if(s||i){let l=E.grabWhile(t=>t!==g);return E.skip(),W(2,l),w(l,i),0}return 2}();break;case 2:i=function(){let t=E.grabWhile(t=>t!==g,!0),e=S(t,{onSkip:L}),r=e.includes(" ");for(l=0;e.hasNext();)l=function(t,e){if(1===l){let r=t=>!("="===t||x(t)),n=t.grabWhile(r),s=t.isLast(),i="="!==t.getCurr();return(t.skip(),s||i?W(4,C(n)):W(3,n),s)?0:i?1:2}if(2===l){let a=!1,o=r=>{let n=t.getPrev(),s=t.getNext(),i=x(r),l=x(s);return!!(a&&$(r))||('"'!==r||"\\"===n||!!(a=!a)||"="===s||!!l)&&(!!e||!1===i)},u=t.grabWhile(o);return(t.skip(),W(4,C(u)),t.isLast())?0:1}let g=e=>!("="===e||x(e)||t.isLast()),h=t.grabWhile(g);if(W(2,h),w(h),t.skip(),e)return 2;let c=t.includes("=");return c?1:2}(e,!r);return E.skip(),0}();break;default:i=function(){if(N(E.getCurr()))return W(6,E.getCurr()),E.skip(),n=0,r++,0;if(x(E.getCurr())){let t=E.grabWhile(x);return W(5,t),0}if(E.getCurr()===u){if(a){let e=u.length+1+a.length,s=`${u}/${a}`,i=E.grabN(e);if(i===s)return 1}else if(E.includes(g))return 1;return W(1,E.getCurr()),E.skip(),0}if(h){if(m(E.getCurr())){let l=E.getCurr(),o=E.getNext();return(E.skip(),k(o))?(E.skip(),W(1,o),0):(W(1,l),0)}let c=t=>A(t)&&!m(t),f=E.grabWhile(c);return W(1,f),0}let p=E.grabWhile(A);return W(1,p),0}()}return o.length=s+1,o},isTokenNested:function(e){let r=u+"/"+e.getValue();return t.indexOf(r)>-1}}}let z=(t,r={})=>{let n=r.openTag||"[",s=r.closeTag||"]",i=null,l=W(),a=W(),o=W(),u=W(),g=new Set,c=t=>{let e=t.getValue();return!g.has(e)&&i.isTokenNested&&i.isTokenNested(t)?(g.add(e),!0):g.has(e)},f=t=>Boolean(g.has(t)),p=t=>!r.onlyAllowTags||!r.onlyAllowTags.length||r.onlyAllowTags.indexOf(t)>=0,d=()=>{o.flushLast()&&u.flushLast()},b=()=>{let t=a.getLast();return t&&Array.isArray(t.content)?t.content:l.toArray()},T=t=>{let r=b();Array.isArray(r)&&(e(t)?p(t.tag)?r.push(t.toTagNode()):(r.push(t.toTagStart({openTag:n,closeTag:s})),t.content.length&&(t.content.forEach(t=>{r.push(t)}),r.push(t.toTagEnd({openTag:n,closeTag:s})))):r.push(t))},y=t=>{d();let e=h.create(t.getValue()),r=c(t);o.push(e),r?a.push(e):T(e)},N=t=>{d();let e=a.flushLast();if(e)T(e);else if("function"==typeof r.onError){let n=t.getValue(),s=t.getLine(),i=t.getColumn();r.onError({message:`Inconsistent tag '${n}' on line ${s} and column ${i}`,tagName:n,lineNumber:s,columnNumber:i})}},x=t=>{t.isStart()&&y(t),t.isEnd()&&N(t)},A=t=>{let e=o.getLast(),r=t.getValue(),n=f(t);if(e){if(t.isAttrName())u.push(r),e.attr(u.getLast(),"");else if(t.isAttrValue()){let s=u.getLast();s?(e.attr(s,r),u.flushLast()):e.attr(r,r)}else t.isText()?n?e.append(r):T(r):t.isTag()&&T(t.toString())}else t.isText()?T(r):t.isTag()&&T(t.toString())},$=t=>{t.isTag()?x(t):A(t)};return(i=(r.createTokenizer?r.createTokenizer:j)(t,{onToken:$,openTag:n,closeTag:s,onlyAllowTags:r.onlyAllowTags,contextFreeTags:r.contextFreeTags,enableEscapeTags:r.enableEscapeTags})).tokenize(),l.toArray()};t.TagNode=h,t.default=z,t.parse=z,Object.defineProperty(t,"__esModule",{value:!0})});
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("@bbob/plugin-helper")):"function"==typeof define&&define.amd?define(["exports","@bbob/plugin-helper"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).BbobParser={},e.pluginHelper)}(this,function(e,t){"use strict";let r="type",n="value",s="line",i=e=>e&&void 0!==e[n]?e[n]:"",l=e=>e&&e[s]||0,u=e=>e&&e.row||0,a=e=>!!e&&void 0!==e[r]&&(5===e[r]||6===e[r]||1===e[r]),o=e=>!!e&&void 0!==e[r]&&2===e[r],g=e=>i(e).charCodeAt(0)===t.SLASH.charCodeAt(0),h=e=>!g(e),f=e=>!!e&&void 0!==e[r]&&3===e[r],p=e=>!!e&&void 0!==e[r]&&4===e[r],A=e=>{let t=i(e);return g(e)?t.slice(1):t},c=e=>(t.OPEN_BRAKET,i(e)+t.CLOSE_BRAKET);class d{isEmpty(){return isNaN(this[r])}isText(){return a(this)}isTag(){return o(this)}isAttrName(){return f(this)}isAttrValue(){return p(this)}isStart(){return h(this)}isEnd(){return g(this)}getName(){return A(this)}getValue(){return i(this)}getLine(){return l(this)}getColumn(){return u(this)}toString(){return c(this)}constructor(e,t,i,l){this[r]=Number(e),this[n]=String(t),this[s]=Number(i),this.row=Number(l)}}function T(e,t){let r={pos:0,len:e.length},n=t=>{let{pos:n}=r,s=e.indexOf(t,n);return s>=0?e.substring(n,s):""},s=t=>e.indexOf(t,r.pos)>=0,i=()=>r.len>r.pos,l=(e=1,n)=>{r.pos+=e,t&&t.onSkip&&!n&&t.onSkip()},u=()=>e.substring(r.pos),a=(t=0)=>e.substring(r.pos,r.pos+t),o=()=>e[r.pos],g=()=>{let t=r.pos-1;return void 0!==e[t]?e[t]:null},h=()=>{let t=r.pos+1;return t<=e.length-1?e[t]:null},f=(t,n)=>{let s=0;if(i())for(s=r.pos;i()&&t(o());)l(1,n);return e.substring(s,r.pos)};this.skip=l,this.hasNext=i,this.getCurr=o,this.getRest=u,this.getNext=h,this.getPrev=g,this.isLast=()=>r.pos===r.len,this.includes=s,this.grabWhile=f,this.grabN=a,this.substrUntilChar=n}let b=(e,t)=>new T(e,t),E=(e,t)=>{for(;e.charAt(0)===t;)e=e.substring(1);for(;e.charAt(e.length-1)===t;)e=e.substring(0,e.length-1);return e},S=e=>e.replace(t.BACKSLASH+t.QUOTEMARK,t.QUOTEMARK);function N(e=[]){let t=()=>Array.isArray(e)&&e.length>0&&void 0!==e[e.length-1]?e[e.length-1]:null,r=()=>!!e.length&&e.pop(),n=t=>e.push(t);this.push=n,this.toArray=()=>e,this.getLast=t,this.flushLast=r}let L=(e=[])=>new N(e),C=(e,t,r=0,n=0)=>new d(e,t,r,n);function x(e,r={}){let n=0,s=0,i=-1,l=0,u=0,a="",o=Array(Math.floor(e.length)),g=r.openTag||t.OPEN_BRAKET,h=r.closeTag||t.CLOSE_BRAKET,f=!!r.enableEscapeTags,p=r.contextFreeTags||[],A=r.onToken||(()=>{}),c=[h,g,t.QUOTEMARK,t.BACKSLASH,t.SPACE,t.TAB,t.EQ,t.N,"!"],d=[g,t.SPACE,t.TAB,t.N],T=[t.SPACE,t.TAB],N=[t.EQ,t.SPACE,t.TAB],L=e=>c.indexOf(e)>=0,x=e=>e===t.N,y=e=>T.indexOf(e)>=0,k=e=>-1===d.indexOf(e),O=e=>N.indexOf(e)>=0,m=e=>e===g||e===h||e===t.BACKSLASH,B=e=>e===t.BACKSLASH,K=()=>{s++},P=e=>S(E(e,t.QUOTEMARK)),Q=(e,t)=>{""!==a&&t&&(a=""),""===a&&p.includes(e)&&(a=e)},w=b(e,{onSkip:K});function R(e,t){let r=C(e,t,n,s);A(r),o[i+=1]=r}return{tokenize:function(){for(l=0;w.hasNext();)switch(l){case 1:l=function(){let e=w.getCurr(),r=w.getNext();w.skip();let n=w.substrUntilChar(h),s=0===n.length||n.indexOf(g)>=0;if(L(r)||s||w.isLast())return R(1,e),0;let i=-1===n.indexOf(t.EQ),l=n[0]===t.SLASH;if(i||l){let u=w.grabWhile(e=>e!==h);return w.skip(),R(2,u),Q(u,l),0}return 2}();break;case 2:l=function(){let e=w.grabWhile(e=>e!==h,!0),r=b(e,{onSkip:K}),n=r.includes(t.SPACE);for(u=0;r.hasNext();)u=function(e,r){if(1===u){let n=e=>!(e===t.EQ||y(e)),s=e.grabWhile(n),i=e.isLast(),l=e.getCurr()!==t.EQ;return(e.skip(),i||l?R(4,P(s)):R(3,s),i)?0:l?1:2}if(2===u){let a=!1,o=n=>{let s=n===t.QUOTEMARK,i=e.getPrev(),l=e.getNext(),u=i===t.BACKSLASH,o=l===t.EQ,g=y(n),h=y(l);return!!(a&&O(n))||(!s||!!u||!!(a=!a)||!!o||!!h)&&(!!r||!1===g)},g=e.grabWhile(o);return(e.skip(),R(4,P(g)),e.isLast())?0:1}let h=r=>!(r===t.EQ||y(r)||e.isLast()),f=e.grabWhile(h);if(R(2,f),Q(f),e.skip(),r)return 2;let p=e.includes(t.EQ);return p?1:2}(r,!n);return w.skip(),0}();break;default:l=function(){if(x(w.getCurr()))return R(6,w.getCurr()),w.skip(),s=0,n++,0;if(y(w.getCurr())){let e=w.grabWhile(y);return R(5,e),0}if(w.getCurr()===g){if(a){let r=g.length+t.SLASH.length+a.length,i=`${g}${t.SLASH}${a}`,l=w.grabN(r);if(l===i)return 1}else if(w.includes(h))return 1;return R(1,w.getCurr()),w.skip(),0}if(f){if(B(w.getCurr())){let u=w.getCurr(),o=w.getNext();return(w.skip(),m(o))?(w.skip(),R(1,o),0):(R(1,u),0)}let p=e=>k(e)&&!B(e),A=w.grabWhile(p);return R(1,A),0}let c=w.grabWhile(k);return R(1,c),0}()}return o.length=i+1,o},isTokenNested:function(r){let n=g+t.SLASH+r.getValue();return e.indexOf(n)>-1}}}let y=(e,r={})=>{let n=r.openTag||t.OPEN_BRAKET,s=r.closeTag||t.CLOSE_BRAKET,i=null,l=L(),u=L(),a=L(),o=L(),g=new Set,h=e=>{let t=e.getValue();return!g.has(t)&&i.isTokenNested&&i.isTokenNested(e)?(g.add(t),!0):g.has(t)},f=e=>Boolean(g.has(e)),p=e=>!r.onlyAllowTags||!r.onlyAllowTags.length||r.onlyAllowTags.indexOf(e)>=0,A=()=>{a.flushLast()&&o.flushLast()},c=()=>{let e=u.getLast();return e&&Array.isArray(e.content)?e.content:l.toArray()},d=e=>{let r=c();Array.isArray(r)&&(t.isTagNode(e)?p(e.tag)?r.push(e.toTagNode()):(r.push(e.toTagStart({openTag:n,closeTag:s})),e.content.length&&(e.content.forEach(e=>{r.push(e)}),r.push(e.toTagEnd({openTag:n,closeTag:s})))):r.push(e))},T=e=>{A();let r=t.TagNode.create(e.getValue()),n=h(e);a.push(r),n?u.push(r):d(r)},b=e=>{A();let t=u.flushLast();if(t)d(t);else if("function"==typeof r.onError){let n=e.getValue(),s=e.getLine(),i=e.getColumn();r.onError({message:`Inconsistent tag '${n}' on line ${s} and column ${i}`,tagName:n,lineNumber:s,columnNumber:i})}},E=e=>{e.isStart()&&T(e),e.isEnd()&&b(e)},S=e=>{let t=a.getLast(),r=e.getValue(),n=f(e);if(t){if(e.isAttrName())o.push(r),t.attr(o.getLast(),"");else if(e.isAttrValue()){let s=o.getLast();s?(t.attr(s,r),o.flushLast()):t.attr(r,r)}else e.isText()?n?t.append(r):d(r):e.isTag()&&d(e.toString())}else e.isText()?d(r):e.isTag()&&d(e.toString())},N=e=>{e.isTag()?E(e):S(e)};return(i=(r.createTokenizer?r.createTokenizer:x)(e,{onToken:N,openTag:n,closeTag:s,onlyAllowTags:r.onlyAllowTags,contextFreeTags:r.contextFreeTags,enableEscapeTags:r.enableEscapeTags})).tokenize(),l.toArray()};Object.defineProperty(e,"TagNode",{enumerable:!0,get:function(){return t.TagNode}}),e.default=y,e.parse=y,Object.defineProperty(e,"__esModule",{value:!0})});
{
"name": "@bbob/parser",
"version": "2.9.0",
"version": "3.0.0",
"description": "A BBCode to AST Parser part of @bbob",

@@ -16,3 +16,3 @@ "keywords": [

"dependencies": {
"@bbob/plugin-helper": "^2.9.0"
"@bbob/plugin-helper": "^3.0.0"
},

@@ -65,4 +65,3 @@ "main": "lib/index.js",

"es"
],
"gitHead": "e5c741cb9a771ae5a7247ab73c2a2f90b8d83b28"
]
}
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc