From 3a672ff3c2c78a31c74b5d8ee8f1122d36d22a05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20B=C3=B6hm?= <188768+fb55@users.noreply.github.com> Date: Fri, 20 Aug 2021 20:57:38 +0100 Subject: [PATCH] refactor(feedhandler): Move logic of `FeedHandler` to function (#912) --- src/FeedHandler.ts | 177 ++++++++++++++++---------------- src/__fixtures__/test-helper.ts | 2 +- 2 files changed, 92 insertions(+), 87 deletions(-) diff --git a/src/FeedHandler.ts b/src/FeedHandler.ts index 13656e3d4..358e63bae 100644 --- a/src/FeedHandler.ts +++ b/src/FeedHandler.ts @@ -53,7 +53,7 @@ interface Feed { items?: FeedItem[]; } -// TODO: Consume data as it is coming in +/** @deprecated Handler is no longer necessary; use `getFeed` or `parseFeed` instead. */ export class FeedHandler extends DomHandler { feed?: Feed; @@ -74,103 +74,108 @@ export class FeedHandler extends DomHandler { } onend(): void { - const feedRoot = getOneElement(isValidFeed, this.dom); + const feed = getFeed(this.dom); - if (!feedRoot) { + if (feed) { + this.feed = feed; + this.handleCallback(null); + } else { this.handleCallback(new Error("couldn't find root of feed")); - return; } + } +} + +/** + * Get the feed object from the root of a DOM tree. + * + * @param dom - The DOM to to extract the feed from. + * @returns The feed. + */ +export function getFeed(dom: Node[]): Feed | null { + const feedRoot = getOneElement(isValidFeed, dom); + + if (!feedRoot) return null; + + const feed: Feed = {}; + + if (feedRoot.name === "feed") { + const childs = feedRoot.children; + feed.type = "atom"; + addConditionally(feed, "id", "id", childs); + addConditionally(feed, "title", "title", childs); + const href = getAttribute("href", getOneElement("link", childs)); + if (href) { + feed.link = href; + } + addConditionally(feed, "description", "subtitle", childs); + + const updated = fetch("updated", childs); + if (updated) { + feed.updated = new Date(updated); + } + + addConditionally(feed, "author", "email", childs, true); + feed.items = getElements("entry", childs).map((item) => { + const entry: FeedItem = {}; + const { children } = item; - const feed: Feed = {}; + addConditionally(entry, "id", "id", children); + addConditionally(entry, "title", "title", children); - if (feedRoot.name === "feed") { - const childs = feedRoot.children; - feed.type = "atom"; - addConditionally(feed, "id", "id", childs); - addConditionally(feed, "title", "title", childs); - const href = getAttribute("href", getOneElement("link", childs)); + const href = getAttribute("href", getOneElement("link", children)); if (href) { - feed.link = href; + entry.link = href; } - addConditionally(feed, "description", "subtitle", childs); - const updated = fetch("updated", childs); - if (updated) { - feed.updated = new Date(updated); + const description = + fetch("summary", children) || fetch("content", children); + if (description) { + entry.description = description; } - addConditionally(feed, "author", "email", childs, true); - feed.items = getElements("entry", childs).map((item) => { - const entry: FeedItem = {}; - const { children } = item; - - addConditionally(entry, "id", "id", children); - addConditionally(entry, "title", "title", children); + const pubDate = fetch("updated", children); + if (pubDate) { + entry.pubDate = new Date(pubDate); + } - const href = getAttribute( - "href", - getOneElement("link", children) - ); - if (href) { - entry.link = href; - } - - const description = - fetch("summary", children) || fetch("content", children); - if (description) { - entry.description = description; - } - - const pubDate = fetch("updated", children); - if (pubDate) { - entry.pubDate = new Date(pubDate); - } + entry.media = getMediaElements(children); - entry.media = getMediaElements(children); + return entry; + }); + } else { + const childs = + getOneElement("channel", feedRoot.children)?.children ?? []; + feed.type = feedRoot.name.substr(0, 3); + feed.id = ""; - return entry; - }); - } else { - const childs = - getOneElement("channel", feedRoot.children)?.children ?? []; - feed.type = feedRoot.name.substr(0, 3); - feed.id = ""; - - addConditionally(feed, "title", "title", childs); - addConditionally(feed, "link", "link", childs); - addConditionally(feed, "description", "description", childs); - - const updated = fetch("lastBuildDate", childs); - if (updated) { - feed.updated = new Date(updated); - } + addConditionally(feed, "title", "title", childs); + addConditionally(feed, "link", "link", childs); + addConditionally(feed, "description", "description", childs); - addConditionally(feed, "author", "managingEditor", childs, true); - - feed.items = getElements("item", feedRoot.children).map( - (item: Element) => { - const entry: FeedItem = {}; - const { children } = item; - addConditionally(entry, "id", "guid", children); - addConditionally(entry, "title", "title", children); - addConditionally(entry, "link", "link", children); - addConditionally( - entry, - "description", - "description", - children - ); - const pubDate = fetch("pubDate", children); - if (pubDate) entry.pubDate = new Date(pubDate); - entry.media = getMediaElements(children); - return entry; - } - ); + const updated = fetch("lastBuildDate", childs); + if (updated) { + feed.updated = new Date(updated); } - this.feed = feed; - this.handleCallback(null); + addConditionally(feed, "author", "managingEditor", childs, true); + + feed.items = getElements("item", feedRoot.children).map( + (item: Element) => { + const entry: FeedItem = {}; + const { children } = item; + addConditionally(entry, "id", "guid", children); + addConditionally(entry, "title", "title", children); + addConditionally(entry, "link", "link", children); + addConditionally(entry, "description", "description", children); + const pubDate = fetch("pubDate", children); + if (pubDate) entry.pubDate = new Date(pubDate); + entry.media = getMediaElements(children); + return entry; + } + ); } + + return feed; } function getMediaElements(where: Node | Node[]): FeedItemMedia[] { @@ -234,7 +239,7 @@ function getOneElement( return DomUtils.getElementsByTagName(tagName, node, true, 1)[0]; } function fetch(tagName: string, where: Node | Node[], recurse = false): string { - return DomUtils.getText( + return DomUtils.textContent( DomUtils.getElementsByTagName(tagName, where, recurse, 1) ).trim(); } @@ -267,13 +272,13 @@ function isValidFeed(value: string) { * Parse a feed. * * @param feed The feed that should be parsed, as a string. - * @param options Optionally, options for parsing. When using this option, you should set `xmlMode` to `true`. + * @param options Optionally, options for parsing. When using this, you should set `xmlMode` to `true`. */ export function parseFeed( feed: string, options: ParserOptions & DomHandlerOptions = { xmlMode: true } -): Feed | undefined { - const handler = new FeedHandler(options); +): Feed | null { + const handler = new DomHandler(null, options); new Parser(handler, options).end(feed); - return handler.feed; + return getFeed(handler.dom); } diff --git a/src/__fixtures__/test-helper.ts b/src/__fixtures__/test-helper.ts index 80f3eb38c..ae48592cf 100644 --- a/src/__fixtures__/test-helper.ts +++ b/src/__fixtures__/test-helper.ts @@ -122,7 +122,7 @@ interface TestFile { * @param getResult Function to be called with the actual results. */ export function createSuite( - name: string, + name: "Events" | "Feeds" | "Stream", getResult: ( file: TestFile, done: (error: Error | null, actual?: unknown | unknown[]) => void