@bbob/plugin-helper
Version:
Set of utils to help write plugins for @bbob/core
227 lines (221 loc) • 7.35 kB
JavaScript
(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.BbobPluginHelper = {}));
})(this, (function (exports) { 'use strict';
const N = '\n';
const TAB = '\t';
const F = '\f';
const R = '\r';
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';
}
// check string is end of line
function isEOL(el) {
return el === N;
}
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|file):/gi, '$1%3A');
}
/**
* @deprecated use escapeAttrValue
*/ const escapeHTML = escapeAttrValue;
/**
* 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 toString = (node, openTag, closeTag)=>{
if (isTagNode(node)) {
return node.toString({
openTag,
closeTag
});
}
return String(node);
};
const nodeTreeToString = (content, openTag, closeTag)=>{
if (Array.isArray(content)) {
return content.reduce((r, node)=>{
if (node !== null) {
return r + toString(node, openTag, closeTag);
}
return r;
}, '');
}
if (content) {
return toString(content, openTag, closeTag);
}
return null;
};
class TagNode {
get length() {
return getNodeLength(this);
}
attr(name, value) {
if (typeof value !== 'undefined') {
this.attrs[name] = value;
}
return this.attrs[name];
}
append(value) {
return appendToNode(this, value);
}
setStart(value) {
this.start = value;
}
setEnd(value) {
this.end = value;
}
toTagStart({ openTag = OPEN_BRAKET, closeTag = CLOSE_BRAKET } = {}) {
const tagAttrs = getTagAttrs(String(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, this.attrs, this.content, this.start, this.end);
}
toString({ openTag = OPEN_BRAKET, closeTag = CLOSE_BRAKET } = {}) {
const content = this.content ? nodeTreeToString(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
})}`;
}
toJSON() {
return {
tag: this.tag,
attrs: this.attrs,
content: this.content,
start: this.start,
end: this.end
};
}
static create(tag, attrs = {}, content = null, start) {
return new TagNode(tag, attrs, content, start);
}
static isOf(node, type) {
return node.tag === type;
}
constructor(tag, attrs, content, start, end){
this.tag = tag;
this.attrs = attrs;
this.content = content;
this.start = start;
this.end = end;
}
}
exports.BACKSLASH = BACKSLASH;
exports.CLOSE_BRAKET = CLOSE_BRAKET;
exports.EQ = EQ;
exports.F = F;
exports.N = N;
exports.OPEN_BRAKET = OPEN_BRAKET;
exports.QUOTEMARK = QUOTEMARK;
exports.R = R;
exports.SLASH = SLASH;
exports.SPACE = SPACE;
exports.TAB = TAB;
exports.TagNode = TagNode;
exports.appendToNode = appendToNode;
exports.attrValue = attrValue;
exports.attrsToString = attrsToString;
exports.escapeAttrValue = escapeAttrValue;
exports.escapeHTML = escapeHTML;
exports.getNodeLength = getNodeLength;
exports.getUniqAttr = getUniqAttr;
exports.isEOL = isEOL;
exports.isStringNode = isStringNode;
exports.isTagNode = isTagNode;
}));