@chrisburnell/eleventy-cache-webmentions 1.2.0 → 1.2.2
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 +85 -26
- package/eleventy-cache-webmentions.js +27 -96
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -32,6 +32,91 @@ module.exports = function (eleventyConfig) {
|
|
|
32
32
|
|
|
33
33
|
## Options
|
|
34
34
|
|
|
35
|
+
<table>
|
|
36
|
+
<thead>
|
|
37
|
+
<tr>
|
|
38
|
+
<th>option</th>
|
|
39
|
+
<th>default value</th>
|
|
40
|
+
<th>description</th>
|
|
41
|
+
<th>version added</th>
|
|
42
|
+
</tr>
|
|
43
|
+
</thead>
|
|
44
|
+
<tbody>
|
|
45
|
+
<tr>
|
|
46
|
+
<td><code>domain</code><br><em>required</em></td>
|
|
47
|
+
<td>—</td>
|
|
48
|
+
<td>The website you’re fetching Webmentions for.</td>
|
|
49
|
+
<td>0.0.1</td>
|
|
50
|
+
</tr>
|
|
51
|
+
<tr>
|
|
52
|
+
<td><code>feed</code><br><em>required</em></td>
|
|
53
|
+
<td>—</td>
|
|
54
|
+
<td>The URL of your Webmention server’s feed for your <code>domain</code>.</td>
|
|
55
|
+
<td>0.2.0</td>
|
|
56
|
+
</tr>
|
|
57
|
+
<tr>
|
|
58
|
+
<td><code>key</code><br><em>required</em></td>
|
|
59
|
+
<td>—</td>
|
|
60
|
+
<td>The key in the above <code>feed</code> whose value is an Array of Webmentions.</td>
|
|
61
|
+
<td>0.0.1</td>
|
|
62
|
+
</tr>
|
|
63
|
+
<tr>
|
|
64
|
+
<td><code>directory</code></td>
|
|
65
|
+
<td><code>".cache"</code></td>
|
|
66
|
+
<td>See <a href="https://www.11ty.dev/docs/plugins/cache/#cache-directory">Eleventy Fetch’s Cache Directory</a> for more information.</td>
|
|
67
|
+
<td>1.1.2</td>
|
|
68
|
+
</tr>
|
|
69
|
+
<tr>
|
|
70
|
+
<td><code>duration</code></td>
|
|
71
|
+
<td><code>"1d"</code> <em>or</em> 1 day</td>
|
|
72
|
+
<td>See <a href="https://www.11ty.dev/docs/plugins/cache/#change-the-cache-duration">Eleventy Fetch’s Cache Duration</a> for more information.</td>
|
|
73
|
+
<td>0.0.1</td>
|
|
74
|
+
</tr>
|
|
75
|
+
<tr>
|
|
76
|
+
<td><code>uniqueKey</code></td>
|
|
77
|
+
<td><code>"webmentions"</code></td>
|
|
78
|
+
<td>The name of the file generated by Eleventy Fetch.</td>
|
|
79
|
+
<td>0.1.9</td>
|
|
80
|
+
</tr>
|
|
81
|
+
<tr>
|
|
82
|
+
<td><code>allowedHTML</code></td>
|
|
83
|
+
<td>See code example below</td>
|
|
84
|
+
<td>See the <a href="https://www.npmjs.com/package/sanitize-html">sanitize-html</a> package for more information.</td>
|
|
85
|
+
<td>0.0.1</td>
|
|
86
|
+
</tr>
|
|
87
|
+
<tr>
|
|
88
|
+
<td><code>allowlist</code></td>
|
|
89
|
+
<td><code>[]</code></td>
|
|
90
|
+
<td>An Array of root URLs from which Webmentions are kept.</td>
|
|
91
|
+
<td>1.1.0</td>
|
|
92
|
+
</tr>
|
|
93
|
+
<tr>
|
|
94
|
+
<td><code>blocklist</code></td>
|
|
95
|
+
<td><code>[]</code></td>
|
|
96
|
+
<td>An Array of root URLs from which Webmentions are discarded.</td>
|
|
97
|
+
<td>1.1.0</td>
|
|
98
|
+
</tr>
|
|
99
|
+
<tr>
|
|
100
|
+
<td><code>urlReplacements</code></td>
|
|
101
|
+
<td><code>{}</code></td>
|
|
102
|
+
<td>An Object of key-value string pairs containing from-to URL replacements on this <code>domain</code>.</td>
|
|
103
|
+
<td>0.0.3</td>
|
|
104
|
+
</tr>
|
|
105
|
+
<tr>
|
|
106
|
+
<td><code>maximumHtmlLength</code></td>
|
|
107
|
+
<td><code>2000</code></td>
|
|
108
|
+
<td>Maximum number of characters in a Webmention’s HTML content, beyond which point a different message is shown, referring to the original source.</td>
|
|
109
|
+
<td>0.0.1</td>
|
|
110
|
+
</tr>
|
|
111
|
+
<tr>
|
|
112
|
+
<td><code>maximumHtmlText</code></td>
|
|
113
|
+
<td><code>"mentioned this in"</code></td>
|
|
114
|
+
<td>The glue-y part of the message displayed when a Webmention content’s character count exceeds <code>maximumHtmlLength</code>.</td>
|
|
115
|
+
<td>0.1.0</td>
|
|
116
|
+
</tr>
|
|
117
|
+
</tbody>
|
|
118
|
+
</table>
|
|
119
|
+
|
|
35
120
|
Advanced control over how the Webmentions are cached and processed is done by passing `options` into the plugin when using `addPlugin`:
|
|
36
121
|
|
|
37
122
|
```javascript
|
|
@@ -39,48 +124,22 @@ const pluginWebmentions = require("@chrisburnell/eleventy-cache-webmentions")
|
|
|
39
124
|
|
|
40
125
|
module.exports = function (eleventyConfig) {
|
|
41
126
|
eleventyConfig.addPlugin(pluginWebmentions, {
|
|
42
|
-
// domain: required or the plugin will not function
|
|
43
|
-
// this is the website that you want to pull in Webmentions for
|
|
44
127
|
domain: "https://example.com",
|
|
45
|
-
// feed: required or the plugin will not function
|
|
46
|
-
// defines the URL of your Webmention server where a feed of Webmentions for your domain can be found
|
|
47
128
|
feed: "https://webmentions.example.com?token=S3cr3tT0k3n",
|
|
48
|
-
// key: required or the plugin will not function
|
|
49
|
-
// dictates the key inside the feed where the array of Webmentions is located
|
|
50
129
|
key: "children",
|
|
51
|
-
// directory: ".cache" by default
|
|
52
|
-
// see https://www.11ty.dev/docs/plugins/cache/#cache-directory for more info
|
|
53
130
|
directory: ".cache",
|
|
54
|
-
// duration: "1d" by default
|
|
55
|
-
// see https://www.11ty.dev/docs/plugins/cache/#change-the-cache-duration for more info
|
|
56
131
|
duration: "1d",
|
|
57
|
-
// uniquekey: "webmentions" by default
|
|
58
|
-
// dictates the name sent to eleventy-fetch to name the file
|
|
59
132
|
uniqueKey: "webmentions",
|
|
60
|
-
// allowedHTML: Object by default
|
|
61
|
-
// see https://www.npmjs.com/package/sanitize-html for more info
|
|
62
133
|
allowedHTML: {
|
|
63
134
|
allowedTags: ["b", "i", "em", "strong", "a"],
|
|
64
135
|
allowedAttributes: {
|
|
65
136
|
a: ["href"],
|
|
66
137
|
},
|
|
67
138
|
},
|
|
68
|
-
// allowlist: [] by default
|
|
69
|
-
// array of root URLs from which webmentions are wanted exclusively
|
|
70
139
|
allowlist: [],
|
|
71
|
-
// blocklist: [] by default
|
|
72
|
-
// array of root URLs from which webmentions are not wanted
|
|
73
|
-
// exclusively
|
|
74
140
|
blocklist: [],
|
|
75
|
-
// urlReplacements: {} by default
|
|
76
|
-
// object of key:value pairs containing from:to URL replacements
|
|
77
141
|
urlReplacements: {},
|
|
78
|
-
// maximumHtmlLength: 2000 by default
|
|
79
|
-
// number of characters in the HTML content at which a different
|
|
80
|
-
// message is shown instead of the content
|
|
81
142
|
maximumHtmlLength: 2000,
|
|
82
|
-
// maximumHtmlText: "mentioned this in" by default
|
|
83
|
-
// message shown when maximumHtmlLength is reached
|
|
84
143
|
maximumHtmlText: "mentioned this in",
|
|
85
144
|
})
|
|
86
145
|
}
|
|
@@ -7,9 +7,7 @@ const absoluteURL = (url, domain) => {
|
|
|
7
7
|
try {
|
|
8
8
|
return new URL(url, domain).toString()
|
|
9
9
|
} catch (e) {
|
|
10
|
-
console.log(
|
|
11
|
-
`Trying to convert ${url} to be an absolute url with base ${domain} and failed.`
|
|
12
|
-
)
|
|
10
|
+
console.log(`Trying to convert ${url} to be an absolute url with base ${domain} and failed.`)
|
|
13
11
|
return url
|
|
14
12
|
}
|
|
15
13
|
}
|
|
@@ -21,13 +19,10 @@ const baseUrl = (url) => {
|
|
|
21
19
|
}
|
|
22
20
|
|
|
23
21
|
const fixUrl = (url, urlReplacements) => {
|
|
24
|
-
return Object.entries(urlReplacements).reduce(
|
|
25
|
-
(
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
},
|
|
29
|
-
url
|
|
30
|
-
)
|
|
22
|
+
return Object.entries(urlReplacements).reduce((accumulator, [key, value]) => {
|
|
23
|
+
const regex = new RegExp(key, "g")
|
|
24
|
+
return accumulator.replace(regex, value)
|
|
25
|
+
}, url)
|
|
31
26
|
}
|
|
32
27
|
|
|
33
28
|
const hostname = (value) => {
|
|
@@ -43,30 +38,15 @@ const epoch = (value) => {
|
|
|
43
38
|
}
|
|
44
39
|
|
|
45
40
|
const getPublished = (webmention) => {
|
|
46
|
-
return
|
|
47
|
-
webmention["wm-received"] ||
|
|
48
|
-
webmention?.["data"]["published"] ||
|
|
49
|
-
webmention["verified_date"] ||
|
|
50
|
-
webmention["published"]
|
|
51
|
-
)
|
|
41
|
+
return webmention["wm-received"] || webmention?.["data"]["published"] || webmention["verified_date"] || webmention["published"]
|
|
52
42
|
}
|
|
53
43
|
|
|
54
44
|
const getContent = (webmention) => {
|
|
55
|
-
return
|
|
56
|
-
webmention?.["content"]?.["html"] ||
|
|
57
|
-
webmention?.["content"] ||
|
|
58
|
-
webmention?.["data"]?.["content"] ||
|
|
59
|
-
""
|
|
60
|
-
)
|
|
45
|
+
return webmention?.["content"]?.["html"] || webmention?.["content"] || webmention?.["data"]?.["content"] || ""
|
|
61
46
|
}
|
|
62
47
|
|
|
63
48
|
const getSource = (webmention) => {
|
|
64
|
-
return
|
|
65
|
-
webmention["url"] ||
|
|
66
|
-
webmention?.["data"]["url"] ||
|
|
67
|
-
webmention["wm-source"] ||
|
|
68
|
-
webmention["source"]
|
|
69
|
-
)
|
|
49
|
+
return webmention["url"] || webmention?.["data"]["url"] || webmention["wm-source"] || webmention["source"]
|
|
70
50
|
}
|
|
71
51
|
|
|
72
52
|
const getTarget = (webmention) => {
|
|
@@ -74,14 +54,11 @@ const getTarget = (webmention) => {
|
|
|
74
54
|
}
|
|
75
55
|
|
|
76
56
|
const getType = (webmention) => {
|
|
77
|
-
return
|
|
78
|
-
webmention["wm-property"] ||
|
|
79
|
-
webmention?.["activity"]["type"] ||
|
|
80
|
-
webmention["type"]
|
|
81
|
-
)
|
|
57
|
+
return webmention["wm-property"] || webmention?.["activity"]["type"] || webmention["type"]
|
|
82
58
|
}
|
|
83
59
|
|
|
84
60
|
const defaults = {
|
|
61
|
+
duration: "1d",
|
|
85
62
|
uniqueKey: "webmentions",
|
|
86
63
|
allowedHTML: {
|
|
87
64
|
allowedTags: ["b", "i", "em", "strong", "a"],
|
|
@@ -98,27 +75,15 @@ const defaults = {
|
|
|
98
75
|
|
|
99
76
|
const fetchWebmentions = async (options) => {
|
|
100
77
|
if (!options.domain) {
|
|
101
|
-
throw new Error(
|
|
102
|
-
"domain is a required field when attempting to retrieve Webmentions. See https://www.npmjs.com/package/@chrisburnell/eleventy-cache-webmentions#installation for more information."
|
|
103
|
-
)
|
|
78
|
+
throw new Error("domain is a required field when attempting to retrieve Webmentions. See https://www.npmjs.com/package/@chrisburnell/eleventy-cache-webmentions#installation for more information.")
|
|
104
79
|
}
|
|
105
80
|
|
|
106
81
|
if (!options.feed) {
|
|
107
|
-
throw new Error(
|
|
108
|
-
"feed is a required field when attempting to retrieve Webmentions. See https://www.npmjs.com/package/@chrisburnell/eleventy-cache-webmentions#installation for more information."
|
|
109
|
-
)
|
|
82
|
+
throw new Error("feed is a required field when attempting to retrieve Webmentions. See https://www.npmjs.com/package/@chrisburnell/eleventy-cache-webmentions#installation for more information.")
|
|
110
83
|
}
|
|
111
84
|
|
|
112
85
|
if (!options.key) {
|
|
113
|
-
throw new Error(
|
|
114
|
-
"key is a required field when attempting to retrieve Webmentions. See https://www.npmjs.com/package/@chrisburnell/eleventy-cache-webmentions#installation for more information."
|
|
115
|
-
)
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
if (!options.uniqueKey) {
|
|
119
|
-
throw new Error(
|
|
120
|
-
"uniqueKey is a required field when attempting to retrieve Webmentions. See https://www.npmjs.com/package/@chrisburnell/eleventy-cache-webmentions#installation for more information."
|
|
121
|
-
)
|
|
86
|
+
throw new Error("key is a required field when attempting to retrieve Webmentions. See https://www.npmjs.com/package/@chrisburnell/eleventy-cache-webmentions#installation for more information.")
|
|
122
87
|
}
|
|
123
88
|
|
|
124
89
|
let asset = new AssetCache(options.uniqueKey, options.directory)
|
|
@@ -135,27 +100,16 @@ const fetchWebmentions = async (options) => {
|
|
|
135
100
|
// results since the most recent webmention
|
|
136
101
|
if (!asset.isCacheValid(options.duration)) {
|
|
137
102
|
const since = webmentions.length ? getPublished(webmentions[0]) : false
|
|
138
|
-
const url = `${options.feed}${
|
|
139
|
-
since
|
|
140
|
-
? `${options.feed.includes("?") ? "&" : "?"}since=${since}`
|
|
141
|
-
: ""
|
|
142
|
-
}`
|
|
103
|
+
const url = `${options.feed}${since ? `${options.feed.includes("?") ? "&" : "?"}since=${since}` : ""}`
|
|
143
104
|
await fetch(url)
|
|
144
105
|
.then(async (response) => {
|
|
145
106
|
if (response.ok) {
|
|
146
107
|
const feed = await response.json()
|
|
147
108
|
if (feed[options.key].length) {
|
|
148
|
-
webmentions = uniqBy(
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
}
|
|
153
|
-
)
|
|
154
|
-
console.log(
|
|
155
|
-
`[${hostname(options.domain)}] ${
|
|
156
|
-
feed[options.key].length
|
|
157
|
-
} new Webmentions fetched into cache.`
|
|
158
|
-
)
|
|
109
|
+
webmentions = uniqBy([...feed[options.key], ...webmentions], (entry) => {
|
|
110
|
+
return getSource(entry)
|
|
111
|
+
})
|
|
112
|
+
console.log(`[${hostname(options.domain)}] ${feed[options.key].length} new Webmentions fetched into cache.`)
|
|
159
113
|
}
|
|
160
114
|
await asset.save(webmentions, "json")
|
|
161
115
|
return webmentions
|
|
@@ -163,14 +117,7 @@ const fetchWebmentions = async (options) => {
|
|
|
163
117
|
return Promise.reject(response)
|
|
164
118
|
})
|
|
165
119
|
.catch((error) => {
|
|
166
|
-
console.log(
|
|
167
|
-
`[${hostname(
|
|
168
|
-
options.domain
|
|
169
|
-
)}] Something went wrong with your request to ${hostname(
|
|
170
|
-
options.feed
|
|
171
|
-
)}!`,
|
|
172
|
-
error
|
|
173
|
-
)
|
|
120
|
+
console.log(`[${hostname(options.domain)}] Something went wrong with your request to ${hostname(options.feed)}!`, error)
|
|
174
121
|
})
|
|
175
122
|
}
|
|
176
123
|
|
|
@@ -218,12 +165,7 @@ const filteredWebmentions = async (options) => {
|
|
|
218
165
|
// Fix local URLs based on urlReplacements and sort Webmentions into groups
|
|
219
166
|
// by target base URL
|
|
220
167
|
rawWebmentions.forEach((webmention) => {
|
|
221
|
-
let url = baseUrl(
|
|
222
|
-
fixUrl(
|
|
223
|
-
getTarget(webmention).replace(/\/?$/, "/"),
|
|
224
|
-
options.urlReplacements
|
|
225
|
-
)
|
|
226
|
-
)
|
|
168
|
+
let url = baseUrl(fixUrl(getTarget(webmention).replace(/\/?$/, "/"), options.urlReplacements))
|
|
227
169
|
|
|
228
170
|
if (!filtered[url]) {
|
|
229
171
|
filtered[url] = []
|
|
@@ -239,6 +181,9 @@ const filteredWebmentions = async (options) => {
|
|
|
239
181
|
})
|
|
240
182
|
}
|
|
241
183
|
|
|
184
|
+
const filteredCount = Object.values(filtered).reduce((count, webmentions) => count + webmentions.length, 0)
|
|
185
|
+
console.log(`[${hostname(options.domain)}] ${filteredCount} filtered Webmentions pulled from cache.`)
|
|
186
|
+
|
|
242
187
|
return filtered
|
|
243
188
|
}
|
|
244
189
|
|
|
@@ -254,10 +199,7 @@ const getWebmentions = async (options, url, allowedTypes = {}) => {
|
|
|
254
199
|
webmentions[url]
|
|
255
200
|
// filter webmentions by allowed response post types
|
|
256
201
|
.filter((entry) => {
|
|
257
|
-
return typeof allowedTypes === "object" &&
|
|
258
|
-
Object.keys(allowedTypes).length
|
|
259
|
-
? allowedTypes.includes(getType(entry))
|
|
260
|
-
: true
|
|
202
|
+
return typeof allowedTypes === "object" && Object.keys(allowedTypes).length ? allowedTypes.includes(getType(entry)) : true
|
|
261
203
|
})
|
|
262
204
|
// sanitize content of webmentions against HTML limit
|
|
263
205
|
.map((entry) => {
|
|
@@ -266,9 +208,7 @@ const getWebmentions = async (options, url, allowedTypes = {}) => {
|
|
|
266
208
|
if (html.length) {
|
|
267
209
|
entry.content = sanitizeHTML(html, options.allowedHTML)
|
|
268
210
|
if (html.length > options.maximumHtmlLength) {
|
|
269
|
-
entry.content = `${
|
|
270
|
-
options.maximumHtmlText
|
|
271
|
-
} <a href="${getSource(entry)}">${getSource(entry)}</a>`
|
|
211
|
+
entry.content = `${options.maximumHtmlText} <a href="${getSource(entry)}">${getSource(entry)}</a>`
|
|
272
212
|
}
|
|
273
213
|
}
|
|
274
214
|
|
|
@@ -297,13 +237,7 @@ module.exports = (eleventyConfig, options = {}) => {
|
|
|
297
237
|
// Global Data
|
|
298
238
|
const filtered = async () => await filteredWebmentions(options)
|
|
299
239
|
eleventyConfig.addGlobalData("webmentionsByUrl", filtered)
|
|
300
|
-
const unfiltered = async () =>
|
|
301
|
-
await filteredWebmentions(options).then((filtered) =>
|
|
302
|
-
Object.values(filtered).reduce(
|
|
303
|
-
(array, webmentions) => [...array, ...webmentions],
|
|
304
|
-
[]
|
|
305
|
-
)
|
|
306
|
-
)
|
|
240
|
+
const unfiltered = async () => await filteredWebmentions(options).then((filtered) => Object.values(filtered).reduce((array, webmentions) => [...array, ...webmentions], []))
|
|
307
241
|
eleventyConfig.addGlobalData("webmentionsAll", unfiltered)
|
|
308
242
|
eleventyConfig.addGlobalData("webmentions", unfiltered)
|
|
309
243
|
|
|
@@ -311,10 +245,7 @@ module.exports = (eleventyConfig, options = {}) => {
|
|
|
311
245
|
eleventyConfig.addLiquidFilter("getWebmentions", getWebmentionsFilter)
|
|
312
246
|
|
|
313
247
|
// Nunjucks Filter
|
|
314
|
-
eleventyConfig.addNunjucksAsyncFilter(
|
|
315
|
-
"getWebmentions",
|
|
316
|
-
getWebmentionsFilter
|
|
317
|
-
)
|
|
248
|
+
eleventyConfig.addNunjucksAsyncFilter("getWebmentions", getWebmentionsFilter)
|
|
318
249
|
}
|
|
319
250
|
|
|
320
251
|
return {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chrisburnell/eleventy-cache-webmentions",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.2",
|
|
4
4
|
"description": "Fetch and cache webmentions using eleventy-fetch.",
|
|
5
5
|
"author": "Chris Burnell <me@chrisburnell.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"@11ty/eleventy-fetch": "^4.0.0",
|
|
27
27
|
"lodash": "^4.17.21",
|
|
28
|
-
"node-fetch": "
|
|
28
|
+
"node-fetch": "2.6.5",
|
|
29
29
|
"sanitize-html": "^2.10.0"
|
|
30
30
|
},
|
|
31
31
|
"main": "eleventy-cache-webmentions.js",
|