mirror of
https://github.com/tenrok/BBob.git
synced 2026-06-14 18:42:24 +03:00
feat: typescript support (#185)
* feat: initial typescript support * feat: typescript support * feat(plugin-helper): move files to typescript * chore: update lock files * feat: preset types * fix: build * fix: benchmark * fix: remove pnpm cache * fix: bench action * fix: pnpm recursive install * fix: nx cache * fix: lock file * fix: workflows * fix: lerna support in pnpm * fix: pnpm workspace * fix: remove unused files * fix: pnpm lock file * fix: update lerna for support pnpm * fix: lerna bootstrap * fix: rollup build * fix: update nx * fix: build * fix: add nx dep target * fix: remove nx cache * fix: workflow run on push only for master * fix: test workflow run on push only for master * fix: remove parallel for gen types * fix: benchmark * fix: benchmark imports * fix: pnpm * fix: types errors and pnpm * fix: types * fix: types * refactor: parser * fix(parser): tests * fix: preset tests * fix: react types * fix: react type declarations * fix: pnpm lock file * fix: react preset types * fix: lock file * fix: vue2 types * feat: dev container support * fix: types * fix: types * refactor: rewrite pkg-task, add nx gen-types deps, fix react/render.ts * refactor: types * fix: types * fix: rename gen-types to types * fix: nx build order * fix: nx reset * fix: define nx deps explicit * fix: build * fix: nx * fix: nx order build * fix: nx deps * fix: bbob cli tests * fix: tests * fix: cli tests and import * fix: test cover * fix: cli cover
This commit is contained in:
@@ -1,77 +0,0 @@
|
||||
import { OPEN_BRAKET, CLOSE_BRAKET, SLASH } from './char';
|
||||
import {
|
||||
getNodeLength, appendToNode, attrsToString, attrValue, getUniqAttr,
|
||||
} from './helpers';
|
||||
|
||||
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 {
|
||||
constructor(tag, attrs, content) {
|
||||
this.tag = tag;
|
||||
this.attrs = attrs;
|
||||
this.content = Array.isArray(content) ? content : [content];
|
||||
}
|
||||
|
||||
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 })}`;
|
||||
}
|
||||
}
|
||||
|
||||
TagNode.create = (tag, attrs = {}, content = []) => new TagNode(tag, attrs, content);
|
||||
TagNode.isOf = (node, type) => (node.tag === type);
|
||||
|
||||
export { TagNode };
|
||||
export default TagNode;
|
||||
@@ -0,0 +1,115 @@
|
||||
import { OPEN_BRAKET, CLOSE_BRAKET, SLASH } from './char';
|
||||
import {
|
||||
getUniqAttr,
|
||||
getNodeLength,
|
||||
appendToNode,
|
||||
attrsToString,
|
||||
attrValue,
|
||||
isTagNode,
|
||||
} from './helpers';
|
||||
|
||||
import type { NodeContent, TagNodeObject, TagNodeTree } from "./types";
|
||||
|
||||
const getTagAttrs = <AttrValue>(tag: string, params: Record<string, AttrValue>) => {
|
||||
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: TagNodeTree, openTag: string, closeTag: string) => {
|
||||
const toString = (node: NodeContent) => {
|
||||
if (isTagNode(node)) {
|
||||
return node.toString({ openTag, closeTag })
|
||||
}
|
||||
|
||||
return String(node)
|
||||
}
|
||||
|
||||
if (Array.isArray(content)) {
|
||||
return content.reduce<string>((r, node) => {
|
||||
if (node !== null) {
|
||||
return r + toString(node)
|
||||
}
|
||||
|
||||
return r
|
||||
}, '')
|
||||
}
|
||||
|
||||
if (content) {
|
||||
return toString(content)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
export class TagNode implements TagNodeObject {
|
||||
public readonly tag: string
|
||||
public attrs: Record<string, unknown>
|
||||
public content: TagNodeTree
|
||||
|
||||
constructor(tag: string, attrs: Record<string, unknown>, content: TagNodeTree) {
|
||||
this.tag = tag;
|
||||
this.attrs = attrs;
|
||||
this.content = content
|
||||
}
|
||||
|
||||
attr(name: string, value?: unknown) {
|
||||
if (typeof value !== 'undefined') {
|
||||
this.attrs[name] = value;
|
||||
}
|
||||
|
||||
return this.attrs[name];
|
||||
}
|
||||
|
||||
append(value: string) {
|
||||
return appendToNode(this, value);
|
||||
}
|
||||
|
||||
get length(): number {
|
||||
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 } = {}): string {
|
||||
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: string, attrs: Record<string, unknown> = {}, content: TagNodeTree = []) {
|
||||
return new TagNode(tag, attrs, content)
|
||||
}
|
||||
|
||||
static isOf(node: TagNode, type: string) {
|
||||
return (node.tag === type)
|
||||
}
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
import { N } from './char';
|
||||
|
||||
const isTagNode = (el) => typeof el === 'object' && !!el.tag;
|
||||
const isStringNode = (el) => typeof el === 'string';
|
||||
const isEOL = (el) => el === N;
|
||||
|
||||
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, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''')
|
||||
// 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,
|
||||
);
|
||||
|
||||
export {
|
||||
attrsToString,
|
||||
attrValue,
|
||||
appendToNode,
|
||||
escapeHTML,
|
||||
getNodeLength,
|
||||
getUniqAttr,
|
||||
isTagNode,
|
||||
isStringNode,
|
||||
isEOL,
|
||||
};
|
||||
@@ -0,0 +1,125 @@
|
||||
import { N } from './char';
|
||||
import type { TagNode } from "./TagNode";
|
||||
import type { NodeContent, StringNode } from "./types";
|
||||
|
||||
function isTagNode(el: unknown): el is TagNode {
|
||||
return typeof el === 'object' && el !== null && 'tag' in el;
|
||||
}
|
||||
|
||||
function isStringNode(el: unknown): el is StringNode {
|
||||
return typeof el === 'string';
|
||||
}
|
||||
|
||||
// check string is end of line
|
||||
function isEOL(el: string) {
|
||||
return el === N
|
||||
}
|
||||
|
||||
function keysReduce<Res, Def extends Res, T extends Record<string, unknown>>(obj: T, reduce: (acc: Def, key: keyof T) => Res, def: Def): Res {
|
||||
const keys = Object.keys(obj)
|
||||
|
||||
return keys.reduce((acc, key) => reduce(acc, key), def)
|
||||
}
|
||||
|
||||
function getNodeLength(node: NodeContent): number {
|
||||
if (isTagNode(node) && Array.isArray(node.content)) {
|
||||
return node.content.reduce<number>((count, contentNode) => {
|
||||
return count + getNodeLength(contentNode)
|
||||
}, 0);
|
||||
}
|
||||
|
||||
if (isStringNode(node)) {
|
||||
return String(node).length;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
function appendToNode(node: TagNode, value: NodeContent) {
|
||||
if (Array.isArray(node.content)) {
|
||||
node.content.push(value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces " to &qquot;
|
||||
* @param {string} value
|
||||
*/
|
||||
function escapeAttrValue(value: string) {
|
||||
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');
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use escapeAttrValue
|
||||
*/
|
||||
const escapeHTML = escapeAttrValue
|
||||
|
||||
/**
|
||||
* Accept name and value and return valid html5 attribute string
|
||||
*/
|
||||
function attrValue<AttrValue = unknown>(name: string, value: AttrValue) {
|
||||
// in case of performance
|
||||
switch (typeof value) {
|
||||
case 'boolean':
|
||||
return value ? `${name}` : ''
|
||||
case 'number':
|
||||
return `${name}="${value}"`
|
||||
case 'string':
|
||||
return `${name}="${escapeAttrValue(value as string)}"`
|
||||
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<AttrValue = unknown>(values: Record<string, AttrValue> | null) {
|
||||
// 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'
|
||||
*/
|
||||
function getUniqAttr<Value>(attrs: Record<string, Value>) {
|
||||
return keysReduce(
|
||||
attrs,
|
||||
(res, key) => (attrs[key] === key ? attrs[key] : null),
|
||||
null,
|
||||
)
|
||||
}
|
||||
|
||||
export {
|
||||
attrsToString,
|
||||
attrValue,
|
||||
appendToNode,
|
||||
escapeHTML,
|
||||
escapeAttrValue,
|
||||
getNodeLength,
|
||||
getUniqAttr,
|
||||
isTagNode,
|
||||
isStringNode,
|
||||
isEOL,
|
||||
};
|
||||
@@ -1,3 +0,0 @@
|
||||
export * from './helpers';
|
||||
export * from './char';
|
||||
export { TagNode } from './TagNode';
|
||||
@@ -0,0 +1,4 @@
|
||||
export * from "./helpers";
|
||||
export * from "./char";
|
||||
export * from "./TagNode";
|
||||
export * from "./types";
|
||||
@@ -0,0 +1,13 @@
|
||||
export type StringNode = string | number
|
||||
|
||||
export interface TagNodeObject {
|
||||
readonly tag: string
|
||||
attrs: Record<string, unknown>
|
||||
content: TagNodeTree
|
||||
}
|
||||
|
||||
export type NodeContent = TagNodeObject | StringNode | null
|
||||
|
||||
export type PartialNodeContent = Partial<TagNodeObject> | StringNode | null
|
||||
|
||||
export type TagNodeTree = NodeContent | NodeContent[] | null
|
||||
Reference in New Issue
Block a user