@bbob/core
Advanced tools
Comparing version 3.0.2 to 4.0.0
1012
dist/index.js
(function (global, factory) { | ||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@bbob/parser')) : | ||
typeof define === 'function' && define.amd ? define(['exports', '@bbob/parser'], factory) : | ||
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.BbobCore = {}, global.parser)); | ||
})(this, (function (exports, parser) { 'use strict'; | ||
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.BbobCore = {})); | ||
})(this, (function (exports) { 'use strict'; | ||
/* eslint-disable no-plusplus */ const isObj = (value)=>typeof value === 'object'; | ||
const N = '\n'; | ||
const TAB = '\t'; | ||
const EQ = '='; | ||
const QUOTEMARK = '"'; | ||
const SPACE = ' '; | ||
const OPEN_BRAKET = '['; | ||
const CLOSE_BRAKET = ']'; | ||
const SLASH = '/'; | ||
const BACKSLASH = '\\'; | ||
function isTagNode(el) { | ||
return typeof el === 'object' && el !== null && 'tag' in el; | ||
} | ||
function isStringNode(el) { | ||
return typeof el === 'string'; | ||
} | ||
function keysReduce(obj, reduce, def) { | ||
const keys = Object.keys(obj); | ||
return keys.reduce((acc, key)=>reduce(acc, key, obj), def); | ||
} | ||
function getNodeLength(node) { | ||
if (isTagNode(node) && Array.isArray(node.content)) { | ||
return node.content.reduce((count, contentNode)=>{ | ||
return count + getNodeLength(contentNode); | ||
}, 0); | ||
} | ||
if (isStringNode(node)) { | ||
return String(node).length; | ||
} | ||
return 0; | ||
} | ||
function appendToNode(node, value) { | ||
if (Array.isArray(node.content)) { | ||
node.content.push(value); | ||
} | ||
} | ||
/** | ||
* Replaces " to &qquot; | ||
* @param {string} value | ||
*/ function escapeAttrValue(value) { | ||
return value.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''')// eslint-disable-next-line no-script-url | ||
.replace(/(javascript|data|vbscript):/gi, '$1%3A'); | ||
} | ||
/** | ||
* Accept name and value and return valid html5 attribute string | ||
*/ function attrValue(name, value) { | ||
// in case of performance | ||
switch(typeof value){ | ||
case 'boolean': | ||
return value ? `${name}` : ''; | ||
case 'number': | ||
return `${name}="${value}"`; | ||
case 'string': | ||
return `${name}="${escapeAttrValue(value)}"`; | ||
case 'object': | ||
return `${name}="${escapeAttrValue(JSON.stringify(value))}"`; | ||
default: | ||
return ''; | ||
} | ||
} | ||
/** | ||
* Transforms attrs to html params string | ||
* @example | ||
* attrsToString({ 'foo': true, 'bar': bar' }) => 'foo="true" bar="bar"' | ||
*/ function attrsToString(values) { | ||
// To avoid some malformed attributes | ||
if (values == null) { | ||
return ''; | ||
} | ||
return keysReduce(values, (arr, key, obj)=>[ | ||
...arr, | ||
attrValue(key, obj[key]) | ||
], [ | ||
'' | ||
]).join(' '); | ||
} | ||
/** | ||
* Gets value from | ||
* @example | ||
* getUniqAttr({ 'foo': true, 'bar': bar' }) => 'bar' | ||
*/ function getUniqAttr(attrs) { | ||
return keysReduce(attrs || {}, (res, key, obj)=>obj[key] === key ? obj[key] : null, null); | ||
} | ||
const getTagAttrs = (tag, params)=>{ | ||
const uniqAttr = getUniqAttr(params); | ||
if (uniqAttr) { | ||
const tagAttr = attrValue(tag, uniqAttr); | ||
const attrs = { | ||
...params | ||
}; | ||
delete attrs[String(uniqAttr)]; | ||
const attrsStr = attrsToString(attrs); | ||
return `${tagAttr}${attrsStr}`; | ||
} | ||
return `${tag}${attrsToString(params)}`; | ||
}; | ||
const renderContent = (content, openTag, closeTag)=>{ | ||
const toString = (node)=>{ | ||
if (isTagNode(node)) { | ||
return node.toString({ | ||
openTag, | ||
closeTag | ||
}); | ||
} | ||
return String(node); | ||
}; | ||
if (Array.isArray(content)) { | ||
return content.reduce((r, node)=>{ | ||
if (node !== null) { | ||
return r + toString(node); | ||
} | ||
return r; | ||
}, ''); | ||
} | ||
if (content) { | ||
return toString(content); | ||
} | ||
return null; | ||
}; | ||
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 content = this.content ? renderContent(this.content, openTag, closeTag) : ''; | ||
const tagStart = this.toTagStart({ | ||
openTag, | ||
closeTag | ||
}); | ||
if (this.content === null || Array.isArray(this.content) && this.content.length === 0) { | ||
return tagStart; | ||
} | ||
return `${tagStart}${content}${this.toTagEnd({ | ||
openTag, | ||
closeTag | ||
})}`; | ||
} | ||
static create(tag, attrs = {}, content = null) { | ||
return new TagNode(tag, attrs, content); | ||
} | ||
static isOf(node, type) { | ||
return node.tag === type; | ||
} | ||
constructor(tag, attrs, content){ | ||
this.tag = tag; | ||
this.attrs = attrs; | ||
this.content = content; | ||
} | ||
} | ||
// type, value, line, row, | ||
const TOKEN_TYPE_ID = 't'; // 0; | ||
const TOKEN_VALUE_ID = 'v'; // 1; | ||
const TOKEN_COLUMN_ID = 'r'; // 2; | ||
const TOKEN_LINE_ID = 'l'; // 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'; | ||
const getTokenValue = (token)=>{ | ||
if (token && typeof token[TOKEN_VALUE_ID] !== 'undefined') { | ||
return token[TOKEN_VALUE_ID]; | ||
} | ||
return ''; | ||
}; | ||
const getTokenLine = (token)=>token && token[TOKEN_LINE_ID] || 0; | ||
const getTokenColumn = (token)=>token && token[TOKEN_COLUMN_ID] || 0; | ||
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; | ||
}; | ||
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; | ||
}; | ||
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 tokenToText = (token)=>{ | ||
let text = OPEN_BRAKET; | ||
text += getTokenValue(token); | ||
text += CLOSE_BRAKET; | ||
return text; | ||
}; | ||
/** | ||
* @export | ||
* @class Token | ||
*/ class Token { | ||
get type() { | ||
return this[TOKEN_TYPE_ID]; | ||
} | ||
isEmpty() { | ||
return this[TOKEN_TYPE_ID] === 0 || 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 tokenToText(this); | ||
} | ||
constructor(type, value, row = 0, col = 0){ | ||
this[TOKEN_LINE_ID] = row; | ||
this[TOKEN_COLUMN_ID] = col; | ||
this[TOKEN_TYPE_ID] = type || 0; | ||
this[TOKEN_VALUE_ID] = String(value); | ||
} | ||
} | ||
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; | ||
class CharGrabber { | ||
skip(num = 1, silent) { | ||
this.c.pos += num; | ||
if (this.o && this.o.onSkip && !silent) { | ||
this.o.onSkip(); | ||
} | ||
} | ||
hasNext() { | ||
return this.c.len > this.c.pos; | ||
} | ||
getCurr() { | ||
if (typeof this.s[this.c.pos] === 'undefined') { | ||
return ''; | ||
} | ||
return this.s[this.c.pos]; | ||
} | ||
getRest() { | ||
return this.s.substring(this.c.pos); | ||
} | ||
getNext() { | ||
const nextPos = this.c.pos + 1; | ||
return nextPos <= this.s.length - 1 ? this.s[nextPos] : null; | ||
} | ||
getPrev() { | ||
const prevPos = this.c.pos - 1; | ||
if (typeof this.s[prevPos] === 'undefined') { | ||
return null; | ||
} | ||
return this.s[prevPos]; | ||
} | ||
isLast() { | ||
return this.c.pos === this.c.len; | ||
} | ||
includes(val) { | ||
return this.s.indexOf(val, this.c.pos) >= 0; | ||
} | ||
grabWhile(condition, silent) { | ||
let start = 0; | ||
if (this.hasNext()) { | ||
start = this.c.pos; | ||
while(this.hasNext() && condition(this.getCurr())){ | ||
this.skip(1, silent); | ||
} | ||
} | ||
return this.s.substring(start, this.c.pos); | ||
} | ||
grabN(num = 0) { | ||
return this.s.substring(this.c.pos, this.c.pos + num); | ||
} | ||
/** | ||
* Grabs rest of string until it find a char | ||
*/ substrUntilChar(char) { | ||
const { pos } = this.c; | ||
const idx = this.s.indexOf(char, pos); | ||
return idx >= 0 ? this.s.substring(pos, idx) : ''; | ||
} | ||
constructor(source, options = {}){ | ||
this.s = source; | ||
this.c = { | ||
pos: 0, | ||
len: source.length | ||
}; | ||
this.o = options; | ||
} | ||
} | ||
/** | ||
* Creates a grabber wrapper for source string, that helps to iterate over string char by char | ||
*/ const createCharGrabber = (source, options)=>new CharGrabber(source, options); | ||
/** | ||
* Trims string from start and end by char | ||
* @example | ||
* trimChar('*hello*', '*') ==> 'hello' | ||
*/ 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 " | ||
*/ const unquote = (str)=>str.replace(BACKSLASH + QUOTEMARK, QUOTEMARK); | ||
// for cases <!-- --> | ||
const EM = '!'; | ||
function createTokenOfType(type, value, r = 0, cl = 0) { | ||
return new Token(type, value, r, cl); | ||
} | ||
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; | ||
const WHITESPACES = [ | ||
SPACE, | ||
TAB | ||
]; | ||
const SPECIAL_CHARS = [ | ||
EQ, | ||
SPACE, | ||
TAB | ||
]; | ||
const isWhiteSpace = (char)=>WHITESPACES.indexOf(char) >= 0; | ||
const isEscapeChar = (char)=>char === BACKSLASH; | ||
const isSpecialChar = (char)=>SPECIAL_CHARS.indexOf(char) >= 0; | ||
const isNewLine = (char)=>char === N; | ||
const unq = (val)=>unquote(trimChar(val, QUOTEMARK)); | ||
function createLexer(buffer, options = {}) { | ||
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 || []).filter(Boolean).map((tag)=>tag.toLowerCase()); | ||
const nestedMap = new Map(); | ||
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 isCharReserved = (char)=>RESERVED_CHARS.indexOf(char) >= 0; | ||
const isCharToken = (char)=>NOT_CHAR_TOKENS.indexOf(char) === -1; | ||
const isEscapableChar = (char)=>char === openTag || char === closeTag || char === BACKSLASH; | ||
const onSkip = ()=>{ | ||
col++; | ||
}; | ||
const checkContextFreeMode = (name, isClosingTag)=>{ | ||
if (contextFreeTag !== '' && isClosingTag) { | ||
contextFreeTag = ''; | ||
} | ||
if (contextFreeTag === '' && contextFreeTags.includes(name.toLowerCase())) { | ||
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 = createTokenOfType(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 = nextChar && isWhiteSpace(nextChar); | ||
if (stateSpecial && isSpecialChar(char)) { | ||
return true; | ||
} | ||
if (isQM && !isPrevSLASH) { | ||
stateSpecial = !stateSpecial; | ||
if (!stateSpecial && !(isNextEQ || isNextWS)) { | ||
return false; | ||
} | ||
} | ||
if (!isSingleValueTag) { | ||
return !isWS; | ||
// return (isEQ || isWS) === false; | ||
} | ||
return true; | ||
}; | ||
const name = tagChars.grabWhile(validAttrValue); | ||
tagChars.skip(); | ||
emitToken(TYPE_ATTR_VALUE, unq(name)); | ||
if (tagChars.isLast()) { | ||
return TAG_STATE_NAME; | ||
} | ||
return TAG_STATE_ATTR; | ||
} | ||
const validName = (char)=>!(char === EQ || isWhiteSpace(char) || tagChars.isLast()); | ||
const name = tagChars.grabWhile(validName); | ||
emitToken(TYPE_TAG, name); | ||
checkContextFreeMode(name); | ||
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 (nextChar && 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 (nextChar && 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 word = chars.grabWhile(isChar); | ||
emitToken(TYPE_WORD, word); | ||
return STATE_WORD; | ||
} | ||
const word = chars.grabWhile(isCharToken); | ||
emitToken(TYPE_WORD, word); | ||
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(); | ||
if (nestedMap.has(value)) { | ||
return !!nestedMap.get(value); | ||
} else { | ||
const status = buffer.indexOf(value) > -1; | ||
nestedMap.set(value, status); | ||
return status; | ||
} | ||
} | ||
return { | ||
tokenize, | ||
isTokenNested | ||
}; | ||
} | ||
class NodeList { | ||
last() { | ||
if (Array.isArray(this.n) && this.n.length > 0 && typeof this.n[this.n.length - 1] !== "undefined") { | ||
return this.n[this.n.length - 1]; | ||
} | ||
return null; | ||
} | ||
flush() { | ||
return this.n.length ? this.n.pop() : false; | ||
} | ||
push(value) { | ||
this.n.push(value); | ||
} | ||
toArray() { | ||
return this.n; | ||
} | ||
constructor(){ | ||
this.n = []; | ||
} | ||
} | ||
const createList = ()=>new NodeList(); | ||
function parse(input, opts = {}) { | ||
const options = opts; | ||
const openTag = options.openTag || OPEN_BRAKET; | ||
const closeTag = options.closeTag || CLOSE_BRAKET; | ||
const onlyAllowTags = (options.onlyAllowTags || []).filter(Boolean).map((tag)=>tag.toLowerCase()); | ||
let tokenizer = null; | ||
/** | ||
* Result AST of nodes | ||
* @private | ||
* @type {NodeList} | ||
*/ const nodes = createList(); | ||
/** | ||
* Temp buffer of nodes that's nested to another node | ||
* @private | ||
*/ 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(); | ||
function isTokenNested(token) { | ||
const value = token.getValue(); | ||
const { isTokenNested } = tokenizer || {}; | ||
if (!nestedTagsMap.has(value) && isTokenNested && isTokenNested(token)) { | ||
nestedTagsMap.add(value); | ||
return true; | ||
} | ||
return nestedTagsMap.has(value); | ||
} | ||
/** | ||
* @private | ||
*/ function isTagNested(tagName) { | ||
return Boolean(nestedTagsMap.has(tagName)); | ||
} | ||
/** | ||
* @private | ||
*/ function isAllowedTag(value) { | ||
if (onlyAllowTags.length) { | ||
return onlyAllowTags.indexOf(value.toLowerCase()) >= 0; | ||
} | ||
return true; | ||
} | ||
/** | ||
* Flushes temp tag nodes and its attributes buffers | ||
* @private | ||
*/ function flushTagNodes() { | ||
if (tagNodes.flush()) { | ||
tagNodesAttrName.flush(); | ||
} | ||
} | ||
/** | ||
* @private | ||
*/ function getNodes() { | ||
const lastNestedNode = nestedNodes.last(); | ||
if (lastNestedNode && isTagNode(lastNestedNode)) { | ||
return lastNestedNode.content; | ||
} | ||
return nodes.toArray(); | ||
} | ||
/** | ||
* @private | ||
*/ function appendNodeAsString(nodes, node, isNested = true) { | ||
if (Array.isArray(nodes) && typeof node !== "undefined") { | ||
nodes.push(node.toTagStart({ | ||
openTag, | ||
closeTag | ||
})); | ||
if (Array.isArray(node.content) && node.content.length) { | ||
node.content.forEach((item)=>{ | ||
nodes.push(item); | ||
}); | ||
if (isNested) { | ||
nodes.push(node.toTagEnd({ | ||
openTag, | ||
closeTag | ||
})); | ||
} | ||
} | ||
} | ||
} | ||
/** | ||
* @private | ||
*/ function appendNodes(nodes, node) { | ||
if (Array.isArray(nodes) && typeof node !== "undefined") { | ||
if (isTagNode(node)) { | ||
if (isAllowedTag(node.tag)) { | ||
nodes.push(node.toTagNode()); | ||
} else { | ||
appendNodeAsString(nodes, node); | ||
} | ||
} else { | ||
nodes.push(node); | ||
} | ||
} | ||
} | ||
/** | ||
* @private | ||
* @param {Token} token | ||
*/ function handleTagStart(token) { | ||
flushTagNodes(); | ||
const tagNode = TagNode.create(token.getValue(), {}, []); | ||
const isNested = isTokenNested(token); | ||
tagNodes.push(tagNode); | ||
if (isNested) { | ||
nestedNodes.push(tagNode); | ||
} else { | ||
const nodes = getNodes(); | ||
appendNodes(nodes, tagNode); | ||
} | ||
} | ||
/** | ||
* @private | ||
* @param {Token} token | ||
*/ function handleTagEnd(token) { | ||
flushTagNodes(); | ||
const lastNestedNode = nestedNodes.flush(); | ||
if (lastNestedNode) { | ||
const nodes = getNodes(); | ||
appendNodes(nodes, lastNestedNode); | ||
} else if (typeof options.onError === "function") { | ||
const tag = token.getValue(); | ||
const line = token.getLine(); | ||
const column = token.getColumn(); | ||
options.onError({ | ||
tagName: tag, | ||
lineNumber: line, | ||
columnNumber: column | ||
}); | ||
} | ||
} | ||
/** | ||
* @private | ||
* @param {Token} token | ||
*/ function handleTag(token) { | ||
// [tag] | ||
if (token.isStart()) { | ||
handleTagStart(token); | ||
} | ||
// [/tag] | ||
if (token.isEnd()) { | ||
handleTagEnd(token); | ||
} | ||
} | ||
/** | ||
* @private | ||
* @param {Token} token | ||
*/ function handleNode(token) { | ||
/** | ||
* @type {TagNode} | ||
*/ const activeTagNode = tagNodes.last(); | ||
const tokenValue = token.getValue(); | ||
const isNested = isTagNested(token.toString()); | ||
const nodes = getNodes(); | ||
if (activeTagNode !== null) { | ||
if (token.isAttrName()) { | ||
tagNodesAttrName.push(tokenValue); | ||
const attrName = tagNodesAttrName.last(); | ||
if (attrName) { | ||
activeTagNode.attr(attrName, ""); | ||
} | ||
} else if (token.isAttrValue()) { | ||
const attrName = tagNodesAttrName.last(); | ||
if (attrName) { | ||
activeTagNode.attr(attrName, tokenValue); | ||
tagNodesAttrName.flush(); | ||
} else { | ||
activeTagNode.attr(tokenValue, tokenValue); | ||
} | ||
} else if (token.isText()) { | ||
if (isNested) { | ||
activeTagNode.append(tokenValue); | ||
} else { | ||
appendNodes(nodes, tokenValue); | ||
} | ||
} else if (token.isTag()) { | ||
// if tag is not allowed, just pass it as is | ||
appendNodes(nodes, token.toString()); | ||
} | ||
} else if (token.isText()) { | ||
appendNodes(nodes, tokenValue); | ||
} else if (token.isTag()) { | ||
// if tag is not allowed, just pass it as is | ||
appendNodes(nodes, token.toString()); | ||
} | ||
} | ||
/** | ||
* @private | ||
* @param {Token} token | ||
*/ function onToken(token) { | ||
if (token.isTag()) { | ||
handleTag(token); | ||
} else { | ||
handleNode(token); | ||
} | ||
} | ||
const lexer = opts.createTokenizer ? opts.createTokenizer : createLexer; | ||
tokenizer = lexer(input, { | ||
onToken, | ||
openTag, | ||
closeTag, | ||
onlyAllowTags: options.onlyAllowTags, | ||
contextFreeTags: options.contextFreeTags, | ||
enableEscapeTags: options.enableEscapeTags | ||
}); | ||
// eslint-disable-next-line no-unused-vars | ||
tokenizer.tokenize(); | ||
// handles situations where we open tag, but forgot close them | ||
// for ex [q]test[/q][u]some[/u][q]some [u]some[/u] // forgot to close [/q] | ||
// so we need to flush nested content to nodes array | ||
const lastNestedNode = nestedNodes.flush(); | ||
if (lastNestedNode !== null && lastNestedNode && isTagNode(lastNestedNode) && isTagNested(lastNestedNode.tag)) { | ||
appendNodeAsString(getNodes(), lastNestedNode, false); | ||
} | ||
return nodes.toArray(); | ||
} | ||
/* eslint-disable no-plusplus */ const isObj = (value)=>typeof value === 'object' && value !== null; | ||
const isBool = (value)=>typeof value === 'boolean'; | ||
@@ -15,3 +903,3 @@ function iterate(t, cb) { | ||
} | ||
} else if (tree && isObj(tree) && tree.content) { | ||
} else if (isObj(tree) && 'content' in tree) { | ||
iterate(tree.content, cb); | ||
@@ -31,27 +919,50 @@ } | ||
} | ||
return Object.keys(expected).every((key)=>{ | ||
const ao = actual[key]; | ||
const eo = expected[key]; | ||
if (isObj(eo) && eo !== null && ao !== null) { | ||
return same(eo, ao); | ||
} | ||
if (isBool(eo)) { | ||
return eo !== (ao === null); | ||
} | ||
return ao === eo; | ||
}); | ||
if (isObj(expected) && isObj(actual)) { | ||
return Object.keys(expected).every((key)=>{ | ||
const ao = actual[key]; | ||
const eo = expected[key]; | ||
if (isObj(eo) && isObj(ao)) { | ||
return same(eo, ao); | ||
} | ||
if (isBool(eo)) { | ||
return eo !== (ao === null); | ||
} | ||
return ao === eo; | ||
}); | ||
} | ||
return false; | ||
} | ||
function match(expression, cb) { | ||
return Array.isArray(expression) ? iterate(this, (node)=>{ | ||
for(let idx = 0; idx < expression.length; idx++){ | ||
if (same(expression[idx], node)) { | ||
return cb(node); | ||
function match(t, expression, cb) { | ||
if (Array.isArray(expression)) { | ||
return iterate(t, (node)=>{ | ||
for(let idx = 0; idx < expression.length; idx++){ | ||
if (same(expression[idx], node)) { | ||
return cb(node); | ||
} | ||
} | ||
} | ||
return node; | ||
}) : iterate(this, (node)=>same(expression, node) ? cb(node) : node); | ||
return node; | ||
}); | ||
} | ||
return iterate(t, (node)=>same(expression, node) ? cb(node) : node); | ||
} | ||
function walk(cb) { | ||
return iterate(this, cb); | ||
let C1 = 'C1'; | ||
let C2 = 'C2'; | ||
function createTree(tree, options) { | ||
const extendedTree = tree; | ||
extendedTree.messages = [ | ||
...extendedTree.messages || [] | ||
]; | ||
extendedTree.options = { | ||
...options, | ||
...extendedTree.options | ||
}; | ||
extendedTree.walk = function walkNodes(cb) { | ||
return iterate(this, cb); | ||
}; | ||
extendedTree.match = function matchNodes(expr, cb) { | ||
return match(this, expr, cb); | ||
}; | ||
return extendedTree; | ||
} | ||
@@ -62,34 +973,36 @@ function bbob(plugs) { | ||
] : plugs || []; | ||
let options = { | ||
skipParse: false | ||
}; | ||
const mockRender = ()=>""; | ||
return { | ||
process (input, opts) { | ||
options = opts || {}; | ||
const parseFn = options.parser || parser.parse; | ||
const options = opts || { | ||
skipParse: false, | ||
parser: parse, | ||
render: mockRender, | ||
data: null | ||
}; | ||
const parseFn = options.parser || parse; | ||
const renderFn = options.render; | ||
const data = options.data || null; | ||
if (typeof parseFn !== 'function') { | ||
throw new Error('"parser" is not a function, please pass to "process(input, { parser })" right function'); | ||
throw new Error(C1); | ||
} | ||
let tree = options.skipParse ? input || [] : parseFn(input, options); | ||
// raw tree before modification with plugins | ||
const raw = tree; | ||
tree.messages = []; | ||
tree.options = options; | ||
tree.walk = walk; | ||
tree.match = match; | ||
plugins.forEach((plugin)=>{ | ||
tree = plugin(tree, { | ||
parse: parseFn, | ||
render: renderFn, | ||
iterate, | ||
match, | ||
data | ||
}) || tree; | ||
}); | ||
const raw = options.skipParse && Array.isArray(input) ? input : parseFn(input, options); | ||
let tree = options.skipParse && Array.isArray(input) ? createTree(input || [], options) : createTree(raw, options); | ||
for(let idx = 0; idx < plugins.length; idx++){ | ||
const plugin = plugins[idx]; | ||
if (typeof plugin === 'function' && renderFn) { | ||
const newTree = plugin(tree, { | ||
parse: parseFn, | ||
render: renderFn, | ||
iterate, | ||
data | ||
}); | ||
tree = createTree(newTree || tree, options); | ||
} | ||
} | ||
return { | ||
get html () { | ||
if (typeof renderFn !== 'function') { | ||
throw new Error('"render" function not defined, please pass to "process(input, { render })"'); | ||
throw new Error(C2); | ||
} | ||
@@ -106,2 +1019,3 @@ return renderFn(tree, tree.options); | ||
exports.createTree = createTree; | ||
exports.default = bbob; | ||
@@ -108,0 +1022,0 @@ |
@@ -1,1 +0,1 @@ | ||
!function(e,r){"object"==typeof exports&&"undefined"!=typeof module?r(exports,require("@bbob/parser")):"function"==typeof define&&define.amd?define(["exports","@bbob/parser"],r):r((e="undefined"!=typeof globalThis?globalThis:e||self).BbobCore={},e.parser)}(this,function(e,r){"use strict";let t=e=>"object"==typeof e,n=e=>"boolean"==typeof e;function o(e,r){let n=e;if(Array.isArray(n))for(let s=0;s<n.length;s++)n[s]=o(r(n[s]),r);else n&&t(n)&&n.content&&o(n.content,r);return n}function s(e,r){return typeof e==typeof r&&(t(e)&&null!==e?Array.isArray(e)?e.every(e=>[].some.call(r,r=>s(e,r))):Object.keys(e).every(o=>{let i=r[o],f=e[o];return t(f)&&null!==f&&null!==i?s(f,i):n(f)?f!==(null===i):i===f}):e===r)}function i(e,r){return Array.isArray(e)?o(this,t=>{for(let n=0;n<e.length;n++)if(s(e[n],t))return r(t);return t}):o(this,t=>s(e,t)?r(t):t)}function f(e){return o(this,e)}e.default=function(e){let t="function"==typeof e?[e]:e||[],n={skipParse:!1};return{process(e,s){n=s||{};let u=n.parser||r.parse,l=n.render,a=n.data||null;if("function"!=typeof u)throw Error('"parser" is not a function, please pass to "process(input, { parser })" right function');let p=n.skipParse?e||[]:u(e,n),c=p;return p.messages=[],p.options=n,p.walk=f,p.match=i,t.forEach(e=>{p=e(p,{parse:u,render:l,iterate:o,match:i,data:a})||p}),{get html(){if("function"!=typeof l)throw Error('"render" function not defined, please pass to "process(input, { render })"');return l(p,p.options)},tree:p,raw:c,messages:p.messages}}}},Object.defineProperty(e,"__esModule",{value:!0})}); | ||
!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).BbobCore={})}(this,function(t){"use strict";function e(t){return"object"==typeof t&&null!==t&&"tag"in t}function r(t,e,r){return Object.keys(t).reduce((r,n)=>e(r,n,t),r)}function n(t){return t.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'").replace(/(javascript|data|vbscript):/gi,"$1%3A")}function s(t,e){switch(typeof e){case"boolean":return e?""+t:"";case"number":return`${t}="${e}"`;case"string":return`${t}="${n(e)}"`;case"object":return`${t}="${n(JSON.stringify(e))}"`;default:return""}}function i(t){return null==t?"":r(t,(t,e,r)=>[...t,s(e,r[e])],[""]).join(" ")}let o=(t,e)=>{let n=r(e||{},(t,e,r)=>r[e]===e?r[e]:null,null);if(n){let r=s(t,n),o={...e};delete o[n+""];let u=i(o);return`${r}${u}`}return`${t}${i(e)}`},u=(t,r,n)=>{let s=t=>e(t)?t.toString({openTag:r,closeTag:n}):t+"";return Array.isArray(t)?t.reduce((t,e)=>null!==e?t+s(e):t,""):t?s(t):null};class l{attr(t,e){return void 0!==e&&(this.attrs[t]=e),this.attrs[t]}append(t){Array.isArray(this.content)&&this.content.push(t)}get length(){return function t(r){return e(r)&&Array.isArray(r.content)?r.content.reduce((e,r)=>e+t(r),0):"string"==typeof r?(r+"").length:0}(this)}toTagStart({openTag:t="[",closeTag:e="]"}={}){let r=o(this.tag,this.attrs);return`${t}${r}${e}`}toTagEnd({openTag:t="[",closeTag:e="]"}={}){return`${t}/${this.tag}${e}`}toTagNode(){return new l(this.tag.toLowerCase(),this.attrs,this.content)}toString({openTag:t="[",closeTag:e="]"}={}){let r=this.content?u(this.content,t,e):"",n=this.toTagStart({openTag:t,closeTag:e});return null===this.content||Array.isArray(this.content)&&0===this.content.length?n:`${n}${r}${this.toTagEnd({openTag:t,closeTag:e})}`}static create(t,e={},r=null){return new l(t,e,r)}static isOf(t,e){return t.tag===e}constructor(t,e,r){this.tag=t,this.attrs=e,this.content=r}}let a=t=>t&&void 0!==t.v?t.v:"",h=t=>t&&t.l||0,c=t=>t&&t.r||0,g=t=>!!t&&void 0!==t.t&&(5===t.t||6===t.t||1===t.t),f=t=>!!t&&void 0!==t.t&&2===t.t,p=t=>47===a(t).charCodeAt(0),d=t=>!p(t),y=t=>!!t&&void 0!==t.t&&3===t.t,b=t=>!!t&&void 0!==t.t&&4===t.t,A=t=>{let e=a(t);return p(t)?e.slice(1):e},T=t=>"["+a(t)+"]";class k{get type(){return this.t}isEmpty(){return 0===this.t||isNaN(this.t)}isText(){return g(this)}isTag(){return f(this)}isAttrName(){return y(this)}isAttrValue(){return b(this)}isStart(){return d(this)}isEnd(){return p(this)}getName(){return A(this)}getValue(){return a(this)}getLine(){return h(this)}getColumn(){return c(this)}toString(){return T(this)}constructor(t,e,r=0,n=0){this.l=r,this.r=n,this.t=t||0,this.v=e+""}}class x{skip(t=1,e){this.c.pos+=t,this.o&&this.o.onSkip&&!e&&this.o.onSkip()}hasNext(){return this.c.len>this.c.pos}getCurr(){return void 0===this.s[this.c.pos]?"":this.s[this.c.pos]}getRest(){return this.s.substring(this.c.pos)}getNext(){let t=this.c.pos+1;return t<=this.s.length-1?this.s[t]:null}getPrev(){let t=this.c.pos-1;return void 0===this.s[t]?null:this.s[t]}isLast(){return this.c.pos===this.c.len}includes(t){return this.s.indexOf(t,this.c.pos)>=0}grabWhile(t,e){let r=0;if(this.hasNext())for(r=this.c.pos;this.hasNext()&&t(this.getCurr());)this.skip(1,e);return this.s.substring(r,this.c.pos)}grabN(t=0){return this.s.substring(this.c.pos,this.c.pos+t)}substrUntilChar(t){let{pos:e}=this.c,r=this.s.indexOf(t,e);return r>=0?this.s.substring(e,r):""}constructor(t,e={}){this.s=t,this.c={pos:0,len:t.length},this.o=e}}let v=(t,e)=>new x(t,e),C=(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},m=t=>t.replace('\\"','"'),N=[" "," "],$=["="," "," "],w=t=>N.indexOf(t)>=0,O=t=>"\\"===t,S=t=>$.indexOf(t)>=0,E=t=>"\n"===t,L=t=>m(C(t,'"'));class j{last(){return Array.isArray(this.n)&&this.n.length>0&&void 0!==this.n[this.n.length-1]?this.n[this.n.length-1]:null}flush(){return!!this.n.length&&this.n.pop()}push(t){this.n.push(t)}toArray(){return this.n}constructor(){this.n=[]}}let W=()=>new j;function V(t,r={}){var n;let s=r.openTag||"[",i=r.closeTag||"]",o=(r.onlyAllowTags||[]).filter(Boolean).map(t=>t.toLowerCase()),u=null,a=W(),h=W(),c=W(),g=W(),f=new Set;function p(){c.flush()&&g.flush()}function d(){let t=h.last();return t&&e(t)?t.content:a.toArray()}function y(t,e,r=!0){Array.isArray(t)&&void 0!==e&&(t.push(e.toTagStart({openTag:s,closeTag:i})),Array.isArray(e.content)&&e.content.length&&(e.content.forEach(e=>{t.push(e)}),r&&t.push(e.toTagEnd({openTag:s,closeTag:i}))))}function b(t,r){if(Array.isArray(t)&&void 0!==r){if(e(r)){var n;(n=r.tag,!o.length||o.indexOf(n.toLowerCase())>=0)?t.push(r.toTagNode()):y(t,r)}else t.push(r)}}(u=(r.createTokenizer?r.createTokenizer:function(t,e={}){let r=0,n=0,s=-1,i=0,o=0,u="",l=Array(Math.floor(t.length)),a=e.openTag||"[",h=e.closeTag||"]",c=!!e.enableEscapeTags,g=(e.contextFreeTags||[]).filter(Boolean).map(t=>t.toLowerCase()),f=new Map,p=e.onToken||(()=>{}),d=[h,a,'"',"\\"," "," ","=","\n","!"],y=[a," "," ","\n"],b=t=>d.indexOf(t)>=0,A=t=>-1===y.indexOf(t),T=t=>t===a||t===h||"\\"===t,x=()=>{n++},C=(t,e)=>{""!==u&&e&&(u=""),""===u&&g.includes(t.toLowerCase())&&(u=t)},m=v(t,{onSkip:x});function N(t,e){let i=function(t,e,r=0,n=0){return new k(t,e,r,n)}(t,e,r,n);p(i),l[s+=1]=i}return{tokenize:function(){for(i=0;m.hasNext();)switch(i){case 1:i=function(){let t=m.getCurr(),e=m.getNext();m.skip();let r=m.substrUntilChar(h),n=0===r.length||r.indexOf(a)>=0;if(e&&b(e)||n||m.isLast())return N(1,t),0;let s=-1===r.indexOf("="),i="/"===r[0];if(s||i){let t=m.grabWhile(t=>t!==h);return m.skip(),N(2,t),C(t,i),0}return 2}();break;case 2:i=function(){let t=v(m.grabWhile(t=>t!==h,!0),{onSkip:x}),e=t.includes(" ");for(o=0;t.hasNext();)o=function(t,e){if(1===o){let e=t.grabWhile(t=>!("="===t||w(t))),r=t.isLast(),n="="!==t.getCurr();return(t.skip(),r||n?N(4,L(e)):N(3,e),r)?0:n?1:2}if(2===o){let r=!1,n=t.grabWhile(n=>{let s='"'===n,i=t.getPrev(),o=t.getNext(),u="="===o,l=w(n),a=o&&w(o);return!!(r&&S(n))||(!s||"\\"===i||!!(r=!r)||!!u||!!a)&&(!!e||!l)});return(t.skip(),N(4,L(n)),t.isLast())?0:1}let r=t.grabWhile(e=>!("="===e||w(e)||t.isLast()));return(N(2,r),C(r),t.skip(),e)?2:t.includes("=")?1:2}(t,!e);return m.skip(),0}();break;default:i=function(){if(E(m.getCurr()))return N(6,m.getCurr()),m.skip(),n=0,r++,0;if(w(m.getCurr()))return N(5,m.grabWhile(w)),0;if(m.getCurr()===a){if(u){let t=a.length+1+u.length,e=`${a}/${u}`;if(m.grabN(t)===e)return 1}else if(m.includes(h))return 1;return N(1,m.getCurr()),m.skip(),0}if(c){if(O(m.getCurr())){let t=m.getCurr(),e=m.getNext();return(m.skip(),e&&T(e))?(m.skip(),N(1,e)):N(1,t),0}return N(1,m.grabWhile(t=>A(t)&&!O(t))),0}return N(1,m.grabWhile(A)),0}()}return l.length=s+1,l},isTokenNested:function(e){let r=a+"/"+e.getValue();if(f.has(r))return!!f.get(r);{let e=t.indexOf(r)>-1;return f.set(r,e),e}}}})(t,{onToken:function(t){t.isTag()?(t.isStart()&&function(t){p();let e=l.create(t.getValue(),{},[]),r=function(t){let e=t.getValue(),{isTokenNested:r}=u||{};return!f.has(e)&&r&&r(t)?(f.add(e),!0):f.has(e)}(t);c.push(e),r?h.push(e):b(d(),e)}(t),t.isEnd()&&function(t){p();let e=h.flush();if(e)b(d(),e);else if("function"==typeof r.onError){let e=t.getValue(),n=t.getLine(),s=t.getColumn();r.onError({tagName:e,lineNumber:n,columnNumber:s})}}(t)):!function(t){var e;let r=c.last(),n=t.getValue(),s=(e=t.toString(),!!f.has(e)),i=d();if(null!==r){if(t.isAttrName()){g.push(n);let t=g.last();t&&r.attr(t,"")}else if(t.isAttrValue()){let t=g.last();t?(r.attr(t,n),g.flush()):r.attr(n,n)}else t.isText()?s?r.append(n):b(i,n):t.isTag()&&b(i,t.toString())}else t.isText()?b(i,n):t.isTag()&&b(i,t.toString())}(t)},openTag:s,closeTag:i,onlyAllowTags:r.onlyAllowTags,contextFreeTags:r.contextFreeTags,enableEscapeTags:r.enableEscapeTags})).tokenize();let A=h.flush();return null!==A&&A&&e(A)&&(n=A.tag,f.has(n))&&y(d(),A,!1),a.toArray()}let P=t=>"object"==typeof t&&null!==t,z=t=>"boolean"==typeof t;function B(t,e){if(Array.isArray(t))for(let r=0;r<t.length;r++)t[r]=B(e(t[r]),e);else P(t)&&"content"in t&&B(t.content,e);return t}function F(t,e){return typeof t==typeof e&&(P(t)&&null!==t?Array.isArray(t)?t.every(t=>[].some.call(e,e=>F(t,e))):!!(P(t)&&P(e))&&Object.keys(t).every(r=>{let n=e[r],s=t[r];return P(s)&&P(n)?F(s,n):z(s)?s!==(null===n):n===s}):t===e)}function M(t,e){return t.messages=[...t.messages||[]],t.options={...e,...t.options},t.walk=function(t){return B(this,t)},t.match=function(t,e){return Array.isArray(t)?B(this,r=>{for(let n=0;n<t.length;n++)if(F(t[n],r))return e(r);return r}):B(this,r=>F(t,r)?e(r):r)},t}t.createTree=M,t.default=function(t){let e="function"==typeof t?[t]:t||[],r=()=>"";return{process(t,n){let s=n||{skipParse:!1,parser:V,render:r,data:null},i=s.parser||V,o=s.render,u=s.data||null;if("function"!=typeof i)throw Error("C1");let l=s.skipParse&&Array.isArray(t)?t:i(t,s),a=s.skipParse&&Array.isArray(t)?M(t||[],s):M(l,s);for(let t=0;t<e.length;t++){let r=e[t];"function"==typeof r&&o&&(a=M(r(a,{parse:i,render:o,iterate:B,data:u})||a,s))}return{get html(){if("function"!=typeof o)throw Error("C2");return o(a,a.options)},tree:a,raw:l,messages:a.messages}}}},Object.defineProperty(t,"__esModule",{value:!0})}); |
import { parse } from '@bbob/parser'; | ||
import { iterate, match } from './utils'; | ||
function walk(cb) { | ||
return iterate(this, cb); | ||
import { C1, C2 } from './errors'; | ||
export function createTree(tree, options) { | ||
const extendedTree = tree; | ||
extendedTree.messages = [ | ||
...extendedTree.messages || [] | ||
]; | ||
extendedTree.options = { | ||
...options, | ||
...extendedTree.options | ||
}; | ||
extendedTree.walk = function walkNodes(cb) { | ||
return iterate(this, cb); | ||
}; | ||
extendedTree.match = function matchNodes(expr, cb) { | ||
return match(this, expr, cb); | ||
}; | ||
return extendedTree; | ||
} | ||
@@ -10,8 +25,11 @@ export default function bbob(plugs) { | ||
] : plugs || []; | ||
let options = { | ||
skipParse: false | ||
}; | ||
const mockRender = ()=>""; | ||
return { | ||
process (input, opts) { | ||
options = opts || {}; | ||
const options = opts || { | ||
skipParse: false, | ||
parser: parse, | ||
render: mockRender, | ||
data: null | ||
}; | ||
const parseFn = options.parser || parse; | ||
@@ -21,24 +39,23 @@ const renderFn = options.render; | ||
if (typeof parseFn !== 'function') { | ||
throw new Error('"parser" is not a function, please pass to "process(input, { parser })" right function'); | ||
throw new Error(C1); | ||
} | ||
let tree = options.skipParse ? input || [] : parseFn(input, options); | ||
// raw tree before modification with plugins | ||
const raw = tree; | ||
tree.messages = []; | ||
tree.options = options; | ||
tree.walk = walk; | ||
tree.match = match; | ||
plugins.forEach((plugin)=>{ | ||
tree = plugin(tree, { | ||
parse: parseFn, | ||
render: renderFn, | ||
iterate, | ||
match, | ||
data | ||
}) || tree; | ||
}); | ||
const raw = options.skipParse && Array.isArray(input) ? input : parseFn(input, options); | ||
let tree = options.skipParse && Array.isArray(input) ? createTree(input || [], options) : createTree(raw, options); | ||
for(let idx = 0; idx < plugins.length; idx++){ | ||
const plugin = plugins[idx]; | ||
if (typeof plugin === 'function' && renderFn) { | ||
const newTree = plugin(tree, { | ||
parse: parseFn, | ||
render: renderFn, | ||
iterate, | ||
data | ||
}); | ||
tree = createTree(newTree || tree, options); | ||
} | ||
} | ||
return { | ||
get html () { | ||
if (typeof renderFn !== 'function') { | ||
throw new Error('"render" function not defined, please pass to "process(input, { render })"'); | ||
throw new Error(C2); | ||
} | ||
@@ -45,0 +62,0 @@ return renderFn(tree, tree.options); |
@@ -1,2 +0,2 @@ | ||
/* eslint-disable no-plusplus */ const isObj = (value)=>typeof value === 'object'; | ||
/* eslint-disable no-plusplus */ const isObj = (value)=>typeof value === 'object' && value !== null; | ||
const isBool = (value)=>typeof value === 'boolean'; | ||
@@ -9,3 +9,3 @@ export function iterate(t, cb) { | ||
} | ||
} else if (tree && isObj(tree) && tree.content) { | ||
} else if (isObj(tree) && 'content' in tree) { | ||
iterate(tree.content, cb); | ||
@@ -25,23 +25,29 @@ } | ||
} | ||
return Object.keys(expected).every((key)=>{ | ||
const ao = actual[key]; | ||
const eo = expected[key]; | ||
if (isObj(eo) && eo !== null && ao !== null) { | ||
return same(eo, ao); | ||
} | ||
if (isBool(eo)) { | ||
return eo !== (ao === null); | ||
} | ||
return ao === eo; | ||
}); | ||
if (isObj(expected) && isObj(actual)) { | ||
return Object.keys(expected).every((key)=>{ | ||
const ao = actual[key]; | ||
const eo = expected[key]; | ||
if (isObj(eo) && isObj(ao)) { | ||
return same(eo, ao); | ||
} | ||
if (isBool(eo)) { | ||
return eo !== (ao === null); | ||
} | ||
return ao === eo; | ||
}); | ||
} | ||
return false; | ||
} | ||
export function match(expression, cb) { | ||
return Array.isArray(expression) ? iterate(this, (node)=>{ | ||
for(let idx = 0; idx < expression.length; idx++){ | ||
if (same(expression[idx], node)) { | ||
return cb(node); | ||
export function match(t, expression, cb) { | ||
if (Array.isArray(expression)) { | ||
return iterate(t, (node)=>{ | ||
for(let idx = 0; idx < expression.length; idx++){ | ||
if (same(expression[idx], node)) { | ||
return cb(node); | ||
} | ||
} | ||
} | ||
return node; | ||
}) : iterate(this, (node)=>same(expression, node) ? cb(node) : node); | ||
return node; | ||
}); | ||
} | ||
return iterate(t, (node)=>same(expression, node) ? cb(node) : node); | ||
} |
@@ -5,5 +5,13 @@ "use strict"; | ||
}); | ||
Object.defineProperty(exports, "default", { | ||
enumerable: true, | ||
get: function() { | ||
function _export(target, all) { | ||
for(var name in all)Object.defineProperty(target, name, { | ||
enumerable: true, | ||
get: all[name] | ||
}); | ||
} | ||
_export(exports, { | ||
createTree: function() { | ||
return createTree; | ||
}, | ||
default: function() { | ||
return bbob; | ||
@@ -14,5 +22,29 @@ } | ||
var _utils = require("./utils"); | ||
function walk(cb) { | ||
return (0, _utils.iterate)(this, cb); | ||
var _errors = require("./errors"); | ||
function _extends() { | ||
_extends = Object.assign || function(target) { | ||
for(var i = 1; i < arguments.length; i++){ | ||
var source = arguments[i]; | ||
for(var key in source){ | ||
if (Object.prototype.hasOwnProperty.call(source, key)) { | ||
target[key] = source[key]; | ||
} | ||
} | ||
} | ||
return target; | ||
}; | ||
return _extends.apply(this, arguments); | ||
} | ||
function createTree(tree, options) { | ||
var extendedTree = tree; | ||
extendedTree.messages = [].concat(extendedTree.messages || []); | ||
extendedTree.options = _extends({}, options, extendedTree.options); | ||
extendedTree.walk = function walkNodes(cb) { | ||
return (0, _utils.iterate)(this, cb); | ||
}; | ||
extendedTree.match = function matchNodes(expr, cb) { | ||
return (0, _utils.match)(this, expr, cb); | ||
}; | ||
return extendedTree; | ||
} | ||
function bbob(plugs) { | ||
@@ -22,8 +54,13 @@ var plugins = typeof plugs === "function" ? [ | ||
] : plugs || []; | ||
var options = { | ||
skipParse: false | ||
var mockRender = function() { | ||
return ""; | ||
}; | ||
return { | ||
process: function process(input, opts) { | ||
options = opts || {}; | ||
var options = opts || { | ||
skipParse: false, | ||
parser: _parser.parse, | ||
render: mockRender, | ||
data: null | ||
}; | ||
var parseFn = options.parser || _parser.parse; | ||
@@ -33,24 +70,23 @@ var renderFn = options.render; | ||
if (typeof parseFn !== "function") { | ||
throw new Error('"parser" is not a function, please pass to "process(input, { parser })" right function'); | ||
throw new Error(_errors.C1); | ||
} | ||
var tree = options.skipParse ? input || [] : parseFn(input, options); | ||
// raw tree before modification with plugins | ||
var raw = tree; | ||
tree.messages = []; | ||
tree.options = options; | ||
tree.walk = walk; | ||
tree.match = _utils.match; | ||
plugins.forEach(function(plugin) { | ||
tree = plugin(tree, { | ||
parse: parseFn, | ||
render: renderFn, | ||
iterate: _utils.iterate, | ||
match: _utils.match, | ||
data: data | ||
}) || tree; | ||
}); | ||
var raw = options.skipParse && Array.isArray(input) ? input : parseFn(input, options); | ||
var tree = options.skipParse && Array.isArray(input) ? createTree(input || [], options) : createTree(raw, options); | ||
for(var idx = 0; idx < plugins.length; idx++){ | ||
var plugin = plugins[idx]; | ||
if (typeof plugin === "function" && renderFn) { | ||
var newTree = plugin(tree, { | ||
parse: parseFn, | ||
render: renderFn, | ||
iterate: _utils.iterate, | ||
data: data | ||
}); | ||
tree = createTree(newTree || tree, options); | ||
} | ||
} | ||
return { | ||
get html () { | ||
if (typeof renderFn !== "function") { | ||
throw new Error('"render" function not defined, please pass to "process(input, { render })"'); | ||
throw new Error(_errors.C2); | ||
} | ||
@@ -57,0 +93,0 @@ return renderFn(tree, tree.options); |
@@ -15,15 +15,15 @@ /* eslint-disable no-plusplus */ "use strict"; | ||
}, | ||
match: function() { | ||
return match; | ||
}, | ||
same: function() { | ||
return same; | ||
}, | ||
match: function() { | ||
return match; | ||
} | ||
}); | ||
var _typeof = function(obj) { | ||
function _type_of(obj) { | ||
"@swc/helpers - typeof"; | ||
return obj && typeof Symbol !== "undefined" && obj.constructor === Symbol ? "symbol" : typeof obj; | ||
}; | ||
} | ||
var isObj = function(value) { | ||
return typeof value === "object"; | ||
return typeof value === "object" && value !== null; | ||
}; | ||
@@ -39,3 +39,3 @@ var isBool = function(value) { | ||
} | ||
} else if (tree && isObj(tree) && tree.content) { | ||
} else if (isObj(tree) && "content" in tree) { | ||
iterate(tree.content, cb); | ||
@@ -46,3 +46,3 @@ } | ||
function same(expected, actual) { | ||
if ((typeof expected === "undefined" ? "undefined" : _typeof(expected)) !== (typeof actual === "undefined" ? "undefined" : _typeof(actual))) { | ||
if ((typeof expected === "undefined" ? "undefined" : _type_of(expected)) !== (typeof actual === "undefined" ? "undefined" : _type_of(actual))) { | ||
return false; | ||
@@ -60,25 +60,31 @@ } | ||
} | ||
return Object.keys(expected).every(function(key) { | ||
var ao = actual[key]; | ||
var eo = expected[key]; | ||
if (isObj(eo) && eo !== null && ao !== null) { | ||
return same(eo, ao); | ||
} | ||
if (isBool(eo)) { | ||
return eo !== (ao === null); | ||
} | ||
return ao === eo; | ||
}); | ||
if (isObj(expected) && isObj(actual)) { | ||
return Object.keys(expected).every(function(key) { | ||
var ao = actual[key]; | ||
var eo = expected[key]; | ||
if (isObj(eo) && isObj(ao)) { | ||
return same(eo, ao); | ||
} | ||
if (isBool(eo)) { | ||
return eo !== (ao === null); | ||
} | ||
return ao === eo; | ||
}); | ||
} | ||
return false; | ||
} | ||
function match(expression, cb) { | ||
return Array.isArray(expression) ? iterate(this, function(node) { | ||
for(var idx = 0; idx < expression.length; idx++){ | ||
if (same(expression[idx], node)) { | ||
return cb(node); | ||
function match(t, expression, cb) { | ||
if (Array.isArray(expression)) { | ||
return iterate(t, function(node) { | ||
for(var idx = 0; idx < expression.length; idx++){ | ||
if (same(expression[idx], node)) { | ||
return cb(node); | ||
} | ||
} | ||
} | ||
return node; | ||
}) : iterate(this, function(node) { | ||
return node; | ||
}); | ||
} | ||
return iterate(t, function(node) { | ||
return same(expression, node) ? cb(node) : node; | ||
}); | ||
} |
{ | ||
"name": "@bbob/core", | ||
"version": "3.0.2", | ||
"version": "4.0.0", | ||
"description": "⚡️Blazing-fast js-bbcode-parser, bbcode js, that transforms and parses to AST with plugin support in pure javascript, no dependencies", | ||
@@ -23,3 +23,5 @@ "keywords": [ | ||
"dependencies": { | ||
"@bbob/parser": "^3.0.2" | ||
"@bbob/parser": "*", | ||
"@bbob/plugin-helper": "*", | ||
"@bbob/types": "*" | ||
}, | ||
@@ -31,2 +33,12 @@ "main": "lib/index.js", | ||
"browserName": "BbobCore", | ||
"types": "types/index.d.ts", | ||
"exports": { | ||
".": { | ||
"types": "./types/index.d.ts", | ||
"import": "./es/index.js", | ||
"require": "./lib/index.js", | ||
"browser": "./dist/index.min.js", | ||
"umd": "./dist/index.min.js" | ||
} | ||
}, | ||
"homepage": "https://github.com/JiLiZART/bbob", | ||
@@ -42,16 +54,6 @@ "author": "Nikolay Kostyurin <jilizart@gmail.com>", | ||
}, | ||
"scripts": { | ||
"build:commonjs": "../../scripts/pkg-task build-commonjs", | ||
"build:es": "../../scripts/pkg-task build-es", | ||
"build:umd": "../../scripts/pkg-task build-umd", | ||
"build": "npm run build:commonjs && npm run build:es && npm run build:umd", | ||
"test": "../../scripts/pkg-task test", | ||
"cover": "../../scripts/pkg-task cover", | ||
"lint": "../../scripts/pkg-task lint", | ||
"size": "../../scripts/pkg-task size", | ||
"bundlesize": "../../scripts/pkg-task bundlesize" | ||
}, | ||
"size-limit": [ | ||
{ | ||
"path": "lib/index.js" | ||
"path": "./dist/index.min.js", | ||
"limit": "4.5 KB" | ||
} | ||
@@ -73,3 +75,15 @@ ], | ||
"es" | ||
] | ||
} | ||
], | ||
"scripts": { | ||
"build:commonjs": "pkg-task", | ||
"build:es": "pkg-task", | ||
"build:umd": "pkg-task", | ||
"build": "pkg-task", | ||
"test": "pkg-task", | ||
"cover": "pkg-task", | ||
"lint": "pkg-task", | ||
"size": "pkg-task", | ||
"bundlesize": "pkg-task", | ||
"types": "pkg-task" | ||
} | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Wildcard dependency
QualityPackage has a dependency with a floating version range. This can cause issues if the dependency publishes a new major version.
Found 3 instances in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
63824
14
1508
0
3
3
1
5
+ Added@bbob/plugin-helper@*
+ Added@bbob/types@*
+ Added@bbob/parser@4.1.1(transitive)
+ Added@bbob/plugin-helper@4.1.1(transitive)
+ Added@bbob/types@4.1.1(transitive)
- Removed@bbob/parser@3.0.2(transitive)
- Removed@bbob/plugin-helper@3.0.2(transitive)
Updated@bbob/parser@*