diff --git a/eslint.config.mjs b/eslint.config.mjs index 4eed786..384576b 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,22 +1,28 @@ +/** eslint-disable-next-line n/no-unpublished-import */ import CodeX from 'eslint-config-codex'; +import { plugin as TsPlugin, parser as TsParser } from 'typescript-eslint'; /** * @todo connect architecture config */ export default [ ...CodeX, { - name: 'ts-notex.web', + name: 'ts-editorls/utils', ignores: ['eslint.config.mjs'], /** * This are the options for typescript files */ languageOptions: { + parser: TsParser, parserOptions: { project: './tsconfig.eslint.json', tsconfigRootDir: './', sourceType: 'module', // Allows for the use of imports }, }, + plugins: { + '@typescript-eslint': TsPlugin, + }, rules: { 'n/no-missing-import': ['off'], @@ -30,7 +36,7 @@ export default [ }], '@typescript-eslint/naming-convention': ['error', { selector: 'property', - format: ['UPPER_CASE' | 'camelCase'], + format: ['UPPER_CASE', 'camelCase', 'PascalCase'], }], }, }, diff --git a/package.json b/package.json index eab9284..f6dde92 100644 --- a/package.json +++ b/package.json @@ -1,17 +1,17 @@ { - "name": "utils", + "name": "@editorjs/utils", "private": true, "workspaces": [ "packages/*" ], "version": "1.0.0", - "description": "Utils that are used by Editor.js and it's tools", - "main": "build/index.js", + "description": "Useful utils for working with Editor.js", + "main": "dist/index.js", "repository": "https://github.com/editor-js/utils", "author": "CodeX Team ", "license": "MIT", "scripts": { - "build": "yarn build:helpers && yarn build:dom && yarn build:caret && yarn build:keyboard", + "build": "tsc && yarn build:helpers && yarn build:dom && yarn build:caret && yarn build:keyboard", "build:helpers": "cd packages/helpers && yarn build", "build:dom": "cd packages/dom && yarn build", "build:caret": "cd packages/caret && yarn build", @@ -20,11 +20,9 @@ "lint:fix": "yarn lint --fix" }, "devDependencies": { - "tslint": "^6.1.1", "@types/node": "^20.10.7", - "@eslint/eslintrc": "^3.1.0", - "typescript-eslint": "^7.9.0", "eslint": "^9.0.0", + "typescript-eslint": "^7.9.0", "eslint-config-codex": "^2.0.0", "typescript": "^5.4.5" } diff --git a/packages/caret/tsconfig.json b/packages/caret/tsconfig.json index 6d6efd6..38db291 100644 --- a/packages/caret/tsconfig.json +++ b/packages/caret/tsconfig.json @@ -6,7 +6,7 @@ "composite": true, "declaration": true }, - "include": ["src"], + "include": ["**/*"], "exclude": ["dist"], "references": [ { "path": "../dom" } diff --git a/packages/dom/src/append.ts b/packages/dom/src/append.ts new file mode 100644 index 0000000..fb1d472 --- /dev/null +++ b/packages/dom/src/append.ts @@ -0,0 +1,17 @@ +/** + * Append one or several elements to the parent + * @param parent - where to append + * @param elements - element or elements list + */ +export function append( + parent: Element | DocumentFragment, + elements: Element | Element[] | DocumentFragment | Text | Text[] +): void { + if (Array.isArray(elements)) { + elements.forEach((el) => { + parent.appendChild(el); + }); + } else { + parent.appendChild(elements); + } +} diff --git a/packages/dom/src/index.ts b/packages/dom/src/index.ts index 1d589fa..6bfea8e 100644 --- a/packages/dom/src/index.ts +++ b/packages/dom/src/index.ts @@ -1,116 +1,17 @@ -import * as _ from './utils'; +import { isNumber, isString } from '@editorjs/utils/helpers'; /** - * DOM manipulations helper - * @todo get rid of class and make separate utility functions + * Swap two elements in parent + * @param el1 - first element that would be swapped with second + * @param el2 - second element that would be swapped with first + * @deprecated */ -export default class Dom { - /** - * Check if passed tag has no closed tag - * @param tag - element to check - * @returns - */ - public static isSingleTag(tag: HTMLElement): boolean { - return tag.tagName && [ - 'AREA', - 'BASE', - 'BR', - 'COL', - 'COMMAND', - 'EMBED', - 'HR', - 'IMG', - 'INPUT', - 'KEYGEN', - 'LINK', - 'META', - 'PARAM', - 'SOURCE', - 'TRACK', - 'WBR', - ].includes(tag.tagName); - } - - /** - * Check if element is BR or WBR - * @param element - element to check - * @returns - */ - public static isLineBreakTag(element: HTMLElement): element is HTMLBRElement { - return element && element.tagName && [ - 'BR', - 'WBR', - ].includes(element.tagName); - } - - /** - * Helper for making Elements with class name and attributes - * @param tagName - new Element tag name - * @param [classNames] - list or name of CSS class name(s) - * @param [attributes] - any attributes - * @returns - */ - public static make(tagName: string, classNames: string | (string | undefined)[] | null = null, attributes: object = {}): HTMLElement { - const el = document.createElement(tagName); - - if (Array.isArray(classNames)) { - const validClassnames = classNames.filter(className => className !== undefined); - - el.classList.add(...validClassnames); - } else if (classNames) { - el.classList.add(classNames); - } - - for (const attrName in attributes) { - if (Object.prototype.hasOwnProperty.call(attributes, attrName)) { - el[attrName] = attributes[attrName]; - } - } - - return el; - } - - /** - * Append one or several elements to the parent - * @param parent - where to append - * @param elements - element or elements list - */ - public static append( - parent: Element | DocumentFragment, - elements: Element | Element[] | DocumentFragment | Text | Text[] - ): void { - if (Array.isArray(elements)) { - elements.forEach(el => parent.appendChild(el)); - } else { - parent.appendChild(elements); - } - } - - /** - * Append element or a couple to the beginning of the parent elements - * @param parent - where to append - * @param elements - element or elements list - */ - public static prepend(parent: Element, elements: Element | Element[]): void { - if (Array.isArray(elements)) { - elements = elements.reverse(); - elements.forEach(el => parent.prepend(el)); - } else { - parent.prepend(elements); - } - } - - /** - * Swap two elements in parent - * @param el1 - from - * @param el2 - to - * @deprecated - */ - public static swap(el1: HTMLElement, el2: HTMLElement): void { - // create marker element and insert it where el1 is - const temp = document.createElement('div'); - const parent = el1.parentNode; +function swap(el1: HTMLElement, el2: HTMLElement): void { + // create marker element and insert it where el1 is + const temp = document.createElement('div'); + const parent = el1.parentNode; + if (parent !== null) { parent.insertBefore(temp, el1); // move el1 to right before el2 @@ -122,381 +23,409 @@ export default class Dom { // remove temporary marker node parent.removeChild(temp); } +} - /** - * Returns CSS selector for all text inputs - */ - public static get allInputsSelector(): string { - const allowedInputTypes = ['text', 'password', 'email', 'number', 'search', 'tel', 'url']; +/** + * Returns CSS selector for all text inputs + */ +function allInputsSelector(): string { + const allowedInputTypes = ['text', 'password', 'email', 'number', 'search', 'tel', 'url']; - return '[contenteditable=true], textarea, input:not([type]), ' - + allowedInputTypes.map(type => `input[type="${type}"]`).join(', '); - } + return '[contenteditable=true], textarea, input:not([type]), ' + + allowedInputTypes.map(type => `input[type="${type}"]`).join(', '); +} - /** - * Find all contenteditable, textarea and editable input elements passed holder contains - * @param holder - element where to find inputs - */ - public static findAllInputs(holder: Element): HTMLElement[] { - return _.array(holder.querySelectorAll(Dom.allInputsSelector)) - /** - * If contenteditable element contains block elements, treat them as inputs. - */ - .reduce((result, input) => { - if (Dom.isNativeInput(input) || Dom.containsOnlyInlineElements(input)) { - return [...result, input]; - } +/** + * Find all contenteditable, textarea and editable input elements passed holder contains + * @param holder - element where to find inputs + */ +function findAllInputs(holder: Element): HTMLElement[] { + return Array.from(holder.querySelectorAll(allInputsSelector())) + /** + * If contenteditable element contains block elements, treat them as inputs. + */ + .reduce((result, input) => { + if (isNativeInput(input) || containsOnlyInlineElements(input)) { + return [...result, input]; + } - return [...result, ...Dom.getDeepestBlockElements(input)]; - }, []); - } + return [...result, ...getDeepestBlockElements(input)]; + }, []); +} +/** + * Search for deepest node which is Leaf. + * Leaf is the vertex that doesn't have any child nodes + * @description Method recursively goes throw the all Node until it finds the Leaf + * @param node - root Node. From this vertex we start Deep-first search + * {@link https://en.wikipedia.org/wiki/Depth-first_search} + * @param [atLast] - find last text node + * @returns - it can be text Node or Element Node, so that caret will able to work with it + * Can return null if node is Document or DocumentFragment, or node is not attached to the DOM + */ +function getDeepestNode(node: Node, atLast = false): Node | null { /** - * Search for deepest node which is Leaf. - * Leaf is the vertex that doesn't have any child nodes - * @description Method recursively goes throw the all Node until it finds the Leaf - * @param node - root Node. From this vertex we start Deep-first search - * {@link https://en.wikipedia.org/wiki/Depth-first_search} - * @param [atLast] - find last text node - * @returns - it can be text Node or Element Node, so that caret will able to work with it - * Can return null if node is Document or DocumentFragment, or node is not attached to the DOM + * Current function have two directions: + * - starts from first child and every time gets first or nextSibling in special cases + * - starts from last child and gets last or previousSibling */ - public static getDeepestNode(node: Node, atLast = false): Node | null { - /** - * Current function have two directions: - * - starts from first child and every time gets first or nextSibling in special cases - * - starts from last child and gets last or previousSibling - */ - const child = atLast ? 'lastChild' : 'firstChild'; - const sibling = atLast ? 'previousSibling' : 'nextSibling'; + const child = atLast ? 'lastChild' : 'firstChild'; + const sibling = atLast ? 'previousSibling' : 'nextSibling'; - if (node && node.nodeType === Node.ELEMENT_NODE && node[child]) { - let nodeChild = node[child] as Node; + if (node && node.nodeType === Node.ELEMENT_NODE && node[child]) { + let nodeChild = node[child] as Node; + /** + * special case when child is single tag that can't contain any content + */ + if ( + isSingleTag(nodeChild as HTMLElement) + && !isNativeInput(nodeChild) + && !isLineBreakTag(nodeChild as HTMLElement) + ) { /** - * special case when child is single tag that can't contain any content + * 1) We need to check the next sibling. If it is Node Element then continue searching for deepest + * from sibling + * + * 2) If single tag's next sibling is null, then go back to parent and check his sibling + * In case of Node Element continue searching + * + * 3) If none of conditions above happened return parent Node Element */ - if ( - Dom.isSingleTag(nodeChild as HTMLElement) - && !Dom.isNativeInput(nodeChild) - && !Dom.isLineBreakTag(nodeChild as HTMLElement) - ) { - /** - * 1) We need to check the next sibling. If it is Node Element then continue searching for deepest - * from sibling - * - * 2) If single tag's next sibling is null, then go back to parent and check his sibling - * In case of Node Element continue searching - * - * 3) If none of conditions above happened return parent Node Element - */ - if (nodeChild[sibling]) { - nodeChild = nodeChild[sibling]; - } else if (nodeChild.parentNode[sibling]) { - nodeChild = nodeChild.parentNode[sibling]; - } else { - return nodeChild.parentNode; - } + if (nodeChild[sibling]) { + nodeChild = nodeChild[sibling]; + } else if (nodeChild.parentNode !== null && nodeChild.parentNode[sibling]) { + nodeChild = nodeChild.parentNode[sibling]; + } else { + return nodeChild.parentNode; } - - return this.getDeepestNode(nodeChild, atLast); } - return node; + return this.getDeepestNode(nodeChild, atLast); } - /** - * Check if object is DOM node - * @param node - object to check - * @returns - */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any - public static isElement(node: any): node is Element { - if (_.isNumber(node)) { - return false; - } + return node; +} - return node && node.nodeType && node.nodeType === Node.ELEMENT_NODE; +/** + * Check if object is DOM node + * @param node - object to check + * @returns + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function isElement(node: any): node is Element { + if (isNumber(node)) { + return false; } - /** - * Check if object is DocumentFragment node - * @param node - object to check - * @returns - */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any - public static isFragment(node: any): node is DocumentFragment { - if (_.isNumber(node)) { - return false; - } + return node && node.nodeType && node.nodeType === Node.ELEMENT_NODE; +} - return node && node.nodeType && node.nodeType === Node.DOCUMENT_FRAGMENT_NODE; +/** + * Check if object is DocumentFragment node + * @param node - object to check + * @returns + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function isFragment(node: any): node is DocumentFragment { + if (isNumber(node)) { + return false; } - /** - * Check if passed element is contenteditable - * @param element - html element to check - * @returns - */ - public static isContentEditable(element: HTMLElement): boolean { - return element.contentEditable === 'true'; + return node && node.nodeType && node.nodeType === Node.DOCUMENT_FRAGMENT_NODE; +} + +/** + * Check if passed element is contenteditable + * @param element - html element to check + * @returns + */ +function isContentEditable(element: HTMLElement): boolean { + return element.contentEditable === 'true'; +} + +/** + * Checks target if it is native input + * @param target - HTML element or string + * @returns + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function isNativeInput(target: any): target is HTMLInputElement | HTMLTextAreaElement { + const nativeInputs = [ + 'INPUT', + 'TEXTAREA', + ]; + + return target && target.tagName ? nativeInputs.includes(target.tagName) : false; +} + +/** + * Checks if we can set caret + * @param target - target to check + * @returns + */ +function canSetCaret(target: HTMLElement): boolean { + let result = true; + + if (isNativeInput(target)) { + switch (target.type) { + case 'file': + case 'checkbox': + case 'radio': + case 'hidden': + case 'submit': + case 'button': + case 'image': + case 'reset': + result = false; + break; + } + } else { + result = isContentEditable(target); } - /** - * Checks target if it is native input - * @param target - HTML element or string - * @returns - */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any - public static isNativeInput(target: any): target is HTMLInputElement | HTMLTextAreaElement { - const nativeInputs = [ - 'INPUT', - 'TEXTAREA', - ]; - - return target && target.tagName ? nativeInputs.includes(target.tagName) : false; + return result; +} + +/** + * Checks node if it is empty + * @description Method checks simple Node without any childs for emptiness + * If you have Node with 2 or more children id depth, you better use {@link Dom#isEmpty} method + * @param node - node to check + * @param [ignoreChars] - char or substring to treat as empty + * @returns true if it is empty + */ +function isNodeEmpty(node: Node, ignoreChars?: string): boolean { + let nodeText; + + if (this.isSingleTag(node as HTMLElement) && !this.isLineBreakTag(node as HTMLElement)) { + return false; } - /** - * Checks if we can set caret - * @param target - target to check - * @returns - */ - public static canSetCaret(target: HTMLElement): boolean { - let result = true; - - if (Dom.isNativeInput(target)) { - switch (target.type) { - case 'file': - case 'checkbox': - case 'radio': - case 'hidden': - case 'submit': - case 'button': - case 'image': - case 'reset': - result = false; - break; - } - } else { - result = Dom.isContentEditable(target); + if (this.isElement(node) && this.isNativeInput(node)) { + nodeText = (node as HTMLInputElement).value; + } else { + if (node.textContent !== null) { + nodeText = node.textContent.replace('\u200B', ''); } + } + + if (ignoreChars) { + nodeText = nodeText.replace(new RegExp(ignoreChars, 'g'), ''); + } + + return nodeText.trim().length === 0; +} - return result; +/** + * checks node if it is doesn't have any child nodes + * @param node - node to check + * @returns + */ +function isLeaf(node: Node): boolean { + if (!node) { + return false; } + return node.childNodes.length === 0; +} + +/** + * breadth-first search (BFS) + * {@link https://en.wikipedia.org/wiki/Breadth-first_search} + * @description Pushes to stack all DOM leafs and checks for emptiness + * @param node - node to check + * @param [ignoreChars] - char or substring to treat as empty + * @returns + */ +function isEmpty(node: Node, ignoreChars?: string): boolean { /** - * Checks node if it is empty - * @description Method checks simple Node without any childs for emptiness - * If you have Node with 2 or more children id depth, you better use {@link Dom#isEmpty} method - * @param node - node to check - * @param [ignoreChars] - char or substring to treat as empty - * @returns true if it is empty + * Normalize node to merge several text nodes to one to reduce tree walker iterations */ - public static isNodeEmpty(node: Node, ignoreChars?: string): boolean { - let nodeText; + node.normalize(); - if (this.isSingleTag(node as HTMLElement) && !this.isLineBreakTag(node as HTMLElement)) { - return false; - } + const treeWalker = [node]; - if (this.isElement(node) && this.isNativeInput(node)) { - nodeText = (node as HTMLInputElement).value; - } else { - nodeText = node.textContent.replace('\u200B', ''); - } + while (treeWalker.length > 0) { + const newNode = treeWalker.shift(); - if (ignoreChars) { - nodeText = nodeText.replace(new RegExp(ignoreChars, 'g'), ''); + if (!newNode) { + continue; } - return nodeText.trim().length === 0; - } + node = newNode; - /** - * checks node if it is doesn't have any child nodes - * @param node - node to check - * @returns - */ - public static isLeaf(node: Node): boolean { - if (!node) { + if (this.isLeaf(node) && !this.isNodeEmpty(node, ignoreChars)) { return false; } - return node.childNodes.length === 0; + if (node.childNodes) { + treeWalker.push(...Array.from(node.childNodes)); + } } - /** - * breadth-first search (BFS) - * {@link https://en.wikipedia.org/wiki/Breadth-first_search} - * @description Pushes to stack all DOM leafs and checks for emptiness - * @param node - node to check - * @param [ignoreChars] - char or substring to treat as empty - * @returns - */ - public static isEmpty(node: Node, ignoreChars?: string): boolean { - /** - * Normalize node to merge several text nodes to one to reduce tree walker iterations - */ - node.normalize(); - - const treeWalker = [node]; + return true; +} - while (treeWalker.length > 0) { - node = treeWalker.shift(); +/** + * Check if string contains html elements + * @param str - string to check + * @returns + */ +function isHTMLString(str: string): boolean { + const wrapper = make('div'); - if (!node) { - continue; - } + wrapper.innerHTML = str; - if (this.isLeaf(node) && !this.isNodeEmpty(node, ignoreChars)) { - return false; - } + return wrapper.childElementCount > 0; +} - if (node.childNodes) { - treeWalker.push(...Array.from(node.childNodes)); - } - } +/** + * Return length of node`s text content + * @param node - node with content + * @returns + */ +function getContentLength(node: Node): number { + if (isNativeInput(node)) { + return (node as HTMLInputElement).value.length; + } - return true; + if (node.nodeType === Node.TEXT_NODE) { + return (node as Text).length; } - /** - * Check if string contains html elements - * @param str - string to check - * @returns - */ - public static isHTMLString(str: string): boolean { - const wrapper = Dom.make('div'); + return node.textContent?.length ?? 0; +} - wrapper.innerHTML = str; +/** + * Return array of names of block html elements + * @returns + */ +function blockElements(): string[] { + return [ + 'address', + 'article', + 'aside', + 'blockquote', + 'canvas', + 'div', + 'dl', + 'dt', + 'fieldset', + 'figcaption', + 'figure', + 'footer', + 'form', + 'h1', + 'h2', + 'h3', + 'h4', + 'h5', + 'h6', + 'header', + 'hgroup', + 'hr', + 'li', + 'main', + 'nav', + 'noscript', + 'ol', + 'output', + 'p', + 'pre', + 'ruby', + 'section', + 'table', + 'tbody', + 'thead', + 'tr', + 'tfoot', + 'ul', + 'video', + ]; +} - return wrapper.childElementCount > 0; +/** + * Check if passed content includes only inline elements + * @param data - element or html string + * @returns + */ +function containsOnlyInlineElements(data: string | Element): boolean { + let wrapper: HTMLElement; + + if (isString(data)) { + wrapper = document.createElement('div'); + wrapper.innerHTML = data; + } else { + wrapper = data; } - /** - * Return length of node`s text content - * @param node - node with content - * @returns - */ - public static getContentLength(node: Node): number { - if (Dom.isNativeInput(node)) { - return (node as HTMLInputElement).value.length; - } + const check = (element: HTMLElement): boolean => { + return !blockElements().includes(element.tagName.toLowerCase()) + && Array.from(element.children).every(check); + }; - if (node.nodeType === Node.TEXT_NODE) { - return (node as Text).length; - } + return Array.from(wrapper.children).every(check); +} - return node.textContent.length; +/** + * Find and return all block elements in the passed parent (including subtree) + * @param parent - root element + * @returns + */ +function getDeepestBlockElements(parent: HTMLElement): HTMLElement[] { + if (containsOnlyInlineElements(parent)) { + return [parent]; } + return Array.from(parent.children).reduce((result, element) => { + return [...result, ...getDeepestBlockElements(element as HTMLElement)]; + }, []); +} + +/** + * Return element's offset related to the document + * @todo handle case when editor initialized in scrollable popup + * @param el - element to compute offset + */ +function offset(el): { /** - * Return array of names of block html elements - * @returns + * Calculated top offset of the passed element */ - public static get blockElements(): string[] { - return [ - 'address', - 'article', - 'aside', - 'blockquote', - 'canvas', - 'div', - 'dl', - 'dt', - 'fieldset', - 'figcaption', - 'figure', - 'footer', - 'form', - 'h1', - 'h2', - 'h3', - 'h4', - 'h5', - 'h6', - 'header', - 'hgroup', - 'hr', - 'li', - 'main', - 'nav', - 'noscript', - 'ol', - 'output', - 'p', - 'pre', - 'ruby', - 'section', - 'table', - 'tbody', - 'thead', - 'tr', - 'tfoot', - 'ul', - 'video', - ]; - } + top: number; /** - * Check if passed content includes only inline elements - * @param data - element or html string - * @returns + * Calculated left offset of the passed element */ - public static containsOnlyInlineElements(data: string | HTMLElement): boolean { - let wrapper: HTMLElement; - - if (_.isString(data)) { - wrapper = document.createElement('div'); - wrapper.innerHTML = data; - } else { - wrapper = data; - } - - const check = (element: HTMLElement): boolean => { - return !Dom.blockElements.includes(element.tagName.toLowerCase()) - && Array.from(element.children).every(check); - }; - - return Array.from(wrapper.children).every(check); - } + left: number; /** - * Find and return all block elements in the passed parent (including subtree) - * @param parent - root element - * @returns + * Calculated right offset of the passed element */ - public static getDeepestBlockElements(parent: HTMLElement): HTMLElement[] { - if (Dom.containsOnlyInlineElements(parent)) { - return [parent]; - } - - return Array.from(parent.children).reduce((result, element) => { - return [...result, ...Dom.getDeepestBlockElements(element as HTMLElement)]; - }, []); - } + right: number; /** - * Return element's offset related to the document - * @todo handle case when editor initialized in scrollable popup - * @param el - element to compute offset + * Calculated bottom offset of the passed element */ - public static offset(el): { top: number; left: number; right: number; bottom: number } { - const rect = el.getBoundingClientRect(); - const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft; - const scrollTop = window.pageYOffset || document.documentElement.scrollTop; - - const top = rect.top + scrollTop; - const left = rect.left + scrollLeft; - - return { - top, - left, - bottom: top + rect.height, - right: left + rect.width, - }; - } + bottom: number; } { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access + const rect = el.getBoundingClientRect(); + const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft; + const scrollTop = window.pageYOffset || document.documentElement.scrollTop; + + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access + const top = rect.top + scrollTop; + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access + const left = rect.left + scrollLeft; + + /* eslint-disable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access */ + return { + top, + left, + bottom: top + rect.height, + right: left + rect.width, + }; + /* eslint-enable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access */ } /** @@ -535,7 +464,7 @@ export function isCollapsedWhitespaces(textContent: string): boolean { * * 3. Calculate the total baseline Y coordinate: * - Sum of `marginTop`, `borderTopWidth`, `paddingTop`, the extra space due to `lineHeight`, and the baseline offset. - * @param element - The element to calculate the baseline for. + * @param element - The element which baseline would be calculated * @returns - The Y coordinate of the text baseline from the top of the element's margin box. */ export function calculateBaseline(element: Element): number { diff --git a/packages/dom/src/isTag.ts b/packages/dom/src/isTag.ts new file mode 100644 index 0000000..313d78e --- /dev/null +++ b/packages/dom/src/isTag.ts @@ -0,0 +1,37 @@ +/** + * Check if passed tag has no closed tag + * @param tag - element to check + * @returns boolean that represents if element is a single tag + */ +export function isSingleTag(tag: HTMLElement): boolean { + return [ + 'AREA', + 'BASE', + 'BR', + 'COL', + 'COMMAND', + 'EMBED', + 'HR', + 'IMG', + 'INPUT', + 'KEYGEN', + 'LINK', + 'META', + 'PARAM', + 'SOURCE', + 'TRACK', + 'WBR', + ].includes(tag.tagName); +} + +/** + * Check if element is BR or WBR + * @param element - element to check + * @returns boolean that represents if element is a line break tag + */ +export function isLineBreakTag(element: HTMLElement): element is HTMLBRElement { + return [ + 'BR', + 'WBR', + ].includes(element.tagName); +} diff --git a/packages/dom/src/make.ts b/packages/dom/src/make.ts new file mode 100644 index 0000000..fea9224 --- /dev/null +++ b/packages/dom/src/make.ts @@ -0,0 +1,27 @@ +/** + * Helper for making Elements with class name and attributes + * @param tagName - new Element tag name + * @param classNames - list or name of CSS class name(s) + * @param attributes - any attributes + * @returns created HTMLElement + */ +export function make(tagName: string, classNames: string | (string | undefined)[] | null = null, attributes: object = {}): HTMLElement { + const el = document.createElement(tagName); + + if (Array.isArray(classNames)) { + const validClassnames = classNames.filter(className => className !== undefined); + + el.classList.add(...validClassnames); + } else if (classNames !== null) { + el.classList.add(classNames); + } + + for (const attrName in attributes) { + if (Boolean(Object.prototype.hasOwnProperty.call(attributes, attrName))) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + el[attrName] = attributes[attrName]; + } + } + + return el; +} diff --git a/packages/dom/src/prepend.ts b/packages/dom/src/prepend.ts new file mode 100644 index 0000000..f937f1f --- /dev/null +++ b/packages/dom/src/prepend.ts @@ -0,0 +1,13 @@ +/** + * Append element or a couple to the beginning of the parent elements + * @param parent - where to append + * @param elements - element or elements list + */ +function prepend(parent: Element, elements: Element | Element[]): void { + if (Array.isArray(elements)) { + elements = elements.reverse(); + elements.forEach(el => parent.prepend(el)); + } else { + parent.prepend(elements); + } +} diff --git a/packages/dom/tsconfig.json b/packages/dom/tsconfig.json index fc6fedf..1c9b4e9 100644 --- a/packages/dom/tsconfig.json +++ b/packages/dom/tsconfig.json @@ -2,11 +2,11 @@ "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", - "rootDir": ".", + "rootDir": "src", "composite": true, "declaration": true, "sourceMap": true }, - "include": ["src/**/*"], + "include": ["**/*"], "exclude": ["dist"] } diff --git a/packages/helpers/src/beautifyShortcut.ts b/packages/helpers/src/beautifyShortcut.ts index 1b394b2..bdcc047 100644 --- a/packages/helpers/src/beautifyShortcut.ts +++ b/packages/helpers/src/beautifyShortcut.ts @@ -1,4 +1,4 @@ -import { getUserOS } from "./userOS"; +import { getUserOS } from './userOS'; /** * Make shortcut command more human-readable diff --git a/packages/helpers/src/cacheable.ts b/packages/helpers/src/cacheable.ts index f0df83a..e3532c9 100644 --- a/packages/helpers/src/cacheable.ts +++ b/packages/helpers/src/cacheable.ts @@ -9,7 +9,8 @@ export function cacheable void, wait?: number, immediate?: boolean): () => void { - let timeout; + let timeout: number | undefined = undefined; return (...args: unknown[]): void => { - // eslint-disable-next-line @typescript-eslint/no-this-alias + // eslint-disable-next-line @typescript-eslint/no-this-alias, @typescript-eslint/no-unsafe-assignment const context = this; // eslint-disable-next-line @typescript-eslint/explicit-function-return-type const later = () => { - timeout = null; - if (!immediate) { + timeout = undefined; + if (immediate !== true) { func.apply(context, args); } }; - const callNow = immediate && !timeout; + const callNow = immediate === true && timeout !== undefined; window.clearTimeout(timeout); timeout = window.setTimeout(later, wait); diff --git a/packages/helpers/src/deepMerge.ts b/packages/helpers/src/deepMerge.ts index b15d74c..a7c638e 100644 --- a/packages/helpers/src/deepMerge.ts +++ b/packages/helpers/src/deepMerge.ts @@ -1,12 +1,12 @@ -import { isObject } from "./typeOf"; +import { isObject } from './typeOf'; /** - * Merge to objects recursively - * @param target - merge target - * @param sources - merge sources - * @returns + * Merge two objects recursively + * @param target - target to be merged + * @param sources - sources to be merged + * @returns sourced merged with sources */ -export function deepMerge(target, ...sources): T { +export function deepMerge(target: T, ...sources: T[]): T { if (!sources.length) { return target; } @@ -15,10 +15,9 @@ export function deepMerge(target, ...sources): T { if (isObject(target) && isObject(source)) { for (const key in source) { if (isObject(source[key])) { - if (!target[key]) { + if (target[key] === null) { Object.assign(target, { [key]: {} }); } - deepMerge(target[key], source[key]); } else { Object.assign(target, { [key]: source[key] }); diff --git a/packages/helpers/src/deprecationAssert.ts b/packages/helpers/src/deprecationAssert.ts index 12386f3..2136b39 100644 --- a/packages/helpers/src/deprecationAssert.ts +++ b/packages/helpers/src/deprecationAssert.ts @@ -1,6 +1,6 @@ /** * Common method for printing a warning about the usage of deprecated property or method. - * @param condition - condition for deprecation. + * @param condition - condition of the deprecation status. * @param oldProperty - deprecated property. * @param newProperty - the property that should be used instead. */ diff --git a/packages/helpers/src/index.ts b/packages/helpers/src/index.ts index 90970d9..e455e17 100644 --- a/packages/helpers/src/index.ts +++ b/packages/helpers/src/index.ts @@ -1,18 +1,17 @@ -import { cacheable } from "./cacheable"; -import { beautifyShortcut } from "./beautifyShortcut"; -import { capitalize } from "./capitalize"; -import { copyTextToClipboard } from "./copyTextToClipboard"; -import { debounce } from "./debounce"; -import { deepMerge } from "./deepMerge"; -import { deprecationAssert } from "./deprecationAssert"; -import { isEmpty, notEmpty } from "./empty"; -import { getValidUrl } from "./getValidUrl"; -import { isPrintableKey } from "./isPrintableKey"; -import { keyCodes, mouseButtons } from "./keyCodes"; -import { PromiseQueue } from "./PromiseQueue"; -import { throttle } from "./throttle"; -import { - typeOf, +import { cacheable } from './cacheable'; +import { beautifyShortcut } from './beautifyShortcut'; +import { capitalize } from './capitalize'; +import { copyTextToClipboard } from './copyTextToClipboard'; +import { debounce } from './debounce'; +import { deepMerge } from './deepMerge'; +import { deprecationAssert } from './deprecationAssert'; +import { isEmpty, notEmpty } from './empty'; +import { getValidUrl } from './getValidUrl'; +import { isPrintableKey } from './isPrintableKey'; +import { keyCodes, mouseButtons } from './keyCodes'; +import { PromiseQueue } from './PromiseQueue'; +import { throttle } from './throttle'; +import { typeOf, isBoolean, isClass, isFunction, @@ -20,8 +19,8 @@ import { isObject, isPromise, isString, - isUndefined } from "./typeOf"; -import { getUserOS, isIosDevice } from "./userOS"; + isUndefined } from './typeOf'; +import { getUserOS, isIosDevice } from './userOS'; export default { cacheable, beautifyShortcut, @@ -48,5 +47,4 @@ export default { cacheable, isString, isUndefined, getUserOS, - isIosDevice, -} + isIosDevice }; diff --git a/packages/helpers/src/isPrintableKey.ts b/packages/helpers/src/isPrintableKey.ts index 1e32339..c899867 100644 --- a/packages/helpers/src/isPrintableKey.ts +++ b/packages/helpers/src/isPrintableKey.ts @@ -1,4 +1,4 @@ - +/* eslint-disable @typescript-eslint/no-magic-numbers */ /** * Returns true if passed key code is printable (a-Z, 0-9, etc) character. * @param keyCode - code of some key @@ -13,3 +13,4 @@ export function isPrintableKey(keyCode: number): boolean { || (keyCode > 185 && keyCode < 193) // ;=,-./` (in order) || (keyCode > 218 && keyCode < 223); // [\]' (in order) } +/* eslint-enable @typescript-eslint/no-magic-numbers */ diff --git a/packages/helpers/src/throttle.ts b/packages/helpers/src/throttle.ts index 4964f43..a5fae7d 100644 --- a/packages/helpers/src/throttle.ts +++ b/packages/helpers/src/throttle.ts @@ -1,4 +1,3 @@ - /** * Returns a function, that, when invoked, will only be triggered at most once during a given window of time. * @param func - function to throttle @@ -8,8 +7,20 @@ * but if you'd like to disable the execution on the leading edge, pass * `{leading: false}`. To disable execution on the trailing edge, ditto. */ -export function throttle(func, wait, options: { leading?: boolean; trailing?: boolean } | undefined = undefined): () => void { - let context; let args; let result; +export function throttle(func, wait, options: { + /** + * Optional leading argument + */ + leading?: boolean; + + /** + * Optional trailing argument + */ + trailing?: boolean; +} | undefined = undefined): () => void { + let context; + let args; + let result; let timeout: null | ReturnType = null; let previous = 0; @@ -20,9 +31,10 @@ export function throttle(func, wait, options: { leading?: boolean; trailing?: bo const later = function (): void { previous = options.leading === false ? 0 : Date.now(); timeout = null; + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access result = func.apply(context, args); - if (!timeout) { + if (timeout === null) { context = args = null; } }; @@ -36,7 +48,7 @@ export function throttle(func, wait, options: { leading?: boolean; trailing?: bo const remaining = wait - (now - previous); - // eslint-disable-next-line @typescript-eslint/no-this-alias + // eslint-disable-next-line @typescript-eslint/no-this-alias, @typescript-eslint/no-unsafe-assignment context = this; args = arguments; @@ -47,9 +59,10 @@ export function throttle(func, wait, options: { leading?: boolean; trailing?: bo timeout = null; } previous = now; + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access result = func.apply(context, args); - if (!timeout) { + if (timeout === null) { context = args = null; } } else if (!timeout && options.trailing !== false) { diff --git a/packages/helpers/src/typeOf.ts b/packages/helpers/src/typeOf.ts index dce86b0..67d1c00 100644 --- a/packages/helpers/src/typeOf.ts +++ b/packages/helpers/src/typeOf.ts @@ -1,4 +1,3 @@ - /** * Return string representation of the object type * @param object - object to get type diff --git a/packages/helpers/src/userOS.ts b/packages/helpers/src/userOS.ts index c588a09..2cf635e 100644 --- a/packages/helpers/src/userOS.ts +++ b/packages/helpers/src/userOS.ts @@ -13,7 +13,7 @@ export function getUserOS(): { [key: string]: boolean } { const userOS = Object.keys(OS).find((os: string) => window.navigator.appVersion.toLowerCase().indexOf(os) !== -1); - if (userOS) { + if (userOS !== undefined) { OS[userOS] = true; return OS; @@ -27,7 +27,7 @@ export function getUserOS(): { [key: string]: boolean } { */ export const isIosDevice = typeof window !== 'undefined' - && window.navigator + && window.navigator !== null && window.navigator.platform && (/iP(ad|hone|od)/.test(window.navigator.platform) || (window.navigator.platform === 'MacIntel' && window.navigator.maxTouchPoints > 1)); diff --git a/packages/helpers/tsconfig.json b/packages/helpers/tsconfig.json index fc6fedf..1c9b4e9 100644 --- a/packages/helpers/tsconfig.json +++ b/packages/helpers/tsconfig.json @@ -2,11 +2,11 @@ "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", - "rootDir": ".", + "rootDir": "src", "composite": true, "declaration": true, "sourceMap": true }, - "include": ["src/**/*"], + "include": ["**/*"], "exclude": ["dist"] } diff --git a/packages/keyboard/tsconfig.json b/packages/keyboard/tsconfig.json index f1c3101..43fd18c 100644 --- a/packages/keyboard/tsconfig.json +++ b/packages/keyboard/tsconfig.json @@ -4,6 +4,6 @@ "outDir": "dist", "rootDir": "src", }, - "include": ["src"], + "include": ["**/*"], "exclude": ["dist"] } diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..41021b0 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,6 @@ +import * as Caret from '@utils/caret/src'; +import * as Keyboard from '@utils/keyboard/src'; +import * as Helpers from '@utils/helpers/src'; +import * as Dom from '@utils/dom/src'; + +export { Caret, Keyboard, Helpers, Dom }; diff --git a/tsconfig.eslint.json b/tsconfig.eslint.json index 6584bfb..0d12c16 100644 --- a/tsconfig.eslint.json +++ b/tsconfig.eslint.json @@ -3,5 +3,6 @@ "include": [ "packages/**/*.ts", "eslint.config.mjs", + "src/**/*.ts" ], } diff --git a/tsconfig.json b/tsconfig.json index 8949e33..474c73d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,6 +4,8 @@ "target": "es2017", "declaration": true, "baseUrl": "./", + "rootDir": "src", + "outDir": "dist", "moduleResolution": "node", // This resolution strategy attempts to mimic the Node.js module resolution mechanism at runtime "lib": ["dom", "es2017", "es2018", "es2019"], @@ -19,7 +21,7 @@ }, "composite": true }, - "include": ["packages/**/*"], + "include": ["src/**/*"], "exclude": [ "node_modules", "packages/**/dist*" @@ -27,6 +29,7 @@ "references": [ { "path": "packages/dom" }, { "path": "packages/caret" }, - { "path": "packages/keyboard" } + { "path": "packages/keyboard" }, + { "path": "packages/helpers" } ] } diff --git a/yarn.lock b/yarn.lock index 5352bd2..faffcc3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,29 +2,6 @@ # yarn lockfile v1 -"@babel/code-frame@^7.0.0": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.24.7.tgz#882fd9e09e8ee324e496bd040401c6f046ef4465" - integrity sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA== - dependencies: - "@babel/highlight" "^7.24.7" - picocolors "^1.0.0" - -"@babel/helper-validator-identifier@^7.24.7": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz#75b889cfaf9e35c2aaf42cf0d72c8e91719251db" - integrity sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w== - -"@babel/highlight@^7.24.7": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.24.7.tgz#a05ab1df134b286558aae0ed41e6c5f731bf409d" - integrity sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw== - dependencies: - "@babel/helper-validator-identifier" "^7.24.7" - chalk "^2.4.2" - js-tokens "^4.0.0" - picocolors "^1.0.0" - "@es-joy/jsdoccomment@~0.46.0": version "0.46.0" resolved "https://registry.yarnpkg.com/@es-joy/jsdoccomment/-/jsdoccomment-0.46.0.tgz#47a2ee4bfc0081f252e058272dfab680aaed464d" @@ -354,13 +331,6 @@ ansi-regex@^5.0.1: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== -ansi-styles@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" - integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== - dependencies: - color-convert "^1.9.0" - ansi-styles@^4.1.0: version "4.3.0" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" @@ -373,13 +343,6 @@ are-docs-informative@^0.0.2: resolved "https://registry.yarnpkg.com/are-docs-informative/-/are-docs-informative-0.0.2.tgz#387f0e93f5d45280373d387a59d34c96db321963" integrity sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig== -argparse@^1.0.7: - version "1.0.10" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" - integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== - dependencies: - sprintf-js "~1.0.2" - argparse@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" @@ -464,9 +427,9 @@ available-typed-arrays@^1.0.7: possible-typed-array-names "^1.0.0" balanced-match@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" - integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== boolbase@^1.0.0: version "1.0.0" @@ -495,11 +458,6 @@ braces@^3.0.3: dependencies: fill-range "^7.1.1" -builtin-modules@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" - integrity sha512-wxXCdllwGhI2kCC0MnvTGYTMvnVZTvqgypkiTI8Pa5tcz2i6VqsqwYGgqwXji+4RgCzms6EajE4IxiUH6HH8nQ== - call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" @@ -516,15 +474,6 @@ callsites@^3.0.0: resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== -chalk@^2.3.0, chalk@^2.4.2: - version "2.4.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" - integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - chalk@^4.0.0: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" @@ -533,13 +482,6 @@ chalk@^4.0.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -color-convert@^1.9.0: - version "1.9.3" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" - integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== - dependencies: - color-name "1.1.3" - color-convert@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" @@ -547,21 +489,11 @@ color-convert@^2.0.1: dependencies: color-name "~1.1.4" -color-name@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" - integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== - color-name@~1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -commander@^2.12.1: - version "2.20.3" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" - integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== - comment-parser@1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/comment-parser/-/comment-parser-1.4.1.tgz#bdafead37961ac079be11eb7ec65c4d021eaf9cc" @@ -570,7 +502,7 @@ comment-parser@1.4.1: concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== cross-spawn@^7.0.2: version "7.0.3" @@ -650,11 +582,6 @@ define-properties@^1.2.0, define-properties@^1.2.1: has-property-descriptors "^1.0.0" object-keys "^1.1.1" -diff@^4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" - integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== - dir-glob@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" @@ -778,11 +705,6 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" -escape-string-regexp@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== - escape-string-regexp@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" @@ -1022,11 +944,6 @@ espree@^9.3.1, espree@^9.6.1: acorn-jsx "^5.3.2" eslint-visitor-keys "^3.4.1" -esprima@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" - integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== - esquery@^1.4.0, esquery@^1.5.0, esquery@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7" @@ -1131,11 +1048,6 @@ for-each@^0.3.3: dependencies: is-callable "^1.1.3" -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== - function-bind@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" @@ -1197,18 +1109,6 @@ glob-parent@^6.0.2: dependencies: is-glob "^4.0.3" -glob@^7.1.1: - version "7.2.3" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" - integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.1.1" - once "^1.3.0" - path-is-absolute "^1.0.0" - globals@^13.24.0: version "13.24.0" resolved "https://registry.yarnpkg.com/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171" @@ -1268,11 +1168,6 @@ has-bigints@^1.0.1, has-bigints@^1.0.2: resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== -has-flag@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" - integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== - has-flag@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" @@ -1327,19 +1222,6 @@ imurmurhash@^0.1.4: resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2: - version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - internal-slot@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.7.tgz#c06dcca3ed874249881007b0a5523b172a190802" @@ -1485,19 +1367,6 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== -js-tokens@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" - integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== - -js-yaml@^3.13.1: - version "3.14.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" - integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" - js-yaml@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" @@ -1584,7 +1453,7 @@ minimatch@9.0.3: dependencies: brace-expansion "^2.0.1" -minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: +minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== @@ -1603,13 +1472,6 @@ minimist@^1.2.0, minimist@^1.2.6: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== -mkdirp@^0.5.3: - version "0.5.6" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" - integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== - dependencies: - minimist "^1.2.6" - ms@2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" @@ -1620,11 +1482,6 @@ ms@^2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== -nanoid@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-4.0.2.tgz#140b3c5003959adbebf521c170f282c5e7f9fb9e" - integrity sha512-7ZtY5KTCNheRGfEFxnedV5zFiORN1+Y1N6zvPTnHQd8ENUvfaDBeuJDZb2bN/oXwXxu3qkTXDzy57W5vAmDTBw== - natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" @@ -1685,13 +1542,6 @@ object.values@^1.1.7: define-properties "^1.2.1" es-object-atoms "^1.0.0" -once@^1.3.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== - dependencies: - wrappy "1" - optionator@^0.9.3: version "0.9.4" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" @@ -1743,11 +1593,6 @@ path-exists@^4.0.0: resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== - path-key@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" @@ -1768,11 +1613,6 @@ pathe@^1.1.2: resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.2.tgz#6c4cb47a945692e48a1ddd6e4094d170516437ec" integrity sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ== -picocolors@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.1.tgz#a8ad579b571952f0e5d25892de5445bcfe25aaa1" - integrity sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew== - picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" @@ -1802,9 +1642,9 @@ prelude-ls@^1.2.1: integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== punycode@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" - integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== queue-microtask@^1.2.2: version "1.2.3" @@ -1831,7 +1671,7 @@ resolve-pkg-maps@^1.0.0: resolved "https://registry.yarnpkg.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz#616b3dc2c57056b5588c31cdf4b3d64db133720f" integrity sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw== -resolve@^1.22.4, resolve@^1.3.2: +resolve@^1.22.4: version "1.22.8" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== @@ -1871,11 +1711,6 @@ safe-regex-test@^1.0.3: es-errors "^1.3.0" is-regex "^1.1.4" -semver@^5.3.0: - version "5.7.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" - integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== - semver@^6.3.1: version "6.3.1" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" @@ -1958,11 +1793,6 @@ spdx-license-ids@^3.0.0: resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.18.tgz#22aa922dcf2f2885a6494a261f2d8b75345d0326" integrity sha512-xxRs31BqRYHwiMzudOrpSiHtZ8i/GeionCBDSilhYRj+9gIcI8wCZTlXZKu9vZIVqViP3dcp9qE5G6AlIaD+TQ== -sprintf-js@~1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" - integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== - string.prototype.trim@^1.2.9: version "1.2.9" resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz#b6fa326d72d2c78b6df02f7759c73f8f6274faa4" @@ -2008,13 +1838,6 @@ strip-json-comments@^3.1.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== -supports-color@^5.3.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" - integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== - dependencies: - has-flag "^3.0.0" - supports-color@^7.1.0: version "7.2.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" @@ -2067,42 +1890,11 @@ tsconfig-paths@^3.15.0: minimist "^1.2.6" strip-bom "^3.0.0" -tslib@^1.13.0, tslib@^1.8.1: - version "1.14.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" - integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== - tslib@^2.6.2: version "2.6.3" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.3.tgz#0438f810ad7a9edcde7a241c3d80db693c8cbfe0" integrity sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ== -tslint@^6.1.1: - version "6.1.3" - resolved "https://registry.yarnpkg.com/tslint/-/tslint-6.1.3.tgz#5c23b2eccc32487d5523bd3a470e9aa31789d904" - integrity sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg== - dependencies: - "@babel/code-frame" "^7.0.0" - builtin-modules "^1.1.1" - chalk "^2.3.0" - commander "^2.12.1" - diff "^4.0.1" - glob "^7.1.1" - js-yaml "^3.13.1" - minimatch "^3.0.4" - mkdirp "^0.5.3" - resolve "^1.3.2" - semver "^5.3.0" - tslib "^1.13.0" - tsutils "^2.29.0" - -tsutils@^2.29.0: - version "2.29.0" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.29.0.tgz#32b488501467acbedd4b85498673a0812aca0b99" - integrity sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA== - dependencies: - tslib "^1.8.1" - type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" @@ -2247,11 +2039,6 @@ word-wrap@^1.2.5: resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== - xml-name-validator@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835"