@ecency/render-helper 2.4.33 → 2.4.35
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser/index.js +173 -48
- package/dist/browser/index.js.map +1 -1
- package/dist/node/index.cjs +173 -48
- package/dist/node/index.cjs.map +1 -1
- package/dist/node/index.mjs +173 -48
- package/dist/node/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/browser/index.js
CHANGED
|
@@ -2,11 +2,11 @@ import { DOMParser as DOMParser$1, XMLSerializer } from '@xmldom/xmldom';
|
|
|
2
2
|
import xss from 'xss';
|
|
3
3
|
import multihash from 'multihashes';
|
|
4
4
|
import querystring from 'querystring';
|
|
5
|
+
import { LRUCache } from 'lru-cache';
|
|
5
6
|
import { Remarkable } from 'remarkable';
|
|
6
7
|
import { linkify as linkify$1 } from 'remarkable/linkify';
|
|
7
8
|
import * as htmlparser2 from 'htmlparser2';
|
|
8
9
|
import * as domSerializerModule from 'dom-serializer';
|
|
9
|
-
import { LRUCache } from 'lru-cache';
|
|
10
10
|
import he from 'he';
|
|
11
11
|
|
|
12
12
|
// src/consts/white-list.const.ts
|
|
@@ -177,23 +177,112 @@ function createParser() {
|
|
|
177
177
|
var DOMParser = createParser();
|
|
178
178
|
|
|
179
179
|
// src/helper.ts
|
|
180
|
+
function isSpaceChar(c) {
|
|
181
|
+
return c === 32 || c === 9 || c === 10 || c === 13 || c === 12;
|
|
182
|
+
}
|
|
183
|
+
function isAsciiLetter(c) {
|
|
184
|
+
return c >= 65 && c <= 90 || c >= 97 && c <= 122;
|
|
185
|
+
}
|
|
186
|
+
function isTagNameChar(c) {
|
|
187
|
+
return isAsciiLetter(c) || c >= 48 && c <= 57;
|
|
188
|
+
}
|
|
189
|
+
function isAttrNameChar(c) {
|
|
190
|
+
return isAsciiLetter(c) || c >= 48 && c <= 57 || c === 45 || c === 95 || c === 58 || c === 46;
|
|
191
|
+
}
|
|
180
192
|
function removeDuplicateAttributes(html) {
|
|
181
|
-
const
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
const
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
193
|
+
const n = html.length;
|
|
194
|
+
let out = "";
|
|
195
|
+
let i = 0;
|
|
196
|
+
while (i < n) {
|
|
197
|
+
const lt = html.indexOf("<", i);
|
|
198
|
+
if (lt < 0) {
|
|
199
|
+
out += html.slice(i);
|
|
200
|
+
break;
|
|
201
|
+
}
|
|
202
|
+
out += html.slice(i, lt);
|
|
203
|
+
if (lt + 1 >= n || !isAsciiLetter(html.charCodeAt(lt + 1))) {
|
|
204
|
+
out += "<";
|
|
205
|
+
i = lt + 1;
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
let p2 = lt + 1;
|
|
209
|
+
while (p2 < n && isTagNameChar(html.charCodeAt(p2))) p2++;
|
|
210
|
+
const tagName = html.slice(lt + 1, p2);
|
|
211
|
+
if (p2 >= n || !isSpaceChar(html.charCodeAt(p2))) {
|
|
212
|
+
out += "<";
|
|
213
|
+
i = lt + 1;
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
const attrs = [];
|
|
217
|
+
const seen = /* @__PURE__ */ new Set();
|
|
218
|
+
let q = p2;
|
|
219
|
+
while (q < n) {
|
|
220
|
+
while (q < n && isSpaceChar(html.charCodeAt(q))) q++;
|
|
221
|
+
if (q >= n) break;
|
|
222
|
+
const ch = html.charCodeAt(q);
|
|
223
|
+
if (ch === 62) break;
|
|
224
|
+
if (ch === 47 && q + 1 < n && html.charCodeAt(q + 1) === 62) break;
|
|
225
|
+
const nameStart = q;
|
|
226
|
+
while (q < n && isAttrNameChar(html.charCodeAt(q))) q++;
|
|
227
|
+
if (q === nameStart) {
|
|
228
|
+
q++;
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
const attrName = html.slice(nameStart, q);
|
|
232
|
+
let r = q;
|
|
233
|
+
while (r < n && isSpaceChar(html.charCodeAt(r))) r++;
|
|
234
|
+
let valueEnd = q;
|
|
235
|
+
if (r < n && html.charCodeAt(r) === 61) {
|
|
236
|
+
r++;
|
|
237
|
+
while (r < n && isSpaceChar(html.charCodeAt(r))) r++;
|
|
238
|
+
if (r < n) {
|
|
239
|
+
const v = html.charCodeAt(r);
|
|
240
|
+
if (v === 34 || v === 39) {
|
|
241
|
+
const quote = html[r];
|
|
242
|
+
const end = html.indexOf(quote, r + 1);
|
|
243
|
+
if (end < 0) {
|
|
244
|
+
const gt = html.indexOf(">", r + 1);
|
|
245
|
+
valueEnd = gt < 0 ? n : gt;
|
|
246
|
+
} else {
|
|
247
|
+
valueEnd = end + 1;
|
|
248
|
+
}
|
|
249
|
+
} else {
|
|
250
|
+
let s = r;
|
|
251
|
+
while (s < n) {
|
|
252
|
+
const k = html.charCodeAt(s);
|
|
253
|
+
if (isSpaceChar(k) || k === 62) break;
|
|
254
|
+
s++;
|
|
255
|
+
}
|
|
256
|
+
valueEnd = s;
|
|
257
|
+
}
|
|
258
|
+
} else {
|
|
259
|
+
valueEnd = r;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
const fullAttr = html.slice(nameStart, valueEnd);
|
|
263
|
+
q = valueEnd;
|
|
264
|
+
const key = attrName.toLowerCase();
|
|
265
|
+
if (!seen.has(key)) {
|
|
266
|
+
seen.add(key);
|
|
267
|
+
attrs.push(fullAttr);
|
|
192
268
|
}
|
|
193
269
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
270
|
+
let selfClose = false;
|
|
271
|
+
if (q < n && html.charCodeAt(q) === 47) {
|
|
272
|
+
selfClose = true;
|
|
273
|
+
q++;
|
|
274
|
+
}
|
|
275
|
+
if (q >= n || html.charCodeAt(q) !== 62) {
|
|
276
|
+
out += "<";
|
|
277
|
+
i = lt + 1;
|
|
278
|
+
continue;
|
|
279
|
+
}
|
|
280
|
+
q++;
|
|
281
|
+
const attrsJoined = attrs.length > 0 ? " " + attrs.join(" ") : "";
|
|
282
|
+
out += "<" + tagName + attrsJoined + (selfClose ? " /" : "") + ">";
|
|
283
|
+
i = q;
|
|
284
|
+
}
|
|
285
|
+
return out;
|
|
197
286
|
}
|
|
198
287
|
function createDoc(html) {
|
|
199
288
|
if (html.trim() === "") {
|
|
@@ -296,6 +385,14 @@ function sanitizeHtml(html) {
|
|
|
296
385
|
});
|
|
297
386
|
}
|
|
298
387
|
var proxyBase = "https://images.ecency.com";
|
|
388
|
+
var urlHashCache = new LRUCache({ max: 500 });
|
|
389
|
+
function getUrlHash(url) {
|
|
390
|
+
const cached = urlHashCache.get(url);
|
|
391
|
+
if (cached) return cached;
|
|
392
|
+
const hash = multihash.toB58String(Buffer.from(url));
|
|
393
|
+
urlHashCache.set(url, hash);
|
|
394
|
+
return hash;
|
|
395
|
+
}
|
|
299
396
|
function setProxyBase(p2) {
|
|
300
397
|
proxyBase = p2;
|
|
301
398
|
}
|
|
@@ -346,7 +443,7 @@ function proxifyImageSrc(url, width = 0, height = 0, _format = "match") {
|
|
|
346
443
|
if (pHash) {
|
|
347
444
|
return `${proxyBase}/p/${pHash}?${qs}`;
|
|
348
445
|
}
|
|
349
|
-
const b58url =
|
|
446
|
+
const b58url = getUrlHash(realUrl.toString());
|
|
350
447
|
return `${proxyBase}/p/${b58url}?${qs}`;
|
|
351
448
|
}
|
|
352
449
|
var SRCSET_WIDTHS = [320, 600, 800, 1024, 1280];
|
|
@@ -1578,7 +1675,6 @@ function markdownToHTML(input, forApp, parentDomain = "ecency.com", seoContext,
|
|
|
1578
1675
|
output = serializer.serializeToString(doc);
|
|
1579
1676
|
} catch (error) {
|
|
1580
1677
|
try {
|
|
1581
|
-
output = md.render(input);
|
|
1582
1678
|
const preSanitized = sanitizeHtml(output);
|
|
1583
1679
|
const dom = htmlparser2.parseDocument(preSanitized, {
|
|
1584
1680
|
// lenient options - don't throw on malformed HTML
|
|
@@ -1618,7 +1714,7 @@ function simpleMarkdownToHTML(input) {
|
|
|
1618
1714
|
const html = getMd().render(input);
|
|
1619
1715
|
return sanitizeHtml(html);
|
|
1620
1716
|
}
|
|
1621
|
-
var cache = new LRUCache({ max:
|
|
1717
|
+
var cache = new LRUCache({ max: 500 });
|
|
1622
1718
|
function setCacheSize(size) {
|
|
1623
1719
|
cache = new LRUCache({ max: size });
|
|
1624
1720
|
}
|
|
@@ -1649,6 +1745,40 @@ var gifLinkRegex = /\.(gif)$/i;
|
|
|
1649
1745
|
function isGifLink(link) {
|
|
1650
1746
|
return gifLinkRegex.test(link);
|
|
1651
1747
|
}
|
|
1748
|
+
var BACKTICK_FENCE_RE = /```[\s\S]*?```/g;
|
|
1749
|
+
var TILDE_FENCE_RE = /~~~[\s\S]*?~~~/g;
|
|
1750
|
+
var INLINE_CODE_RE = /`[^`\n]*`/g;
|
|
1751
|
+
var INDENTED_CODE_RE = /^(?: {4}|\t).+$/gm;
|
|
1752
|
+
var MD_IMAGE_RE = /!\[[^\]]*\]\(\s*([^)\s]+)(?:\s+["'][^"']*["'])?\s*\)/;
|
|
1753
|
+
var HTML_IMAGE_RE = /<img\b[^>]*?\bsrc\s*=\s*["']([^"']+)["']/i;
|
|
1754
|
+
var SAFE_URL_RE = /^https?:\/\//i;
|
|
1755
|
+
function findFirstImageUrl(body) {
|
|
1756
|
+
if (!body) return null;
|
|
1757
|
+
const cleaned = body.replace(BACKTICK_FENCE_RE, "").replace(TILDE_FENCE_RE, "").replace(INLINE_CODE_RE, "").replace(INDENTED_CODE_RE, "");
|
|
1758
|
+
const mdMatch = cleaned.match(MD_IMAGE_RE);
|
|
1759
|
+
const htmlMatch = cleaned.match(HTML_IMAGE_RE);
|
|
1760
|
+
if (mdMatch) {
|
|
1761
|
+
const url = mdMatch[1];
|
|
1762
|
+
if (!url || !SAFE_URL_RE.test(url) || url.includes("(")) {
|
|
1763
|
+
return null;
|
|
1764
|
+
}
|
|
1765
|
+
}
|
|
1766
|
+
const mdValid = !!mdMatch;
|
|
1767
|
+
const htmlValid = !!(htmlMatch && htmlMatch[1] && SAFE_URL_RE.test(htmlMatch[1]));
|
|
1768
|
+
if (mdValid && htmlValid) {
|
|
1769
|
+
return (mdMatch.index ?? 0) < (htmlMatch.index ?? 0) ? mdMatch[1] : htmlMatch[1];
|
|
1770
|
+
}
|
|
1771
|
+
if (mdValid) return mdMatch[1];
|
|
1772
|
+
if (htmlValid) return htmlMatch[1];
|
|
1773
|
+
return null;
|
|
1774
|
+
}
|
|
1775
|
+
function proxifyFound(src, width, height, format) {
|
|
1776
|
+
const decoded = he.decode(src);
|
|
1777
|
+
if (isGifLink(decoded)) {
|
|
1778
|
+
return proxifyImageSrc(decoded, 0, 0, format);
|
|
1779
|
+
}
|
|
1780
|
+
return proxifyImageSrc(decoded, width, height, format);
|
|
1781
|
+
}
|
|
1652
1782
|
function getImage(entry, width = 0, height = 0, format = "match") {
|
|
1653
1783
|
let meta;
|
|
1654
1784
|
if (typeof entry.json_metadata === "object") {
|
|
@@ -1680,6 +1810,10 @@ function getImage(entry, width = 0, height = 0, format = "match") {
|
|
|
1680
1810
|
}
|
|
1681
1811
|
return proxifyImageSrc(meta.image[0], width, height, format);
|
|
1682
1812
|
}
|
|
1813
|
+
const fast = findFirstImageUrl(entry.body);
|
|
1814
|
+
if (fast) {
|
|
1815
|
+
return proxifyFound(fast, width, height, format);
|
|
1816
|
+
}
|
|
1683
1817
|
const html = markdown2Html(entry);
|
|
1684
1818
|
const doc = createDoc(html);
|
|
1685
1819
|
if (!doc) {
|
|
@@ -1691,16 +1825,16 @@ function getImage(entry, width = 0, height = 0, format = "match") {
|
|
|
1691
1825
|
if (!src) {
|
|
1692
1826
|
return null;
|
|
1693
1827
|
}
|
|
1694
|
-
|
|
1695
|
-
if (isGifLink(decodedSrc)) {
|
|
1696
|
-
return proxifyImageSrc(decodedSrc, 0, 0, format);
|
|
1697
|
-
}
|
|
1698
|
-
return proxifyImageSrc(decodedSrc, width, height, format);
|
|
1828
|
+
return proxifyFound(src, width, height, format);
|
|
1699
1829
|
}
|
|
1700
1830
|
return null;
|
|
1701
1831
|
}
|
|
1702
1832
|
function catchPostImage(obj, width = 0, height = 0, format = "match") {
|
|
1703
1833
|
if (typeof obj === "string") {
|
|
1834
|
+
const fast = findFirstImageUrl(obj);
|
|
1835
|
+
if (fast) {
|
|
1836
|
+
return proxifyFound(fast, width, height, format);
|
|
1837
|
+
}
|
|
1704
1838
|
const html = markdown2Html(obj);
|
|
1705
1839
|
const doc = createDoc(html);
|
|
1706
1840
|
if (!doc) {
|
|
@@ -1712,11 +1846,7 @@ function catchPostImage(obj, width = 0, height = 0, format = "match") {
|
|
|
1712
1846
|
if (!src) {
|
|
1713
1847
|
return null;
|
|
1714
1848
|
}
|
|
1715
|
-
|
|
1716
|
-
if (isGifLink(decodedSrc)) {
|
|
1717
|
-
return proxifyImageSrc(decodedSrc, 0, 0, format);
|
|
1718
|
-
}
|
|
1719
|
-
return proxifyImageSrc(decodedSrc, width, height, format);
|
|
1849
|
+
return proxifyFound(src, width, height, format);
|
|
1720
1850
|
}
|
|
1721
1851
|
return null;
|
|
1722
1852
|
}
|
|
@@ -1729,6 +1859,20 @@ function catchPostImage(obj, width = 0, height = 0, format = "match") {
|
|
|
1729
1859
|
cacheSet(key, res);
|
|
1730
1860
|
return res;
|
|
1731
1861
|
}
|
|
1862
|
+
var summaryRenderer = new Remarkable({
|
|
1863
|
+
html: true,
|
|
1864
|
+
breaks: true,
|
|
1865
|
+
typographer: false
|
|
1866
|
+
});
|
|
1867
|
+
summaryRenderer.core.ruler.enable(["abbr"]);
|
|
1868
|
+
summaryRenderer.block.ruler.enable(["footnote", "deflist"]);
|
|
1869
|
+
summaryRenderer.inline.ruler.enable([
|
|
1870
|
+
"footnote_inline",
|
|
1871
|
+
"ins",
|
|
1872
|
+
"mark",
|
|
1873
|
+
"sub",
|
|
1874
|
+
"sup"
|
|
1875
|
+
]);
|
|
1732
1876
|
var joint = (arr, limit = 200) => {
|
|
1733
1877
|
let result = "";
|
|
1734
1878
|
if (arr) {
|
|
@@ -1754,25 +1898,6 @@ function postBodySummary(entryBody, length = 200, platform = "web") {
|
|
|
1754
1898
|
return "";
|
|
1755
1899
|
}
|
|
1756
1900
|
entryBody = cleanReply(entryBody);
|
|
1757
|
-
const mdd = new Remarkable({
|
|
1758
|
-
html: true,
|
|
1759
|
-
breaks: true,
|
|
1760
|
-
typographer: false
|
|
1761
|
-
}).use(linkify$1);
|
|
1762
|
-
mdd.core.ruler.enable([
|
|
1763
|
-
"abbr"
|
|
1764
|
-
]);
|
|
1765
|
-
mdd.block.ruler.enable([
|
|
1766
|
-
"footnote",
|
|
1767
|
-
"deflist"
|
|
1768
|
-
]);
|
|
1769
|
-
mdd.inline.ruler.enable([
|
|
1770
|
-
"footnote_inline",
|
|
1771
|
-
"ins",
|
|
1772
|
-
"mark",
|
|
1773
|
-
"sub",
|
|
1774
|
-
"sup"
|
|
1775
|
-
]);
|
|
1776
1901
|
const entities = entryBody.match(ENTITY_REGEX);
|
|
1777
1902
|
const entityPlaceholders = [];
|
|
1778
1903
|
if (entities && platform !== "web") {
|
|
@@ -1785,7 +1910,7 @@ function postBodySummary(entryBody, length = 200, platform = "web") {
|
|
|
1785
1910
|
}
|
|
1786
1911
|
let text2 = "";
|
|
1787
1912
|
try {
|
|
1788
|
-
text2 =
|
|
1913
|
+
text2 = summaryRenderer.render(entryBody);
|
|
1789
1914
|
} catch (err) {
|
|
1790
1915
|
console.error("[postBodySummary] Failed to render markdown:", {
|
|
1791
1916
|
error: err instanceof Error ? err.message : String(err),
|