@automattic/social-previews 3.2.3 → 3.2.5
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/CHANGELOG.md +16 -0
- package/SECURITY.md +0 -1
- package/dist/index.d.mts +29 -1
- package/dist/index.d.ts +29 -1
- package/dist/index.js +111 -42
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +86 -17
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
package/dist/index.mjs
CHANGED
|
@@ -50,6 +50,43 @@ var formatMastodonDate = new Intl.DateTimeFormat("en-US", {
|
|
|
50
50
|
day: "numeric",
|
|
51
51
|
year: "numeric"
|
|
52
52
|
}).format;
|
|
53
|
+
var collapseWhitespace = (text) => text.replace(/\s+/g, " ").trim();
|
|
54
|
+
var countOccurrences = (haystack, needle) => {
|
|
55
|
+
let count = 0;
|
|
56
|
+
for (let pos = haystack.indexOf(needle); pos !== -1; pos = haystack.indexOf(needle, pos + 1)) {
|
|
57
|
+
count++;
|
|
58
|
+
}
|
|
59
|
+
return count;
|
|
60
|
+
};
|
|
61
|
+
var nthIndexOf = (haystack, needle, n) => {
|
|
62
|
+
let pos = haystack.indexOf(needle);
|
|
63
|
+
while (pos !== -1 && n > 0) {
|
|
64
|
+
n--;
|
|
65
|
+
pos = haystack.indexOf(needle, pos + 1);
|
|
66
|
+
}
|
|
67
|
+
return pos;
|
|
68
|
+
};
|
|
69
|
+
function parseHyperlinks(html) {
|
|
70
|
+
if (!html) {
|
|
71
|
+
return [];
|
|
72
|
+
}
|
|
73
|
+
const doc = document.implementation.createHTMLDocument("");
|
|
74
|
+
doc.body.innerHTML = html;
|
|
75
|
+
const links = [];
|
|
76
|
+
for (const anchor of Array.from(doc.body.querySelectorAll("a[href]"))) {
|
|
77
|
+
const href = anchor.getAttribute("href") ?? "";
|
|
78
|
+
const text = collapseWhitespace(anchor.textContent ?? "");
|
|
79
|
+
if (!/^https?:\/\//i.test(href) || "" === text || text === href) {
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
const range = doc.createRange();
|
|
83
|
+
range.selectNodeContents(doc.body);
|
|
84
|
+
range.setEndBefore(anchor);
|
|
85
|
+
const occurrence = countOccurrences(collapseWhitespace(range.toString()), text);
|
|
86
|
+
links.push({ text, href, occurrence });
|
|
87
|
+
}
|
|
88
|
+
return links;
|
|
89
|
+
}
|
|
53
90
|
var hashtagUrlMap = {
|
|
54
91
|
twitter: "https://twitter.com/hashtag/%1$s",
|
|
55
92
|
facebook: "https://www.facebook.com/hashtag/%1$s",
|
|
@@ -68,7 +105,8 @@ function preparePreviewText(text, options) {
|
|
|
68
105
|
maxLines,
|
|
69
106
|
hyperlinkHashtags = true,
|
|
70
107
|
// Instagram doesn't support hyperlink URLs at the moment.
|
|
71
|
-
hyperlinkUrls = "instagram" !== platform
|
|
108
|
+
hyperlinkUrls = "instagram" !== platform,
|
|
109
|
+
hyperlinks
|
|
72
110
|
} = options;
|
|
73
111
|
let result = stripHtmlTags(text);
|
|
74
112
|
result = result.replaceAll(/(?:\s*[\n\r]){2,}/g, "\n\n");
|
|
@@ -98,6 +136,31 @@ function preparePreviewText(text, options) {
|
|
|
98
136
|
result = result.replace(fullMatch, `${whitespace}<Hashtag${index} />`);
|
|
99
137
|
});
|
|
100
138
|
}
|
|
139
|
+
if (hyperlinks?.length) {
|
|
140
|
+
const matches = [];
|
|
141
|
+
hyperlinks.forEach(({ text: anchorText, href, occurrence = 0 }, index) => {
|
|
142
|
+
if (!anchorText) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
const pos = nthIndexOf(result, anchorText, occurrence);
|
|
146
|
+
if (pos === -1) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
const overlaps = matches.some(
|
|
150
|
+
(match) => pos < match.pos + match.text.length && match.pos < pos + anchorText.length
|
|
151
|
+
);
|
|
152
|
+
if (!overlaps) {
|
|
153
|
+
matches.push({ pos, text: anchorText, href, index });
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
matches.sort((a, b) => b.pos - a.pos);
|
|
157
|
+
for (const { pos, text: anchorText, href, index } of matches) {
|
|
158
|
+
const token = `Hyperlink${index}`;
|
|
159
|
+
componentMap[token] = /* @__PURE__ */ jsx("a", { href, rel: "noopener noreferrer", target: "_blank" });
|
|
160
|
+
const wrapped = `<${token}>${anchorText}</${token}>`;
|
|
161
|
+
result = result.slice(0, pos) + wrapped + result.slice(pos + anchorText.length);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
101
164
|
result = result.replace(/\n/g, "<br />");
|
|
102
165
|
componentMap.br = /* @__PURE__ */ jsx("br", {});
|
|
103
166
|
return createInterpolateElement(result, componentMap);
|
|
@@ -836,7 +899,8 @@ var TumblrPostPreview = ({
|
|
|
836
899
|
image,
|
|
837
900
|
user,
|
|
838
901
|
url,
|
|
839
|
-
media
|
|
902
|
+
media,
|
|
903
|
+
hyperlinks
|
|
840
904
|
}) => {
|
|
841
905
|
const avatarUrl = user?.avatarUrl;
|
|
842
906
|
const mediaItem = media?.[0];
|
|
@@ -847,7 +911,8 @@ var TumblrPostPreview = ({
|
|
|
847
911
|
/* @__PURE__ */ jsxs16("div", { className: "tumblr-preview__body", children: [
|
|
848
912
|
title ? /* @__PURE__ */ jsx25("div", { className: "tumblr-preview__title", children: tumblrTitle(title) }) : null,
|
|
849
913
|
description && /* @__PURE__ */ jsx25("div", { className: "tumblr-preview__description", children: /* @__PURE__ */ jsx25(ExpandableText, { text: description, children: (visibleText) => preparePreviewText(tumblrDescription(visibleText), {
|
|
850
|
-
platform: "tumblr"
|
|
914
|
+
platform: "tumblr",
|
|
915
|
+
hyperlinks
|
|
851
916
|
}) }) }),
|
|
852
917
|
mediaItem ? /* @__PURE__ */ jsx25("div", { className: "tumblr-preview__media-item", children: mediaItem.type.startsWith("video/") ? /* @__PURE__ */ jsx25("video", { controls: true, className: "tumblr-preview__media--video", children: /* @__PURE__ */ jsx25("source", { src: mediaItem.url, type: mediaItem.type }) }) : /* @__PURE__ */ jsx25("img", { className: "tumblr-preview__image", src: mediaItem.url, alt: "" }) }) : image && /* @__PURE__ */ jsx25(
|
|
853
918
|
"img",
|
|
@@ -1275,8 +1340,6 @@ var DEFAULT_MASTODON_INSTANCE = "mastodon.social";
|
|
|
1275
1340
|
// src/mastodon-preview/helpers.ts
|
|
1276
1341
|
var TITLE_LENGTH4 = 200;
|
|
1277
1342
|
var BODY_LENGTH = 500;
|
|
1278
|
-
var URL_LENGTH2 = 30;
|
|
1279
|
-
var BODY_CHAR_LIMIT = BODY_LENGTH - URL_LENGTH2;
|
|
1280
1343
|
var ADDRESS_PATTERN = /^@([^@]*)@([^@]*)$/i;
|
|
1281
1344
|
var mastodonTitle = (text) => firstValid(
|
|
1282
1345
|
shortEnough(TITLE_LENGTH4),
|
|
@@ -1286,11 +1349,10 @@ var mastodonBody = (text, options) => {
|
|
|
1286
1349
|
const { instance, offset } = options;
|
|
1287
1350
|
return preparePreviewText(text, {
|
|
1288
1351
|
platform: "mastodon",
|
|
1289
|
-
maxChars: BODY_LENGTH -
|
|
1352
|
+
maxChars: BODY_LENGTH - offset,
|
|
1290
1353
|
hashtagDomain: instance
|
|
1291
1354
|
});
|
|
1292
1355
|
};
|
|
1293
|
-
var mastodonUrl = (text) => firstValid(shortEnough(URL_LENGTH2), hardTruncation(URL_LENGTH2))(stripHtmlTags(text)) || "";
|
|
1294
1356
|
var getMastodonAddressDetails = (address) => {
|
|
1295
1357
|
const matches = address.match(ADDRESS_PATTERN);
|
|
1296
1358
|
return {
|
|
@@ -1393,7 +1455,7 @@ import clsx4 from "clsx";
|
|
|
1393
1455
|
// src/mastodon-preview/post/body/index.tsx
|
|
1394
1456
|
import { Fragment as Fragment4, jsx as jsx40, jsxs as jsxs29 } from "react/jsx-runtime";
|
|
1395
1457
|
var MastonPostBody = (props) => {
|
|
1396
|
-
const { title, description, customText,
|
|
1458
|
+
const { title, description, customText, user, children } = props;
|
|
1397
1459
|
const instance = user?.address ? getMastodonAddressDetails(user.address).instance : "";
|
|
1398
1460
|
const options = {
|
|
1399
1461
|
instance,
|
|
@@ -1418,7 +1480,6 @@ var MastonPostBody = (props) => {
|
|
|
1418
1480
|
}
|
|
1419
1481
|
return /* @__PURE__ */ jsxs29("div", { className: "mastodon-preview__body", children: [
|
|
1420
1482
|
bodyTxt,
|
|
1421
|
-
/* @__PURE__ */ jsx40("a", { href: url, target: "_blank", rel: "noreferrer noopener", children: mastodonUrl(url.replace(/^https?:\/\//, "")) }),
|
|
1422
1483
|
children
|
|
1423
1484
|
] });
|
|
1424
1485
|
};
|
|
@@ -1817,28 +1878,35 @@ var actions_default4 = BlueskyPostActions;
|
|
|
1817
1878
|
// src/bluesky-preview/helpers.ts
|
|
1818
1879
|
var TITLE_LENGTH5 = 200;
|
|
1819
1880
|
var BODY_LENGTH2 = 300;
|
|
1820
|
-
var
|
|
1821
|
-
var
|
|
1881
|
+
var URL_LENGTH2 = 40;
|
|
1882
|
+
var BODY_CHAR_LIMIT = BODY_LENGTH2 - URL_LENGTH2;
|
|
1822
1883
|
var blueskyTitle = (text) => firstValid(
|
|
1823
1884
|
shortEnough(TITLE_LENGTH5),
|
|
1824
1885
|
hardTruncation(TITLE_LENGTH5)
|
|
1825
1886
|
)(stripHtmlTags(text)) || "";
|
|
1826
1887
|
var blueskyBody = (text, options = {}) => {
|
|
1827
|
-
const { offset = 0, reserveUrlSpace = true } = options;
|
|
1888
|
+
const { offset = 0, reserveUrlSpace = true, hyperlinks } = options;
|
|
1828
1889
|
return preparePreviewText(text, {
|
|
1829
1890
|
platform: "bluesky",
|
|
1830
|
-
maxChars: BODY_LENGTH2 - (reserveUrlSpace ?
|
|
1891
|
+
maxChars: BODY_LENGTH2 - (reserveUrlSpace ? URL_LENGTH2 : 0) - offset,
|
|
1892
|
+
hyperlinks
|
|
1831
1893
|
});
|
|
1832
1894
|
};
|
|
1833
|
-
var blueskyUrl = (text) => firstValid(shortEnough(
|
|
1895
|
+
var blueskyUrl = (text) => firstValid(shortEnough(URL_LENGTH2), hardTruncation(URL_LENGTH2))(stripHtmlTags(text)) || "";
|
|
1834
1896
|
|
|
1835
1897
|
// src/bluesky-preview/post/body/index.tsx
|
|
1836
1898
|
import { Fragment as Fragment6, jsx as jsx54, jsxs as jsxs37 } from "react/jsx-runtime";
|
|
1837
|
-
var BlueskyPostBody = ({
|
|
1899
|
+
var BlueskyPostBody = ({
|
|
1900
|
+
customText,
|
|
1901
|
+
url,
|
|
1902
|
+
children,
|
|
1903
|
+
appendUrl,
|
|
1904
|
+
hyperlinks
|
|
1905
|
+
}) => {
|
|
1838
1906
|
const showUrl = appendUrl && !!url && !customText?.includes(url);
|
|
1839
1907
|
return /* @__PURE__ */ jsxs37("div", { className: "bluesky-preview__body", children: [
|
|
1840
1908
|
customText ? /* @__PURE__ */ jsxs37(Fragment6, { children: [
|
|
1841
|
-
/* @__PURE__ */ jsx54("div", { children: blueskyBody(customText, { reserveUrlSpace: showUrl }) }),
|
|
1909
|
+
/* @__PURE__ */ jsx54("div", { children: blueskyBody(customText, { reserveUrlSpace: showUrl, hyperlinks }) }),
|
|
1842
1910
|
showUrl ? /* @__PURE__ */ jsxs37(Fragment6, { children: [
|
|
1843
1911
|
/* @__PURE__ */ jsx54("br", {}),
|
|
1844
1912
|
/* @__PURE__ */ jsx54("a", { href: url, target: "_blank", rel: "noreferrer noopener", children: blueskyUrl(url.replace(/^https?:\/\//, "")) })
|
|
@@ -2445,6 +2513,7 @@ export {
|
|
|
2445
2513
|
TumblrPreviews,
|
|
2446
2514
|
TwitterLinkPreview,
|
|
2447
2515
|
TwitterPostPreview,
|
|
2448
|
-
TwitterPreviews
|
|
2516
|
+
TwitterPreviews,
|
|
2517
|
+
parseHyperlinks
|
|
2449
2518
|
};
|
|
2450
2519
|
//# sourceMappingURL=index.mjs.map
|