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 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><a class="rlc-container" href="https://blog.google/products/android/world-emoji-day-2024/">
67
- <div class="rlc-info">
68
- <div class="rlc-title">10 fun facts about emoji for World Emoji Day</div>
69
- <div class="rlc-description">Celebrate World Emoji Day with Google, and check out what’s new for Emoji Kitchen.</div>
70
- <div class="rlc-url-container">
71
- <img class="rlc-favicon" loading="lazy" decoding="async" src="https://www.google.com/s2/favicons?domain=blog.google" alt="favicon" width="16" height="16">
72
- <span class="rlc-url">blog.google</span>
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
- </div>
75
- <div class="rlc-image-container">
76
- <img class="rlc-image" loading="lazy" decoding="async" src="https://storage.googleapis.com/gweb-uniblog-publish-prod/images/world_emoji_day_v2_1.width-1300.png" alt="https://storage.googleapis.com/gweb-uniblog-publish-prod/images/world_emoji_day_v2_1.width-1300.png">
77
- </div>
78
- </a></p>
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/rehype-og-card-cache/"`
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/rehype-og-card-cache/",
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 cachePath = yield downloadImage(OGData.OGImageURL, mergedOptions.serverCachePath, mergedOptions.crawlerUserAgent);
82
- if (cachePath) {
83
- OGData.OGImageURL = `/${cachePath}`;
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 cachePath = yield downloadImage(OGData.faviconURL, mergedOptions.serverCachePath, mergedOptions.crawlerUserAgent);
88
- if (cachePath) {
89
- OGData.faviconURL = `/${cachePath}`;
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, ...OGCard);
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, RootContent, Text } from "hast";
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/rehype-og-card-cache/"
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
- }>) => RootContent[];
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
- // eslint-disable-next-line no-undefined
70
- description: result.ogDescription ? sanitizeHtml(result.ogDescription) : undefined,
68
+ description: result.ogDescription,
71
69
  displayURL: url,
72
70
  faviconURL,
73
- title: result.ogTitle ? sanitizeHtml(result.ogTitle) : url,
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
- data.title = sanitize(data.title);
105
- if (data.description) {
106
- data.description = sanitize(data.description);
107
- }
108
- const imageContainerElement = data.OGImageURL
109
- ? `
110
- <div class="rlc-image-container">
111
- <img class="rlc-image"
112
- loading="${options.loading}"
113
- decoding="${options.decoding}"
114
- src="${data.OGImageURL}" alt="${data.OGImageAlt || data.OGImageURL}"
115
- ${data.OGImageWidth ? `width="${data.OGImageWidth}"` : ""}
116
- ${data.OGImageHeight ? `height="${data.OGImageHeight}"` : ""} >
117
- </div>`
118
- : "";
119
- const descriptionElement = data.description
120
- ? `
121
- <div class="rlc-description">${data.description}</div>`
122
- : "";
123
- // The `loading` attribute must be specified before the `src` attribute because of a bug in Firefox.
124
- // See https://bugzil.la/1647077 for more information.
125
- const card = `
126
- <a class="rlc-container" href="${data.url}">
127
- <div class="rlc-info">
128
- <div class="rlc-title">${data.title}</div>${descriptionElement}
129
- <div class="rlc-url-container">
130
- <img class="rlc-favicon"
131
- loading="${options.loading}"
132
- decoding="${options.decoding}"
133
- src="${data.faviconURL}" alt="favicon" width="16" height="16">
134
- <span class="rlc-url">${data.displayURL}</span>
135
- </div>
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 savePath;
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.1.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
  }