From 70f28332b662c7f8b4292db9129fabd4be07f0ca Mon Sep 17 00:00:00 2001 From: Christopher Chedeau Date: Thu, 12 Feb 2015 14:43:41 -0800 Subject: [PATCH] Initial version of the automatically generated docs --- docs/GettingStarted.md | 1 + website/.gitignore | 2 + website/core/Header.js | 33 +---- website/core/metadata.js | 15 -- website/core/slugify.js | 35 +++++ website/react-docgen/.gitignore | 1 + website/server/convert.js | 135 +++++++++--------- website/server/extractDocs.js | 53 +++++++ website/server/generate.js | 2 +- .../src/react-native/docs/getting-started.js | 85 ----------- 10 files changed, 165 insertions(+), 197 deletions(-) create mode 100644 website/.gitignore delete mode 100644 website/core/metadata.js create mode 100644 website/core/slugify.js create mode 100644 website/react-docgen/.gitignore create mode 100644 website/server/extractDocs.js delete mode 100644 website/src/react-native/docs/getting-started.js diff --git a/docs/GettingStarted.md b/docs/GettingStarted.md index 24365afccff..eda46461b56 100644 --- a/docs/GettingStarted.md +++ b/docs/GettingStarted.md @@ -4,6 +4,7 @@ title: Getting Started layout: docs category: Quick Start permalink: docs/getting-started.html +next: navigatorios --- diff --git a/website/.gitignore b/website/.gitignore new file mode 100644 index 00000000000..37a25d50ab1 --- /dev/null +++ b/website/.gitignore @@ -0,0 +1,2 @@ +src/react-native/docs/** +core/metadata.js diff --git a/website/core/Header.js b/website/core/Header.js index 0a2bf3a179a..f49cae3747b 100644 --- a/website/core/Header.js +++ b/website/core/Header.js @@ -4,40 +4,11 @@ */ var React = require('React'); +var slugify = require('slugify'); var Header = React.createClass({ - slug: function(string) { - // var accents = 'àáäâèéëêìíïîòóöôùúüûñç'; - var accents = '\u00e0\u00e1\u00e4\u00e2\u00e8' + - '\u00e9\u00eb\u00ea\u00ec\u00ed\u00ef' + - '\u00ee\u00f2\u00f3\u00f6\u00f4\u00f9' + - '\u00fa\u00fc\u00fb\u00f1\u00e7'; - - var without = 'aaaaeeeeiiiioooouuuunc'; - - return string - .toString() - - // Handle uppercase characters - .toLowerCase() - - // Handle accentuated characters - .replace( - new RegExp('[' + accents + ']', 'g'), - function (c) { return without.charAt(accents.indexOf(c)); }) - - // Dash special characters - .replace(/[^a-z0-9]/g, '-') - - // Compress multiple dash - .replace(/-+/g, '-') - - // Trim dashes - .replace(/^-|-$/g, ''); - }, - render: function() { - var slug = this.slug(this.props.toSlug || this.props.children); + var slug = slugify(this.props.toSlug || this.props.children); var H = React.DOM['h' + this.props.level]; return this.transferPropsTo( diff --git a/website/core/metadata.js b/website/core/metadata.js deleted file mode 100644 index e3c129eaf8e..00000000000 --- a/website/core/metadata.js +++ /dev/null @@ -1,15 +0,0 @@ -/** - * @generated - * @providesModule Metadata - */ -module.exports = { - "files": [ - { - "id": "getting-started", - "title": "Getting Started", - "layout": "docs", - "category": "Quick Start", - "permalink": "docs/getting-started.html" - } - ] -}; \ No newline at end of file diff --git a/website/core/slugify.js b/website/core/slugify.js new file mode 100644 index 00000000000..9f9c48b6fbc --- /dev/null +++ b/website/core/slugify.js @@ -0,0 +1,35 @@ +/** + * @providesModule slugify + */ + +var slugify = function(string) { + // var accents = 'àáäâèéëêìíïîòóöôùúüûñç'; + var accents = '\u00e0\u00e1\u00e4\u00e2\u00e8' + + '\u00e9\u00eb\u00ea\u00ec\u00ed\u00ef' + + '\u00ee\u00f2\u00f3\u00f6\u00f4\u00f9' + + '\u00fa\u00fc\u00fb\u00f1\u00e7'; + + var without = 'aaaaeeeeiiiioooouuuunc'; + + return string + .toString() + + // Handle uppercase characters + .toLowerCase() + + // Handle accentuated characters + .replace( + new RegExp('[' + accents + ']', 'g'), + function (c) { return without.charAt(accents.indexOf(c)); }) + + // Dash special characters + .replace(/[^a-z0-9]/g, '-') + + // Compress multiple dash + .replace(/-+/g, '-') + + // Trim dashes + .replace(/^-|-$/g, ''); +}; + +module.exports = slugify; diff --git a/website/react-docgen/.gitignore b/website/react-docgen/.gitignore new file mode 100644 index 00000000000..849ddff3b7e --- /dev/null +++ b/website/react-docgen/.gitignore @@ -0,0 +1 @@ +dist/ diff --git a/website/server/convert.js b/website/server/convert.js index c3fceaa7907..de04eb9486a 100644 --- a/website/server/convert.js +++ b/website/server/convert.js @@ -3,6 +3,7 @@ var glob = require('glob'); var mkdirp = require('mkdirp'); var optimist = require('optimist'); var path = require('path'); +var extractDocs = require('./extractDocs'); var argv = optimist.argv; function splitHeader(content) { @@ -28,89 +29,93 @@ function backtickify(str) { function execute() { var MD_DIR = '../docs/'; - glob('src/react-native/docs/*.*', function(er, files) { - files.forEach(function(file) { - try { - fs.unlinkSync(file); - } catch(e) { - /* seriously, unlink throws when the file doesn't exist :( */ - } - }); + var files = glob.sync('src/react-native/docs/*.*') + files.forEach(function(file) { + try { + fs.unlinkSync(file); + } catch(e) { + /* seriously, unlink throws when the file doesn't exist :( */ + } }); var metadatas = { files: [], }; - glob(MD_DIR + '**/*.*', function (er, files) { - files.forEach(function(file) { - var extension = path.extname(file); - if (extension === '.md' || extension === '.markdown') { - var content = fs.readFileSync(file, {encoding: 'utf8'}); - var metadata = {}; + function handleMarkdown(content) { + var metadata = {}; - // Extract markdown metadata header - 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; - } - metadatas.files.push(metadata); + // Extract markdown metadata header + 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; + } + metadatas.files.push(metadata); - if (metadata.permalink.match(/^https?:/)) { - return; - } + if (metadata.permalink.match(/^https?:/)) { + return; + } - // Create a dummy .js version that just calls the associated layout - var layout = metadata.layout[0].toUpperCase() + metadata.layout.substr(1) + 'Layout'; + // Create a dummy .js version that just calls the associated layout + var layout = metadata.layout[0].toUpperCase() + metadata.layout.substr(1) + 'Layout'; - var content = ( - '/**\n' + - ' * @generated\n' + - ' * @jsx React.DOM\n' + - ' */\n' + - 'var React = require("React");\n' + - 'var layout = require("' + layout + '");\n' + - 'var content = ' + backtickify(both.content) + '\n' + - 'var Post = React.createClass({\n' + - ' render: function() {\n' + - ' return layout({metadata: ' + JSON.stringify(metadata) + '}, content);\n' + - ' }\n' + - '});\n' + - // TODO: Use React statics after upgrading React - 'Post.content = content;\n' + - 'module.exports = Post;\n' - ); - - var targetFile = 'src/react-native/' + metadata.permalink.replace(/\.html$/, '.js'); - mkdirp.sync(targetFile.replace(new RegExp('/[^/]*$'), '')); - fs.writeFileSync(targetFile, content); - } - - if (extension === '.json') { - var content = fs.readFileSync(file, {encoding: 'utf8'}); - metadatas[path.basename(file, '.json')] = JSON.parse(content); - } - }); - - fs.writeFileSync( - 'core/metadata.js', + var content = ( '/**\n' + ' * @generated\n' + - ' * @providesModule Metadata\n' + + ' * @jsx React.DOM\n' + ' */\n' + - 'module.exports = ' + JSON.stringify(metadatas, null, 2) + ';' + 'var React = require("React");\n' + + 'var layout = require("' + layout + '");\n' + + 'var content = ' + backtickify(both.content) + '\n' + + 'var Post = React.createClass({\n' + + ' render: function() {\n' + + ' return layout({metadata: ' + JSON.stringify(metadata) + '}, content);\n' + + ' }\n' + + '});\n' + + // TODO: Use React statics after upgrading React + 'Post.content = content;\n' + + 'module.exports = Post;\n' ); + + var targetFile = 'src/react-native/' + metadata.permalink.replace(/\.html$/, '.js'); + mkdirp.sync(targetFile.replace(new RegExp('/[^/]*$'), '')); + fs.writeFileSync(targetFile, content); + } + + extractDocs().forEach(handleMarkdown); + + var files = glob.sync(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); + } + + if (extension === '.json') { + var content = fs.readFileSync(file, {encoding: 'utf8'}); + metadatas[path.basename(file, '.json')] = JSON.parse(content); + } }); + + fs.writeFileSync( + 'core/metadata.js', + '/**\n' + + ' * @generated\n' + + ' * @providesModule Metadata\n' + + ' */\n' + + 'module.exports = ' + JSON.stringify(metadatas, null, 2) + ';' + ); } if (argv.convert) { - console.log('convert!') + console.log('convert!'); execute(); } diff --git a/website/server/extractDocs.js b/website/server/extractDocs.js new file mode 100644 index 00000000000..9b7c0609a63 --- /dev/null +++ b/website/server/extractDocs.js @@ -0,0 +1,53 @@ +var docs = require('../react-docgen'); +var fs = require('fs'); +var path = require('path'); +var slugify = require('../core/slugify'); + +function getNameFromPath(filepath) { + var ext = null; + while (ext = path.extname(filepath)) { + filepath = path.basename(filepath, ext); + } + return filepath; +} + +function docsToMarkdown(filepath, i) { + var json = docs.parseSource(fs.readFileSync(filepath)); + var componentName = getNameFromPath(filepath); + + var res = [ + '---', + 'id: ' + slugify(componentName), + 'title: ' + componentName, + 'layout: docs', + 'category: Components', + 'permalink: docs/' + slugify(componentName) + '.html', + components[i + 1] && ('next: ' + slugify(getNameFromPath(components[i + 1]))), + '---', + ' ', + json.description, + ' ', + '# Props', + '```', + JSON.stringify(json.props, null, 2), + '```', + ].filter(function(line) { return line; }).join('\n'); + return res; +} + +var components = [ + '../Libraries/Components/Navigation/NavigatorIOS.ios.js', + '../Libraries/Components/Image/Image.ios.js', + '../Libraries/Components/ListView/ListView.js', + '../Libraries/Components/Navigation/NavigatorIOS.ios.js', + '../Libraries/Components/ScrollView/ScrollView.ios.js', + '../Libraries/Components/Text/Text.js', + '../Libraries/Components/TextInput/TextInput.ios.js', + '../Libraries/Components/Touchable/TouchableHighlight.js', + '../Libraries/Components/Touchable/TouchableWithoutFeedback.js', +// '../Libraries/Components/View/View.js', +]; + +module.exports = function() { + return components.map(docsToMarkdown); +}; diff --git a/website/server/generate.js b/website/server/generate.js index ef60e1715fe..0ec92103659 100644 --- a/website/server/generate.js +++ b/website/server/generate.js @@ -26,7 +26,7 @@ var queue = (function() { is_executing = true; fn(function() { is_executing = false; - execute() + execute(); }); } return {push: push}; diff --git a/website/src/react-native/docs/getting-started.js b/website/src/react-native/docs/getting-started.js deleted file mode 100644 index 19b6fc34b61..00000000000 --- a/website/src/react-native/docs/getting-started.js +++ /dev/null @@ -1,85 +0,0 @@ -/** - * @generated - * @jsx React.DOM - */ -var React = require("React"); -var layout = require("DocsLayout"); -var content = ` - -Our first React Native implementation is \`ReactKit\`, targeting iOS. We are also -working on an Android implementation which we will release later. \`ReactKit\` -apps are built using the [React JS](https://github.com/facebook/react) framework, and render directly to -native UIKit elements using a fully asynchronous architecture. There is no -browser and no HTML. We have picked what we think is the best set of features -from these and other technologies to build what we hope to become the best -product development framework available, with an emphasis on iteration speed, -developer delight, continuity of technology, and absolutely beautiful and fast -products with no compromises in quality or capability. - -## Requirements - -1. OS X - This repo only contains the iOS implementation right now, and Xcode only runs on Mac. -2. New to Xcode? [Download it](https://developer.apple.com/xcode/downloads/) from the Mac App Store. -3. [Homebrew](http://brew.sh/) is the recommended way to install node, watchman, and flow. -4. New to node or npm? \`brew install node\` -5. We recommend installing [watchman](https://facebook.github.io/watchman/docs/install.html), otherwise you might hit a node file watching bug. \`brew install watchman\` -6. If you want to use [flow](http://www.flowtype.org), \`brew install flow\` - -## Quick start - -Get up and running with our Movies sample app: - -1. Once you have the repo cloned and met all the requirements above, start the -packager that will transform your JS code on-the-fly: - - \`\`\` - npm install - npm start - \`\`\` -2. Open the \`Examples/Movies/Movies.xcodeproj\` project in Xcode. -3. Make sure the target is set to \`Movies\` and that you have an iOS simulator -selected to run the app. -4. Build and run the project with the Xcode run button. - -You should now see the Movies app running on your iOS simulator. -Congratulations! You've just successfully run your first React Native app. - -Now try editing a JavaScript file and viewing your changes. Let's change the -movie search placeholder text: - -1. Open the \`Examples/Movies/SearchScreen.js\` file in your favorite JavaScript -editor. -2. Look for the current search placeholder text and change it to "Search for an -awesome movie...". -3. Hit cmd+R ([twice](http://openradar.appspot.com/19613391)) in your iOS simulator to reload the app and see your change. -If you don't immediately see your changes, try restarting your app within Xcode. - -Feel free to browse the Movies sample files and customize various properties to -get familiar with the codebase and React Native. - -Also check out the UI Component Explorer for more sample code: -\`Examples/UIExplorer/UIExplorer.xcodeproj\`. **Make sure to close the Movies -project first - Xcode will break if you have two projects open that reference -the same library.** - -## Troubleshooting - -+ Xcode will break if you have two examples open at the same time. -+ If \`npm start\` fails with log spew like: - \`\`\` - 2015-02-02 10:56 node[24294] (FSEvents.framework) FSEventStreamStart: register_with_server: ERROR: f2d_register_rpc() => (null) (-21) - \`\`\` -then you've hit the node file watching bug - \`brew install watchman\` should fix the issue. -+ Jest testing does not yet work on node versions after 0.10.x. -+ You can verify the packager is working by loading the [bundle](http://localhost:8081/Examples/Movies/MoviesApp.includeRequire.runModule.bundle) in your browser and -inspecting the contents. - -Please report any other issues you encounter so we can fix them ASAP. -` -var Post = React.createClass({ - render: function() { - return layout({metadata: {"id":"getting-started","title":"Getting Started","layout":"docs","category":"Quick Start","permalink":"docs/getting-started.html"}}, content); - } -}); -Post.content = content; -module.exports = Post;