From 9cb370dd5bccf4d9fa2ccca43150eb1c6cf3f8c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ramos?= Date: Fri, 28 Oct 2016 13:27:33 -0700 Subject: [PATCH] Generate Atom feed for the React Native blog. Summary: An Atom feed is now generated as part of the build script. This is done statically and not as a React view because React is not the right tool for generating XML documents. Some additional metadata is stored in `metadata-blog.js` and duplicated to `metadata-blog.json` in the `server/` directory to aid in the generation of the feed. Let me know if there's a better way to import this data using the existing Haste module that wouldn't require writing an additional JSON file. The feed will be available at https://facebook.github.io/react-native/blog.xml A sample output of the Atom feed is included at the bottom. It is a [valid Atom 1.0 feed](https://validator.w3.org/feed/check.cgi), with some additional recommendations that can be ignored for now. > Congratulations! > > [Valid Atom 1.0] This is a valid Atom 1.0 feed. > Recommendations > > This feed is valid, but interoperability with the widest range of feed readers could be improved by implementing the following recommendations. > line 2, col Closes https://github.com/facebook/react-native/pull/10611 Differential Revision: D4097381 Pulled By: mkonicek fbshipit-source-id: 8d2e18923358d1903b2715b00c48680b0c4dff68 --- website/.gitignore | 1 + website/core/BlogPost.js | 2 +- website/package.json | 1 + website/server/convert.js | 15 +++++++++++-- website/server/generate.js | 44 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 60 insertions(+), 3 deletions(-) diff --git a/website/.gitignore b/website/.gitignore index 555763f8a..df8535b40 100644 --- a/website/.gitignore +++ b/website/.gitignore @@ -1,5 +1,6 @@ src/react-native/docs/** src/react-native/blog/** core/metadata* +server/metadata* *.log /build/ diff --git a/website/core/BlogPost.js b/website/core/BlogPost.js index 8fdf40dd1..05dc85d4a 100644 --- a/website/core/BlogPost.js +++ b/website/core/BlogPost.js @@ -22,7 +22,7 @@ var BlogPost = React.createClass({ var post = this.props.post; var content = this.props.content; - var match = post.path.match(/([0-9]+)\/([0-9]+)\/([0-9]+)/); + var match = post.publishedAt.match(/([0-9]+)-([0-9]+)-([0-9]+)/); // Because JavaScript sucks at date handling :( var year = match[1]; var month = [ diff --git a/website/package.json b/website/package.json index a68dbc4ce..ef806fe40 100644 --- a/website/package.json +++ b/website/package.json @@ -14,6 +14,7 @@ "bluebird": "^2.9.21", "connect": "2.8.3", "deep-assign": "^2.0.0", + "feed": "^0.3.0", "flow-parser": "^0.32.0", "fs.extra": "1.3.2", "glob": "6.0.4", diff --git a/website/server/convert.js b/website/server/convert.js index 330f10c24..b58c7ec1e 100644 --- a/website/server/convert.js +++ b/website/server/convert.js @@ -191,9 +191,16 @@ function execute() { .replace(/\./g, '-') .replace(/\-md$/, '.html'); + // Extract 2015-08-13 from 2015/08/13/blog-post-name-0-5.html + var match = filePath.match(/([0-9]+)\/([0-9]+)\/([0-9]+)/); + var year = match[1]; + var month = match[2]; + var day = match[3]; + var publishedAt = year + '-' + month + '-' + day; + var res = extractMetadata(fs.readFileSync(file, {encoding: 'utf8'})); var rawContent = res.rawContent; - var metadata = Object.assign({path: filePath, content: rawContent}, res.metadata); + var metadata = Object.assign({path: filePath, content: rawContent, publishedAt: publishedAt}, res.metadata); metadatasBlog.files.push(metadata); @@ -210,7 +217,6 @@ function execute() { buildFile('BlogPageLayout', { page: page, perPage: perPage }) ); } - fs.writeFileSync( 'core/metadata-blog.js', '/**\n' + @@ -219,6 +225,11 @@ function execute() { ' */\n' + 'module.exports = ' + JSON.stringify(metadatasBlog, null, 2) + ';' ); + fs.writeFileSync( + 'server/metadata-blog.json', + JSON.stringify(metadatasBlog, null, 2) + ); + } if (argv.convert) { diff --git a/website/server/generate.js b/website/server/generate.js index dc96e2e8d..e89af678b 100644 --- a/website/server/generate.js +++ b/website/server/generate.js @@ -14,6 +14,7 @@ var glob = require('glob'); var fs = require('fs.extra'); var mkdirp = require('mkdirp'); var server = require('./server.js'); +var Feed = require('feed'); require('./convert.js')(); server.noconvert = true; @@ -23,6 +24,49 @@ server.noconvert = true; // requests. var queue = Promise.resolve(); +// Generate RSS Feeds +queue = queue.then(function() { + return new Promise(function(resolve, reject) { + var targetFile = 'build/react-native/blog/feed.xml'; + + var basePath = 'https://facebook.github.io/react-native/'; + var blogPath = basePath + 'blog/'; + + var metadataBlog = JSON.parse(fs.readFileSync('server/metadata-blog.json')); + var latestPost = metadataBlog.files[0]; + var feed = new Feed({ + title: 'React Native Blog', + description: 'The best place to stay up-to-date with the latest React Native news and events.', + id: blogPath, + link: blogPath, + image: basePath + 'img/header_logo.png', + copyright: 'Copyright © ' + new Date().getFullYear() + ' Facebook Inc.', + updated: new Date(latestPost.publishedAt), + }); + + metadataBlog.files.forEach(function(post) { + var url = blogPath + post.path; + feed.addItem({ + title: post.title, + id: url, + link: url, + date: new Date(post.publishedAt), + author: [{ + name: post.author, + link: post.authorURL + }], + description: post.content.trim().split('\n')[0], + }); + }); + + mkdirp.sync(targetFile.replace(new RegExp('/[^/]*$'), '')); + fs.writeFileSync(targetFile, feed.render('atom-1.0')); + console.log('Generated RSS feed') + resolve(); + }); +}); + +// Generate HTML for each non-source code JS file glob('src/**/*.*', function(er, files) { files.forEach(function(file) { var targetFile = file.replace(/^src/, 'build');