Permalink
Browse files

`@phenomic/plugin-rss-feed`: this is now a thing and can generate m…

…ultiples feed
  • Loading branch information...
MoOx committed Mar 29, 2018
1 parent 7fdd43c commit 4584963559c0ef9e71612e709b14b9006085ffed
Showing with 148 additions and 36 deletions.
  1. +2 −0 packages/plugin-rss-feed/package.json
  2. +127 −35 packages/plugin-rss-feed/src/index.js
  3. +1 −0 website/__tests__/index.js
  4. +18 −1 website/package.json
@@ -23,7 +23,9 @@
"main": "lib/index.js",
"files": ["lib", "src", "!**/__tests__"],
"dependencies": {
"@phenomic/api-client": "^1.0.0-beta.0",
"debug": "^2.6.0",
"express": "^4.14.0",
"rss": "^1.2.1"
},
"peerDependencies": {
@@ -1,45 +1,137 @@
import RSS from "rss";
// @flow
import url from "url";
const oneYear = 1000 * 60 * 60 * 24 * 365;
import fetchRestApi from "@phenomic/api-client/lib/fetch";
import query from "@phenomic/api-client/lib/query";
import logger from "@phenomic/core/lib/logger";
import express from "express";
import RSS from "rss";
const pluginName = "@phenomic/plugin-rss-feed";
const log = logger(pluginName);
const debug = require("debug")("phenomic:plugin:rss-feed");
export default function() {
// @todo fix this ROOT url thing
export type options = {
feeds: {
[feedUrl: string]: {
// https://www.npmjs.com/package/rss#feedoptions
feedOptions: Object,
query: PhenomicQueryConfig,
map?: ((phenomicItem: Object) => Object) | { [key: string]: string }
}
}
};
const makeItemFromObject = (item, map) =>
// $FlowFixMe waaat?
Object.keys(map).reduce((acc, key) => {
acc[key] = item[map[key]];
return acc;
}, {});
const defaultMap = {
title: "title",
url: "id",
date: "date",
// @todo
// description: "excerpt",
// optional, we assume this are good defaults
author: "author",
categories: "tags"
};
const defaultOptions = {
feeds: {
"feed.xml": {
feedOptions: {},
query: { path: "posts", limit: 20 }
}
}
};
const makeFeed = async (ROOT, feedUrl, feedConfig): Promise<string> => {
const rss = new RSS({
feed_url: ROOT + feedUrl,
site_url: ROOT,
generator: "Phenomic",
...feedConfig.feedOptions
});
const items = await fetchRestApi(query(feedConfig.query));
items.list.forEach(item => {
// special trick for rss feeds :)
item.id =
ROOT +
(feedConfig.query.path ? feedConfig.query.path + "/" : "") +
item.id +
"/";
const mappedItem =
typeof feedConfig.map === "function"
? feedConfig.map(item)
: makeItemFromObject(item, feedConfig.map || defaultMap);
debug("item", item, mappedItem);
rss.item(mappedItem);
});
return rss.xml(
process.env.PHENOMIC_ENV === "development" ? { indent: true } : {}
);
};
const getFeedKeys = options => {
const keys = Object.keys(options.feeds);
if (!keys.length) {
log.warn(
`No 'feeds' founds in options. Please add entries.\n Current options:\n ${JSON.stringify(
options,
null,
2
)}).`
);
}
return keys;
};
const getRoot = (config: PhenomicConfig) =>
url.format(config.baseUrl).slice(0, -1) +
(process.env.PHENOMIC_ENV === "development" ? `:${config.port}/` : "/");
const rssFeed: PhenomicPluginModule<options> = (
config: PhenomicConfig,
options: options = defaultOptions
) => {
return {
name: "@phenomic/plugin-rss-feed",
async build(fetch: PhenomicFetch, writeFile: Function) {
// @todo handle this fetch
// $FlowFixMe handle this fetch
const feed = fetch("/feed.xml");
return writeFile("feed.xml", feed);
},
define(serverAPI: express$Application) {
// $FlowFixMe flow is lost with async function for express
serverAPI.get("/feed.xml", async function(req, res) {
debug(req.url);
const rss = new RSS({
// feed_url: ROOT,
feed_url: "HTTP://TODO-ROOT/"
name: pluginName,
addDevServerMiddlewares() {
const router = express.Router();
getFeedKeys(options).forEach(feedUrl => {
router.get("/" + feedUrl, async (req, res: express$Response) => {
debug(req.url);
const output = await makeFeed(
getRoot(config),
feedUrl,
options.feeds[feedUrl]
);
res.type("xml").send(output);
});
const posts = await req.db.getList({ path: "posts" });
const postsFromLastYear = posts.filter(
post => Date.now() - new Date(post.date).getTime() < oneYear
);
postsFromLastYear.forEach(post => {
rss.item({
title: post.title,
// url: ROOT + post.path,
url: "HTTP://TODO-ROOT/" + post.path,
description: post.content,
categories: post.tags,
author: post.author,
date: post.date
});
});
res.type("xml").send(rss.xml());
});
return [router];
},
resolveURLs() {
return Promise.resolve(getFeedKeys(options));
},
async renderStatic({ location }: { location: string }) {
return [
{
path: location,
contents: await makeFeed(
getRoot(config),
location,
options.feeds[location]
)
}
];
}
};
}
};
export default rssFeed;
@@ -13,6 +13,7 @@ it("should have basic pages", () => {
expect(files.filter(f => f.startsWith("docs/"))).toMatchSnapshot();
expect(files.includes("news/index.html")).toBeTruthy();
expect(files.includes("showcase/index.html")).toBeTruthy();
expect(files.includes("feed.xml")).toBeTruthy();
});
it("should have some generated pages for pagination", () => {
View
@@ -36,7 +36,24 @@
"rimraf": "^2.6.1"
},
"phenomic": {
"presets": ["@phenomic/preset-react-app"]
"baseUrl": "http://phenomic.io/",
"presets": ["@phenomic/preset-react-app"],
"plugins": [
[
"@phenomic/plugin-rss-feed",
{
"feeds": {
"feed.xml": {
"feedOptions": {
"title": "Phenomic.io",
"description": "Phenomic news"
},
"query": { "path": "news", "limit": 20 }
}
}
}
]
]
},
"screenshotsSize": {
"large": {

0 comments on commit 4584963

Please sign in to comment.