@ecency/render-helper 2.4.32 → 2.4.34

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.
@@ -1,13 +1,13 @@
1
- import { DOMParser as DOMParser$1, XMLSerializer as XMLSerializer$1 } from '@xmldom/xmldom';
1
+ 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
- import he2 from 'he';
8
8
  import * as htmlparser2 from 'htmlparser2';
9
9
  import * as domSerializerModule from 'dom-serializer';
10
- import { LRUCache } from 'lru-cache';
10
+ import he from 'he';
11
11
 
12
12
  // src/consts/white-list.const.ts
13
13
  var WHITE_LIST = [
@@ -168,16 +168,13 @@ var ALLOWED_ATTRIBUTES = {
168
168
  "del": [],
169
169
  "ins": []
170
170
  };
171
- var hasDOMParser = typeof globalThis.DOMParser !== "undefined";
172
- var hasXMLSerializer = typeof globalThis.XMLSerializer !== "undefined";
173
- var lenientErrorHandler = (level, msg, _context) => {
174
- if (process.env.NODE_ENV === "development") {
175
- console.warn("[DOMParser]", level, msg);
176
- }
177
- return void 0;
178
- };
179
- var DOMParser = hasDOMParser ? new globalThis.DOMParser() : new DOMParser$1({ onError: lenientErrorHandler });
180
- var XMLSerializer = hasXMLSerializer ? globalThis.XMLSerializer : XMLSerializer$1;
171
+ function createParser() {
172
+ return new DOMParser$1({
173
+ onError(level, msg) {
174
+ }
175
+ });
176
+ }
177
+ var DOMParser = createParser();
181
178
 
182
179
  // src/helper.ts
183
180
  function removeDuplicateAttributes(html) {
@@ -299,6 +296,14 @@ function sanitizeHtml(html) {
299
296
  });
300
297
  }
301
298
  var proxyBase = "https://images.ecency.com";
299
+ var urlHashCache = new LRUCache({ max: 500 });
300
+ function getUrlHash(url) {
301
+ const cached = urlHashCache.get(url);
302
+ if (cached) return cached;
303
+ const hash = multihash.toB58String(Buffer.from(url));
304
+ urlHashCache.set(url, hash);
305
+ return hash;
306
+ }
302
307
  function setProxyBase(p2) {
303
308
  proxyBase = p2;
304
309
  }
@@ -349,7 +354,7 @@ function proxifyImageSrc(url, width = 0, height = 0, _format = "match") {
349
354
  if (pHash) {
350
355
  return `${proxyBase}/p/${pHash}?${qs}`;
351
356
  }
352
- const b58url = multihash.toB58String(Buffer.from(realUrl.toString()));
357
+ const b58url = getUrlHash(realUrl.toString());
353
358
  return `${proxyBase}/p/${b58url}?${qs}`;
354
359
  }
355
360
  var SRCSET_WIDTHS = [320, 600, 800, 1024, 1280];
@@ -1593,8 +1598,7 @@ function markdownToHTML(input, forApp, parentDomain = "ecency.com", seoContext,
1593
1598
  traverse(doc, forApp, 0, { firstImageFound: false }, parentDomain, seoContext, renderOptions);
1594
1599
  output = serializer.serializeToString(doc);
1595
1600
  } catch (fallbackError) {
1596
- const escapedContent = he2.encode(output || md.render(input));
1597
- output = `<p dir="auto">${escapedContent}</p>`;
1601
+ output = sanitizeHtml(output || md.render(input));
1598
1602
  }
1599
1603
  }
1600
1604
  if (forApp && output && entityPlaceholders.length > 0) {
@@ -1603,7 +1607,7 @@ function markdownToHTML(input, forApp, parentDomain = "ecency.com", seoContext,
1603
1607
  output = output.split(placeholder).join(entity);
1604
1608
  });
1605
1609
  }
1606
- output = output.replace(/ xmlns="http:\/\/www.w3.org\/1999\/xhtml"/g, "").replace('<body id="root">', "").replace("</body>", "").trim();
1610
+ output = output.replace(/ xmlns="http:\/\/www.w3.org\/1999\/xhtml"/g, "").replace(/^<\?xml[^?]*\?>/, "").replace(/^<!DOCTYPE[^>]*>/i, "").replace(/<\/?html[^>]*>/g, "").replace(/<head[^>]*>[\s\S]*?<\/head>/g, "").replace('<body id="root">', "").replace("</body>", "").trim();
1607
1611
  return sanitizeHtml(output);
1608
1612
  }
1609
1613
  var mdInstance = null;
@@ -1622,7 +1626,7 @@ function simpleMarkdownToHTML(input) {
1622
1626
  const html = getMd().render(input);
1623
1627
  return sanitizeHtml(html);
1624
1628
  }
1625
- var cache = new LRUCache({ max: 60 });
1629
+ var cache = new LRUCache({ max: 500 });
1626
1630
  function setCacheSize(size) {
1627
1631
  cache = new LRUCache({ max: size });
1628
1632
  }
@@ -1653,6 +1657,40 @@ var gifLinkRegex = /\.(gif)$/i;
1653
1657
  function isGifLink(link) {
1654
1658
  return gifLinkRegex.test(link);
1655
1659
  }
1660
+ var BACKTICK_FENCE_RE = /```[\s\S]*?```/g;
1661
+ var TILDE_FENCE_RE = /~~~[\s\S]*?~~~/g;
1662
+ var INLINE_CODE_RE = /`[^`\n]*`/g;
1663
+ var INDENTED_CODE_RE = /^(?: {4}|\t).+$/gm;
1664
+ var MD_IMAGE_RE = /!\[[^\]]*\]\(\s*([^)\s]+)(?:\s+["'][^"']*["'])?\s*\)/;
1665
+ var HTML_IMAGE_RE = /<img\b[^>]*?\bsrc\s*=\s*["']([^"']+)["']/i;
1666
+ var SAFE_URL_RE = /^https?:\/\//i;
1667
+ function findFirstImageUrl(body) {
1668
+ if (!body) return null;
1669
+ const cleaned = body.replace(BACKTICK_FENCE_RE, "").replace(TILDE_FENCE_RE, "").replace(INLINE_CODE_RE, "").replace(INDENTED_CODE_RE, "");
1670
+ const mdMatch = cleaned.match(MD_IMAGE_RE);
1671
+ const htmlMatch = cleaned.match(HTML_IMAGE_RE);
1672
+ if (mdMatch) {
1673
+ const url = mdMatch[1];
1674
+ if (!url || !SAFE_URL_RE.test(url) || url.includes("(")) {
1675
+ return null;
1676
+ }
1677
+ }
1678
+ const mdValid = !!mdMatch;
1679
+ const htmlValid = !!(htmlMatch && htmlMatch[1] && SAFE_URL_RE.test(htmlMatch[1]));
1680
+ if (mdValid && htmlValid) {
1681
+ return (mdMatch.index ?? 0) < (htmlMatch.index ?? 0) ? mdMatch[1] : htmlMatch[1];
1682
+ }
1683
+ if (mdValid) return mdMatch[1];
1684
+ if (htmlValid) return htmlMatch[1];
1685
+ return null;
1686
+ }
1687
+ function proxifyFound(src, width, height, format) {
1688
+ const decoded = he.decode(src);
1689
+ if (isGifLink(decoded)) {
1690
+ return proxifyImageSrc(decoded, 0, 0, format);
1691
+ }
1692
+ return proxifyImageSrc(decoded, width, height, format);
1693
+ }
1656
1694
  function getImage(entry, width = 0, height = 0, format = "match") {
1657
1695
  let meta;
1658
1696
  if (typeof entry.json_metadata === "object") {
@@ -1665,7 +1703,7 @@ function getImage(entry, width = 0, height = 0, format = "match") {
1665
1703
  }
1666
1704
  }
1667
1705
  if (meta && typeof meta.image === "string" && meta.image.length > 0) {
1668
- const decodedImage = he2.decode(meta.image);
1706
+ const decodedImage = he.decode(meta.image);
1669
1707
  if (isGifLink(decodedImage)) {
1670
1708
  return proxifyImageSrc(decodedImage, 0, 0, format);
1671
1709
  }
@@ -1673,7 +1711,7 @@ function getImage(entry, width = 0, height = 0, format = "match") {
1673
1711
  }
1674
1712
  if (meta && meta.image && !!meta.image.length && meta.image[0]) {
1675
1713
  if (typeof meta.image[0] === "string") {
1676
- const decodedImage = he2.decode(meta.image[0]);
1714
+ const decodedImage = he.decode(meta.image[0]);
1677
1715
  if (isGifLink(decodedImage)) {
1678
1716
  return proxifyImageSrc(decodedImage, 0, 0, format);
1679
1717
  }
@@ -1684,6 +1722,10 @@ function getImage(entry, width = 0, height = 0, format = "match") {
1684
1722
  }
1685
1723
  return proxifyImageSrc(meta.image[0], width, height, format);
1686
1724
  }
1725
+ const fast = findFirstImageUrl(entry.body);
1726
+ if (fast) {
1727
+ return proxifyFound(fast, width, height, format);
1728
+ }
1687
1729
  const html = markdown2Html(entry);
1688
1730
  const doc = createDoc(html);
1689
1731
  if (!doc) {
@@ -1695,16 +1737,16 @@ function getImage(entry, width = 0, height = 0, format = "match") {
1695
1737
  if (!src) {
1696
1738
  return null;
1697
1739
  }
1698
- const decodedSrc = he2.decode(src);
1699
- if (isGifLink(decodedSrc)) {
1700
- return proxifyImageSrc(decodedSrc, 0, 0, format);
1701
- }
1702
- return proxifyImageSrc(decodedSrc, width, height, format);
1740
+ return proxifyFound(src, width, height, format);
1703
1741
  }
1704
1742
  return null;
1705
1743
  }
1706
1744
  function catchPostImage(obj, width = 0, height = 0, format = "match") {
1707
1745
  if (typeof obj === "string") {
1746
+ const fast = findFirstImageUrl(obj);
1747
+ if (fast) {
1748
+ return proxifyFound(fast, width, height, format);
1749
+ }
1708
1750
  const html = markdown2Html(obj);
1709
1751
  const doc = createDoc(html);
1710
1752
  if (!doc) {
@@ -1716,11 +1758,7 @@ function catchPostImage(obj, width = 0, height = 0, format = "match") {
1716
1758
  if (!src) {
1717
1759
  return null;
1718
1760
  }
1719
- const decodedSrc = he2.decode(src);
1720
- if (isGifLink(decodedSrc)) {
1721
- return proxifyImageSrc(decodedSrc, 0, 0, format);
1722
- }
1723
- return proxifyImageSrc(decodedSrc, width, height, format);
1761
+ return proxifyFound(src, width, height, format);
1724
1762
  }
1725
1763
  return null;
1726
1764
  }
@@ -1733,6 +1771,20 @@ function catchPostImage(obj, width = 0, height = 0, format = "match") {
1733
1771
  cacheSet(key, res);
1734
1772
  return res;
1735
1773
  }
1774
+ var summaryRenderer = new Remarkable({
1775
+ html: true,
1776
+ breaks: true,
1777
+ typographer: false
1778
+ });
1779
+ summaryRenderer.core.ruler.enable(["abbr"]);
1780
+ summaryRenderer.block.ruler.enable(["footnote", "deflist"]);
1781
+ summaryRenderer.inline.ruler.enable([
1782
+ "footnote_inline",
1783
+ "ins",
1784
+ "mark",
1785
+ "sub",
1786
+ "sup"
1787
+ ]);
1736
1788
  var joint = (arr, limit = 200) => {
1737
1789
  let result = "";
1738
1790
  if (arr) {
@@ -1758,25 +1810,6 @@ function postBodySummary(entryBody, length = 200, platform = "web") {
1758
1810
  return "";
1759
1811
  }
1760
1812
  entryBody = cleanReply(entryBody);
1761
- const mdd = new Remarkable({
1762
- html: true,
1763
- breaks: true,
1764
- typographer: false
1765
- }).use(linkify$1);
1766
- mdd.core.ruler.enable([
1767
- "abbr"
1768
- ]);
1769
- mdd.block.ruler.enable([
1770
- "footnote",
1771
- "deflist"
1772
- ]);
1773
- mdd.inline.ruler.enable([
1774
- "footnote_inline",
1775
- "ins",
1776
- "mark",
1777
- "sub",
1778
- "sup"
1779
- ]);
1780
1813
  const entities = entryBody.match(ENTITY_REGEX);
1781
1814
  const entityPlaceholders = [];
1782
1815
  if (entities && platform !== "web") {
@@ -1789,7 +1822,7 @@ function postBodySummary(entryBody, length = 200, platform = "web") {
1789
1822
  }
1790
1823
  let text2 = "";
1791
1824
  try {
1792
- text2 = mdd.render(entryBody);
1825
+ text2 = summaryRenderer.render(entryBody);
1793
1826
  } catch (err) {
1794
1827
  console.error("[postBodySummary] Failed to render markdown:", {
1795
1828
  error: err instanceof Error ? err.message : String(err),
@@ -1809,7 +1842,7 @@ function postBodySummary(entryBody, length = 200, platform = "web") {
1809
1842
  text2 = joint(text2.split(" "), length);
1810
1843
  }
1811
1844
  if (text2) {
1812
- text2 = he2.decode(text2);
1845
+ text2 = he.decode(text2);
1813
1846
  }
1814
1847
  return text2;
1815
1848
  }