mirror of
https://github.com/status-im/react-native.git
synced 2025-01-09 17:15:54 +00:00
4ee7d9430c
Summary: The showcase is displayed in the homepage as well as in a standalone showcase page. The source data for these two pages is embedded in each page, resulting in repetition of data across both. This PR splits out all of the showcase data to its own file, which is then loaded into the Metadata component during website generation. `cd website && npm start`, then verified index and showcase loaded successfully `cd website && node server/generate.js`, confirmed script runs successfully Closes https://github.com/facebook/react-native/pull/12626 Differential Revision: D4632036 Pulled By: hramos fbshipit-source-id: 2d1ad890e78e457205179e36c3ef04ffec354ad9
266 lines
7.6 KiB
JavaScript
266 lines
7.6 KiB
JavaScript
/**
|
|
* Copyright (c) 2015-present, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var fs = require('fs')
|
|
var glob = require('glob');
|
|
var mkdirp = require('mkdirp');
|
|
var optimist = require('optimist');
|
|
var path = require('path');
|
|
var removeMd = require('remove-markdown');
|
|
var extractDocs = require('./extractDocs');
|
|
var cache = require('memory-cache');
|
|
var argv = optimist.argv;
|
|
|
|
function splitHeader(content) {
|
|
var lines = content.split(/\r?\n/);
|
|
for (var i = 1; i < lines.length - 1; ++i) {
|
|
if (lines[i] === '---') {
|
|
break;
|
|
}
|
|
}
|
|
return {
|
|
header: i < lines.length - 1 ?
|
|
lines.slice(1, i + 1).join('\n') : null,
|
|
content: lines.slice(i + 1).join('\n')
|
|
};
|
|
}
|
|
|
|
function rmFile(file) {
|
|
try {
|
|
fs.unlinkSync(file);
|
|
} catch(e) {
|
|
/* seriously, unlink throws when the file doesn't exist :( */
|
|
}
|
|
}
|
|
|
|
function backtickify(str) {
|
|
var escaped = '`' + str.replace(/\\/g, '\\\\').replace(/`/g, '\\`').replace(/{/g, '\\{') + '`';
|
|
// Replace require( with require\( so node-haste doesn't replace example
|
|
// require calls in the docs
|
|
return escaped.replace(/require\(/g, 'require\\(');
|
|
}
|
|
|
|
function writeFileAndCreateFolder(file, content) {
|
|
mkdirp.sync(file.replace(new RegExp('/[^/]*$'), ''));
|
|
fs.writeFileSync(file, content);
|
|
}
|
|
|
|
// Extract markdown metadata header
|
|
function extractMetadata(content) {
|
|
var metadata = {};
|
|
var both = splitHeader(content);
|
|
var lines = both.header.split('\n');
|
|
for (var i = 0; i < lines.length - 1; ++i) {
|
|
var keyvalue = lines[i].split(':');
|
|
var key = keyvalue[0].trim();
|
|
var value = keyvalue.slice(1).join(':').trim();
|
|
// Handle the case where you have "Community #10"
|
|
try { value = JSON.parse(value); } catch(e) { }
|
|
metadata[key] = value;
|
|
}
|
|
return {metadata: metadata, rawContent: both.content};
|
|
}
|
|
|
|
function buildFile(layout, metadata, rawContent) {
|
|
return [
|
|
'/**',
|
|
' * @generated',
|
|
' */',
|
|
'var React = require("React");',
|
|
'var Layout = require("' + layout + '");',
|
|
rawContent && 'var content = ' + backtickify(rawContent) + ';',
|
|
'var Post = React.createClass({',
|
|
rawContent && ' statics: { content: content },',
|
|
' render: function() {',
|
|
' return (',
|
|
' <Layout metadata={' + JSON.stringify(metadata) + '}>',
|
|
rawContent && ' {content}',
|
|
' </Layout>',
|
|
' );',
|
|
' }',
|
|
'});',
|
|
'module.exports = Post;'
|
|
].filter(e => e).join('\n');
|
|
}
|
|
|
|
function execute(options) {
|
|
if (options === undefined) {
|
|
options = {};
|
|
}
|
|
|
|
var DOCS_MD_DIR = '../docs/';
|
|
var BLOG_MD_DIR = '../blog/';
|
|
var CONFIG_JSON_DIR = '../';
|
|
|
|
glob.sync('src/react-native/docs/*.*').forEach(rmFile);
|
|
glob.sync('src/react-native/blog/*.*').forEach(rmFile);
|
|
glob.sync('../blog/img/*.*').forEach(file => {
|
|
writeFileAndCreateFolder(
|
|
'src/react-native/blog/img/' + path.basename(file),
|
|
fs.readFileSync(file)
|
|
);
|
|
});
|
|
|
|
var metadatas = {
|
|
files: [],
|
|
};
|
|
|
|
function handleMarkdown(content, filename) {
|
|
if (content.slice(0, 3) !== '---') {
|
|
return;
|
|
}
|
|
|
|
const res = extractMetadata(content);
|
|
const metadata = res.metadata;
|
|
const rawContent = res.rawContent;
|
|
|
|
if (metadata.sidebar !== false) {
|
|
metadatas.files.push(metadata);
|
|
}
|
|
|
|
if (metadata.permalink.match(/^https?:/)) {
|
|
return;
|
|
}
|
|
|
|
metadata.filename = filename;
|
|
|
|
// Create a dummy .js version that just calls the associated layout
|
|
var layout = metadata.layout[0].toUpperCase() + metadata.layout.substr(1) + 'Layout';
|
|
|
|
writeFileAndCreateFolder(
|
|
'src/react-native/' + metadata.permalink.replace(/\.html$/, '.js'),
|
|
buildFile(layout, metadata, rawContent)
|
|
);
|
|
}
|
|
|
|
if (options.extractDocs) {
|
|
// Rendering docs can take up to 8 seconds. We wait until /docs/ are
|
|
// requested before doing so, then we store the results in memory to
|
|
// speed up subsequent requests.
|
|
var extractedDocs = cache.get('extractedDocs');
|
|
if (!extractedDocs) {
|
|
extractedDocs = extractDocs();
|
|
cache.put('extractedDocs', extractedDocs);
|
|
}
|
|
extractedDocs.forEach(function(content) {
|
|
handleMarkdown(content, null);
|
|
});
|
|
|
|
var files = glob.sync(DOCS_MD_DIR + '**/*.*');
|
|
files.forEach(function(file) {
|
|
var extension = path.extname(file);
|
|
if (extension === '.md' || extension === '.markdown') {
|
|
var content = fs.readFileSync(file, {encoding: 'utf8'});
|
|
handleMarkdown(content, path.basename(file));
|
|
}
|
|
|
|
if (extension === '.json') {
|
|
var content = fs.readFileSync(file, {encoding: 'utf8'});
|
|
metadatas[path.basename(file, '.json')] = JSON.parse(content);
|
|
}
|
|
});
|
|
}
|
|
|
|
// we need to pass globals for the components to be configurable
|
|
// metadata is generated in this process which has access to process.env
|
|
// but the web pages are generated in a sandbox context and have only access to CommonJS module files
|
|
metadatas.config = Object.create(null);
|
|
Object
|
|
.keys(process.env)
|
|
.filter(key => key.startsWith('RN_'))
|
|
.forEach((key) => {
|
|
metadatas.config[key] = process.env[key];
|
|
});
|
|
|
|
// load showcase apps into metadata
|
|
var showcaseApps = JSON.parse(fs.readFileSync(
|
|
path.basename(CONFIG_JSON_DIR + 'showcase.json'),
|
|
{encoding: 'utf8'}
|
|
));
|
|
metadatas.showcaseApps = showcaseApps;
|
|
|
|
fs.writeFileSync(
|
|
'core/metadata.js',
|
|
'/**\n' +
|
|
' * @generated\n' +
|
|
' * @providesModule Metadata\n' +
|
|
' */\n' +
|
|
'module.exports = ' + JSON.stringify(metadatas, null, 2) + ';'
|
|
);
|
|
|
|
|
|
files = glob.sync(BLOG_MD_DIR + '**/*.md');
|
|
const metadatasBlog = {
|
|
files: [],
|
|
};
|
|
|
|
files.sort().reverse().forEach(file => {
|
|
// Transform
|
|
// 2015-08-13-blog-post-name-0.5.md
|
|
// into
|
|
// 2015/08/13/blog-post-name-0-5.html
|
|
var filePath = path.basename(file)
|
|
.replace('-', '/')
|
|
.replace('-', '/')
|
|
.replace('-', '/')
|
|
// react-middleware is broken with files that contains multiple . like react-0.14.js
|
|
.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 excerpt = removeMd(rawContent).trim().split('\n')[0];
|
|
var metadata = Object.assign({path: filePath, content: rawContent, publishedAt: publishedAt, excerpt: excerpt}, res.metadata);
|
|
|
|
metadatasBlog.files.push(metadata);
|
|
|
|
writeFileAndCreateFolder(
|
|
'src/react-native/blog/' + filePath.replace(/\.html$/, '.js'),
|
|
buildFile('BlogPostLayout', metadata, rawContent)
|
|
);
|
|
});
|
|
|
|
var perPage = 15;
|
|
for (var page = 0; page < Math.ceil(metadatasBlog.files.length / perPage); ++page) {
|
|
writeFileAndCreateFolder(
|
|
'src/react-native/blog' + (page > 0 ? '/page' + (page + 1) : '') + '/index.js',
|
|
buildFile('BlogPageLayout', { page: page, perPage: perPage })
|
|
);
|
|
}
|
|
fs.writeFileSync(
|
|
'core/metadata-blog.js',
|
|
'/**\n' +
|
|
' * @generated\n' +
|
|
' * @providesModule MetadataBlog\n' +
|
|
' */\n' +
|
|
'module.exports = ' + JSON.stringify(metadatasBlog, null, 2) + ';'
|
|
);
|
|
fs.writeFileSync(
|
|
'server/metadata-blog.json',
|
|
JSON.stringify(metadatasBlog, null, 2)
|
|
);
|
|
|
|
}
|
|
|
|
if (argv.convert) {
|
|
console.log('convert!');
|
|
execute();
|
|
}
|
|
|
|
module.exports = execute;
|