diff --git a/README.md b/README.md index 2b0f35df0bad9f79f523b1960aaeea20a872f6c7..9b9464f7e5a19792504c54c2fe08edfa8c5a1ee8 100644 --- a/README.md +++ b/README.md @@ -15,3 +15,8 @@ If you are going to create a new test case, please follow the structure inside t _Important note: MIME normaliser does not cover all provided test cases. Some of them explicitly ignored to indicate which tests has to be fixed. Please refer to `failingCases` arrays inside the test files._ + +Tests are using both JSDOM and custom Vereign [DOM parser](https://code.vereign.com/code/js-toolbox/gsdom). +Vereign DOM parser has been developed to support MIME normalisation in Google Add-on and is a must for testing. + +Whenever you develop a new normalisation logic, ensure that Vereign DOM parser support functions you apply. diff --git a/__tests__/html-gmail-outlook.test.ts b/__tests__/html-gmail-outlook.test.ts index a17b718b2090ada1f796a7776019a39435a65b12..f2b34d548aa2dd4fefec3aec873f46b8e3c9450d 100644 --- a/__tests__/html-gmail-outlook.test.ts +++ b/__tests__/html-gmail-outlook.test.ts @@ -49,6 +49,9 @@ describe("[HTML] GMail-Outlook", () => { "25reply", "26reply", "27reply", + "21forward", // missing files + "23forward", // missing files + "24forward", // missing files ]) ); }); diff --git a/__tests__/html-outlook-outlook.test.ts b/__tests__/html-outlook-outlook.test.ts index 1acaa4aa9db275ee1361cbc4f1198433fff8fada..7cc5eda6bee57de8267a22ccf1e41f27275d924e 100644 --- a/__tests__/html-outlook-outlook.test.ts +++ b/__tests__/html-outlook-outlook.test.ts @@ -17,12 +17,15 @@ describe("Outlook emails HTML normalization", () => { "Emails Edge", describeFunction("edge", [ "21", // This case has a src mismatch for the same image. Reproduce this case again + "08", // Files are missing for test case + "10", // Files are missing for test case ]) ); describe( "Emails Safari", describeFunction("safari", [ - "04", // This case contains <section> tag which is ignored by Outlook, and it also inserts a plenty of empty divs + "04", // This case contains <section> tag which is ignored by Outlook, and it also inserts a plenty of empty divs, + "08", ]) ); @@ -48,6 +51,7 @@ describe("Outlook emails HTML normalization", () => { "25", "26", "28", + "10", // missing files ]) ); }); diff --git a/__tests__/plain-gmail-outlook.test.ts b/__tests__/plain-gmail-outlook.test.ts index f40d1f6a3c6c22ed1eff002e7ac9d4fa21a36f91..bcb98da21c7d6da66eadce0514d008b1eaa23182 100644 --- a/__tests__/plain-gmail-outlook.test.ts +++ b/__tests__/plain-gmail-outlook.test.ts @@ -22,6 +22,9 @@ describe("[Plain] Gmail-Outlook normalization", () => { "25reply", "26reply", "27reply", + "21forward", // missing file + "23forward", // missing file + "24forward", // missing file ]) ); }); diff --git a/__tests__/plain-outlook-outlook.test.ts b/__tests__/plain-outlook-outlook.test.ts index b4d729e076f256e96a4f2fead44ae4f98c944d4e..2298b6a19a88a400ba5d228d2f4ecb5bb46f6171 100644 --- a/__tests__/plain-outlook-outlook.test.ts +++ b/__tests__/plain-outlook-outlook.test.ts @@ -9,8 +9,26 @@ const testsPath = path.resolve(__dirname, `.${TESTS_GLOBAL_PATH}`); describe("Outlook emails Plain normalization", () => { const describeFunction = createDescribePlainTestCases(testsPath); describe("Emails Chrome", describeFunction("chrome")); - describe("Emails Edge", describeFunction("edge", ["21"])); - describe("Emails Safari", describeFunction("safari")); + describe( + "Emails Edge", + describeFunction("edge", [ + "21", + "08", // missing files + "10", // missing files + ]) + ); + describe( + "Emails Safari", + describeFunction("safari", [ + "08", // missing files + ]) + ); describe("Emails MacOS", describeFunction("macos", ["21", "23", "25"])); - describe("Emails Windows", describeFunction("windows", ["25"])); + describe( + "Emails Windows", + describeFunction("windows", [ + "25", + "10", // missing file + ]) + ); }); diff --git a/__tests__/utils.ts b/__tests__/utils.ts index 1d60560b329b426a9bed05e7c8730176814d070e..7a0e7b901caccb3cd579f1746b92f0f20c267119 100644 --- a/__tests__/utils.ts +++ b/__tests__/utils.ts @@ -7,6 +7,7 @@ const SENT_PLAIN_NAME = "s_plainContent.data"; const RECEIVED_PLAIN_NAME = "r_plainContent.data"; import { PlainNormalizer, HTMLNormalizer } from "../src"; import { expect, test } from "@jest/globals"; +import { DOM } from "@vereign/dom"; export const getNormalizedPlain = ( testCasePath: string @@ -50,7 +51,7 @@ export const getNormalizedHtml = ( .readFileSync(`${testCasePath}/${RECEIVED_HTML_NAME}`) .toString(); - const sentDOM = new JSDOM(sentHtml); + const sentDOM = new DOM(sentHtml); const receivedDOM = new JSDOM(receivedHtml); const sentNormalizedHtml = HTMLNormalizer.normalizeVendorHtml( @@ -75,23 +76,31 @@ export const createDescribeHtmlTestCases = ( /** * @param casesGroupName - name of the folder with cases * @param failingCases - a list of cases that are failing and ignored. Pending to be fixed + * @param casesToCheckOnly - a filter to use if you want to check specific cases */ - (casesGroupName: string, failingCases: Array<string> = []) => (): void => { + ( + casesGroupName: string, + failingCases?: Array<string>, + casesToCheckOnly?: Array<string> + ) => (): void => { const testsCasesPath = testsPath + "/" + casesGroupName; - const testCasesDirs = getTestCasesDirs(testsCasesPath).filter( - (dir) => !failingCases.includes(dir) - ); + let testCasesDirs = getTestCasesDirs(testsCasesPath); + + if (casesToCheckOnly && casesToCheckOnly.length) { + testCasesDirs = testCasesDirs.filter((dir) => + casesToCheckOnly.includes(dir) + ); + } + + if (failingCases && failingCases.length) { + testCasesDirs = testCasesDirs.filter( + (dir) => !failingCases.includes(dir) + ); + } test.each(testCasesDirs)("Case %s", (dirName: string) => { const testCasePath = testsCasesPath + "/" + dirName; - let normalizedHtmls; - try { - normalizedHtmls = getNormalizedHtml(testCasePath, vendor); - } catch (e) { - console.log(`Invalid test case: ${casesGroupName}/${dirName}`); - return; - } - + const normalizedHtmls = getNormalizedHtml(testCasePath, vendor); const { sentHtml, receivedHtml } = normalizedHtmls; // expect(receivedHtml.length).toBeGreaterThan(0); @@ -111,13 +120,7 @@ export const createDescribePlainTestCases = (testsPath: string) => ( test.each(testCasesDirs)("Case %s", (dirName: string) => { const testCasePath = testsCasesPath + "/" + dirName; - let normalizedPlain; - try { - normalizedPlain = getNormalizedPlain(testCasePath); - } catch (e) { - console.log(`Invalid test case: ${casesName}/${dirName}`); - return; - } + const normalizedPlain = getNormalizedPlain(testCasePath); const { sentPlain, receivedPlain } = normalizedPlain; diff --git a/dist/HTMLNormalizer/HTMLNormalizer.js b/dist/HTMLNormalizer/HTMLNormalizer.js index 1cf9605dbd1c344b46a44d14203c567bfa746d05..68dc062b75b86094cc0fbeb7158641480a0f1311 100644 --- a/dist/HTMLNormalizer/HTMLNormalizer.js +++ b/dist/HTMLNormalizer/HTMLNormalizer.js @@ -102,7 +102,8 @@ const printHtmlNode = (node, printFunction, depth) => { if (node.firstChild) { result += ">"; result += "\n"; - result += exports.printHtmlChildren(node, printFunction, depth + 1); + const printout = exports.printHtmlChildren(node, printFunction, depth + 1); + result += printout; result += "</" + node.nodeName + ">"; } else { @@ -115,7 +116,7 @@ const printHtmlNode = (node, printFunction, depth) => { }; exports.printHtmlNode = printHtmlNode; const cleanupHtmlNodeAttributes = (node, cleanupElementAttributes) => { - if (node.nodeType === node.ELEMENT_NODE) { + if (node.nodeType === constants_1.ELEMENT_NODE) { cleanupElementAttributes(node); } let child = node.firstChild; @@ -128,11 +129,11 @@ exports.cleanupHtmlNodeAttributes = cleanupHtmlNodeAttributes; const pruneHtmlNode = (node, pruneElement) => { let toBeRemoved = false; switch (node.nodeType) { - case node.COMMENT_NODE: - case node.DOCUMENT_TYPE_NODE: + case constants_1.COMMENT_NODE: + case constants_1.DOCUMENT_TYPE_NODE: toBeRemoved = true; break; - case node.TEXT_NODE: { + case constants_1.TEXT_NODE: { const trimmedText = node.textContent.trim(); if (trimmedText === "") { toBeRemoved = true; @@ -142,7 +143,7 @@ const pruneHtmlNode = (node, pruneElement) => { } break; } - case node.ELEMENT_NODE: + case constants_1.ELEMENT_NODE: toBeRemoved = pruneElement(node); } if (toBeRemoved) { @@ -169,7 +170,7 @@ const escapeHtmlString = (string) => { let html = ""; let index = 0; let lastIndex = 0; - for (let index = match.index; index < str.length; index++) { + for (index = match.index; index < str.length; index++) { switch (str.charCodeAt(index)) { case 34: // " escape = """; diff --git a/dist/HTMLNormalizer/strategies/common.js b/dist/HTMLNormalizer/strategies/common.js index 26712527557f0086d98de0589865476de62f1739..8bf68f4807488fe2eddcdcabfc5e327f187779ec 100644 --- a/dist/HTMLNormalizer/strategies/common.js +++ b/dist/HTMLNormalizer/strategies/common.js @@ -34,16 +34,12 @@ const pruneElement = (element) => { if (isDummyQrCode(element)) { return true; } - if (element.nodeName.toLowerCase() === "div" && - element.childNodes.length === 0) { - return true; - } return !!exports.ELEMENT_TYPES_TO_REMOVE[element.nodeName.toLowerCase()]; }; exports.pruneElement = pruneElement; const cloneAnchorFromPane = (a, pane) => { try { - const url = new URL(a.href); + const url = new URL(a.getAttribute("href")); // If this is external url if (url.host && url.protocol) { pane.parentNode.insertBefore(a.cloneNode(false), pane); diff --git a/dist/HTMLNormalizer/strategies/gmail.js b/dist/HTMLNormalizer/strategies/gmail.js index 2faccd189909c4df2d77cf6076fc104d8b180b15..d179dc21794aea0fc1eba35678477a9527928a22 100644 --- a/dist/HTMLNormalizer/strategies/gmail.js +++ b/dist/HTMLNormalizer/strategies/gmail.js @@ -13,8 +13,8 @@ const amendGmailNodes = (document) => { */ const attachmentsPanes = Array.from(document.getElementsByClassName("gmail_chip")); attachmentsPanes.forEach((pane) => { - const as = pane.querySelectorAll("a"); - as.forEach((a) => { + const as = pane.getElementsByTagName("a"); + Array.from(as).forEach((a) => { common_1.cloneAnchorFromPane(a, pane); }); }); diff --git a/dist/HTMLNormalizer/strategies/nodesAmendingFunctions.d.ts b/dist/HTMLNormalizer/strategies/nodesAmendingFunctions.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..edaf502f86786f53b7de96549eb34fa40f391896 --- /dev/null +++ b/dist/HTMLNormalizer/strategies/nodesAmendingFunctions.d.ts @@ -0,0 +1 @@ +export declare const unwindTags: (node: Element | Document, tagName: string) => void; diff --git a/dist/HTMLNormalizer/strategies/nodesAmendingFunctions.js b/dist/HTMLNormalizer/strategies/nodesAmendingFunctions.js new file mode 100644 index 0000000000000000000000000000000000000000..da12e8e0ec9d4230830c359e6ae1cc8259e26c82 --- /dev/null +++ b/dist/HTMLNormalizer/strategies/nodesAmendingFunctions.js @@ -0,0 +1,36 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.unwindTags = void 0; +const unwindTags = (node, tagName) => { + const tags = node.getElementsByTagName(tagName); + //Sort tags by depth to start unwinding the deepest ones, which does not contain nested spans + const tagsDepths = {}; + Array.from(tags).forEach((span) => { + let descendant = span; + let parent = descendant.parentNode; + let depth = 0; + while (parent && descendant !== parent) { + descendant = parent; + parent = descendant.parentNode; + depth++; + } + if (!tagsDepths[depth]) { + tagsDepths[depth] = []; + } + tagsDepths[depth].push(span); + }); + Object.keys(tagsDepths) + .sort((a, b) => parseInt(b) - parseInt(a)) + .forEach((depth) => { + tagsDepths[depth].forEach((span) => { + let child = span.firstChild; + const parent = span.parentNode; + while (child) { + parent.insertBefore(child.cloneNode(true), span); + child = child.nextSibling; + } + span.parentNode.removeChild(span); + }); + }); +}; +exports.unwindTags = unwindTags; diff --git a/dist/HTMLNormalizer/strategies/outlook.js b/dist/HTMLNormalizer/strategies/outlook.js index 860c1d49716ef719eb6fcf72a78e2d121edbd036..e30b79d3a9b51a0c19e1f6a4615a8042e75bd8bb 100644 --- a/dist/HTMLNormalizer/strategies/outlook.js +++ b/dist/HTMLNormalizer/strategies/outlook.js @@ -31,7 +31,7 @@ const removeQrCodeNodes = (document) => { let toRemove = []; let child = node.firstChild; while (child) { - if (child.nodeType == child.ELEMENT_NODE) { + if (child.nodeType == constants_1.ELEMENT_NODE) { toRemove = [...toRemove, ...remove(child)]; const childElement = child; const id = childElement.getAttribute("id"); diff --git a/dist/constants.d.ts b/dist/constants.d.ts index 0fd8b9d434473f6c2a57db12f55e2af5c5591083..5ace58f155a0f1cd981a5bf5ae20be85e396f2e8 100644 --- a/dist/constants.d.ts +++ b/dist/constants.d.ts @@ -1,4 +1,6 @@ export declare const ELEMENT_NODE = 1; +export declare const COMMENT_NODE = 8; +export declare const DOCUMENT_TYPE_NODE = 10; export declare const TEXT_NODE = 3; export declare const DOCUMENT_NODE = 9; export declare const EMAIL_VENDORS: { diff --git a/dist/constants.js b/dist/constants.js index 9b8e0062b3859dbec0dd886609504ddf805cbf61..a02993813e78f9f81adfd62f3ed96b5b2f3f9a06 100644 --- a/dist/constants.js +++ b/dist/constants.js @@ -1,7 +1,9 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.EMAIL_VENDORS = exports.DOCUMENT_NODE = exports.TEXT_NODE = exports.ELEMENT_NODE = void 0; +exports.EMAIL_VENDORS = exports.DOCUMENT_NODE = exports.TEXT_NODE = exports.DOCUMENT_TYPE_NODE = exports.COMMENT_NODE = exports.ELEMENT_NODE = void 0; exports.ELEMENT_NODE = 1; +exports.COMMENT_NODE = 8; +exports.DOCUMENT_TYPE_NODE = 10; exports.TEXT_NODE = 3; exports.DOCUMENT_NODE = 9; exports.EMAIL_VENDORS = { diff --git a/package.json b/package.json index 1fcd57429a89931091fd88c5bd2620d3327d32d0..5805b72051fb21f31a605de843d07ed8acdae5ff 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "@babel/preset-typescript": "^7.10.4", "@typescript-eslint/eslint-plugin": "^3.10.1", "@typescript-eslint/parser": "^3.10.1", + "@vereign/dom": "git+ssh://git@code.vereign.com:code/js-toolbox/gsdom.git", "babel-eslint": "^10.1.0", "babel-jest": "^26.3.0", "eslint": "^7.7.0", diff --git a/src/HTMLNormalizer/HTMLNormalizer.ts b/src/HTMLNormalizer/HTMLNormalizer.ts index 754d67b2a8d9a34f2cbf2d8e880551ea5ab99ecb..3eb2bc8c077fb86929f8b7ae405d5b47cfa2e8a2 100644 --- a/src/HTMLNormalizer/HTMLNormalizer.ts +++ b/src/HTMLNormalizer/HTMLNormalizer.ts @@ -1,13 +1,23 @@ -import { DOCUMENT_NODE, ELEMENT_NODE, TEXT_NODE } from "../constants"; +import { + COMMENT_NODE, + DOCUMENT_NODE, + DOCUMENT_TYPE_NODE, + ELEMENT_NODE, + TEXT_NODE, +} from "../constants"; import { amendOutlookNodes, cleanupOutlookElementAttributes, printOutlookElement, - pruneOutlookElement + pruneOutlookElement, } from "./strategies/outlook"; -import {EMAIL_VENDORS} from "../constants"; -import {removeSpacesAndLinebreaks} from "../utils"; -import {amendGmailNodes, cleanupGMailElementAttributes, pruneGmailElement} from "./strategies/gmail"; +import { EMAIL_VENDORS } from "../constants"; +import { removeSpacesAndLinebreaks } from "../utils"; +import { + amendGmailNodes, + cleanupGMailElementAttributes, + pruneGmailElement, +} from "./strategies/gmail"; const nodesAmendingFunctions = { [EMAIL_VENDORS.GMAIL]: amendGmailNodes, @@ -28,7 +38,10 @@ const vendorPrintingFunctions = { [EMAIL_VENDORS.OUTLOOK]: printOutlookElement, }; -export const normalizeVendorHtml = (document: HTMLDocument, vendor: string): string => { +export const normalizeVendorHtml = ( + document: HTMLDocument, + vendor: string +): string => { const mimeBody = document.body; const amendNodesFunction = nodesAmendingFunctions[vendor]; @@ -52,8 +65,7 @@ export const normalizeVendorHtml = (document: HTMLDocument, vendor: string): str /** * Cleanup unnecessary attributes of nodes */ - const elementAttributesCleanupFunction = - attributesCleanupFunctions[vendor]; + const elementAttributesCleanupFunction = attributesCleanupFunctions[vendor]; if (elementAttributesCleanupFunction) { cleanupHtmlNodeAttributes(document, elementAttributesCleanupFunction); @@ -132,7 +144,8 @@ export const printHtmlNode = ( if (node.firstChild) { result += ">"; result += "\n"; - result += printHtmlChildren(node, printFunction, depth + 1); + const printout = printHtmlChildren(node, printFunction, depth + 1); + result += printout; result += "</" + node.nodeName + ">"; } else { result += "/>"; @@ -148,7 +161,7 @@ export const cleanupHtmlNodeAttributes = ( node: Node, cleanupElementAttributes: (element: HTMLElement) => void ): void => { - if (node.nodeType === node.ELEMENT_NODE) { + if (node.nodeType === ELEMENT_NODE) { cleanupElementAttributes(node as HTMLElement); } @@ -166,11 +179,11 @@ export const pruneHtmlNode = ( let toBeRemoved = false; switch (node.nodeType) { - case node.COMMENT_NODE: - case node.DOCUMENT_TYPE_NODE: + case COMMENT_NODE: + case DOCUMENT_TYPE_NODE: toBeRemoved = true; break; - case node.TEXT_NODE: { + case TEXT_NODE: { const trimmedText = node.textContent.trim(); if (trimmedText === "") { toBeRemoved = true; @@ -179,7 +192,7 @@ export const pruneHtmlNode = ( } break; } - case node.ELEMENT_NODE: + case ELEMENT_NODE: toBeRemoved = pruneElement(node as HTMLElement); } @@ -215,7 +228,7 @@ export const escapeHtmlString = (string: string): string => { let index = 0; let lastIndex = 0; - for (let index = match.index; index < str.length; index++) { + for (index = match.index; index < str.length; index++) { switch (str.charCodeAt(index)) { case 34: // " escape = """; diff --git a/src/HTMLNormalizer/strategies/common.ts b/src/HTMLNormalizer/strategies/common.ts index 55c701e07180556b8e8d6d9ed824ad0bbe0f4928..bc2b330f2960b5ce1bbdc0c3c3564a74dbe9b5f4 100644 --- a/src/HTMLNormalizer/strategies/common.ts +++ b/src/HTMLNormalizer/strategies/common.ts @@ -36,13 +36,6 @@ export const pruneElement = (element: HTMLElement): boolean => { return true; } - if ( - element.nodeName.toLowerCase() === "div" && - element.childNodes.length === 0 - ) { - return true; - } - return !!ELEMENT_TYPES_TO_REMOVE[element.nodeName.toLowerCase()]; }; @@ -51,7 +44,7 @@ export const cloneAnchorFromPane = ( pane: HTMLElement ): void => { try { - const url = new URL(a.href); + const url = new URL(a.getAttribute("href")); // If this is external url if (url.host && url.protocol) { pane.parentNode.insertBefore(a.cloneNode(false), pane); diff --git a/src/HTMLNormalizer/strategies/gmail.ts b/src/HTMLNormalizer/strategies/gmail.ts index ee4aa8746491245f17c18fa7c33f92b6019aba16..fa829354844928993ffec7176dd3e95638c5892b 100644 --- a/src/HTMLNormalizer/strategies/gmail.ts +++ b/src/HTMLNormalizer/strategies/gmail.ts @@ -20,8 +20,8 @@ export const amendGmailNodes = (document: HTMLDocument): void => { ); attachmentsPanes.forEach((pane) => { - const as = pane.querySelectorAll("a"); - as.forEach((a) => { + const as = pane.getElementsByTagName("a"); + Array.from(as).forEach((a) => { cloneAnchorFromPane(a, pane as HTMLElement); }); }); diff --git a/src/HTMLNormalizer/strategies/outlook.ts b/src/HTMLNormalizer/strategies/outlook.ts index ab31b1af8dbe46fe05a572e2923515ec1d8fe529..2f47a2bda9f0b3097447f32108c5a9d88ca113b5 100644 --- a/src/HTMLNormalizer/strategies/outlook.ts +++ b/src/HTMLNormalizer/strategies/outlook.ts @@ -36,7 +36,7 @@ const removeQrCodeNodes = (document: HTMLDocument) => { let child = node.firstChild; while (child) { - if (child.nodeType == child.ELEMENT_NODE) { + if (child.nodeType == ELEMENT_NODE) { toRemove = [...toRemove, ...remove(child as Element)]; const childElement = child as Element; diff --git a/src/constants.ts b/src/constants.ts index a6398305ccc66024fe05b08a3e9fec77c2ae5724..4e81cd92f03508d675fe24d619036a24f357dcca 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,4 +1,6 @@ export const ELEMENT_NODE = 1; +export const COMMENT_NODE = 8; +export const DOCUMENT_TYPE_NODE = 10; export const TEXT_NODE = 3; export const DOCUMENT_NODE = 9; diff --git a/yarn.lock b/yarn.lock index 24d4a893b99dc457fa1d676eb73e1f841ab415e5..0a980499fac759d5e636333644d60444746d6cb6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1168,6 +1168,11 @@ resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== +"@types/parse5@^5.0.3": + version "5.0.3" + resolved "https://registry.yarnpkg.com/@types/parse5/-/parse5-5.0.3.tgz#e7b5aebbac150f8b5fdd4a46e7f0bd8e65e19109" + integrity sha512-kUNnecmtkunAoQ3CnjmMkzNU/gtxG8guhi+Fk2U/kOpIKjIMKnXGp4IJCgQJrXSgMsWYimYG4TGjz/UzbGEBTw== + "@types/prettier@^2.0.0": version "2.1.5" resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.1.5.tgz#b6ab3bba29e16b821d84e09ecfaded462b816b00" @@ -1250,6 +1255,13 @@ dependencies: eslint-visitor-keys "^1.1.0" +"@vereign/dom@git+ssh://git@code.vereign.com:code/js-toolbox/gsdom.git": + version "1.0.0" + resolved "git+ssh://git@code.vereign.com:code/js-toolbox/gsdom.git#49702bc108d5ac402fde1a58a0709cb225e70295" + dependencies: + "@types/parse5" "^5.0.3" + parse5 "^6.0.1" + abab@^2.0.3: version "2.0.5" resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a" @@ -3857,6 +3869,11 @@ parse5@5.1.1: resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.1.tgz#f68e4e5ba1852ac2cadc00f4555fff6c2abb6178" integrity sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug== +parse5@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" + integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== + pascalcase@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14"