mirror of
https://github.com/tenrok/BBob.git
synced 2026-05-24 14:04:06 +03:00
0edd490a24
* Add extensions to all imports * Set module resolution to `bundler` to avoid Node specific behavior * Use `ts2mjs` to rename files to `mjs` * Add extensions to `@bbob/types` imports * Fix `package.json` for proper ESM extension and type separation * More module resolution stuff change (`node16` for everything, `node` for Vue 2 plugin) * Use `ts-jest-resolver` for `js` -> `ts` resolving in Jest * Add changeset * Add import extensions to frontend libs
127 lines
2.9 KiB
TypeScript
127 lines
2.9 KiB
TypeScript
import type { NodeContent, StringNode } from "@bbob/types";
|
|
|
|
import { N } from './char.js';
|
|
import type { TagNode } from "./TagNode.js";
|
|
|
|
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, obj: T) => Res, def: Def): Res {
|
|
const keys = Object.keys(obj)
|
|
|
|
return keys.reduce((acc, key) => reduce(acc, key, obj), 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|file):/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, obj) => [...arr, attrValue(key, obj[key])],
|
|
[''],
|
|
).join(' ');
|
|
}
|
|
|
|
/**
|
|
* Gets value from
|
|
* @example
|
|
* getUniqAttr({ 'foo': true, 'bar': bar' }) => 'bar'
|
|
*/
|
|
function getUniqAttr<Value>(attrs?: Record<string, Value>) {
|
|
return keysReduce(
|
|
attrs || {},
|
|
(res, key, obj) => (obj[key] === key ? obj[key] : null),
|
|
null,
|
|
)
|
|
}
|
|
|
|
export {
|
|
attrsToString,
|
|
attrValue,
|
|
appendToNode,
|
|
escapeHTML,
|
|
escapeAttrValue,
|
|
getNodeLength,
|
|
getUniqAttr,
|
|
isTagNode,
|
|
isStringNode,
|
|
isEOL,
|
|
};
|