@chrisburnell/eleventy-cache-webmentions 0.2.1 → 1.0.0-beta.3
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/eleventy-cache-webmentions.js +129 -120
- package/package.json +1 -1
|
@@ -2,6 +2,7 @@ const fetch = require("node-fetch")
|
|
|
2
2
|
const sanitizeHTML = require("sanitize-html")
|
|
3
3
|
const uniqBy = require("lodash/uniqBy")
|
|
4
4
|
const { AssetCache } = require("@11ty/eleventy-fetch")
|
|
5
|
+
const { defaultsDeep } = require("lodash")
|
|
5
6
|
|
|
6
7
|
const absoluteURL = (url, domain) => {
|
|
7
8
|
try {
|
|
@@ -57,156 +58,164 @@ const getType = (webmention) => {
|
|
|
57
58
|
return webmention["wm-property"] || webmention?.["activity"]["type"] || webmention["type"]
|
|
58
59
|
}
|
|
59
60
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
allowedAttributes: {
|
|
67
|
-
a: ["href"],
|
|
68
|
-
},
|
|
69
|
-
},
|
|
70
|
-
urlReplacements: {},
|
|
71
|
-
maximumHtmlLength: 2000,
|
|
72
|
-
maximumHtmlText: "mentioned this in",
|
|
61
|
+
const defaults = {
|
|
62
|
+
uniqueKey: "webmentions",
|
|
63
|
+
allowedHTML: {
|
|
64
|
+
allowedTags: ["b", "i", "em", "strong", "a"],
|
|
65
|
+
allowedAttributes: {
|
|
66
|
+
a: ["href"],
|
|
73
67
|
},
|
|
74
|
-
|
|
75
|
-
|
|
68
|
+
},
|
|
69
|
+
urlReplacements: {},
|
|
70
|
+
maximumHtmlLength: 2000,
|
|
71
|
+
maximumHtmlText: "mentioned this in",
|
|
72
|
+
}
|
|
76
73
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
74
|
+
const fetchWebmentions = async (options) => {
|
|
75
|
+
if (!options.domain) {
|
|
76
|
+
throw new Error("domain is a required field when adding the plugin to your eleventyConfig using addPlugin. See https://chrisburnell.com/eleventy-cache-webmentions/#installation for more information.")
|
|
77
|
+
}
|
|
80
78
|
|
|
81
|
-
|
|
79
|
+
if (!options.feed) {
|
|
80
|
+
throw new Error("feed is a required field when adding the plugin to your eleventyConfig using addPlugin. See https://chrisburnell.com/eleventy-cache-webmentions/#installation for more information.")
|
|
81
|
+
}
|
|
82
82
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
}
|
|
83
|
+
if (!options.key) {
|
|
84
|
+
throw new Error("key is a required field when adding the plugin to your eleventyConfig using addPlugin. See https://chrisburnell.com/eleventy-cache-webmentions/#installation for more information.")
|
|
85
|
+
}
|
|
87
86
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
const since = webmentions.length ? getPublished(webmentions[0]) : false
|
|
92
|
-
const url = `${options.feed}${since ? `${options.feed.includes("?") ? "&" : "?"}since=${since}` : ""}`
|
|
93
|
-
await fetch(url)
|
|
94
|
-
.then(async (response) => {
|
|
95
|
-
if (response.ok) {
|
|
96
|
-
const feed = await response.json()
|
|
97
|
-
if (feed[options.key].length) {
|
|
98
|
-
webmentions = uniqBy([...feed[options.key], ...webmentions], (entry) => {
|
|
99
|
-
return getSource(entry)
|
|
100
|
-
})
|
|
101
|
-
console.log(`[${hostname(options.domain)}] ${feed[options.key].length} new Webmentions fetched into cache.`)
|
|
102
|
-
}
|
|
103
|
-
await asset.save(webmentions, "json")
|
|
104
|
-
return webmentions
|
|
105
|
-
}
|
|
106
|
-
return Promise.reject(response)
|
|
107
|
-
})
|
|
108
|
-
.catch((error) => {
|
|
109
|
-
console.log(`[${hostname(options.domain)}] Something went wrong with your request to ${hostname(options.feed)}!`, error)
|
|
110
|
-
})
|
|
111
|
-
}
|
|
87
|
+
if (!options.uniqueKey) {
|
|
88
|
+
throw new Error("uniqueKey is a required field when adding the plugin to your eleventyConfig using addPlugin. See https://chrisburnell.com/eleventy-cache-webmentions/#installation for more information.")
|
|
89
|
+
}
|
|
112
90
|
|
|
113
|
-
|
|
91
|
+
let asset = new AssetCache(options.uniqueKey)
|
|
92
|
+
asset.ensureDir()
|
|
93
|
+
|
|
94
|
+
let webmentions = []
|
|
95
|
+
|
|
96
|
+
// If there is a cached file at all, grab its contents now
|
|
97
|
+
if (asset.isCacheValid("9001y")) {
|
|
98
|
+
webmentions = await asset.getCachedValue()
|
|
114
99
|
}
|
|
115
100
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
101
|
+
// If there is a cached file but it is outside of expiry, fetch fresh
|
|
102
|
+
// results since the most recent webmention
|
|
103
|
+
if (!asset.isCacheValid(options.duration)) {
|
|
104
|
+
const since = webmentions.length ? getPublished(webmentions[0]) : false
|
|
105
|
+
const url = `${options.feed}${since ? `${options.feed.includes("?") ? "&" : "?"}since=${since}` : ""}`
|
|
106
|
+
await fetch(url)
|
|
107
|
+
.then(async (response) => {
|
|
108
|
+
if (response.ok) {
|
|
109
|
+
const feed = await response.json()
|
|
110
|
+
if (feed[options.key].length) {
|
|
111
|
+
webmentions = uniqBy([...feed[options.key], ...webmentions], (entry) => {
|
|
112
|
+
return getSource(entry)
|
|
113
|
+
})
|
|
114
|
+
console.log(`[${hostname(options.domain)}] ${feed[options.key].length} new Webmentions fetched into cache.`)
|
|
115
|
+
}
|
|
116
|
+
await asset.save(webmentions, "json")
|
|
117
|
+
return webmentions
|
|
118
|
+
}
|
|
119
|
+
return Promise.reject(response)
|
|
120
|
+
})
|
|
121
|
+
.catch((error) => {
|
|
122
|
+
console.log(`[${hostname(options.domain)}] Something went wrong with your request to ${hostname(options.feed)}!`, error)
|
|
123
|
+
})
|
|
124
|
+
}
|
|
119
125
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
// Get the target of the Webmention and fix it up
|
|
123
|
-
let url = baseUrl(fixUrl(getTarget(webmention).replace(/\/?$/, "/"), options.urlReplacements))
|
|
126
|
+
return webmentions
|
|
127
|
+
}
|
|
124
128
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
129
|
+
const filteredWebmentions = async (options) => {
|
|
130
|
+
const rawWebmentions = await fetchWebmentions(options)
|
|
131
|
+
let webmentions = {}
|
|
128
132
|
|
|
129
|
-
|
|
130
|
-
|
|
133
|
+
// Sort Webmentions into groups by target
|
|
134
|
+
rawWebmentions.forEach((webmention) => {
|
|
135
|
+
// Get the target of the Webmention and fix it up
|
|
136
|
+
let url = baseUrl(fixUrl(getTarget(webmention).replace(/\/?$/, "/"), options.urlReplacements))
|
|
131
137
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
webmentions[url] = uniqBy(webmentions[url], (entry) => {
|
|
135
|
-
return getSource(entry)
|
|
136
|
-
})
|
|
138
|
+
if (!webmentions[url]) {
|
|
139
|
+
webmentions[url] = []
|
|
137
140
|
}
|
|
138
141
|
|
|
139
|
-
|
|
142
|
+
webmentions[url].push(webmention)
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
// Sort Webmentions in groups by url and remove duplicates by `url`
|
|
146
|
+
for (let url in webmentions) {
|
|
147
|
+
webmentions[url] = uniqBy(webmentions[url], (entry) => {
|
|
148
|
+
return getSource(entry)
|
|
149
|
+
})
|
|
140
150
|
}
|
|
141
151
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
url = absoluteURL(url, options.domain)
|
|
152
|
+
return webmentions
|
|
153
|
+
}
|
|
145
154
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
155
|
+
const getWebmentions = async (options, url, allowedTypes = {}) => {
|
|
156
|
+
const webmentions = await filteredWebmentions(options)
|
|
157
|
+
url = absoluteURL(url, options.domain)
|
|
149
158
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
entry.content = `${options.maximumHtmlText} <a href="${getSource(entry)}">${getSource(entry)}</a>`
|
|
163
|
-
} else if (Object.keys(options.allowedHTML).length) {
|
|
164
|
-
entry.content = sanitizeHTML(html, options.allowedHTML)
|
|
165
|
-
} else {
|
|
166
|
-
entry.content = html
|
|
167
|
-
}
|
|
159
|
+
if (!url || !webmentions || !webmentions[url]) {
|
|
160
|
+
return []
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const results = webmentions[url]
|
|
164
|
+
// filter webmentions by allowed response post types
|
|
165
|
+
.filter((entry) => {
|
|
166
|
+
return typeof allowedTypes === "object" && Object.keys(allowedTypes).length ? allowedTypes.includes(getType(entry)) : true
|
|
167
|
+
})
|
|
168
|
+
// sanitize content of webmentions against HTML limit
|
|
169
|
+
.map((entry) => {
|
|
170
|
+
if (!("content" in entry) || !("data" in entry)) {
|
|
168
171
|
return entry
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
})
|
|
172
|
+
}
|
|
173
|
+
const html = getContent(entry)
|
|
174
|
+
if (html && html.length > options.maximumHtmlLength) {
|
|
175
|
+
entry.content = `${options.maximumHtmlText} <a href="${getSource(entry)}">${getSource(entry)}</a>`
|
|
176
|
+
} else if (Object.keys(options.allowedHTML).length) {
|
|
177
|
+
entry.content = sanitizeHTML(html, options.allowedHTML)
|
|
178
|
+
} else {
|
|
179
|
+
entry.content = html
|
|
180
|
+
}
|
|
181
|
+
return entry
|
|
182
|
+
})
|
|
183
|
+
// sort by published
|
|
184
|
+
.sort((a, b) => {
|
|
185
|
+
return epoch(getPublished(a)) - epoch(getPublished(b))
|
|
186
|
+
})
|
|
174
187
|
|
|
175
|
-
|
|
176
|
-
|
|
188
|
+
return results
|
|
189
|
+
}
|
|
177
190
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
}
|
|
183
|
-
const webmentions = await getWebmentions(url, allowedTypes)
|
|
184
|
-
callback(null, webmentions)
|
|
191
|
+
const getWebmentionsFilter = async (options, url, allowedTypes, callback) => {
|
|
192
|
+
if (typeof callback !== "function") {
|
|
193
|
+
callback = allowedTypes
|
|
194
|
+
allowedTypes = {}
|
|
185
195
|
}
|
|
196
|
+
const webmentions = await getWebmentions(options, url, allowedTypes)
|
|
197
|
+
callback(null, webmentions)
|
|
198
|
+
}
|
|
186
199
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
throw new Error("domain is a required field when adding the plugin to your eleventyConfig using addPlugin. See https://chrisburnell.com/eleventy-cache-webmentions/#installation for more information.")
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
if (!options.feed) {
|
|
193
|
-
throw new Error("feed is a required field when adding the plugin to your eleventyConfig using addPlugin. See https://chrisburnell.com/eleventy-cache-webmentions/#installation for more information.")
|
|
194
|
-
}
|
|
200
|
+
module.exports = (eleventyConfig, options = {}) => {
|
|
201
|
+
options = Object.assign(defaults, options)
|
|
195
202
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
203
|
+
if (eleventyConfig) {
|
|
204
|
+
// Global Data
|
|
205
|
+
eleventyConfig.addGlobalData("webmentions", filteredWebmentions(options))
|
|
199
206
|
|
|
200
207
|
// Liquid Filter
|
|
201
208
|
eleventyConfig.addLiquidFilter("getWebmentions", getWebmentionsFilter)
|
|
202
209
|
|
|
203
210
|
// Nunjucks Filter
|
|
204
211
|
eleventyConfig.addNunjucksAsyncFilter("getWebmentions", getWebmentionsFilter)
|
|
212
|
+
}
|
|
205
213
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
214
|
+
return {
|
|
215
|
+
defaults: defaults,
|
|
216
|
+
webmentions: filteredWebmentions(options),
|
|
217
|
+
fetchWebmentions: fetchWebmentions,
|
|
218
|
+
filteredWebmentions: filteredWebmentions,
|
|
219
|
+
getWebmentions: getWebmentions,
|
|
211
220
|
}
|
|
212
221
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chrisburnell/eleventy-cache-webmentions",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0-beta.3",
|
|
4
4
|
"description": "Fetch and cache webmentions using eleventy-fetch.",
|
|
5
5
|
"main": "eleventy-cache-webmentions.js",
|
|
6
6
|
"author": "Chris Burnell <me@chrisburnell.com>",
|