├── .eleventy.js
├── .gitignore
├── .nvmrc
├── .prettierrc
├── README.md
├── filters.js
├── package-lock.json
├── package.json
└── webmentions.js
/.eleventy.js:
--------------------------------------------------------------------------------
1 | const Webmentions = require("./webmentions");
2 | const WebmentionFilters = require("./filters");
3 |
4 | const config = async (eleventyConfig, options = {}) => {
5 | const webmentions = Webmentions(options);
6 | const filters = WebmentionFilters(options);
7 |
8 | const data = webmentions.get();
9 |
10 | eleventyConfig.addGlobalData("webmentions", async () => {
11 | const { children } = await data;
12 | return children;
13 | });
14 |
15 | eleventyConfig.addGlobalData("webmentionsLastFetched", async () => {
16 | const { lastFetched } = await data;
17 | return new Date(lastFetched);
18 | });
19 |
20 | eleventyConfig.addFilter("webmentionsForPage", filters.mentions);
21 | eleventyConfig.addFilter("webmentionCountForPage", filters.count);
22 | };
23 |
24 | config.defaults = {
25 | ...Webmentions.defaults,
26 | ...WebmentionFilters.defaults,
27 | };
28 |
29 | module.exports = config;
30 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | 16.13.0
2 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "tabWidth": 2,
3 | "useTabs": false
4 | }
5 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # eleventy-plugin-webmentions
2 |
3 | A plugin for eleventy to fetch and filter [webmentions](https://indieweb.org/Webmention) from [Webmention.io](https://webmention.io).
4 |
5 | ## Install
6 |
7 | Available on [npm](https://www.npmjs.com/package/eleventy-plugin-webmentions).
8 |
9 | `npm install --save-dev eleventy-plugin-webmentions`
10 |
11 | ## Usage
12 |
13 | In your Eleventy config file (probably `.eleventy.js`), load the plugin module and use `.addPlugin` to add it to Eleventy with an options object that defines the `domain` and the Webmention.io `token`. Like this:
14 |
15 | ```javascript
16 | const Webmentions = require("eleventy-plugin-webmentions");
17 |
18 | module.exports = function (eleventyConfig) {
19 | eleventyConfig.addPlugin(Webmentions, {
20 | domain: "lukeb.co.uk",
21 | token: "ABC123XYZ987",
22 | });
23 | };
24 | ```
25 |
26 | REMEMBER: You’re only allowed one `module.exports` in your configuration file, so make sure you only copy the `require` and the `.addPlugin` lines above! (Including the configuration options)
27 |
28 | The plugin then adds 2 global data objects. One is called `webmentionsLastFetched` and is a `Date` object with the date that the plugin last fetched webmentions, and the other is called `webmentions` and is an array of webmention objects that look similar to this:
29 |
30 | ```javascript
31 | {
32 | type: 'entry',
33 | author: {
34 | type: 'card',
35 | name: 'Zach Leatherman',
36 | photo: 'https://webmention.io/avatar/pbs.twimg.com/d9711a9ad30ae05a761e4a728883bcbdd852cbf7d41437925b0afc47a8589795.jpg',
37 | url: 'https://twitter.com/zachleat'
38 | },
39 | url: 'https://twitter.com/zachleat/status/1524800520208142337',
40 | published: '2022-05-12T17:15:48+00:00',
41 | 'wm-received': '2022-05-13T00:05:16Z',
42 | 'wm-id': 1397424,
43 | 'wm-source': 'https://brid.gy/comment/twitter/CodeFoodPixels/1524795680966991874/1524800520208142337',
44 | 'wm-target': 'https://lukeb.co.uk/blog/2022/01/17/pixelated-rounded-corners-with-css-clip-path/',
45 | content: {
46 | html: 'The step-by-step here was/is incredible detailed!\n' +
47 | '\n' +
48 | '',
49 | text: 'The step-by-step here was/is incredible detailed!',
50 | value: 'The step-by-step here was/is incredible detailed! '
51 | },
52 | 'in-reply-to': 'https://lukeb.co.uk/blog/2022/01/17/pixelated-rounded-corners-with-css-clip-path/',
53 | 'wm-property': 'in-reply-to',
54 | 'wm-private': false
55 | }
56 | ```
57 |
58 | It also adds 2 filters:
59 |
60 | - `webmentionsForPage` will return the webmentions for that page, in the structure defined by the `mentionTypes` option.
61 | - `webmentionCountForPage` will return the number of webmentions for a page, filtered by the types used in the `mentionTypes` option.
62 |
63 | Here is an example of using the filters in nunjucks:
64 |
65 | ```nunjucks
66 | {# Get the webmentions for the current page #}
67 | {%- set currentPostMentions = webmentions | webmentionsForPage -%}
68 |
69 | {# Get the webmentions for a specific page #}
70 | {%- set postMentions = webmentions | webmentionsForPage(post.url) -%}
71 |
72 | {# Get the webmention count for the current page #}
73 | {%- set currentPostMentionCount = webmentions | webmentionCountForPage -%}
74 |
75 | {# Get the webmention count for a page #}
76 | {%- set postMentionCount = webmentions | webmentionCountForPage(post.url) -%}
77 |
78 | ```
79 |
80 | ## Configuration
81 |
82 | Below are all the options that can be passed to the plugin:
83 |
84 |
85 |
86 |
87 | Option |
88 | Type |
89 | Required? |
90 | Default |
91 | Description |
92 |
93 |
94 |
95 |
96 |
97 | `domain`
98 |
99 | |
100 | string |
101 |
102 |
103 | **Required**
104 |
105 | |
106 |
107 |
108 | `undefined`
109 |
110 | |
111 | The domain you wish to get the webmentions for. |
112 |
113 |
114 |
115 |
116 |
117 | `token`
118 |
119 | |
120 | string |
121 |
122 |
123 | **Required**
124 |
125 | |
126 |
127 |
128 | `undefined`
129 |
130 | |
131 | The webmention.io token (found at the bottom of [the webmention.io settings page](https://webmention.io/settings)). |
132 |
133 |
134 |
135 |
136 |
137 | `cacheDirectory`
138 |
139 | |
140 | string |
141 | Optional |
142 |
143 |
144 | `./_webmentioncache`
145 |
146 | |
147 | The directory for webmentions to be cached to. |
148 |
149 |
150 |
151 |
152 |
153 | `cacheTime`
154 |
155 | |
156 | integer |
157 | Optional |
158 |
159 |
160 | `3600`
161 |
162 | |
163 | The time in seconds for the cached webmentions to be considered "fresh". |
164 |
165 |
166 |
167 |
168 |
169 | `truncate`
170 |
171 | |
172 | boolean |
173 | Optional |
174 |
175 |
176 | `true`
177 |
178 | |
179 | Whether or not to truncate the webmentions |
180 |
181 |
182 |
183 |
184 |
185 | `maxContentLength`
186 |
187 | |
188 | integer |
189 | Optional |
190 |
191 |
192 | `280`
193 |
194 | |
195 | The length to truncate webmentions to if `truncate` is true |
196 |
197 |
198 |
199 |
200 |
201 | `truncationMarker`
202 |
203 | |
204 | string |
205 | Optional |
206 |
207 |
208 | `…`
209 |
210 | |
211 | The string to truncate the content with |
212 |
213 |
214 |
215 |
216 |
217 | `htmlContent`
218 |
219 | |
220 | boolean |
221 | Optional |
222 |
223 |
224 | `true`
225 |
226 | |
227 | Whether or not to return HTML content from the webmentions. If `false`, just text content will be returned. |
228 |
229 |
230 |
231 |
232 |
233 | `useCanonicalTwitterUrls`
234 |
235 | |
236 | boolean |
237 | Optional |
238 |
239 |
240 | `true`
241 |
242 | |
243 |
244 |
245 | Whether or not to convert Twitter URLs using [tweetback-canonical](https://github.com/tweetback/tweetback-canonical)
246 |
247 | |
248 |
249 |
250 |
251 |
252 |
253 | `pageAliases`
254 |
255 | |
256 | object |
257 | Optional |
258 |
259 |
260 | `{}`
261 |
262 | |
263 |
264 |
265 | An object keyed by page path, with the values either being a string of a page that is an alias of that page (e.g an old page that has been redirected) or an array of strings.
266 |
267 | |
268 |
269 |
270 |
271 |
272 |
273 | `mentionTypes`
274 |
275 | |
276 | object |
277 | Optional |
278 |
279 |
280 | ```javascript
281 | {
282 | likes: ["like-of"],
283 | reposts: ["repost-of"],
284 | comments: [
285 | "mention-of",
286 | "in-reply-to"
287 | ]
288 | }
289 | ```
290 |
291 | |
292 |
293 |
294 | A single layer object with groupings and types that should be returned for that grouping. The object can have any keys you wish (doesn't have to be `likes`, `reposts` and `comments` like the default) but each value should be an array of webmention types.[You can find a list of possible types here](https://github.com/aaronpk/webmention.io#find-links-of-a-specific-type-to-a-specific-page)
295 |
296 | |
297 |
298 |
299 |
300 |
301 |
302 | `sanitizeOptions`
303 |
304 | |
305 | object |
306 | Optional |
307 |
308 |
309 | ```javascript
310 | {
311 | allowedTags: ["b", "i", "em", "strong", "a", "p"],
312 | allowedAttributes: {
313 | a: ["href"],
314 | },
315 | }
316 | ```
317 |
318 | |
319 |
320 |
321 | A set of options passed to `sanitize-html`. You can find a full list of available options here [You can find a full list of available options here](https://github.com/apostrophecms/sanitize-html)
322 |
323 | |
324 |
325 |
326 |
327 |
328 |
329 | `sortFunction`
330 |
331 | |
332 | function |
333 | Optional |
334 |
335 |
336 | ```javascript
337 | (a, b) => {
338 | new Date(a.published || a["wm-received"]) -
339 | new Date(b.published || b["wm-received"])
340 | ```
341 |
342 | |
343 | A function to use when sorting the webmentions. By default, the webmentions will be sorted in date ascending order, either by when they were published or when they were recieved. |
344 |
345 |
346 |
347 |
348 | ### Defaults
349 |
350 | All of the defaults are exposed on the `defaults` property of the module, so they can be used in your config if necessary.
351 |
352 | Here is an example of extending the `sanitizeOptions` object:
353 |
354 | ```javascript
355 | const Webmentions = require("eleventy-plugin-webmentions");
356 |
357 | module.exports = function (eleventyConfig) {
358 | eleventyConfig.addPlugin(Webmentions, {
359 | domain: "lukeb.co.uk",
360 | token: "ABC123XYZ987",
361 | sanitizeOptions: {
362 | ...Webmentions.defaults.sanitizeOptions,
363 | allowedTags: [
364 | ...Webmentions.defaults.sanitizeOptions.allowedTags,
365 | "iframe",
366 | "marquee",
367 | ],
368 | disallowedTagsMode: "escape",
369 | },
370 | });
371 | };
372 | ```
373 |
--------------------------------------------------------------------------------
/filters.js:
--------------------------------------------------------------------------------
1 | const { URL } = require("url");
2 |
3 | const defaults = {
4 | mentionTypes: {
5 | likes: ["like-of"],
6 | reposts: ["repost-of"],
7 | comments: ["mention-of", "in-reply-to"],
8 | },
9 | };
10 |
11 | function stripOuterSlashes(str) {
12 | let start = 0;
13 | while (str[start++] === "/");
14 | let end = str.length;
15 | while (str[--end] === "/");
16 | return str.slice(start - 1, end + 1);
17 | }
18 |
19 | const filters = ({
20 | mentionTypes = defaults.mentionTypes,
21 | pageAliases = {},
22 | }) => {
23 | const cleanedAliases = Object.keys(pageAliases).reduce((cleaned, key) => {
24 | cleaned[stripOuterSlashes(key.toLowerCase())] =
25 | typeof pageAliases[key] === "string"
26 | ? [stripOuterSlashes(pageAliases[key].toLowerCase())]
27 | : pageAliases[key].map((alias) =>
28 | stripOuterSlashes(alias.toLowerCase())
29 | );
30 |
31 | return cleaned;
32 | }, {});
33 |
34 | function filterWebmentions(webmentions, page) {
35 | const pageUrl = new URL(page, "https://lukeb.co.uk");
36 | const normalizedPagePath = stripOuterSlashes(
37 | pageUrl.pathname.toLowerCase()
38 | );
39 |
40 | const flattenedMentionTypes = Object.values(mentionTypes).flat();
41 |
42 | return webmentions
43 | .filter((mention) => {
44 | const target = new URL(mention["wm-target"]);
45 | const normalisedTargetPath = stripOuterSlashes(
46 | target.pathname.toLowerCase()
47 | );
48 | return (
49 | normalizedPagePath === normalisedTargetPath ||
50 | cleanedAliases[normalizedPagePath]?.includes(normalisedTargetPath)
51 | );
52 | })
53 | .filter(
54 | (entry) => !!entry.author && (!!entry.author.name || entry.author.url)
55 | )
56 | .filter((mention) =>
57 | flattenedMentionTypes.includes(mention["wm-property"])
58 | );
59 | }
60 |
61 | function count(webmentions, pageUrl) {
62 | const page =
63 | pageUrl ||
64 | this.page?.url ||
65 | this.ctx?.page?.url ||
66 | this.context?.environments?.page?.url;
67 |
68 | return filterWebmentions(webmentions, page).length;
69 | }
70 |
71 | function mentions(webmentions, pageUrl) {
72 | const page =
73 | pageUrl ||
74 | this.page?.url ||
75 | this.ctx?.page?.url ||
76 | this.context?.environments?.page?.url;
77 |
78 | const filteredWebmentions = filterWebmentions(webmentions, page);
79 |
80 | const returnedWebmentions = {
81 | total: filteredWebmentions.length,
82 | };
83 |
84 | Object.keys(mentionTypes).map((type) => {
85 | returnedWebmentions[type] = filteredWebmentions.filter((mention) =>
86 | mentionTypes[type].includes(mention["wm-property"])
87 | );
88 | });
89 |
90 | return returnedWebmentions;
91 | }
92 |
93 | return {
94 | count,
95 | mentions,
96 | };
97 | };
98 |
99 | filters.defaults = defaults;
100 |
101 | module.exports = filters;
102 |
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "eleventy-plugin-webmentions",
3 | "version": "2.1.0",
4 | "lockfileVersion": 2,
5 | "requires": true,
6 | "packages": {
7 | "": {
8 | "name": "eleventy-plugin-webmentions",
9 | "version": "2.1.0",
10 | "license": "MIT",
11 | "dependencies": {
12 | "@tweetback/canonical": "^2.0.27",
13 | "html-entities": "^2.3.3",
14 | "node-fetch": "^2.6.6",
15 | "sanitize-html": "^2.7.0",
16 | "truncate-html": "^1.0.4"
17 | }
18 | },
19 | "node_modules/@tweetback/canonical": {
20 | "version": "2.0.27",
21 | "resolved": "https://registry.npmjs.org/@tweetback/canonical/-/canonical-2.0.27.tgz",
22 | "integrity": "sha512-kfSBGynrL/nDBp0oQyCXo5geZi2+q9A63IrmtKaVHCmPMhFs6U8USnnFB3Ldft7XKEyDjM/EGFB7SOnT09OwqA=="
23 | },
24 | "node_modules/@types/cheerio": {
25 | "version": "0.22.31",
26 | "resolved": "https://registry.npmjs.org/@types/cheerio/-/cheerio-0.22.31.tgz",
27 | "integrity": "sha512-Kt7Cdjjdi2XWSfrZ53v4Of0wG3ZcmaegFXjMmz9tfNrZSkzzo36G0AL1YqSdcIA78Etjt6E609pt5h1xnQkPUw==",
28 | "dependencies": {
29 | "@types/node": "*"
30 | }
31 | },
32 | "node_modules/@types/node": {
33 | "version": "18.0.0",
34 | "resolved": "https://registry.npmjs.org/@types/node/-/node-18.0.0.tgz",
35 | "integrity": "sha512-cHlGmko4gWLVI27cGJntjs/Sj8th9aYwplmZFwmmgYQQvL5NUsgVJG7OddLvNfLqYS31KFN0s3qlaD9qCaxACA=="
36 | },
37 | "node_modules/boolbase": {
38 | "version": "1.0.0",
39 | "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
40 | "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="
41 | },
42 | "node_modules/cheerio": {
43 | "version": "0.22.0",
44 | "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.22.0.tgz",
45 | "integrity": "sha512-8/MzidM6G/TgRelkzDG13y3Y9LxBjCb+8yOEZ9+wwq5gVF2w2pV0wmHvjfT0RvuxGyR7UEuK36r+yYMbT4uKgA==",
46 | "dependencies": {
47 | "css-select": "~1.2.0",
48 | "dom-serializer": "~0.1.0",
49 | "entities": "~1.1.1",
50 | "htmlparser2": "^3.9.1",
51 | "lodash.assignin": "^4.0.9",
52 | "lodash.bind": "^4.1.4",
53 | "lodash.defaults": "^4.0.1",
54 | "lodash.filter": "^4.4.0",
55 | "lodash.flatten": "^4.2.0",
56 | "lodash.foreach": "^4.3.0",
57 | "lodash.map": "^4.4.0",
58 | "lodash.merge": "^4.4.0",
59 | "lodash.pick": "^4.2.1",
60 | "lodash.reduce": "^4.4.0",
61 | "lodash.reject": "^4.4.0",
62 | "lodash.some": "^4.4.0"
63 | },
64 | "engines": {
65 | "node": ">= 0.6"
66 | }
67 | },
68 | "node_modules/cheerio/node_modules/dom-serializer": {
69 | "version": "0.1.1",
70 | "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz",
71 | "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==",
72 | "dependencies": {
73 | "domelementtype": "^1.3.0",
74 | "entities": "^1.1.1"
75 | }
76 | },
77 | "node_modules/cheerio/node_modules/domelementtype": {
78 | "version": "1.3.1",
79 | "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz",
80 | "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w=="
81 | },
82 | "node_modules/cheerio/node_modules/domhandler": {
83 | "version": "2.4.2",
84 | "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz",
85 | "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==",
86 | "dependencies": {
87 | "domelementtype": "1"
88 | }
89 | },
90 | "node_modules/cheerio/node_modules/domutils": {
91 | "version": "1.7.0",
92 | "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz",
93 | "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==",
94 | "dependencies": {
95 | "dom-serializer": "0",
96 | "domelementtype": "1"
97 | }
98 | },
99 | "node_modules/cheerio/node_modules/entities": {
100 | "version": "1.1.2",
101 | "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz",
102 | "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w=="
103 | },
104 | "node_modules/cheerio/node_modules/htmlparser2": {
105 | "version": "3.10.1",
106 | "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz",
107 | "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==",
108 | "dependencies": {
109 | "domelementtype": "^1.3.1",
110 | "domhandler": "^2.3.0",
111 | "domutils": "^1.5.1",
112 | "entities": "^1.1.1",
113 | "inherits": "^2.0.1",
114 | "readable-stream": "^3.1.1"
115 | }
116 | },
117 | "node_modules/css-select": {
118 | "version": "1.2.0",
119 | "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz",
120 | "integrity": "sha512-dUQOBoqdR7QwV90WysXPLXG5LO7nhYBgiWVfxF80DKPF8zx1t/pUd2FYy73emg3zrjtM6dzmYgbHKfV2rxiHQA==",
121 | "dependencies": {
122 | "boolbase": "~1.0.0",
123 | "css-what": "2.1",
124 | "domutils": "1.5.1",
125 | "nth-check": "~1.0.1"
126 | }
127 | },
128 | "node_modules/css-select/node_modules/dom-serializer": {
129 | "version": "0.2.2",
130 | "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz",
131 | "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==",
132 | "dependencies": {
133 | "domelementtype": "^2.0.1",
134 | "entities": "^2.0.0"
135 | }
136 | },
137 | "node_modules/css-select/node_modules/domutils": {
138 | "version": "1.5.1",
139 | "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz",
140 | "integrity": "sha512-gSu5Oi/I+3wDENBsOWBiRK1eoGxcywYSqg3rR960/+EfY0CF4EX1VPkgHOZ3WiS/Jg2DtliF6BhWcHlfpYUcGw==",
141 | "dependencies": {
142 | "dom-serializer": "0",
143 | "domelementtype": "1"
144 | }
145 | },
146 | "node_modules/css-select/node_modules/domutils/node_modules/domelementtype": {
147 | "version": "1.3.1",
148 | "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz",
149 | "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w=="
150 | },
151 | "node_modules/css-what": {
152 | "version": "2.1.3",
153 | "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz",
154 | "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==",
155 | "engines": {
156 | "node": "*"
157 | }
158 | },
159 | "node_modules/deepmerge": {
160 | "version": "4.2.2",
161 | "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz",
162 | "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==",
163 | "engines": {
164 | "node": ">=0.10.0"
165 | }
166 | },
167 | "node_modules/dom-serializer": {
168 | "version": "1.4.1",
169 | "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
170 | "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==",
171 | "dependencies": {
172 | "domelementtype": "^2.0.1",
173 | "domhandler": "^4.2.0",
174 | "entities": "^2.0.0"
175 | },
176 | "funding": {
177 | "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
178 | }
179 | },
180 | "node_modules/domelementtype": {
181 | "version": "2.3.0",
182 | "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
183 | "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
184 | "funding": [
185 | {
186 | "type": "github",
187 | "url": "https://github.com/sponsors/fb55"
188 | }
189 | ]
190 | },
191 | "node_modules/domhandler": {
192 | "version": "4.3.1",
193 | "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz",
194 | "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==",
195 | "dependencies": {
196 | "domelementtype": "^2.2.0"
197 | },
198 | "engines": {
199 | "node": ">= 4"
200 | },
201 | "funding": {
202 | "url": "https://github.com/fb55/domhandler?sponsor=1"
203 | }
204 | },
205 | "node_modules/domutils": {
206 | "version": "2.8.0",
207 | "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
208 | "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
209 | "dependencies": {
210 | "dom-serializer": "^1.0.1",
211 | "domelementtype": "^2.2.0",
212 | "domhandler": "^4.2.0"
213 | },
214 | "funding": {
215 | "url": "https://github.com/fb55/domutils?sponsor=1"
216 | }
217 | },
218 | "node_modules/entities": {
219 | "version": "2.2.0",
220 | "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
221 | "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
222 | "funding": {
223 | "url": "https://github.com/fb55/entities?sponsor=1"
224 | }
225 | },
226 | "node_modules/escape-string-regexp": {
227 | "version": "4.0.0",
228 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
229 | "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
230 | "engines": {
231 | "node": ">=10"
232 | },
233 | "funding": {
234 | "url": "https://github.com/sponsors/sindresorhus"
235 | }
236 | },
237 | "node_modules/html-entities": {
238 | "version": "2.3.3",
239 | "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz",
240 | "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA=="
241 | },
242 | "node_modules/htmlparser2": {
243 | "version": "6.1.0",
244 | "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz",
245 | "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==",
246 | "funding": [
247 | "https://github.com/fb55/htmlparser2?sponsor=1",
248 | {
249 | "type": "github",
250 | "url": "https://github.com/sponsors/fb55"
251 | }
252 | ],
253 | "dependencies": {
254 | "domelementtype": "^2.0.1",
255 | "domhandler": "^4.0.0",
256 | "domutils": "^2.5.2",
257 | "entities": "^2.0.0"
258 | }
259 | },
260 | "node_modules/inherits": {
261 | "version": "2.0.4",
262 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
263 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
264 | },
265 | "node_modules/is-plain-object": {
266 | "version": "5.0.0",
267 | "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
268 | "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==",
269 | "engines": {
270 | "node": ">=0.10.0"
271 | }
272 | },
273 | "node_modules/lodash.assignin": {
274 | "version": "4.2.0",
275 | "resolved": "https://registry.npmjs.org/lodash.assignin/-/lodash.assignin-4.2.0.tgz",
276 | "integrity": "sha512-yX/rx6d/UTVh7sSVWVSIMjfnz95evAgDFdb1ZozC35I9mSFCkmzptOzevxjgbQUsc78NR44LVHWjsoMQXy9FDg=="
277 | },
278 | "node_modules/lodash.bind": {
279 | "version": "4.2.1",
280 | "resolved": "https://registry.npmjs.org/lodash.bind/-/lodash.bind-4.2.1.tgz",
281 | "integrity": "sha512-lxdsn7xxlCymgLYo1gGvVrfHmkjDiyqVv62FAeF2i5ta72BipE1SLxw8hPEPLhD4/247Ijw07UQH7Hq/chT5LA=="
282 | },
283 | "node_modules/lodash.defaults": {
284 | "version": "4.2.0",
285 | "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
286 | "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ=="
287 | },
288 | "node_modules/lodash.filter": {
289 | "version": "4.6.0",
290 | "resolved": "https://registry.npmjs.org/lodash.filter/-/lodash.filter-4.6.0.tgz",
291 | "integrity": "sha512-pXYUy7PR8BCLwX5mgJ/aNtyOvuJTdZAo9EQFUvMIYugqmJxnrYaANvTbgndOzHSCSR0wnlBBfRXJL5SbWxo3FQ=="
292 | },
293 | "node_modules/lodash.flatten": {
294 | "version": "4.4.0",
295 | "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz",
296 | "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g=="
297 | },
298 | "node_modules/lodash.foreach": {
299 | "version": "4.5.0",
300 | "resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz",
301 | "integrity": "sha512-aEXTF4d+m05rVOAUG3z4vZZ4xVexLKZGF0lIxuHZ1Hplpk/3B6Z1+/ICICYRLm7c41Z2xiejbkCkJoTlypoXhQ=="
302 | },
303 | "node_modules/lodash.map": {
304 | "version": "4.6.0",
305 | "resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz",
306 | "integrity": "sha512-worNHGKLDetmcEYDvh2stPCrrQRkP20E4l0iIS7F8EvzMqBBi7ltvFN5m1HvTf1P7Jk1txKhvFcmYsCr8O2F1Q=="
307 | },
308 | "node_modules/lodash.merge": {
309 | "version": "4.6.2",
310 | "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
311 | "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="
312 | },
313 | "node_modules/lodash.pick": {
314 | "version": "4.4.0",
315 | "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz",
316 | "integrity": "sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q=="
317 | },
318 | "node_modules/lodash.reduce": {
319 | "version": "4.6.0",
320 | "resolved": "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz",
321 | "integrity": "sha512-6raRe2vxCYBhpBu+B+TtNGUzah+hQjVdu3E17wfusjyrXBka2nBS8OH/gjVZ5PvHOhWmIZTYri09Z6n/QfnNMw=="
322 | },
323 | "node_modules/lodash.reject": {
324 | "version": "4.6.0",
325 | "resolved": "https://registry.npmjs.org/lodash.reject/-/lodash.reject-4.6.0.tgz",
326 | "integrity": "sha512-qkTuvgEzYdyhiJBx42YPzPo71R1aEr0z79kAv7Ixg8wPFEjgRgJdUsGMG3Hf3OYSF/kHI79XhNlt+5Ar6OzwxQ=="
327 | },
328 | "node_modules/lodash.some": {
329 | "version": "4.6.0",
330 | "resolved": "https://registry.npmjs.org/lodash.some/-/lodash.some-4.6.0.tgz",
331 | "integrity": "sha512-j7MJE+TuT51q9ggt4fSgVqro163BEFjAt3u97IqU+JA2DkWl80nFTrowzLpZ/BnpN7rrl0JA/593NAdd8p/scQ=="
332 | },
333 | "node_modules/nanoid": {
334 | "version": "3.3.4",
335 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
336 | "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
337 | "bin": {
338 | "nanoid": "bin/nanoid.cjs"
339 | },
340 | "engines": {
341 | "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
342 | }
343 | },
344 | "node_modules/node-fetch": {
345 | "version": "2.6.7",
346 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
347 | "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
348 | "dependencies": {
349 | "whatwg-url": "^5.0.0"
350 | },
351 | "engines": {
352 | "node": "4.x || >=6.0.0"
353 | },
354 | "peerDependencies": {
355 | "encoding": "^0.1.0"
356 | },
357 | "peerDependenciesMeta": {
358 | "encoding": {
359 | "optional": true
360 | }
361 | }
362 | },
363 | "node_modules/nth-check": {
364 | "version": "1.0.2",
365 | "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz",
366 | "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==",
367 | "dependencies": {
368 | "boolbase": "~1.0.0"
369 | }
370 | },
371 | "node_modules/parse-srcset": {
372 | "version": "1.0.2",
373 | "resolved": "https://registry.npmjs.org/parse-srcset/-/parse-srcset-1.0.2.tgz",
374 | "integrity": "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q=="
375 | },
376 | "node_modules/picocolors": {
377 | "version": "1.0.0",
378 | "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
379 | "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
380 | },
381 | "node_modules/postcss": {
382 | "version": "8.4.14",
383 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz",
384 | "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==",
385 | "funding": [
386 | {
387 | "type": "opencollective",
388 | "url": "https://opencollective.com/postcss/"
389 | },
390 | {
391 | "type": "tidelift",
392 | "url": "https://tidelift.com/funding/github/npm/postcss"
393 | }
394 | ],
395 | "dependencies": {
396 | "nanoid": "^3.3.4",
397 | "picocolors": "^1.0.0",
398 | "source-map-js": "^1.0.2"
399 | },
400 | "engines": {
401 | "node": "^10 || ^12 || >=14"
402 | }
403 | },
404 | "node_modules/readable-stream": {
405 | "version": "3.6.0",
406 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
407 | "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
408 | "dependencies": {
409 | "inherits": "^2.0.3",
410 | "string_decoder": "^1.1.1",
411 | "util-deprecate": "^1.0.1"
412 | },
413 | "engines": {
414 | "node": ">= 6"
415 | }
416 | },
417 | "node_modules/safe-buffer": {
418 | "version": "5.2.1",
419 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
420 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
421 | "funding": [
422 | {
423 | "type": "github",
424 | "url": "https://github.com/sponsors/feross"
425 | },
426 | {
427 | "type": "patreon",
428 | "url": "https://www.patreon.com/feross"
429 | },
430 | {
431 | "type": "consulting",
432 | "url": "https://feross.org/support"
433 | }
434 | ]
435 | },
436 | "node_modules/sanitize-html": {
437 | "version": "2.7.0",
438 | "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.7.0.tgz",
439 | "integrity": "sha512-jfQelabOn5voO7FAfnQF7v+jsA6z9zC/O4ec0z3E35XPEtHYJT/OdUziVWlKW4irCr2kXaQAyXTXDHWAibg1tA==",
440 | "dependencies": {
441 | "deepmerge": "^4.2.2",
442 | "escape-string-regexp": "^4.0.0",
443 | "htmlparser2": "^6.0.0",
444 | "is-plain-object": "^5.0.0",
445 | "parse-srcset": "^1.0.2",
446 | "postcss": "^8.3.11"
447 | }
448 | },
449 | "node_modules/source-map-js": {
450 | "version": "1.0.2",
451 | "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
452 | "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
453 | "engines": {
454 | "node": ">=0.10.0"
455 | }
456 | },
457 | "node_modules/string_decoder": {
458 | "version": "1.3.0",
459 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
460 | "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
461 | "dependencies": {
462 | "safe-buffer": "~5.2.0"
463 | }
464 | },
465 | "node_modules/tr46": {
466 | "version": "0.0.3",
467 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
468 | "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o="
469 | },
470 | "node_modules/truncate-html": {
471 | "version": "1.0.4",
472 | "resolved": "https://registry.npmjs.org/truncate-html/-/truncate-html-1.0.4.tgz",
473 | "integrity": "sha512-FpDAlPzpJ3jlZiNEahRs584FS3jOSQafgj4cC9DmAYPct6uMZDLY625+eErRd43G35vGDrNq3i7b4aYUQ/Bxqw==",
474 | "dependencies": {
475 | "@types/cheerio": "^0.22.8",
476 | "cheerio": "0.22.0"
477 | }
478 | },
479 | "node_modules/util-deprecate": {
480 | "version": "1.0.2",
481 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
482 | "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
483 | },
484 | "node_modules/webidl-conversions": {
485 | "version": "3.0.1",
486 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
487 | "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE="
488 | },
489 | "node_modules/whatwg-url": {
490 | "version": "5.0.0",
491 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
492 | "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=",
493 | "dependencies": {
494 | "tr46": "~0.0.3",
495 | "webidl-conversions": "^3.0.0"
496 | }
497 | }
498 | },
499 | "dependencies": {
500 | "@tweetback/canonical": {
501 | "version": "2.0.27",
502 | "resolved": "https://registry.npmjs.org/@tweetback/canonical/-/canonical-2.0.27.tgz",
503 | "integrity": "sha512-kfSBGynrL/nDBp0oQyCXo5geZi2+q9A63IrmtKaVHCmPMhFs6U8USnnFB3Ldft7XKEyDjM/EGFB7SOnT09OwqA=="
504 | },
505 | "@types/cheerio": {
506 | "version": "0.22.31",
507 | "resolved": "https://registry.npmjs.org/@types/cheerio/-/cheerio-0.22.31.tgz",
508 | "integrity": "sha512-Kt7Cdjjdi2XWSfrZ53v4Of0wG3ZcmaegFXjMmz9tfNrZSkzzo36G0AL1YqSdcIA78Etjt6E609pt5h1xnQkPUw==",
509 | "requires": {
510 | "@types/node": "*"
511 | }
512 | },
513 | "@types/node": {
514 | "version": "18.0.0",
515 | "resolved": "https://registry.npmjs.org/@types/node/-/node-18.0.0.tgz",
516 | "integrity": "sha512-cHlGmko4gWLVI27cGJntjs/Sj8th9aYwplmZFwmmgYQQvL5NUsgVJG7OddLvNfLqYS31KFN0s3qlaD9qCaxACA=="
517 | },
518 | "boolbase": {
519 | "version": "1.0.0",
520 | "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
521 | "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="
522 | },
523 | "cheerio": {
524 | "version": "0.22.0",
525 | "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.22.0.tgz",
526 | "integrity": "sha512-8/MzidM6G/TgRelkzDG13y3Y9LxBjCb+8yOEZ9+wwq5gVF2w2pV0wmHvjfT0RvuxGyR7UEuK36r+yYMbT4uKgA==",
527 | "requires": {
528 | "css-select": "~1.2.0",
529 | "dom-serializer": "~0.1.0",
530 | "entities": "~1.1.1",
531 | "htmlparser2": "^3.9.1",
532 | "lodash.assignin": "^4.0.9",
533 | "lodash.bind": "^4.1.4",
534 | "lodash.defaults": "^4.0.1",
535 | "lodash.filter": "^4.4.0",
536 | "lodash.flatten": "^4.2.0",
537 | "lodash.foreach": "^4.3.0",
538 | "lodash.map": "^4.4.0",
539 | "lodash.merge": "^4.4.0",
540 | "lodash.pick": "^4.2.1",
541 | "lodash.reduce": "^4.4.0",
542 | "lodash.reject": "^4.4.0",
543 | "lodash.some": "^4.4.0"
544 | },
545 | "dependencies": {
546 | "dom-serializer": {
547 | "version": "0.1.1",
548 | "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz",
549 | "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==",
550 | "requires": {
551 | "domelementtype": "^1.3.0",
552 | "entities": "^1.1.1"
553 | }
554 | },
555 | "domelementtype": {
556 | "version": "1.3.1",
557 | "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz",
558 | "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w=="
559 | },
560 | "domhandler": {
561 | "version": "2.4.2",
562 | "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz",
563 | "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==",
564 | "requires": {
565 | "domelementtype": "1"
566 | }
567 | },
568 | "domutils": {
569 | "version": "1.7.0",
570 | "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz",
571 | "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==",
572 | "requires": {
573 | "dom-serializer": "0",
574 | "domelementtype": "1"
575 | }
576 | },
577 | "entities": {
578 | "version": "1.1.2",
579 | "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz",
580 | "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w=="
581 | },
582 | "htmlparser2": {
583 | "version": "3.10.1",
584 | "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz",
585 | "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==",
586 | "requires": {
587 | "domelementtype": "^1.3.1",
588 | "domhandler": "^2.3.0",
589 | "domutils": "^1.5.1",
590 | "entities": "^1.1.1",
591 | "inherits": "^2.0.1",
592 | "readable-stream": "^3.1.1"
593 | }
594 | }
595 | }
596 | },
597 | "css-select": {
598 | "version": "1.2.0",
599 | "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz",
600 | "integrity": "sha512-dUQOBoqdR7QwV90WysXPLXG5LO7nhYBgiWVfxF80DKPF8zx1t/pUd2FYy73emg3zrjtM6dzmYgbHKfV2rxiHQA==",
601 | "requires": {
602 | "boolbase": "~1.0.0",
603 | "css-what": "2.1",
604 | "domutils": "1.5.1",
605 | "nth-check": "~1.0.1"
606 | },
607 | "dependencies": {
608 | "dom-serializer": {
609 | "version": "0.2.2",
610 | "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz",
611 | "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==",
612 | "requires": {
613 | "domelementtype": "^2.0.1",
614 | "entities": "^2.0.0"
615 | }
616 | },
617 | "domutils": {
618 | "version": "1.5.1",
619 | "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz",
620 | "integrity": "sha512-gSu5Oi/I+3wDENBsOWBiRK1eoGxcywYSqg3rR960/+EfY0CF4EX1VPkgHOZ3WiS/Jg2DtliF6BhWcHlfpYUcGw==",
621 | "requires": {
622 | "dom-serializer": "0",
623 | "domelementtype": "1"
624 | },
625 | "dependencies": {
626 | "domelementtype": {
627 | "version": "1.3.1",
628 | "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz",
629 | "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w=="
630 | }
631 | }
632 | }
633 | }
634 | },
635 | "css-what": {
636 | "version": "2.1.3",
637 | "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz",
638 | "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg=="
639 | },
640 | "deepmerge": {
641 | "version": "4.2.2",
642 | "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz",
643 | "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg=="
644 | },
645 | "dom-serializer": {
646 | "version": "1.4.1",
647 | "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
648 | "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==",
649 | "requires": {
650 | "domelementtype": "^2.0.1",
651 | "domhandler": "^4.2.0",
652 | "entities": "^2.0.0"
653 | }
654 | },
655 | "domelementtype": {
656 | "version": "2.3.0",
657 | "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
658 | "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="
659 | },
660 | "domhandler": {
661 | "version": "4.3.1",
662 | "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz",
663 | "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==",
664 | "requires": {
665 | "domelementtype": "^2.2.0"
666 | }
667 | },
668 | "domutils": {
669 | "version": "2.8.0",
670 | "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
671 | "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
672 | "requires": {
673 | "dom-serializer": "^1.0.1",
674 | "domelementtype": "^2.2.0",
675 | "domhandler": "^4.2.0"
676 | }
677 | },
678 | "entities": {
679 | "version": "2.2.0",
680 | "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
681 | "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A=="
682 | },
683 | "escape-string-regexp": {
684 | "version": "4.0.0",
685 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
686 | "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="
687 | },
688 | "html-entities": {
689 | "version": "2.3.3",
690 | "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz",
691 | "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA=="
692 | },
693 | "htmlparser2": {
694 | "version": "6.1.0",
695 | "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz",
696 | "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==",
697 | "requires": {
698 | "domelementtype": "^2.0.1",
699 | "domhandler": "^4.0.0",
700 | "domutils": "^2.5.2",
701 | "entities": "^2.0.0"
702 | }
703 | },
704 | "inherits": {
705 | "version": "2.0.4",
706 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
707 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
708 | },
709 | "is-plain-object": {
710 | "version": "5.0.0",
711 | "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
712 | "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q=="
713 | },
714 | "lodash.assignin": {
715 | "version": "4.2.0",
716 | "resolved": "https://registry.npmjs.org/lodash.assignin/-/lodash.assignin-4.2.0.tgz",
717 | "integrity": "sha512-yX/rx6d/UTVh7sSVWVSIMjfnz95evAgDFdb1ZozC35I9mSFCkmzptOzevxjgbQUsc78NR44LVHWjsoMQXy9FDg=="
718 | },
719 | "lodash.bind": {
720 | "version": "4.2.1",
721 | "resolved": "https://registry.npmjs.org/lodash.bind/-/lodash.bind-4.2.1.tgz",
722 | "integrity": "sha512-lxdsn7xxlCymgLYo1gGvVrfHmkjDiyqVv62FAeF2i5ta72BipE1SLxw8hPEPLhD4/247Ijw07UQH7Hq/chT5LA=="
723 | },
724 | "lodash.defaults": {
725 | "version": "4.2.0",
726 | "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
727 | "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ=="
728 | },
729 | "lodash.filter": {
730 | "version": "4.6.0",
731 | "resolved": "https://registry.npmjs.org/lodash.filter/-/lodash.filter-4.6.0.tgz",
732 | "integrity": "sha512-pXYUy7PR8BCLwX5mgJ/aNtyOvuJTdZAo9EQFUvMIYugqmJxnrYaANvTbgndOzHSCSR0wnlBBfRXJL5SbWxo3FQ=="
733 | },
734 | "lodash.flatten": {
735 | "version": "4.4.0",
736 | "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz",
737 | "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g=="
738 | },
739 | "lodash.foreach": {
740 | "version": "4.5.0",
741 | "resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz",
742 | "integrity": "sha512-aEXTF4d+m05rVOAUG3z4vZZ4xVexLKZGF0lIxuHZ1Hplpk/3B6Z1+/ICICYRLm7c41Z2xiejbkCkJoTlypoXhQ=="
743 | },
744 | "lodash.map": {
745 | "version": "4.6.0",
746 | "resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz",
747 | "integrity": "sha512-worNHGKLDetmcEYDvh2stPCrrQRkP20E4l0iIS7F8EvzMqBBi7ltvFN5m1HvTf1P7Jk1txKhvFcmYsCr8O2F1Q=="
748 | },
749 | "lodash.merge": {
750 | "version": "4.6.2",
751 | "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
752 | "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="
753 | },
754 | "lodash.pick": {
755 | "version": "4.4.0",
756 | "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz",
757 | "integrity": "sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q=="
758 | },
759 | "lodash.reduce": {
760 | "version": "4.6.0",
761 | "resolved": "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz",
762 | "integrity": "sha512-6raRe2vxCYBhpBu+B+TtNGUzah+hQjVdu3E17wfusjyrXBka2nBS8OH/gjVZ5PvHOhWmIZTYri09Z6n/QfnNMw=="
763 | },
764 | "lodash.reject": {
765 | "version": "4.6.0",
766 | "resolved": "https://registry.npmjs.org/lodash.reject/-/lodash.reject-4.6.0.tgz",
767 | "integrity": "sha512-qkTuvgEzYdyhiJBx42YPzPo71R1aEr0z79kAv7Ixg8wPFEjgRgJdUsGMG3Hf3OYSF/kHI79XhNlt+5Ar6OzwxQ=="
768 | },
769 | "lodash.some": {
770 | "version": "4.6.0",
771 | "resolved": "https://registry.npmjs.org/lodash.some/-/lodash.some-4.6.0.tgz",
772 | "integrity": "sha512-j7MJE+TuT51q9ggt4fSgVqro163BEFjAt3u97IqU+JA2DkWl80nFTrowzLpZ/BnpN7rrl0JA/593NAdd8p/scQ=="
773 | },
774 | "nanoid": {
775 | "version": "3.3.4",
776 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
777 | "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw=="
778 | },
779 | "node-fetch": {
780 | "version": "2.6.7",
781 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
782 | "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
783 | "requires": {
784 | "whatwg-url": "^5.0.0"
785 | }
786 | },
787 | "nth-check": {
788 | "version": "1.0.2",
789 | "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz",
790 | "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==",
791 | "requires": {
792 | "boolbase": "~1.0.0"
793 | }
794 | },
795 | "parse-srcset": {
796 | "version": "1.0.2",
797 | "resolved": "https://registry.npmjs.org/parse-srcset/-/parse-srcset-1.0.2.tgz",
798 | "integrity": "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q=="
799 | },
800 | "picocolors": {
801 | "version": "1.0.0",
802 | "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
803 | "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
804 | },
805 | "postcss": {
806 | "version": "8.4.14",
807 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz",
808 | "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==",
809 | "requires": {
810 | "nanoid": "^3.3.4",
811 | "picocolors": "^1.0.0",
812 | "source-map-js": "^1.0.2"
813 | }
814 | },
815 | "readable-stream": {
816 | "version": "3.6.0",
817 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
818 | "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
819 | "requires": {
820 | "inherits": "^2.0.3",
821 | "string_decoder": "^1.1.1",
822 | "util-deprecate": "^1.0.1"
823 | }
824 | },
825 | "safe-buffer": {
826 | "version": "5.2.1",
827 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
828 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
829 | },
830 | "sanitize-html": {
831 | "version": "2.7.0",
832 | "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.7.0.tgz",
833 | "integrity": "sha512-jfQelabOn5voO7FAfnQF7v+jsA6z9zC/O4ec0z3E35XPEtHYJT/OdUziVWlKW4irCr2kXaQAyXTXDHWAibg1tA==",
834 | "requires": {
835 | "deepmerge": "^4.2.2",
836 | "escape-string-regexp": "^4.0.0",
837 | "htmlparser2": "^6.0.0",
838 | "is-plain-object": "^5.0.0",
839 | "parse-srcset": "^1.0.2",
840 | "postcss": "^8.3.11"
841 | }
842 | },
843 | "source-map-js": {
844 | "version": "1.0.2",
845 | "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
846 | "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw=="
847 | },
848 | "string_decoder": {
849 | "version": "1.3.0",
850 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
851 | "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
852 | "requires": {
853 | "safe-buffer": "~5.2.0"
854 | }
855 | },
856 | "tr46": {
857 | "version": "0.0.3",
858 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
859 | "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o="
860 | },
861 | "truncate-html": {
862 | "version": "1.0.4",
863 | "resolved": "https://registry.npmjs.org/truncate-html/-/truncate-html-1.0.4.tgz",
864 | "integrity": "sha512-FpDAlPzpJ3jlZiNEahRs584FS3jOSQafgj4cC9DmAYPct6uMZDLY625+eErRd43G35vGDrNq3i7b4aYUQ/Bxqw==",
865 | "requires": {
866 | "@types/cheerio": "^0.22.8",
867 | "cheerio": "0.22.0"
868 | }
869 | },
870 | "util-deprecate": {
871 | "version": "1.0.2",
872 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
873 | "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
874 | },
875 | "webidl-conversions": {
876 | "version": "3.0.1",
877 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
878 | "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE="
879 | },
880 | "whatwg-url": {
881 | "version": "5.0.0",
882 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
883 | "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=",
884 | "requires": {
885 | "tr46": "~0.0.3",
886 | "webidl-conversions": "^3.0.0"
887 | }
888 | }
889 | }
890 | }
891 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "eleventy-plugin-webmentions",
3 | "version": "2.1.0",
4 | "description": "An eleventy plugin to fetch webmentions and helper methods to display them",
5 | "main": ".eleventy.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "author": "Luke Bonaccorsi",
10 | "license": "MIT",
11 | "dependencies": {
12 | "@tweetback/canonical": "^2.0.27",
13 | "html-entities": "^2.3.3",
14 | "node-fetch": "^2.6.6",
15 | "sanitize-html": "^2.7.0",
16 | "truncate-html": "^1.0.4"
17 | },
18 | "repository": {
19 | "type": "git",
20 | "url": "git+https://github.com/CodeFoodPixels/eleventy-plugin-webmentions.git"
21 | },
22 | "keywords": [
23 | "Eleventy",
24 | "11ty",
25 | "webmentions"
26 | ],
27 | "bugs": {
28 | "url": "https://github.com/CodeFoodPixels/eleventy-plugin-webmentions/issues"
29 | },
30 | "homepage": "https://github.com/CodeFoodPixels/eleventy-plugin-webmentions#readme"
31 | }
32 |
--------------------------------------------------------------------------------
/webmentions.js:
--------------------------------------------------------------------------------
1 | const fs = require("fs").promises;
2 | const fetch = require("node-fetch");
3 | const truncateHTML = require("truncate-html");
4 | const sanitizeHTML = require("sanitize-html");
5 | const { encode } = require("html-entities");
6 | const canonical = import("@tweetback/canonical");
7 |
8 | const defaults = {
9 | cacheDirectory: "./_webmentioncache",
10 | cacheTime: 3600,
11 | truncate: true,
12 | maxContentLength: 280,
13 | truncationMarker: "…",
14 | htmlContent: true,
15 | useCanonicalTwitterUrls: true,
16 | sanitizeOptions: {
17 | allowedTags: ["b", "i", "em", "strong", "a", "p"],
18 | allowedAttributes: {
19 | a: ["href"],
20 | },
21 | },
22 | sortFunction: (a, b) =>
23 | new Date(a.published || a["wm-received"]) -
24 | new Date(b.published || b["wm-received"]),
25 | };
26 | function Webmentions({
27 | domain,
28 | token,
29 | cacheDirectory = defaults.cacheDirectory,
30 | cacheTime = defaults.cacheTime,
31 | truncate = defaults.truncate,
32 | maxContentLength = defaults.maxContentLength,
33 | truncationMarker = defaults.truncationMarker,
34 | htmlContent = defaults.htmlContent,
35 | useCanonicalTwitterUrls = defaults.useCanonicalTwitterUrls,
36 | sanitizeOptions = defaults.sanitizeOptions,
37 | sortFunction = defaults.sortFunction,
38 | }) {
39 | if (
40 | (typeof domain !== "string" && !Array.isArray(domain)) ||
41 | domain.length === 0
42 | ) {
43 | throw new Error("Domain must be provided as a string");
44 | }
45 |
46 | if (!Array.isArray(domain)) {
47 | domain = [domain];
48 | }
49 |
50 | if (
51 | (typeof token !== "string" && !Array.isArray(token)) ||
52 | token.length === 0
53 | ) {
54 | throw new Error("Token must be provided as a string.");
55 | }
56 |
57 | if (!Array.isArray(token)) {
58 | token = [token];
59 | }
60 |
61 | function getUrl(idx) {
62 | return `https://webmention.io/api/mentions.jf2?domain=${domain[idx]}&token=${token[idx]}`;
63 | }
64 |
65 | async function fetchWebmentions(idx, since, page = 0) {
66 | const PER_PAGE = 1000;
67 |
68 | const params = `&per-page=${PER_PAGE}&page=${page}${
69 | since ? `&since=${since}` : ""
70 | }`;
71 | console.log(`Getting ${getUrl(idx)}${params}`);
72 | const response = await fetch(`${getUrl(idx)}${params}`);
73 |
74 | if (response.ok) {
75 | const feed = await response.json();
76 | if (feed.children.length === PER_PAGE) {
77 | const olderMentions = await fetchWebmentions(idx, since, page + 1);
78 |
79 | return [...feed.children, ...olderMentions];
80 | }
81 | return feed.children;
82 | }
83 |
84 | return [];
85 | }
86 |
87 | async function writeToCache(data) {
88 | const filePath = `${cacheDirectory}/webmentions.json`;
89 | const fileContent = JSON.stringify(data, null, 2);
90 |
91 | // create cache folder if it doesnt exist already
92 | if (!(await fs.stat(cacheDirectory).catch(() => false))) {
93 | await fs.mkdir(cacheDirectory);
94 | }
95 | // write data to cache json file
96 | await fs.writeFile(filePath, fileContent);
97 | }
98 |
99 | async function readFromCache() {
100 | const filePath = `${cacheDirectory}/webmentions.json`;
101 |
102 | if (await fs.stat(filePath).catch(() => false)) {
103 | const cacheFile = await fs.readFile(filePath);
104 | return JSON.parse(cacheFile);
105 | }
106 |
107 | return {
108 | lastFetched: null,
109 | children: [],
110 | };
111 | }
112 |
113 | async function clean(entry) {
114 | const { transform } = await canonical;
115 |
116 | if (useCanonicalTwitterUrls) {
117 | entry.url = transform(entry.url);
118 | entry.author.url = transform(entry.author.url);
119 | }
120 |
121 | if (entry.content) {
122 | if (entry.content.html && htmlContent) {
123 | if (useCanonicalTwitterUrls) {
124 | entry.content.html = entry.content.html.replaceAll(
125 | /"(https:\/\/twitter.com\/(.+?))"/g,
126 | function (match, p1) {
127 | return transform(p1);
128 | }
129 | );
130 | }
131 |
132 | if (!entry.content.html.match(/^<\/?[a-z][\s\S]*>/)) {
133 | const paragraphs = entry.content.html
134 | .split("\n")
135 | .filter((p) => p.length > 0);
136 |
137 | entry.content.html = `