@ecency/render-helper 2.4.30 → 2.4.32

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,4 +1,4 @@
1
- import { DOMParser as DOMParser$1, XMLSerializer } from '@xmldom/xmldom';
1
+ import { DOMParser as DOMParser$1, XMLSerializer as XMLSerializer$1 } from '@xmldom/xmldom';
2
2
  import xss from 'xss';
3
3
  import multihash from 'multihashes';
4
4
  import querystring from 'querystring';
@@ -123,6 +123,8 @@ var ALLOWED_ATTRIBUTES = {
123
123
  ],
124
124
  "img": [
125
125
  "src",
126
+ "srcset",
127
+ "sizes",
126
128
  "alt",
127
129
  "class",
128
130
  "loading",
@@ -166,17 +168,16 @@ var ALLOWED_ATTRIBUTES = {
166
168
  "del": [],
167
169
  "ins": []
168
170
  };
169
- var lenientErrorHandler = (level, msg, context) => {
171
+ var hasDOMParser = typeof globalThis.DOMParser !== "undefined";
172
+ var hasXMLSerializer = typeof globalThis.XMLSerializer !== "undefined";
173
+ var lenientErrorHandler = (level, msg, _context) => {
170
174
  if (process.env.NODE_ENV === "development") {
171
175
  console.warn("[DOMParser]", level, msg);
172
176
  }
173
177
  return void 0;
174
178
  };
175
- var DOMParser = new DOMParser$1({
176
- // Use onError instead of deprecated errorHandler
177
- // By providing a non-throwing error handler, parsing continues despite malformed HTML
178
- onError: lenientErrorHandler
179
- });
179
+ var DOMParser = hasDOMParser ? new globalThis.DOMParser() : new DOMParser$1({ onError: lenientErrorHandler });
180
+ var XMLSerializer = hasXMLSerializer ? globalThis.XMLSerializer : XMLSerializer$1;
180
181
 
181
182
  // src/helper.ts
182
183
  function removeDuplicateAttributes(html) {
@@ -254,6 +255,8 @@ function isValidUsername(username) {
254
255
  !label.includes("..");
255
256
  });
256
257
  }
258
+
259
+ // src/methods/get-inner-html.method.ts
257
260
  function getSerializedInnerHTML(node) {
258
261
  const serializer = new XMLSerializer();
259
262
  if (node.childNodes[0]) {
@@ -281,6 +284,10 @@ function sanitizeHtml(html) {
281
284
  const decodedLower = decoded.toLowerCase();
282
285
  if (name.startsWith("on")) return "";
283
286
  if (tag === "img" && name === "src" && (!/^https?:\/\//.test(decodedLower) || decodedLower.startsWith("javascript:"))) return "";
287
+ if (tag === "img" && name === "srcset") {
288
+ const candidates = decoded.split(",").map((c) => c.trim().split(/\s+/)[0]);
289
+ if (candidates.some((url) => !/^https?:\/\//.test(url))) return "";
290
+ }
284
291
  if (tag === "video" && ["src", "poster"].includes(name) && (!/^https?:\/\//.test(decodedLower) || decodedLower.startsWith("javascript:"))) return "";
285
292
  if (tag === "img" && ["dynsrc", "lowsrc"].includes(name)) return "";
286
293
  if (tag === "span" && name === "class" && decoded.toLowerCase().trim() === "wr") return "";
@@ -295,6 +302,9 @@ var proxyBase = "https://images.ecency.com";
295
302
  function setProxyBase(p2) {
296
303
  proxyBase = p2;
297
304
  }
305
+ function getProxyBase() {
306
+ return proxyBase;
307
+ }
298
308
  function extractPHash(url) {
299
309
  if (url.startsWith(`${proxyBase}/p/`)) {
300
310
  const [hash] = url.split("/p/")[1].split("?");
@@ -342,8 +352,24 @@ function proxifyImageSrc(url, width = 0, height = 0, _format = "match") {
342
352
  const b58url = multihash.toB58String(Buffer.from(realUrl.toString()));
343
353
  return `${proxyBase}/p/${b58url}?${qs}`;
344
354
  }
355
+ var SRCSET_WIDTHS = [320, 600, 800, 1024, 1280];
356
+ function buildSrcSet(url) {
357
+ if (!url || typeof url !== "string") return "";
358
+ const escapedBase = proxyBase.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
359
+ const proxyPattern = new RegExp(`^${escapedBase}/p/([^?]+)`);
360
+ const match = url.match(proxyPattern);
361
+ if (match) {
362
+ const phash = extractPHash(url) || match[1];
363
+ return SRCSET_WIDTHS.map((w) => `${proxyBase}/p/${phash}?format=match&mode=fit&width=${w} ${w}w`).join(", ");
364
+ }
365
+ return SRCSET_WIDTHS.map((w) => {
366
+ const proxied = proxifyImageSrc(url, w);
367
+ return proxied ? `${proxied} ${w}w` : "";
368
+ }).filter(Boolean).join(", ");
369
+ }
345
370
 
346
371
  // src/methods/img.method.ts
372
+ var IMAGE_SIZES = "(max-width: 768px) 100vw, 700px";
347
373
  function img(el, state) {
348
374
  const src = el.getAttribute("src") || "";
349
375
  const decodedSrc = decodeURIComponent(
@@ -353,11 +379,15 @@ function img(el, state) {
353
379
  const isInvalid = !src || decodedSrc.startsWith("javascript") || decodedSrc.startsWith("vbscript") || decodedSrc === "x";
354
380
  if (isInvalid) {
355
381
  el.removeAttribute("src");
382
+ el.removeAttribute("srcset");
383
+ el.removeAttribute("sizes");
356
384
  return;
357
385
  }
358
386
  const isRelative = !/^https?:\/\//i.test(decodedSrc) && !decodedSrc.startsWith("/");
359
387
  if (isRelative) {
360
388
  el.removeAttribute("src");
389
+ el.removeAttribute("srcset");
390
+ el.removeAttribute("sizes");
361
391
  return;
362
392
  }
363
393
  el.setAttribute("itemprop", "image");
@@ -372,22 +402,41 @@ function img(el, state) {
372
402
  }
373
403
  const cls = el.getAttribute("class") || "";
374
404
  const shouldReplace = !cls.includes("no-replace");
375
- const hasAlreadyProxied = src.startsWith("https://images.ecency.com/p/") || src.startsWith("https://images.ecency.com/u/") || /^https:\/\/images\.ecency\.com\/\d+x\d+\//.test(src);
405
+ const base = getProxyBase().replace(/\/+$/, "");
406
+ const hasAlreadyProxied = src.startsWith(`${base}/p/`) || src.startsWith(`${base}/u/`) || new RegExp(`^${base.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}/\\d+x\\d+/`).test(src);
376
407
  if (shouldReplace && !hasAlreadyProxied) {
377
408
  const proxified = proxifyImageSrc(decodedSrc);
378
409
  if (proxified) {
379
410
  el.setAttribute("src", proxified);
411
+ const srcset = buildSrcSet(decodedSrc);
412
+ if (srcset) {
413
+ el.setAttribute("srcset", srcset);
414
+ el.setAttribute("sizes", IMAGE_SIZES);
415
+ }
416
+ }
417
+ } else if (shouldReplace && hasAlreadyProxied) {
418
+ if (src.startsWith(`${base}/p/`)) {
419
+ const srcset = buildSrcSet(src);
420
+ if (srcset) {
421
+ el.setAttribute("srcset", srcset);
422
+ el.setAttribute("sizes", IMAGE_SIZES);
423
+ }
380
424
  }
381
425
  }
382
426
  }
383
427
  function createImageHTML(src, isLCP) {
384
428
  const proxified = proxifyImageSrc(src);
385
429
  if (!proxified) return "";
430
+ const base = getProxyBase().replace(/\/+$/, "");
431
+ const isAlreadyProxied = src.startsWith(`${base}/u/`) || new RegExp(`^${base.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}/\\d+x\\d+/`).test(src);
432
+ const srcset = isAlreadyProxied ? "" : buildSrcSet(src);
386
433
  const loading = isLCP ? "eager" : "lazy";
387
434
  const fetch = isLCP ? 'fetchpriority="high"' : 'decoding="async"';
435
+ const srcsetAttr = srcset ? `srcset="${srcset}" sizes="${IMAGE_SIZES}"` : "";
388
436
  return `<img
389
437
  class="markdown-img-link"
390
438
  src="${proxified}"
439
+ ${srcsetAttr}
391
440
  loading="${loading}"
392
441
  ${fetch}
393
442
  itemprop="image"
@@ -1780,6 +1829,6 @@ function getPostBodySummary(obj, length, platform) {
1780
1829
  return res;
1781
1830
  }
1782
1831
 
1783
- export { SECTION_LIST, catchPostImage, isValidPermlink, getPostBodySummary as postBodySummary, proxifyImageSrc, markdown2Html as renderPostBody, setCacheSize, setProxyBase, simpleMarkdownToHTML };
1832
+ export { SECTION_LIST, buildSrcSet, catchPostImage, isValidPermlink, getPostBodySummary as postBodySummary, proxifyImageSrc, markdown2Html as renderPostBody, setCacheSize, setProxyBase, simpleMarkdownToHTML };
1784
1833
  //# sourceMappingURL=index.mjs.map
1785
1834
  //# sourceMappingURL=index.mjs.map