@ecency/render-helper 2.4.14 → 2.4.15
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.d.ts +15 -2
- package/dist/browser/index.js +20 -12
- package/dist/browser/index.js.map +1 -1
- package/dist/node/index.cjs +20 -12
- package/dist/node/index.cjs.map +1 -1
- package/dist/node/index.mjs +20 -12
- package/dist/node/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/browser/index.d.ts
CHANGED
|
@@ -9,7 +9,20 @@ interface Entry {
|
|
|
9
9
|
json_metadata?: any;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
/**
|
|
13
|
+
* SEO context for controlling rel attributes on external links in user-generated content.
|
|
14
|
+
*
|
|
15
|
+
* By default, all external links get rel="nofollow ugc noopener" to prevent link spam.
|
|
16
|
+
* High-quality content (high author reputation + meaningful post rewards) earns followed links.
|
|
17
|
+
*/
|
|
18
|
+
interface SeoContext {
|
|
19
|
+
/** Human-readable author reputation score (after accountReputation() conversion) */
|
|
20
|
+
authorReputation?: number;
|
|
21
|
+
/** Total post payout in USD */
|
|
22
|
+
postPayout?: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
declare function markdown2Html(obj: Entry | string, forApp?: boolean, webp?: boolean, parentDomain?: string, seoContext?: SeoContext): string;
|
|
13
26
|
|
|
14
27
|
declare function catchPostImage(obj: Entry | string, width?: number, height?: number, format?: string): string | null;
|
|
15
28
|
|
|
@@ -32,4 +45,4 @@ declare const SECTION_LIST: string[];
|
|
|
32
45
|
|
|
33
46
|
declare function isValidPermlink(permlink: string): boolean;
|
|
34
47
|
|
|
35
|
-
export { type Entry, SECTION_LIST, catchPostImage, isValidPermlink, getPostBodySummary as postBodySummary, proxifyImageSrc, markdown2Html as renderPostBody, setCacheSize, setProxyBase };
|
|
48
|
+
export { type Entry, SECTION_LIST, type SeoContext, catchPostImage, isValidPermlink, getPostBodySummary as postBodySummary, proxifyImageSrc, markdown2Html as renderPostBody, setCacheSize, setProxyBase };
|
package/dist/browser/index.js
CHANGED
|
@@ -388,6 +388,14 @@ function createImageHTML(src, isLCP, webp) {
|
|
|
388
388
|
}
|
|
389
389
|
|
|
390
390
|
// src/methods/a.method.ts
|
|
391
|
+
var NOFOLLOW_REPUTATION_THRESHOLD = 40;
|
|
392
|
+
var FOLLOW_PAYOUT_THRESHOLD = 5;
|
|
393
|
+
function getExternalLinkRel(seoContext) {
|
|
394
|
+
if (seoContext?.authorReputation !== void 0 && seoContext?.postPayout !== void 0 && seoContext.authorReputation >= NOFOLLOW_REPUTATION_THRESHOLD && seoContext.postPayout > FOLLOW_PAYOUT_THRESHOLD) {
|
|
395
|
+
return "noopener";
|
|
396
|
+
}
|
|
397
|
+
return "nofollow ugc noopener";
|
|
398
|
+
}
|
|
391
399
|
var normalizeValue = (value) => value ? value.trim() : "";
|
|
392
400
|
var matchesHref = (href, value) => {
|
|
393
401
|
const normalizedHref = normalizeValue(href);
|
|
@@ -421,7 +429,7 @@ var addLineBreakBeforePostLink = (el, forApp, isInline) => {
|
|
|
421
429
|
el.parentNode.insertBefore(br, el);
|
|
422
430
|
}
|
|
423
431
|
};
|
|
424
|
-
function a(el, forApp, webp, parentDomain = "ecency.com") {
|
|
432
|
+
function a(el, forApp, webp, parentDomain = "ecency.com", seoContext) {
|
|
425
433
|
if (!el || !el.parentNode) {
|
|
426
434
|
return;
|
|
427
435
|
}
|
|
@@ -981,7 +989,7 @@ function a(el, forApp, webp, parentDomain = "ecency.com") {
|
|
|
981
989
|
el.setAttribute("class", "markdown-internal-link");
|
|
982
990
|
} else {
|
|
983
991
|
el.setAttribute("target", "_blank");
|
|
984
|
-
el.setAttribute("rel",
|
|
992
|
+
el.setAttribute("rel", getExternalLinkRel(seoContext));
|
|
985
993
|
}
|
|
986
994
|
el.setAttribute("href", href);
|
|
987
995
|
}
|
|
@@ -1270,7 +1278,7 @@ function text(node, forApp, webp) {
|
|
|
1270
1278
|
}
|
|
1271
1279
|
|
|
1272
1280
|
// src/methods/traverse.method.ts
|
|
1273
|
-
function traverse(node, forApp, depth = 0, webp = false, state = { firstImageFound: false }, parentDomain = "ecency.com") {
|
|
1281
|
+
function traverse(node, forApp, depth = 0, webp = false, state = { firstImageFound: false }, parentDomain = "ecency.com", seoContext) {
|
|
1274
1282
|
if (!node || !node.childNodes) {
|
|
1275
1283
|
return;
|
|
1276
1284
|
}
|
|
@@ -1278,7 +1286,7 @@ function traverse(node, forApp, depth = 0, webp = false, state = { firstImageFou
|
|
|
1278
1286
|
const child = node.childNodes[i];
|
|
1279
1287
|
if (!child) return;
|
|
1280
1288
|
if (child.nodeName.toLowerCase() === "a") {
|
|
1281
|
-
a(child, forApp, webp, parentDomain);
|
|
1289
|
+
a(child, forApp, webp, parentDomain, seoContext);
|
|
1282
1290
|
}
|
|
1283
1291
|
if (child.nodeName.toLowerCase() === "iframe") {
|
|
1284
1292
|
iframe(child, parentDomain);
|
|
@@ -1294,7 +1302,7 @@ function traverse(node, forApp, depth = 0, webp = false, state = { firstImageFou
|
|
|
1294
1302
|
}
|
|
1295
1303
|
const currentChild = node.childNodes[i];
|
|
1296
1304
|
if (currentChild) {
|
|
1297
|
-
traverse(currentChild, forApp, depth + 1, webp, state, parentDomain);
|
|
1305
|
+
traverse(currentChild, forApp, depth + 1, webp, state, parentDomain, seoContext);
|
|
1298
1306
|
}
|
|
1299
1307
|
});
|
|
1300
1308
|
}
|
|
@@ -1345,7 +1353,7 @@ function fixBlockLevelTagsInParagraphs(html) {
|
|
|
1345
1353
|
html = html.replace(/<p><br>\s*<\/p>/g, "");
|
|
1346
1354
|
return html;
|
|
1347
1355
|
}
|
|
1348
|
-
function markdownToHTML(input, forApp, webp, parentDomain = "ecency.com") {
|
|
1356
|
+
function markdownToHTML(input, forApp, webp, parentDomain = "ecency.com", seoContext) {
|
|
1349
1357
|
input = input.replace(new RegExp("https://leofinance.io/threads/view/", "g"), "/@");
|
|
1350
1358
|
input = input.replace(new RegExp("https://leofinance.io/posts/", "g"), "/@");
|
|
1351
1359
|
input = input.replace(new RegExp("https://leofinance.io/threads/", "g"), "/@");
|
|
@@ -1405,7 +1413,7 @@ function markdownToHTML(input, forApp, webp, parentDomain = "ecency.com") {
|
|
|
1405
1413
|
output = md.render(input);
|
|
1406
1414
|
output = fixBlockLevelTagsInParagraphs(output);
|
|
1407
1415
|
const doc = DOMParser.parseFromString(`<body id="root">${removeDuplicateAttributes(output)}</body>`, "text/html");
|
|
1408
|
-
traverse(doc, forApp, 0, webp, { firstImageFound: false }, parentDomain);
|
|
1416
|
+
traverse(doc, forApp, 0, webp, { firstImageFound: false }, parentDomain, seoContext);
|
|
1409
1417
|
output = serializer.serializeToString(doc);
|
|
1410
1418
|
} catch (error) {
|
|
1411
1419
|
try {
|
|
@@ -1418,7 +1426,7 @@ function markdownToHTML(input, forApp, webp, parentDomain = "ecency.com") {
|
|
|
1418
1426
|
});
|
|
1419
1427
|
const repairedHtml = domSerializer(dom.children);
|
|
1420
1428
|
const doc = DOMParser.parseFromString(`<body id="root">${removeDuplicateAttributes(repairedHtml)}</body>`, "text/html");
|
|
1421
|
-
traverse(doc, forApp, 0, webp, { firstImageFound: false }, parentDomain);
|
|
1429
|
+
traverse(doc, forApp, 0, webp, { firstImageFound: false }, parentDomain, seoContext);
|
|
1422
1430
|
output = serializer.serializeToString(doc);
|
|
1423
1431
|
} catch (fallbackError) {
|
|
1424
1432
|
const escapedContent = he2.encode(output || md.render(input));
|
|
@@ -1446,18 +1454,18 @@ function cacheSet(key, value) {
|
|
|
1446
1454
|
}
|
|
1447
1455
|
|
|
1448
1456
|
// src/markdown-2-html.ts
|
|
1449
|
-
function markdown2Html(obj, forApp = true, webp = false, parentDomain = "ecency.com") {
|
|
1457
|
+
function markdown2Html(obj, forApp = true, webp = false, parentDomain = "ecency.com", seoContext) {
|
|
1450
1458
|
if (typeof obj === "string") {
|
|
1451
1459
|
const cleanedStr = cleanReply(obj);
|
|
1452
|
-
return markdownToHTML(cleanedStr, forApp, webp, parentDomain);
|
|
1460
|
+
return markdownToHTML(cleanedStr, forApp, webp, parentDomain, seoContext);
|
|
1453
1461
|
}
|
|
1454
|
-
const key = `${makeEntryCacheKey(obj)}-md${webp ? "-webp" : ""}-${forApp ? "app" : "site"}-${parentDomain}`;
|
|
1462
|
+
const key = `${makeEntryCacheKey(obj)}-md${webp ? "-webp" : ""}-${forApp ? "app" : "site"}-${parentDomain}${seoContext ? `-seo${seoContext.authorReputation ?? ""}-${seoContext.postPayout ?? ""}` : ""}`;
|
|
1455
1463
|
const item = cacheGet(key);
|
|
1456
1464
|
if (item) {
|
|
1457
1465
|
return item;
|
|
1458
1466
|
}
|
|
1459
1467
|
const cleanBody = cleanReply(obj.body);
|
|
1460
|
-
const res = markdownToHTML(cleanBody, forApp, webp, parentDomain);
|
|
1468
|
+
const res = markdownToHTML(cleanBody, forApp, webp, parentDomain, seoContext);
|
|
1461
1469
|
cacheSet(key, res);
|
|
1462
1470
|
return res;
|
|
1463
1471
|
}
|