feat: migrate to node-html-parser

This commit is contained in:
thecodrr
2021-11-25 09:46:45 +05:00
parent 1f6088a864
commit 92a8239899
11 changed files with 299 additions and 5414 deletions

View File

@@ -17,6 +17,8 @@ async function writeEncrypted(filename, { data, type, key }) {
if (hasItem(filename)) return { hash, hashType }; if (hasItem(filename)) return { hash, hashType };
fs[filename] = data; fs[filename] = data;
return { return {
chunkSize: 512,
alg: "xcha-stream",
hash, hash,
hashType, hashType,
iv: "some iv", iv: "some iv",

View File

@@ -62,6 +62,10 @@ async function hash(password, userId) {
return password; return password;
} }
async function generateCryptoKey(password) {
return password;
}
module.exports = { module.exports = {
read, read,
readMulti, readMulti,
@@ -71,6 +75,7 @@ module.exports = {
encrypt, encrypt,
decrypt, decrypt,
deriveCryptoKey, deriveCryptoKey,
generateCryptoKey,
getCryptoKey, getCryptoKey,
getAllKeys, getAllKeys,
hash, hash,

View File

@@ -63,7 +63,7 @@ test("get all notes", () =>
test("note without a title should get title from content", () => test("note without a title should get title from content", () =>
noteTest().then(async ({ db, id }) => { noteTest().then(async ({ db, id }) => {
let note = db.notes.note(id); let note = db.notes.note(id);
expect(note.title).toBe("Hello This is colorful"); expect(note.title).toBe("HelloThis is colorful");
})); }));
test("note should get headline from content", () => test("note should get headline from content", () =>

View File

@@ -34,7 +34,8 @@ var TEST_NOTE = {
}, },
}; };
const IMG_CONTENT = `<p>This is a note for me.j</p>\n<p><img src="data:image/png;base64,iVBORw0K" class="attachment" alt="Screenshot_20210915_102333.png" data-mime="image/png" data-hash="d3eab72e94e3cd35" data-filename="Screenshot_20210915_102333.png" data-size="68609" style="float: left;" /> &nbsp;</p>\n<p>&nbsp;</p>`; const IMG_CONTENT = `<p>This is a note for me.j</p>\n<p><img src="data:image/png;base64,iVBORw0K" data-hash="d3eab72e94e3cd35" class="attachment" alt="Screenshot_20210915_102333.png" data-mime="image/png" data-filename="Screenshot_20210915_102333.png" data-size="68609" style="float: left;" /> &nbsp;</p>\n<p>&nbsp;</p>`;
const IMG_CONTENT_WITHOUT_HASH = `<p>This is a note for me.j</p>\n<p><img src="data:image/png;base64,iVBORw0K" class="attachment" alt="Screenshot_20210915_102333.png" data-mime="image/png" data-filename="Screenshot_20210915_102333.png" data-size="68609" style="float: left;" /> &nbsp;</p>\n<p>&nbsp;</p>`;
const LONG_TEXT = const LONG_TEXT =
"Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum."; "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.";
@@ -73,6 +74,7 @@ export {
noteTest, noteTest,
groupedTest, groupedTest,
IMG_CONTENT, IMG_CONTENT,
IMG_CONTENT_WITHOUT_HASH,
StorageInterface, StorageInterface,
TEST_NOTEBOOK, TEST_NOTEBOOK,
TEST_NOTEBOOK2, TEST_NOTEBOOK2,

View File

@@ -109,7 +109,9 @@ export default class Content extends Collection {
const allAttachments = this._db.attachments.all; const allAttachments = this._db.attachments.all;
const content = getContentFromData(contentItem.type, contentItem.data); const content = getContentFromData(contentItem.type, contentItem.data);
const { data, attachments } = content.extractAttachments(); const { data, attachments } = await content.extractAttachments(
(data, type) => this._db.attachments.save(data, type)
);
const noteAttachments = allAttachments.filter((attachment) => const noteAttachments = allAttachments.filter((attachment) =>
hasItem(attachment.noteIds, contentItem.noteId) hasItem(attachment.noteIds, contentItem.noteId)
@@ -131,18 +133,6 @@ export default class Content extends Collection {
} }
for (let attachment of toAdd) { for (let attachment of toAdd) {
if (attachment.data && attachment.type) {
const { key, metadata } = await this._db.attachments.save(
attachment.data,
attachment.type
);
attachment = {
type: attachment.type,
filename: metadata.hash,
...metadata,
key,
};
}
await this._db.attachments.add(attachment, contentItem.noteId); await this._db.attachments.add(attachment, contentItem.noteId);
} }

View File

@@ -1,12 +1,15 @@
import { IMG_CONTENT } from "../../__tests__/utils"; import { IMG_CONTENT, IMG_CONTENT_WITHOUT_HASH } from "../../__tests__/utils";
import Tiny from "../tiny"; import Tiny from "../tiny";
test("img src is empty after extract attachments", async () => { test("img src is empty after extract attachments", async () => {
const tiny = new Tiny(IMG_CONTENT); const tiny = new Tiny(IMG_CONTENT_WITHOUT_HASH);
const result = tiny.extractAttachments(); const result = await tiny.extractAttachments(async () => {
return { key: "hello", metadata: { hash: "helloworld" } };
});
expect(result.attachments.length).toBe(1); expect(result.attachments.length).toBe(1);
expect(result.data).not.toContain(`src="data:image/png;`); expect(result.data).not.toContain(`src="data:image/png;`);
expect(result.data).not.toContain(`src=`); expect(result.data).not.toContain(`src=`);
expect(result.data).toContain(`data-hash=\"helloworld\"`);
}); });
test("img src is present after insert attachments", async () => { test("img src is present after insert attachments", async () => {

View File

@@ -1,14 +1,11 @@
import showdown from "showdown"; import showdown from "showdown";
import { decode, DecodingMode, EntityLevel } from "entities"; import { decode, DecodingMode, EntityLevel } from "entities";
import dataurl from "../utils/dataurl";
import { parseHTML } from "../utils/html-parser";
var converter = new showdown.Converter(); var converter = new showdown.Converter();
converter.setFlavor("original"); converter.setFlavor("original");
const HASH_REGEX = /data-hash="(\S+)"/g;
const SRC_HASH_REGEX =
/src="data:(image\/.+);base64,(\S+)"|data-hash="(\S+)"/gm;
const TAG_REGEX = /<(span|img)[^>]+/gm;
const splitter = /\W+/gm; const splitter = /\W+/gm;
class Tiny { class Tiny {
@@ -22,10 +19,8 @@ class Tiny {
} }
toTXT() { toTXT() {
return decode( const document = parseHTML(this.data);
this.data.replace(/<br[^>]*>/gi, "\n").replace(/<[^>]+>/g, ""), return document.textContent || document.body.textContent;
{ level: EntityLevel.HTML, mode: DecodingMode.Strict }
).trim();
} }
toMD() { toMD() {
@@ -60,90 +55,80 @@ class Tiny {
} }
async insertMedia(getData) { async insertMedia(getData) {
let document = this.data; let document = parseHTML(this.data);
const attachmentElements = document.querySelectorAll("img");
const matches = Array.from(this.data.matchAll(TAG_REGEX)); for (var i = 0; i < attachmentElements.length; ++i) {
for (let i = 0; i < matches.length; ++i) { const attachment = attachmentElements[i];
const match = matches[i]; switch (attachment.tagName) {
const nodeName = match[1]; case "IMG": {
const hash = getDatasetAttribute(attachment, "hash");
if (!hash) continue;
if (nodeName === "img") { const src = await getData(hash, {
const node = match[0]; total: attachmentElements.length,
current: i,
const hashMatch = node.match(HASH_REGEX); });
if (!hashMatch) continue; if (!src) continue;
attachment.setAttribute("src", src);
const [attribute, hash] = hashMatch; break;
}
const src = await getData(hash, {
total: matches.length,
current: i,
});
if (!src) continue;
const srcAttribute = createAttribute("src", src);
const replacedNode = replaceAt(
node,
hashMatch.index,
attribute,
srcAttribute
);
document = replaceAt(this.data, match.index, node, replacedNode);
} }
} }
return document; return document.outerHTML || document.body.innerHTML;
} }
extractAttachments() { async extractAttachments(store) {
const attachments = []; const attachments = [];
let document = this.data; let document = parseHTML(this.data);
for (let match of this.data.matchAll(TAG_REGEX)) { const attachmentElements = document.querySelectorAll("img,span");
const nodeName = match[1];
const node = match[0];
const attachment = { hash: undefined, data: undefined, type: undefined };
switch (nodeName) { for (var i = 0; i < attachmentElements.length; ++i) {
case "img": { const attachment = attachmentElements[i];
const replacedNode = node.replace( switch (attachment.tagName) {
SRC_HASH_REGEX, case "IMG": {
(match, mime, data, hash) => { if (!getDatasetAttribute(attachment, "hash")) {
if (mime) attachment.type = mime; const src = attachment.getAttribute("src");
if (data) attachment.data = data; if (!src) continue;
if (hash) attachment.hash = hash;
return match.startsWith("src") ? "" : match; const { data, mime } = dataurl.toObject(src);
} if (!data) continue;
);
document = replaceAt(this.data, match.index, node, replacedNode); const type =
getDatasetAttribute(attachment, "mime") || mime || "image/jpeg";
const { key, metadata } = await store(data, "base64");
setDatasetAttribute(attachment, "hash", metadata.hash);
attachments.push({
type,
filename:
getDatasetAttribute(attachment, "filename") || metadata.hash,
...metadata,
key,
});
} else {
attachments.push({
hash: getDatasetAttribute(attachment, "hash"),
});
}
attachment.removeAttribute("src");
break; break;
} }
case "span": { default: {
const matches = HASH_REGEX.exec(node); if (!getDatasetAttribute(attachment, "hash")) continue;
if (!matches) continue; attachments.push({
const [_match, hash] = matches; hash: getDatasetAttribute(attachment, "hash"),
attachments.push({ hash }); });
break; break;
} }
} }
if (attachment.hash || attachment.data) attachments.push(attachment);
} }
return { data: document.outerHTML || document.body.innerHTML, attachments };
return { data: document, attachments };
} }
} }
export default Tiny; export default Tiny;
function replaceAt(str, index, match, replacement) {
let start = str.slice(0, index);
start += replacement;
start += str.slice(index + match.length);
return start;
}
function createAttribute(key, value) {
return `${key}="${value}"`;
}
function getHeadlineFromText(text) { function getHeadlineFromText(text) {
for (var i = 0; i < text.length; ++i) { for (var i = 0; i < text.length; ++i) {
const char = text[i]; const char = text[i];
@@ -176,3 +161,11 @@ function getTitleFromText(text) {
} }
return title; return title;
} }
function getDatasetAttribute(element, attribute) {
return element.getAttribute(`data-${attribute}`);
}
function setDatasetAttribute(element, attribute, value) {
return element.setAttribute(`data-${attribute}`, value);
}

View File

@@ -1,12 +1,12 @@
{ {
"name": "notes-core", "name": "notes-core",
"version": "6.10.0", "version": "6.11.0",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "notes-core", "name": "notes-core",
"version": "6.10.0", "version": "6.11.0",
"dependencies": { "dependencies": {
"@stablelib/blake2s": "^1.0.1", "@stablelib/blake2s": "^1.0.1",
"async-mutex": "^0.3.2", "async-mutex": "^0.3.2",
@@ -14,6 +14,7 @@
"entities": "^3.0.1", "entities": "^3.0.1",
"fast-sort": "^2.0.1", "fast-sort": "^2.0.1",
"no-internet": "^1.5.2", "no-internet": "^1.5.2",
"node-html-parser": "^5.1.0",
"qclone": "^1.0.4", "qclone": "^1.0.4",
"showdown": "https://github.com/thecodrr/showdown", "showdown": "https://github.com/thecodrr/showdown",
"spark-md5": "^3.0.1" "spark-md5": "^3.0.1"
@@ -3014,6 +3015,11 @@
"integrity": "sha1-8kiPMleC9m0XSEL0gZkuL6ulbzg=", "integrity": "sha1-8kiPMleC9m0XSEL0gZkuL6ulbzg=",
"dev": true "dev": true
}, },
"node_modules/boolbase": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
"integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24="
},
"node_modules/brace-expansion": { "node_modules/brace-expansion": {
"version": "1.1.11", "version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -3365,6 +3371,32 @@
"node": ">=4.8" "node": ">=4.8"
} }
}, },
"node_modules/css-select": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/css-select/-/css-select-4.1.3.tgz",
"integrity": "sha512-gT3wBNd9Nj49rAbmtFHj1cljIAOLYSX1nZ8CB7TBO3INYckygm5B7LISU/szY//YmdiSLbJvDLOx9VnMVpMBxA==",
"dependencies": {
"boolbase": "^1.0.0",
"css-what": "^5.0.0",
"domhandler": "^4.2.0",
"domutils": "^2.6.0",
"nth-check": "^2.0.0"
},
"funding": {
"url": "https://github.com/sponsors/fb55"
}
},
"node_modules/css-what": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz",
"integrity": "sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw==",
"engines": {
"node": ">= 6"
},
"funding": {
"url": "https://github.com/sponsors/fb55"
}
},
"node_modules/cssom": { "node_modules/cssom": {
"version": "0.3.8", "version": "0.3.8",
"resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz",
@@ -3592,6 +3624,38 @@
"node": ">= 6" "node": ">= 6"
} }
}, },
"node_modules/dom-serializer": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz",
"integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==",
"dependencies": {
"domelementtype": "^2.0.1",
"domhandler": "^4.2.0",
"entities": "^2.0.0"
},
"funding": {
"url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
}
},
"node_modules/dom-serializer/node_modules/entities": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
"integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/domelementtype": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz",
"integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/fb55"
}
]
},
"node_modules/domexception": { "node_modules/domexception": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz",
@@ -3613,6 +3677,33 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/domhandler": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.2.2.tgz",
"integrity": "sha512-PzE9aBMsdZO8TK4BnuJwH0QT41wgMbRzuZrHUcpYncEjmQazq8QEaBWgLG7ZyC/DAZKEgglpIA6j4Qn/HmxS3w==",
"dependencies": {
"domelementtype": "^2.2.0"
},
"engines": {
"node": ">= 4"
},
"funding": {
"url": "https://github.com/fb55/domhandler?sponsor=1"
}
},
"node_modules/domutils": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
"integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
"dependencies": {
"dom-serializer": "^1.0.1",
"domelementtype": "^2.2.0",
"domhandler": "^4.2.0"
},
"funding": {
"url": "https://github.com/fb55/domutils?sponsor=1"
}
},
"node_modules/dotenv": { "node_modules/dotenv": {
"version": "10.0.0", "version": "10.0.0",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz",
@@ -4211,6 +4302,14 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/he": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
"bin": {
"he": "bin/he"
}
},
"node_modules/home-or-tmp": { "node_modules/home-or-tmp": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz",
@@ -6020,6 +6119,15 @@
"node": "4.x || >=6.0.0" "node": "4.x || >=6.0.0"
} }
}, },
"node_modules/node-html-parser": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/node-html-parser/-/node-html-parser-5.1.0.tgz",
"integrity": "sha512-l6C1Gf1o7YuxeMGa17PypEez/rj+ii3q4/NZG37nRmWSLDjHyB0WNrlE4h2UW92D0JSfUSfu+lOvxThttVe7Jw==",
"dependencies": {
"css-select": "^4.1.3",
"he": "1.2.0"
}
},
"node_modules/node-int64": { "node_modules/node-int64": {
"version": "0.4.0", "version": "0.4.0",
"resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz",
@@ -6090,6 +6198,17 @@
"node": ">=4" "node": ">=4"
} }
}, },
"node_modules/nth-check": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz",
"integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==",
"dependencies": {
"boolbase": "^1.0.0"
},
"funding": {
"url": "https://github.com/fb55/nth-check?sponsor=1"
}
},
"node_modules/nwsapi": { "node_modules/nwsapi": {
"version": "2.2.0", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz",
@@ -10737,6 +10856,11 @@
"integrity": "sha1-8kiPMleC9m0XSEL0gZkuL6ulbzg=", "integrity": "sha1-8kiPMleC9m0XSEL0gZkuL6ulbzg=",
"dev": true "dev": true
}, },
"boolbase": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
"integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24="
},
"brace-expansion": { "brace-expansion": {
"version": "1.1.11", "version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -11040,6 +11164,23 @@
"which": "^1.2.9" "which": "^1.2.9"
} }
}, },
"css-select": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/css-select/-/css-select-4.1.3.tgz",
"integrity": "sha512-gT3wBNd9Nj49rAbmtFHj1cljIAOLYSX1nZ8CB7TBO3INYckygm5B7LISU/szY//YmdiSLbJvDLOx9VnMVpMBxA==",
"requires": {
"boolbase": "^1.0.0",
"css-what": "^5.0.0",
"domhandler": "^4.2.0",
"domutils": "^2.6.0",
"nth-check": "^2.0.0"
}
},
"css-what": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz",
"integrity": "sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw=="
},
"cssom": { "cssom": {
"version": "0.3.8", "version": "0.3.8",
"resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz",
@@ -11224,6 +11365,28 @@
"integrity": "sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew==", "integrity": "sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew==",
"dev": true "dev": true
}, },
"dom-serializer": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz",
"integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==",
"requires": {
"domelementtype": "^2.0.1",
"domhandler": "^4.2.0",
"entities": "^2.0.0"
},
"dependencies": {
"entities": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
"integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A=="
}
}
},
"domelementtype": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz",
"integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A=="
},
"domexception": { "domexception": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz",
@@ -11241,6 +11404,24 @@
} }
} }
}, },
"domhandler": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.2.2.tgz",
"integrity": "sha512-PzE9aBMsdZO8TK4BnuJwH0QT41wgMbRzuZrHUcpYncEjmQazq8QEaBWgLG7ZyC/DAZKEgglpIA6j4Qn/HmxS3w==",
"requires": {
"domelementtype": "^2.2.0"
}
},
"domutils": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
"integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
"requires": {
"dom-serializer": "^1.0.1",
"domelementtype": "^2.2.0",
"domhandler": "^4.2.0"
}
},
"dotenv": { "dotenv": {
"version": "10.0.0", "version": "10.0.0",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz",
@@ -11703,6 +11884,11 @@
} }
} }
}, },
"he": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="
},
"home-or-tmp": { "home-or-tmp": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz",
@@ -13161,6 +13347,15 @@
"integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==", "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==",
"dev": true "dev": true
}, },
"node-html-parser": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/node-html-parser/-/node-html-parser-5.1.0.tgz",
"integrity": "sha512-l6C1Gf1o7YuxeMGa17PypEez/rj+ii3q4/NZG37nRmWSLDjHyB0WNrlE4h2UW92D0JSfUSfu+lOvxThttVe7Jw==",
"requires": {
"css-select": "^4.1.3",
"he": "1.2.0"
}
},
"node-int64": { "node-int64": {
"version": "0.4.0", "version": "0.4.0",
"resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz",
@@ -13222,6 +13417,14 @@
"path-key": "^2.0.0" "path-key": "^2.0.0"
} }
}, },
"nth-check": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz",
"integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==",
"requires": {
"boolbase": "^1.0.0"
}
},
"nwsapi": { "nwsapi": {
"version": "2.2.0", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz",

View File

@@ -29,6 +29,7 @@
"entities": "^3.0.1", "entities": "^3.0.1",
"fast-sort": "^2.0.1", "fast-sort": "^2.0.1",
"no-internet": "^1.5.2", "no-internet": "^1.5.2",
"node-html-parser": "^5.1.0",
"qclone": "^1.0.4", "qclone": "^1.0.4",
"showdown": "https://github.com/thecodrr/showdown", "showdown": "https://github.com/thecodrr/showdown",
"spark-md5": "^3.0.1" "spark-md5": "^3.0.1"

View File

@@ -0,0 +1,6 @@
import { parse } from "node-html-parser";
export const parseHTML =
typeof DOMParser === "undefined"
? (input) => parse(input)
: (input) => new DOMParser().parseFromString(input, "text/html");

File diff suppressed because it is too large Load Diff