Socket
Socket
Sign inDemoInstall

@bbob/html

Package Overview
Dependencies
Maintainers
1
Versions
47
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@bbob/html - npm Package Compare versions

Comparing version 2.9.0 to 3.0.0

1005

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.BbobHtml = {}));
})(this, (function (exports) { 'use strict';
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@bbob/core'), require('@bbob/plugin-helper')) :
typeof define === 'function' && define.amd ? define(['exports', '@bbob/core', '@bbob/plugin-helper'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.BbobHtml = {}, global.core, global.pluginHelper));
})(this, (function (exports, core, 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 = '\\';
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);
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;
// 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;
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);
// 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
};
}
/**
* @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();
};
/* eslint-disable no-plusplus */ const isObj = (value)=>typeof value === 'object';
const isBool = (value)=>typeof value === 'boolean';
function iterate(t, cb) {
const tree = t;
if (Array.isArray(tree)) {
for(let idx = 0; idx < tree.length; idx++){
tree[idx] = iterate(cb(tree[idx]), cb);
}
} else if (tree && isObj(tree) && tree.content) {
iterate(tree.content, cb);
}
return tree;
}
function same(expected, actual) {
if (typeof expected !== typeof actual) {
return false;
}
if (!isObj(expected) || expected === null) {
return expected === actual;
}
if (Array.isArray(expected)) {
return expected.every((exp)=>[].some.call(actual, (act)=>same(exp, act)));
}
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;
});
}
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);
}
}
return node;
}) : iterate(this, (node)=>same(expression, node) ? cb(node) : node);
}
function walk(cb) {
return iterate(this, cb);
}
function bbob(plugs) {
const plugins = typeof plugs === 'function' ? [
plugs
] : plugs || [];
let options = {
skipParse: false
};
return {
process (input, opts) {
options = opts || {};
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');
}
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;
});
return {
get html () {
if (typeof renderFn !== 'function') {
throw new Error('"render" function not defined, please pass to "process(input, { render })"');
}
return renderFn(tree, tree.options);
},
tree,
raw,
messages: tree.messages
};
}
};
}
const SELFCLOSE_END_TAG = '/>';

@@ -1019,3 +28,3 @@ const CLOSE_START_TAG = '</';

node.tag,
attrsToString(node.attrs),
pluginHelper.attrsToString(node.attrs),
SELFCLOSE_END_TAG

@@ -1028,3 +37,3 @@ ].join('');

node.tag,
attrsToString(node.attrs),
pluginHelper.attrsToString(node.attrs),
END_TAG,

@@ -1048,3 +57,3 @@ renderNodes(node.content),

}), '');
const toHTML = (source, plugins, options)=>bbob(plugins).process(source, {
const toHTML = (source, plugins, options)=>core(plugins).process(source, {
...options,

@@ -1051,0 +60,0 @@ render: renderNodes

2

dist/index.min.js

@@ -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).BbobHtml={})}(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"),o=(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]():""},a=t=>null==t?"":n(t,(e,r)=>[...e,o(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=o(t,r),s={...e};delete s[r];let i=a(s);return`${n}${i}`}return`${t}${a(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]:"",y=t=>t&&t[p]||0,b=t=>t&&t.row||0,T=t=>!!t&&void 0!==t[c]&&(5===t[c]||6===t[c]||1===t[c]),A=t=>!!t&&void 0!==t[c]&&2===t[c],k=t=>47===d(t).charCodeAt(0),m=t=>!k(t),x=t=>!!t&&void 0!==t[c]&&3===t[c],N=t=>!!t&&void 0!==t[c]&&4===t[c],$=t=>{let e=d(t);return k(t)?e.slice(1):e},w=t=>d(t)+"]";class L{isEmpty(){return isNaN(this[c])}isText(){return T(this)}isTag(){return A(this)}isAttrName(){return x(this)}isAttrValue(){return N(this)}isStart(){return m(this)}isEnd(){return k(this)}getName(){return $(this)}getValue(){return d(this)}getLine(){return y(this)}getColumn(){return b(this)}toString(){return w(this)}constructor(t,e,r,n){this[c]=Number(t),this[f]=String(e),this[p]=Number(r),this.row=Number(n)}}function v(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()},o=()=>t.substring(r.pos),a=(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=o,this.getNext=h,this.getPrev=g,this.isLast=()=>r.pos===r.len,this.includes=s,this.grabWhile=c,this.grabN=a,this.substrUntilChar=n}let C=(t,e)=>new v(t,e),E=(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 S(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 j=(t=[])=>new S(t),W=(t,e,r=0,n=0)=>new L(t,e,r,n);function V(t,e={}){let r=0,n=0,s=-1,i=0,l=0,o="",a=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"],y=[" "," "],b=["="," "," "],T=t=>p.indexOf(t)>=0,A=t=>"\n"===t,k=t=>y.indexOf(t)>=0,m=t=>-1===d.indexOf(t),x=t=>b.indexOf(t)>=0,N=t=>t===u||t===g||"\\"===t,$=t=>"\\"===t,w=()=>{n++},L=t=>O(E(t,'"')),v=(t,e)=>{""!==o&&e&&(o=""),""===o&&c.includes(t)&&(o=t)},S=C(t,{onSkip:w});function j(t,e){let i=W(t,e,r,n);f(i),a[s+=1]=i}return{tokenize:function(){for(i=0;S.hasNext();)switch(i){case 1:i=function(){let t=S.getCurr(),e=S.getNext();S.skip();let r=S.substrUntilChar(g),n=0===r.length||r.indexOf(u)>=0;if(T(e)||n||S.isLast())return j(1,t),0;let s=-1===r.indexOf("="),i="/"===r[0];if(s||i){let l=S.grabWhile(t=>t!==g);return S.skip(),j(2,l),v(l,i),0}return 2}();break;case 2:i=function(){let t=S.grabWhile(t=>t!==g,!0),e=C(t,{onSkip:w}),r=e.includes(" ");for(l=0;e.hasNext();)l=function(t,e){if(1===l){let r=t=>!("="===t||k(t)),n=t.grabWhile(r),s=t.isLast(),i="="!==t.getCurr();return(t.skip(),s||i?j(4,L(n)):j(3,n),s)?0:i?1:2}if(2===l){let o=!1,a=r=>{let n=t.getPrev(),s=t.getNext(),i=k(r),l=k(s);return!!(o&&x(r))||('"'!==r||"\\"===n||!!(o=!o)||"="===s||!!l)&&(!!e||!1===i)},u=t.grabWhile(a);return(t.skip(),j(4,L(u)),t.isLast())?0:1}let g=e=>!("="===e||k(e)||t.isLast()),h=t.grabWhile(g);if(j(2,h),v(h),t.skip(),e)return 2;let c=t.includes("=");return c?1:2}(e,!r);return S.skip(),0}();break;default:i=function(){if(A(S.getCurr()))return j(6,S.getCurr()),S.skip(),n=0,r++,0;if(k(S.getCurr())){let t=S.grabWhile(k);return j(5,t),0}if(S.getCurr()===u){if(o){let e=u.length+1+o.length,s=`${u}/${o}`,i=S.grabN(e);if(i===s)return 1}else if(S.includes(g))return 1;return j(1,S.getCurr()),S.skip(),0}if(h){if($(S.getCurr())){let l=S.getCurr(),a=S.getNext();return(S.skip(),N(a))?(S.skip(),j(1,a),0):(j(1,l),0)}let c=t=>m(t)&&!$(t),f=S.grabWhile(c);return j(1,f),0}let p=S.grabWhile(m);return j(1,p),0}()}return a.length=s+1,a},isTokenNested:function(e){let r=u+"/"+e.getValue();return t.indexOf(r)>-1}}}let P=(t,r={})=>{let n=r.openTag||"[",s=r.closeTag||"]",i=null,l=j(),o=j(),a=j(),u=j(),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=()=>{a.flushLast()&&u.flushLast()},y=()=>{let t=o.getLast();return t&&Array.isArray(t.content)?t.content:l.toArray()},b=t=>{let r=y();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))},T=t=>{d();let e=h.create(t.getValue()),r=c(t);a.push(e),r?o.push(e):b(e)},A=t=>{d();let e=o.flushLast();if(e)b(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})}},k=t=>{t.isStart()&&T(t),t.isEnd()&&A(t)},m=t=>{let e=a.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):b(r):t.isTag()&&b(t.toString())}else t.isText()?b(r):t.isTag()&&b(t.toString())},x=t=>{t.isTag()?k(t):m(t)};return(i=(r.createTokenizer?r.createTokenizer:V)(t,{onToken:x,openTag:n,closeTag:s,onlyAllowTags:r.onlyAllowTags,contextFreeTags:r.contextFreeTags,enableEscapeTags:r.enableEscapeTags})).tokenize(),l.toArray()},z=t=>"object"==typeof t,F=t=>"boolean"==typeof t;function B(t,e){let r=t;if(Array.isArray(r))for(let n=0;n<r.length;n++)r[n]=B(e(r[n]),e);else r&&z(r)&&r.content&&B(r.content,e);return r}function M(t,e){return typeof t==typeof e&&(z(t)&&null!==t?Array.isArray(t)?t.every(t=>[].some.call(e,e=>M(t,e))):Object.keys(t).every(r=>{let n=e[r],s=t[r];return z(s)&&null!==s&&null!==n?M(s,n):F(s)?s!==(null===n):n===s}):t===e)}function U(t,e){return Array.isArray(t)?B(this,r=>{for(let n=0;n<t.length;n++)if(M(t[n],r))return e(r);return r}):B(this,r=>M(t,r)?e(r):r)}function _(t){return B(this,t)}let q=(t,{stripTags:e=!1})=>{if(!t)return"";let r=typeof t;return"string"===r||"number"===r?t:"object"===r?!0===e?H(t.content,{stripTags:e}):null===t.content?["<",t.tag,a(t.attrs),"/>"].join(""):["<",t.tag,a(t.attrs),">",H(t.content),"</",t.tag,">"].join(""):Array.isArray(t)?H(t,{stripTags:e}):""},H=(t,{stripTags:e=!1}={})=>[].concat(t).reduce((t,r)=>t+q(r,{stripTags:e}),"");t.default=(t,e,r)=>(function(t){let e="function"==typeof t?[t]:t||[],r={skipParse:!1};return{process(t,n){r=n||{};let s=r.parser||P,i=r.render,l=r.data||null;if("function"!=typeof s)throw Error('"parser" is not a function, please pass to "process(input, { parser })" right function');let o=r.skipParse?t||[]:s(t,r),a=o;return o.messages=[],o.options=r,o.walk=_,o.match=U,e.forEach(t=>{o=t(o,{parse:s,render:i,iterate:B,match:U,data:l})||o}),{get html(){if("function"!=typeof i)throw Error('"render" function not defined, please pass to "process(input, { render })"');return i(o,o.options)},tree:o,raw:a,messages:o.messages}}}})(e).process(t,{...r,render:H}).html,t.render=H,Object.defineProperty(t,"__esModule",{value:!0})});
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("@bbob/core"),require("@bbob/plugin-helper")):"function"==typeof define&&define.amd?define(["exports","@bbob/core","@bbob/plugin-helper"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).BbobHtml={},e.core,e.pluginHelper)}(this,function(e,t,r){"use strict";let n=(e,{stripTags:t=!1})=>{if(!e)return"";let n=typeof e;return"string"===n||"number"===n?e:"object"===n?!0===t?o(e.content,{stripTags:t}):null===e.content?["<",e.tag,r.attrsToString(e.attrs),"/>"].join(""):["<",e.tag,r.attrsToString(e.attrs),">",o(e.content),"</",e.tag,">"].join(""):Array.isArray(e)?o(e,{stripTags:t}):""},o=(e,{stripTags:t=!1}={})=>[].concat(e).reduce((e,r)=>e+n(r,{stripTags:t}),"");e.default=(e,r,n)=>t(r).process(e,{...n,render:o}).html,e.render=o,Object.defineProperty(e,"__esModule",{value:!0})});
{
"name": "@bbob/html",
"version": "2.9.0",
"version": "3.0.0",
"description": "A BBCode to HTML Renderer part of @bbob",

@@ -12,4 +12,4 @@ "keywords": [

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

@@ -61,4 +61,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