rehype-og-card 0.1.0 → 0.2.0
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/README.md +16 -14
- package/dist/index.js +10 -8
- package/dist/util.d.ts +3 -3
- package/dist/util.js +36 -56
- package/package.json +2 -3
package/README.md
CHANGED
|
@@ -58,24 +58,26 @@ https://blog.google/products/android/world-emoji-day-2024/
|
|
|
58
58
|
main();
|
|
59
59
|
```
|
|
60
60
|
|
|
61
|
-
Output:
|
|
61
|
+
Output (formatted for readability):
|
|
62
62
|
|
|
63
63
|
```html
|
|
64
64
|
<p>this is a link: https://blog.google/products/android/world-emoji-day-2024/</p>
|
|
65
65
|
<p><code>https://blog.google/products/android/world-emoji-day-2024/</code></p>
|
|
66
|
-
<p><
|
|
67
|
-
<
|
|
68
|
-
<div class="
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
<
|
|
72
|
-
|
|
66
|
+
<p><div class="og-card-container">
|
|
67
|
+
<a href="https://blog.google/products/android/world-emoji-day-2024/">
|
|
68
|
+
<div class="og-card-info">
|
|
69
|
+
<div class="og-card-title">10 fun facts about emoji for World Emoji Day</div>
|
|
70
|
+
<div class="og-card-description">Celebrate World Emoji Day with Google, and check out what’s new for Emoji Kitchen.</div>
|
|
71
|
+
<div class="og-card-url-container">
|
|
72
|
+
<img class="og-card-favicon" alt="favicon" decoding="async" height="16" loading="lazy" src="https://www.google.com/s2/favicons?domain=blog.google" width="16">
|
|
73
|
+
<span class="og-card-url">blog.google</span>
|
|
74
|
+
</div>
|
|
73
75
|
</div>
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
</
|
|
78
|
-
</
|
|
76
|
+
<div class="og-card-image-container">
|
|
77
|
+
<img class="og-card-image" alt="https://storage.googleapis.com/gweb-uniblog-publish-prod/images/world_emoji_day_v2_1.width-1300.png" decoding="async" loading="lazy" src="https://storage.googleapis.com/gweb-uniblog-publish-prod/images/world_emoji_day_v2_1.width-1300.png">
|
|
78
|
+
</div>
|
|
79
|
+
</a>
|
|
80
|
+
</div></p>
|
|
79
81
|
<p><a href="https://blog.google/products/android/world-emoji-day-2024/">https://blog.google/products/android/world-emoji-day-2024/</a></p>
|
|
80
82
|
<p><a href="https://blog.google/products/android/world-emoji-day-2024/">Google blog</a></p>
|
|
81
83
|
```
|
|
@@ -146,7 +148,7 @@ Whether to cache the OG data on the server.
|
|
|
146
148
|
Path to save the server cache.
|
|
147
149
|
|
|
148
150
|
- Type: `string`
|
|
149
|
-
- Default: `"./public
|
|
151
|
+
- Default: `"./public"`
|
|
150
152
|
|
|
151
153
|
### `shortenURL`
|
|
152
154
|
|
package/dist/index.js
CHANGED
|
@@ -9,6 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
};
|
|
10
10
|
import { convertTextToAnchorElement, createOGCard, downloadImage, getOGData, isAnchorElement, isTextNode, isValidURL } from "./util.js";
|
|
11
11
|
import { isElement } from "hast-util-is-element";
|
|
12
|
+
import path from "path";
|
|
12
13
|
import { visitParents } from "unist-util-visit-parents";
|
|
13
14
|
const DEFAULT_OPTIONS = {
|
|
14
15
|
crawlerUserAgent:
|
|
@@ -19,7 +20,7 @@ const DEFAULT_OPTIONS = {
|
|
|
19
20
|
excludeDomains: [],
|
|
20
21
|
loading: "lazy",
|
|
21
22
|
serverCache: true,
|
|
22
|
-
serverCachePath: "./public
|
|
23
|
+
serverCachePath: "./public",
|
|
23
24
|
shortenURL: true
|
|
24
25
|
};
|
|
25
26
|
/**
|
|
@@ -30,6 +31,7 @@ const DEFAULT_OPTIONS = {
|
|
|
30
31
|
// eslint-disable-next-line max-lines-per-function
|
|
31
32
|
const rehypeOGCard = (options) => {
|
|
32
33
|
const mergedOptions = Object.assign(Object.assign({}, DEFAULT_OPTIONS), options);
|
|
34
|
+
mergedOptions.serverCachePath = path.posix.join(mergedOptions.serverCachePath, "./rehype-og-card/");
|
|
33
35
|
/**
|
|
34
36
|
* Transform function to create OG card from bare links.
|
|
35
37
|
* @param tree Root node of the HAST tree.
|
|
@@ -78,15 +80,15 @@ const rehypeOGCard = (options) => {
|
|
|
78
80
|
if (!OGData)
|
|
79
81
|
return;
|
|
80
82
|
if (OGData.OGImageURL && mergedOptions.serverCache) {
|
|
81
|
-
const
|
|
82
|
-
if (
|
|
83
|
-
OGData.OGImageURL =
|
|
83
|
+
const filename = yield downloadImage(OGData.OGImageURL, mergedOptions.serverCachePath, mergedOptions.crawlerUserAgent);
|
|
84
|
+
if (filename) {
|
|
85
|
+
OGData.OGImageURL = path.posix.join("/rehype-og-card", filename);
|
|
84
86
|
}
|
|
85
87
|
}
|
|
86
88
|
if (OGData.faviconURL && mergedOptions.serverCache) {
|
|
87
|
-
const
|
|
88
|
-
if (
|
|
89
|
-
OGData.faviconURL =
|
|
89
|
+
const filename = yield downloadImage(OGData.faviconURL, mergedOptions.serverCachePath, mergedOptions.crawlerUserAgent);
|
|
90
|
+
if (filename) {
|
|
91
|
+
OGData.faviconURL = path.posix.join("/rehype-og-card", filename);
|
|
90
92
|
}
|
|
91
93
|
}
|
|
92
94
|
if (mergedOptions.shortenURL) {
|
|
@@ -100,7 +102,7 @@ const rehypeOGCard = (options) => {
|
|
|
100
102
|
if (index === -1)
|
|
101
103
|
return;
|
|
102
104
|
// eslint-disable-next-line no-magic-numbers
|
|
103
|
-
ancestors[ancestors.length - 1].children.splice(index, 1,
|
|
105
|
+
ancestors[ancestors.length - 1].children.splice(index, 1, OGCard);
|
|
104
106
|
});
|
|
105
107
|
linkCardPromises.push(linkCardPromise());
|
|
106
108
|
});
|
package/dist/util.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Element, ElementContent,
|
|
1
|
+
import type { Element, ElementContent, Text } from "hast";
|
|
2
2
|
interface RehypeOGCardOptions {
|
|
3
3
|
/**
|
|
4
4
|
* Domains to exclude from conversion.
|
|
@@ -32,7 +32,7 @@ interface RehypeOGCardOptions {
|
|
|
32
32
|
serverCache?: boolean;
|
|
33
33
|
/**
|
|
34
34
|
* Path to save the server cache.
|
|
35
|
-
* @default "./public
|
|
35
|
+
* @default "./public"
|
|
36
36
|
*/
|
|
37
37
|
serverCachePath?: string;
|
|
38
38
|
/**
|
|
@@ -92,7 +92,7 @@ declare const getOGData: (url: string, userAgent: string) => Promise<OGCardData
|
|
|
92
92
|
declare const createOGCard: (data: OGCardData, options: Required<{
|
|
93
93
|
loading: RehypeOGCardOptions["loading"];
|
|
94
94
|
decoding: RehypeOGCardOptions["decoding"];
|
|
95
|
-
}>) =>
|
|
95
|
+
}>) => Element;
|
|
96
96
|
/**
|
|
97
97
|
* Convert text node to anchor element. **This function does not check if the text is a valid URL.**
|
|
98
98
|
* @param text Text node to convert.
|
package/dist/util.js
CHANGED
|
@@ -8,11 +8,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
10
|
import { createHash } from "crypto";
|
|
11
|
-
import { fromHtml } from "hast-util-from-html";
|
|
12
11
|
import fs from "fs/promises";
|
|
12
|
+
import { h } from "hastscript";
|
|
13
13
|
import { isElement } from "hast-util-is-element";
|
|
14
14
|
import path from "path";
|
|
15
|
-
import sanitizeHtml from "sanitize-html";
|
|
16
15
|
import scraper from "open-graph-scraper";
|
|
17
16
|
/**
|
|
18
17
|
* Check if the node is a text node.
|
|
@@ -66,11 +65,10 @@ const getOGData = (url, userAgent) => __awaiter(void 0, void 0, void 0, function
|
|
|
66
65
|
OGImageHeight: OGImage === null || OGImage === void 0 ? void 0 : OGImage.height,
|
|
67
66
|
OGImageURL: OGImage === null || OGImage === void 0 ? void 0 : OGImage.url,
|
|
68
67
|
OGImageWidth: OGImage === null || OGImage === void 0 ? void 0 : OGImage.width,
|
|
69
|
-
|
|
70
|
-
description: result.ogDescription ? sanitizeHtml(result.ogDescription) : undefined,
|
|
68
|
+
description: result.ogDescription,
|
|
71
69
|
displayURL: url,
|
|
72
70
|
faviconURL,
|
|
73
|
-
title: result.ogTitle
|
|
71
|
+
title: result.ogTitle || url,
|
|
74
72
|
url
|
|
75
73
|
};
|
|
76
74
|
}
|
|
@@ -80,20 +78,6 @@ const getOGData = (url, userAgent) => __awaiter(void 0, void 0, void 0, function
|
|
|
80
78
|
return null;
|
|
81
79
|
}
|
|
82
80
|
});
|
|
83
|
-
/**
|
|
84
|
-
* Sanitize HTML.
|
|
85
|
-
* @param html HTML to sanitize.
|
|
86
|
-
* @returns Sanitized HTML.
|
|
87
|
-
*/
|
|
88
|
-
const sanitize = (html) => {
|
|
89
|
-
const options = {
|
|
90
|
-
allowedAttributes: {},
|
|
91
|
-
allowedTags: [],
|
|
92
|
-
disallowedTagsMode: "recursiveEscape"
|
|
93
|
-
// eslint-disable-next-line no-magic-numbers
|
|
94
|
-
};
|
|
95
|
-
return sanitizeHtml(html, options);
|
|
96
|
-
};
|
|
97
81
|
/**
|
|
98
82
|
* Create OG card element from given data.
|
|
99
83
|
* @param data Data to create OG card.
|
|
@@ -101,42 +85,38 @@ const sanitize = (html) => {
|
|
|
101
85
|
* @returns OG card element.
|
|
102
86
|
*/
|
|
103
87
|
const createOGCard = (data, options) => {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
</div>${imageContainerElement}
|
|
137
|
-
</a>
|
|
138
|
-
`.trim();
|
|
139
|
-
return fromHtml(card, { fragment: true }).children;
|
|
88
|
+
const card = h("div.og-card-container", [
|
|
89
|
+
h("a", { href: data.url }, [
|
|
90
|
+
h("div.og-card-info", [
|
|
91
|
+
h("div.og-card-title", data.title),
|
|
92
|
+
data.description ? h("div.og-card-description", data.description) : null,
|
|
93
|
+
h("div.og-card-url-container", [
|
|
94
|
+
h("img.og-card-favicon", {
|
|
95
|
+
alt: "favicon",
|
|
96
|
+
decoding: options.decoding,
|
|
97
|
+
height: 16,
|
|
98
|
+
loading: options.loading,
|
|
99
|
+
src: data.faviconURL,
|
|
100
|
+
width: 16
|
|
101
|
+
}),
|
|
102
|
+
h("span.og-card-url", data.displayURL)
|
|
103
|
+
])
|
|
104
|
+
]),
|
|
105
|
+
data.OGImageURL
|
|
106
|
+
? h("div.og-card-image-container", [
|
|
107
|
+
h("img.og-card-image", {
|
|
108
|
+
alt: data.OGImageAlt || data.OGImageURL,
|
|
109
|
+
decoding: options.decoding,
|
|
110
|
+
height: data.OGImageHeight,
|
|
111
|
+
loading: options.loading,
|
|
112
|
+
src: data.OGImageURL,
|
|
113
|
+
width: data.OGImageWidth
|
|
114
|
+
})
|
|
115
|
+
])
|
|
116
|
+
: null
|
|
117
|
+
])
|
|
118
|
+
]);
|
|
119
|
+
return card;
|
|
140
120
|
};
|
|
141
121
|
/**
|
|
142
122
|
* Convert text node to anchor element. **This function does not check if the text is a valid URL.**
|
|
@@ -178,7 +158,7 @@ const downloadImage = (url, directly, userAgent) => __awaiter(void 0, void 0, vo
|
|
|
178
158
|
const savePath = path.posix.join(directly, filename);
|
|
179
159
|
yield fs.mkdir(directly, { recursive: true });
|
|
180
160
|
yield fs.writeFile(savePath, buffer);
|
|
181
|
-
return
|
|
161
|
+
return filename;
|
|
182
162
|
}
|
|
183
163
|
catch (error) {
|
|
184
164
|
// eslint-disable-next-line no-console
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rehype-og-card",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Rehype plugin to convert text links to link cards",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -57,10 +57,9 @@
|
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
59
|
"@types/hast": "^3.0.4",
|
|
60
|
-
"hast-util-from-html": "^2.0.1",
|
|
61
60
|
"hast-util-is-element": "^3.0.0",
|
|
61
|
+
"hastscript": "^9.0.0",
|
|
62
62
|
"open-graph-scraper": "^6.6.3",
|
|
63
|
-
"sanitize-html": "^2.13.0",
|
|
64
63
|
"unist-util-visit-parents": "^6.0.1"
|
|
65
64
|
}
|
|
66
65
|
}
|