UNPKG

16.1 kBJavaScriptView Raw
1import { sanitize } from 'dompurify';
2import useSWR from 'swr';
3import { parseFeed } from 'htmlparser2';
4
5/** searches for a tag in the node list, prevents recursive searches */
6
7var findByTag = function findByTag(nodes, tagName) {
8 return Array.from(nodes).find(function (e) {
9 return e.nodeName === tagName;
10 });
11};
12/** searches for nodes which have a matching tagName, prevents recursive searches */
13
14
15var filterByTag = function filterByTag(nodes, tagName) {
16 return Array.from(nodes).filter(function (e) {
17 return e.nodeName === tagName;
18 });
19};
20/** shortcut method for `findByTag()` that accesses children */
21
22
23var findChildTag = function findChildTag(parent, tagName) {
24 return findByTag(parent.children, tagName);
25};
26/** shortcut method for `filterByTag()` that accesses children */
27
28
29var filterChildTags = function filterChildTags(parent, tagName) {
30 return filterByTag(parent.children, tagName);
31};
32/** parses the feed */
33
34
35function parseAtomFeed(data) {
36 var parser = new DOMParser();
37 var xml = parser.parseFromString(data, 'text/xml');
38 var feed = findChildTag(xml, 'feed');
39
40 if (feed) {
41 var _sanitizeTextContent, _findChildTag$textCon, _findChildTag, _sanitizeTextContent2;
42
43 return {
44 id: (_sanitizeTextContent = sanitizeTextContent(findChildTag(feed, 'id'))) != null ? _sanitizeTextContent : '',
45 title: parseAtomText(findChildTag(feed, 'title')),
46 updated: new Date((_findChildTag$textCon = (_findChildTag = findChildTag(feed, 'updated')) == null ? void 0 : _findChildTag.textContent) != null ? _findChildTag$textCon : 0),
47 entries: filterChildTags(feed, 'entry').map(function (e) {
48 return parseAtomEntry(e);
49 }),
50 author: filterChildTags(feed, 'author').map(function (author) {
51 return parseAtomPerson(author);
52 }),
53 link: filterChildTags(feed, 'link').map(function (link) {
54 return parseAtomLink(link);
55 }),
56 category: filterChildTags(feed, 'category').map(function (category) {
57 return parseAtomCategory(category);
58 }),
59 contributor: filterChildTags(feed, 'contributor').map(function (contributor) {
60 return parseAtomPerson(contributor);
61 }),
62 generator: {
63 value: (_sanitizeTextContent2 = sanitizeTextContent(findChildTag(feed, 'generator'))) != null ? _sanitizeTextContent2 : '',
64 uri: sanitizeTextAttribute(findChildTag(feed, 'generator'), 'uri'),
65 version: sanitizeTextAttribute(findChildTag(feed, 'generator'), 'version')
66 },
67 icon: sanitizeTextContent(findChildTag(feed, 'icon')),
68 logo: sanitizeTextContent(findChildTag(feed, 'logo')),
69 rights: parseAtomText(findChildTag(feed, 'rights')),
70 subtitle: sanitizeTextContent(findChildTag(feed, 'subtitle'))
71 };
72 }
73
74 throw Error('No <feed> tag found.');
75}
76function parseAtomEntry(entry) {
77 var _sanitizeTextContent3, _findChildTag$textCon2, _findChildTag2, _findChildTag$textCon3, _findChildTag3;
78
79 return {
80 id: (_sanitizeTextContent3 = sanitizeTextContent(findChildTag(entry, 'id'))) != null ? _sanitizeTextContent3 : '',
81 title: parseAtomText(findChildTag(entry, 'title')),
82 updated: new Date((_findChildTag$textCon2 = (_findChildTag2 = findChildTag(entry, 'updated')) == null ? void 0 : _findChildTag2.textContent) != null ? _findChildTag$textCon2 : 0),
83 author: filterChildTags(entry, 'author').map(function (author) {
84 return parseAtomPerson(author);
85 }),
86 content: parseAtomContent(findChildTag(entry, 'content')),
87 link: filterChildTags(entry, 'link').map(function (link) {
88 return parseAtomLink(link);
89 }),
90 summary: parseAtomText(findChildTag(entry, 'summary')),
91 category: filterChildTags(entry, 'category').map(function (category) {
92 return parseAtomCategory(category);
93 }),
94 contributor: filterChildTags(entry, 'contributor').map(function (contributor) {
95 return parseAtomPerson(contributor);
96 }),
97 published: findChildTag(entry, 'published') ? new Date((_findChildTag$textCon3 = (_findChildTag3 = findChildTag(entry, 'published')) == null ? void 0 : _findChildTag3.textContent) != null ? _findChildTag$textCon3 : 0) : undefined,
98 rights: parseAtomText(findChildTag(entry, 'rights')),
99 source: parseAtomSource(findChildTag(entry, 'source'))
100 };
101}
102/** safely decode text content */
103
104function safelyDecodeAtomText(type, element) {
105 if (element !== undefined) {
106 var _element$textContent;
107
108 // If type="xhtml", then this element contains inline xhtml, wrapped in a div element.
109 // This means that the existing `.innerHTML` is ready to be santized
110 if (type === 'xhtml') return sanitize(element.innerHTML); // If type="html", then this element contains entity escaped html.
111 // using `.textContent` will un-escape the text
112 else if (type === 'html') return sanitize((_element$textContent = element.textContent) != null ? _element$textContent : ''); // If type="text", then this element contains plain text with no entity escaped html.
113 // This means that the content of `.innerHTML` are **intended** to be safe.
114 // However, we don't want to leave an attack vector open, so we're going to sanitize it anyway.
115 else if (type === 'text') return sanitize(element.innerHTML);
116 }
117
118 return '';
119}
120/** shortcut for safely decoding the `.textContent` value of an element */
121
122function sanitizeTextContent(element) {
123 var _element$textContent2;
124
125 return element !== undefined ? sanitize((_element$textContent2 = element == null ? void 0 : element.textContent) != null ? _element$textContent2 : '') : undefined;
126}
127/** shortcut for safely decoding the an attribute value of an element */
128
129function sanitizeTextAttribute(element, attributeName) {
130 return element !== undefined ? element.getAttribute(attributeName) !== null ? sanitize(element.getAttribute(attributeName)) : undefined : undefined;
131}
132function parseAtomContent(content) {
133 var _sanitizeTextAttribut;
134
135 var type = (_sanitizeTextAttribut = sanitizeTextAttribute(content, 'type')) != null ? _sanitizeTextAttribut : undefined;
136 return {
137 type: type,
138 src: sanitizeTextAttribute(content, 'src'),
139 value: safelyDecodeAtomText(type, content)
140 };
141}
142function parseAtomText(text) {
143 var _sanitizeTextAttribut2;
144
145 var type = (_sanitizeTextAttribut2 = sanitizeTextAttribute(text, 'type')) != null ? _sanitizeTextAttribut2 : undefined;
146 return {
147 type: type,
148 value: safelyDecodeAtomText(type, text)
149 };
150}
151function parseAtomPerson(person) {
152 var _findChildTag$textCon4, _findChildTag4;
153
154 return {
155 name: sanitize((_findChildTag$textCon4 = (_findChildTag4 = findChildTag(person, 'name')) == null ? void 0 : _findChildTag4.textContent) != null ? _findChildTag$textCon4 : ''),
156 uri: sanitizeTextContent(findChildTag(person, 'uri')),
157 email: sanitizeTextContent(findChildTag(person, 'email'))
158 };
159}
160function parseAtomLink(link) {
161 var _sanitizeTextAttribut3;
162
163 return {
164 href: (_sanitizeTextAttribut3 = sanitizeTextAttribute(link, 'href')) != null ? _sanitizeTextAttribut3 : '',
165 rel: sanitizeTextAttribute(link, 'ref'),
166 type: sanitizeTextAttribute(link, 'type'),
167 hreflang: sanitizeTextAttribute(link, 'hreflang'),
168 title: sanitizeTextAttribute(link, 'title'),
169 length: sanitizeTextAttribute(link, 'length')
170 };
171}
172function parseAtomCategory(category) {
173 var _sanitizeTextAttribut4, _sanitizeTextAttribut5, _sanitizeTextAttribut6;
174
175 return {
176 term: (_sanitizeTextAttribut4 = sanitizeTextAttribute(category, 'term')) != null ? _sanitizeTextAttribut4 : '',
177 scheme: (_sanitizeTextAttribut5 = sanitizeTextAttribute(category, 'scheme')) != null ? _sanitizeTextAttribut5 : undefined,
178 label: (_sanitizeTextAttribut6 = sanitizeTextAttribute(category, 'label')) != null ? _sanitizeTextAttribut6 : undefined
179 };
180}
181function parseAtomSource(source) {
182 if (source !== undefined) {
183 var _sanitizeTextContent4, _sanitizeTextContent5, _findChildTag$textCon5, _findChildTag5;
184
185 return {
186 id: (_sanitizeTextContent4 = sanitizeTextContent(findChildTag(source, 'id'))) != null ? _sanitizeTextContent4 : '',
187 title: (_sanitizeTextContent5 = sanitizeTextContent(findChildTag(source, 'title'))) != null ? _sanitizeTextContent5 : '',
188 updated: new Date((_findChildTag$textCon5 = (_findChildTag5 = findChildTag(source, 'title')) == null ? void 0 : _findChildTag5.textContent) != null ? _findChildTag$textCon5 : 0)
189 };
190 }
191
192 return undefined;
193}
194
195var Parser = {
196 __proto__: null,
197 parseAtomFeed: parseAtomFeed,
198 parseAtomEntry: parseAtomEntry,
199 safelyDecodeAtomText: safelyDecodeAtomText,
200 sanitizeTextContent: sanitizeTextContent,
201 sanitizeTextAttribute: sanitizeTextAttribute,
202 parseAtomContent: parseAtomContent,
203 parseAtomText: parseAtomText,
204 parseAtomPerson: parseAtomPerson,
205 parseAtomLink: parseAtomLink,
206 parseAtomCategory: parseAtomCategory,
207 parseAtomSource: parseAtomSource
208};
209
210function isAtomFeed(obj, _argumentName) {
211 return (obj !== null && typeof obj === "object" || typeof obj === "function") && typeof obj.id === "string" && isAtomTitle(obj.title) && obj.updated instanceof Date && Array.isArray(obj.entries) && obj.entries.every(function (e) {
212 return isAtomEntry(e);
213 }) && (typeof obj.author === "undefined" || Array.isArray(obj.author) && obj.author.every(function (e) {
214 return isAtomAuthor(e);
215 })) && (typeof obj.link === "undefined" || Array.isArray(obj.link) && obj.link.every(function (e) {
216 return isAtomLink(e);
217 })) && (typeof obj.category === "undefined" || Array.isArray(obj.category) && obj.category.every(function (e) {
218 return isAtomCategory(e);
219 })) && (typeof obj.contributor === "undefined" || Array.isArray(obj.contributor) && obj.contributor.every(function (e) {
220 return isAtomContributor(e);
221 })) && (typeof obj.generator === "undefined" || isAtomGenerator(obj.generator)) && (typeof obj.icon === "undefined" || typeof obj.icon === "string") && (typeof obj.logo === "undefined" || typeof obj.logo === "string") && (typeof obj.rights === "undefined" || isAtomRights(obj.rights)) && (typeof obj.subtitle === "undefined" || typeof obj.subtitle === "string");
222}
223function isAtomGenerator(obj, _argumentName) {
224 return (obj !== null && typeof obj === "object" || typeof obj === "function") && typeof obj.value === "string" && (typeof obj.uri === "undefined" || typeof obj.uri === "string") && (typeof obj.version === "undefined" || typeof obj.version === "string");
225}
226function isAtomEntry(obj, _argumentName) {
227 return (obj !== null && typeof obj === "object" || typeof obj === "function") && typeof obj.id === "string" && isAtomTitle(obj.title) && obj.updated instanceof Date && (typeof obj.author === "undefined" || Array.isArray(obj.author) && obj.author.every(function (e) {
228 return isAtomAuthor(e);
229 })) && (typeof obj.content === "undefined" || isAtomContent(obj.content)) && (typeof obj.link === "undefined" || Array.isArray(obj.link) && obj.link.every(function (e) {
230 return isAtomLink(e);
231 })) && (typeof obj.summary === "undefined" || isAtomSummary(obj.summary)) && (typeof obj.category === "undefined" || Array.isArray(obj.category) && obj.category.every(function (e) {
232 return isAtomCategory(e);
233 })) && (typeof obj.contributor === "undefined" || Array.isArray(obj.contributor) && obj.contributor.every(function (e) {
234 return isAtomContributor(e);
235 })) && (typeof obj.published === "undefined" || obj.published instanceof Date) && (typeof obj.rights === "undefined" || isAtomRights(obj.rights)) && (typeof obj.source === "undefined" || isAtomSource(obj.source));
236}
237function isAtomSource(obj, _argumentName) {
238 return (obj !== null && typeof obj === "object" || typeof obj === "function") && typeof obj.id === "string" && typeof obj.title === "string" && obj.updated instanceof Date;
239}
240function isAtomCategory(obj, _argumentName) {
241 return (obj !== null && typeof obj === "object" || typeof obj === "function") && typeof obj.term === "string" && (typeof obj.scheme === "undefined" || typeof obj.scheme === "string") && (typeof obj.label === "undefined" || typeof obj.label === "string");
242}
243function isAtomContent(obj, _argumentName) {
244 return (obj !== null && typeof obj === "object" || typeof obj === "function") && (typeof obj.type === "undefined" || obj.type === "text" || obj.type === "html" || obj.type === "xhtml") && (typeof obj.src === "undefined" || typeof obj.src === "string") && typeof obj.value === "string";
245}
246function isAtomLink(obj, _argumentName) {
247 return (obj !== null && typeof obj === "object" || typeof obj === "function") && typeof obj.href === "string" && (typeof obj.rel === "undefined" || obj.rel === "alternate" || obj.rel === "enclosure" || obj.rel === "related" || obj.rel === "self" || obj.rel === "via") && (typeof obj.type === "undefined" || typeof obj.type === "string") && (typeof obj.hreflang === "undefined" || typeof obj.hreflang === "string") && (typeof obj.title === "undefined" || typeof obj.title === "string") && (typeof obj.length === "undefined" || typeof obj.length === "string");
248}
249function isAtomPerson(obj, _argumentName) {
250 return (obj !== null && typeof obj === "object" || typeof obj === "function") && typeof obj.name === "string" && (typeof obj.uri === "undefined" || typeof obj.uri === "string") && (typeof obj.email === "undefined" || typeof obj.email === "string");
251}
252function isAtomLinkRelType(obj, _argumentName) {
253 return obj === "alternate" || obj === "enclosure" || obj === "related" || obj === "self" || obj === "via";
254}
255function isAtomAuthor(obj, _argumentName) {
256 return isAtomPerson(obj);
257}
258function isAtomContributor(obj, _argumentName) {
259 return isAtomPerson(obj);
260}
261function isAtomText(obj, _argumentName) {
262 return (obj !== null && typeof obj === "object" || typeof obj === "function") && (typeof obj.type === "undefined" || obj.type === "text" || obj.type === "html" || obj.type === "xhtml") && typeof obj.value === "string";
263}
264function isAtomTextType(obj, _argumentName) {
265 return obj === "text" || obj === "html" || obj === "xhtml";
266}
267function isAtomTitle(obj, _argumentName) {
268 return isAtomText(obj);
269}
270function isAtomSummary(obj, _argumentName) {
271 return isAtomText(obj);
272}
273function isAtomRights(obj, _argumentName) {
274 return isAtomText(obj);
275}
276
277var index_guard = {
278 __proto__: null,
279 isAtomFeed: isAtomFeed,
280 isAtomGenerator: isAtomGenerator,
281 isAtomEntry: isAtomEntry,
282 isAtomSource: isAtomSource,
283 isAtomCategory: isAtomCategory,
284 isAtomContent: isAtomContent,
285 isAtomLink: isAtomLink,
286 isAtomPerson: isAtomPerson,
287 isAtomLinkRelType: isAtomLinkRelType,
288 isAtomAuthor: isAtomAuthor,
289 isAtomContributor: isAtomContributor,
290 isAtomText: isAtomText,
291 isAtomTextType: isAtomTextType,
292 isAtomTitle: isAtomTitle,
293 isAtomSummary: isAtomSummary,
294 isAtomRights: isAtomRights
295};
296
297/**
298 * The React hook used for reading the Atom feed.
299 * @param feedURL The URL of the Atom feed
300 * @param options Options that are passed to `useSWR()` behind the scenes.
301 * More info: https://swr.vercel.app/docs/options#options
302 * @returns The decoded Atom feed or any errors seen along the way
303 */
304
305function useAtomFeed(feedURL, options) {
306 var fetcher = function fetcher(url) {
307 return fetch(url).then(function (res) {
308 return res.text();
309 });
310 };
311
312 var _useSWR = useSWR(feedURL, fetcher, options),
313 data = _useSWR.data,
314 error = _useSWR.error,
315 isValidating = _useSWR.isValidating; // if data is defined
316
317
318 if (data) {
319 // attempt to decode
320 try {
321 var decoded = parseFeed(data, {
322 xmlMode: true
323 }); // return a good decode
324
325 return {
326 data: decoded,
327 error: error,
328 isValidating: isValidating
329 };
330 } catch (parseError) {
331 // return a decode failure
332 return {
333 data: undefined,
334 error: parseError,
335 isValidating: isValidating
336 };
337 }
338 } else {
339 // data is undefined
340 return {
341 data: undefined,
342 error: error,
343 isValidating: isValidating
344 };
345 }
346}
347
348export { index_guard as Guards, Parser, useAtomFeed };
349//# sourceMappingURL=use-atom-feed.esm.js.map