@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.
@@ -54,6 +54,11 @@ declare function setProxyBase(p: string): void;
54
54
  * @param _format - @deprecated Ignored. Always uses 'match' — format is handled server-side via Accept header.
55
55
  */
56
56
  declare function proxifyImageSrc(url?: string, width?: number, height?: number, _format?: string): string;
57
+ /**
58
+ * Builds a srcset string with multiple width variants for responsive images.
59
+ * Uses the image proxy's width parameter to serve appropriately sized images.
60
+ */
61
+ declare function buildSrcSet(url?: string): string;
57
62
 
58
63
  declare function setCacheSize(size: number): void;
59
64
 
@@ -71,4 +76,4 @@ declare function isValidPermlink(permlink: string): boolean;
71
76
  */
72
77
  declare function simpleMarkdownToHTML(input: string): string;
73
78
 
74
- export { type Entry, type RenderOptions, SECTION_LIST, type SeoContext, catchPostImage, isValidPermlink, getPostBodySummary as postBodySummary, proxifyImageSrc, markdown2Html as renderPostBody, setCacheSize, setProxyBase, simpleMarkdownToHTML };
79
+ export { type Entry, type RenderOptions, SECTION_LIST, type SeoContext, buildSrcSet, catchPostImage, isValidPermlink, getPostBodySummary as postBodySummary, proxifyImageSrc, markdown2Html as renderPostBody, setCacheSize, setProxyBase, simpleMarkdownToHTML };
@@ -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,14 +168,13 @@ 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
  return void 0;
171
175
  };
172
- var DOMParser = new DOMParser$1({
173
- // Use onError instead of deprecated errorHandler
174
- // By providing a non-throwing error handler, parsing continues despite malformed HTML
175
- onError: lenientErrorHandler
176
- });
176
+ var DOMParser = hasDOMParser ? new globalThis.DOMParser() : new DOMParser$1({ onError: lenientErrorHandler });
177
+ var XMLSerializer = hasXMLSerializer ? globalThis.XMLSerializer : XMLSerializer$1;
177
178
 
178
179
  // src/helper.ts
179
180
  function removeDuplicateAttributes(html) {
@@ -251,6 +252,8 @@ function isValidUsername(username) {
251
252
  !label.includes("..");
252
253
  });
253
254
  }
255
+
256
+ // src/methods/get-inner-html.method.ts
254
257
  function getSerializedInnerHTML(node) {
255
258
  const serializer = new XMLSerializer();
256
259
  if (node.childNodes[0]) {
@@ -278,6 +281,10 @@ function sanitizeHtml(html) {
278
281
  const decodedLower = decoded.toLowerCase();
279
282
  if (name.startsWith("on")) return "";
280
283
  if (tag === "img" && name === "src" && (!/^https?:\/\//.test(decodedLower) || decodedLower.startsWith("javascript:"))) return "";
284
+ if (tag === "img" && name === "srcset") {
285
+ const candidates = decoded.split(",").map((c) => c.trim().split(/\s+/)[0]);
286
+ if (candidates.some((url) => !/^https?:\/\//.test(url))) return "";
287
+ }
281
288
  if (tag === "video" && ["src", "poster"].includes(name) && (!/^https?:\/\//.test(decodedLower) || decodedLower.startsWith("javascript:"))) return "";
282
289
  if (tag === "img" && ["dynsrc", "lowsrc"].includes(name)) return "";
283
290
  if (tag === "span" && name === "class" && decoded.toLowerCase().trim() === "wr") return "";
@@ -292,6 +299,9 @@ var proxyBase = "https://images.ecency.com";
292
299
  function setProxyBase(p2) {
293
300
  proxyBase = p2;
294
301
  }
302
+ function getProxyBase() {
303
+ return proxyBase;
304
+ }
295
305
  function extractPHash(url) {
296
306
  if (url.startsWith(`${proxyBase}/p/`)) {
297
307
  const [hash] = url.split("/p/")[1].split("?");
@@ -339,8 +349,24 @@ function proxifyImageSrc(url, width = 0, height = 0, _format = "match") {
339
349
  const b58url = multihash.toB58String(Buffer.from(realUrl.toString()));
340
350
  return `${proxyBase}/p/${b58url}?${qs}`;
341
351
  }
352
+ var SRCSET_WIDTHS = [320, 600, 800, 1024, 1280];
353
+ function buildSrcSet(url) {
354
+ if (!url || typeof url !== "string") return "";
355
+ const escapedBase = proxyBase.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
356
+ const proxyPattern = new RegExp(`^${escapedBase}/p/([^?]+)`);
357
+ const match = url.match(proxyPattern);
358
+ if (match) {
359
+ const phash = extractPHash(url) || match[1];
360
+ return SRCSET_WIDTHS.map((w) => `${proxyBase}/p/${phash}?format=match&mode=fit&width=${w} ${w}w`).join(", ");
361
+ }
362
+ return SRCSET_WIDTHS.map((w) => {
363
+ const proxied = proxifyImageSrc(url, w);
364
+ return proxied ? `${proxied} ${w}w` : "";
365
+ }).filter(Boolean).join(", ");
366
+ }
342
367
 
343
368
  // src/methods/img.method.ts
369
+ var IMAGE_SIZES = "(max-width: 768px) 100vw, 700px";
344
370
  function img(el, state) {
345
371
  const src = el.getAttribute("src") || "";
346
372
  const decodedSrc = decodeURIComponent(
@@ -350,11 +376,15 @@ function img(el, state) {
350
376
  const isInvalid = !src || decodedSrc.startsWith("javascript") || decodedSrc.startsWith("vbscript") || decodedSrc === "x";
351
377
  if (isInvalid) {
352
378
  el.removeAttribute("src");
379
+ el.removeAttribute("srcset");
380
+ el.removeAttribute("sizes");
353
381
  return;
354
382
  }
355
383
  const isRelative = !/^https?:\/\//i.test(decodedSrc) && !decodedSrc.startsWith("/");
356
384
  if (isRelative) {
357
385
  el.removeAttribute("src");
386
+ el.removeAttribute("srcset");
387
+ el.removeAttribute("sizes");
358
388
  return;
359
389
  }
360
390
  el.setAttribute("itemprop", "image");
@@ -369,22 +399,41 @@ function img(el, state) {
369
399
  }
370
400
  const cls = el.getAttribute("class") || "";
371
401
  const shouldReplace = !cls.includes("no-replace");
372
- 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);
402
+ const base = getProxyBase().replace(/\/+$/, "");
403
+ const hasAlreadyProxied = src.startsWith(`${base}/p/`) || src.startsWith(`${base}/u/`) || new RegExp(`^${base.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}/\\d+x\\d+/`).test(src);
373
404
  if (shouldReplace && !hasAlreadyProxied) {
374
405
  const proxified = proxifyImageSrc(decodedSrc);
375
406
  if (proxified) {
376
407
  el.setAttribute("src", proxified);
408
+ const srcset = buildSrcSet(decodedSrc);
409
+ if (srcset) {
410
+ el.setAttribute("srcset", srcset);
411
+ el.setAttribute("sizes", IMAGE_SIZES);
412
+ }
413
+ }
414
+ } else if (shouldReplace && hasAlreadyProxied) {
415
+ if (src.startsWith(`${base}/p/`)) {
416
+ const srcset = buildSrcSet(src);
417
+ if (srcset) {
418
+ el.setAttribute("srcset", srcset);
419
+ el.setAttribute("sizes", IMAGE_SIZES);
420
+ }
377
421
  }
378
422
  }
379
423
  }
380
424
  function createImageHTML(src, isLCP) {
381
425
  const proxified = proxifyImageSrc(src);
382
426
  if (!proxified) return "";
427
+ const base = getProxyBase().replace(/\/+$/, "");
428
+ const isAlreadyProxied = src.startsWith(`${base}/u/`) || new RegExp(`^${base.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}/\\d+x\\d+/`).test(src);
429
+ const srcset = isAlreadyProxied ? "" : buildSrcSet(src);
383
430
  const loading = isLCP ? "eager" : "lazy";
384
431
  const fetch = isLCP ? 'fetchpriority="high"' : 'decoding="async"';
432
+ const srcsetAttr = srcset ? `srcset="${srcset}" sizes="${IMAGE_SIZES}"` : "";
385
433
  return `<img
386
434
  class="markdown-img-link"
387
435
  src="${proxified}"
436
+ ${srcsetAttr}
388
437
  loading="${loading}"
389
438
  ${fetch}
390
439
  itemprop="image"
@@ -1777,6 +1826,6 @@ function getPostBodySummary(obj, length, platform) {
1777
1826
  return res;
1778
1827
  }
1779
1828
 
1780
- export { SECTION_LIST, catchPostImage, isValidPermlink, getPostBodySummary as postBodySummary, proxifyImageSrc, markdown2Html as renderPostBody, setCacheSize, setProxyBase, simpleMarkdownToHTML };
1829
+ export { SECTION_LIST, buildSrcSet, catchPostImage, isValidPermlink, getPostBodySummary as postBodySummary, proxifyImageSrc, markdown2Html as renderPostBody, setCacheSize, setProxyBase, simpleMarkdownToHTML };
1781
1830
  //# sourceMappingURL=index.js.map
1782
1831
  //# sourceMappingURL=index.js.map