From d255c1afc6603202fcee12daa586d7376fc8d7e0 Mon Sep 17 00:00:00 2001 From: Eric Simons Date: Sun, 15 Oct 2017 11:36:37 -0700 Subject: [PATCH 1/8] sync js files to codepen --- .eslintignore | 5 ++- content/docs/hello-world.md | 2 +- content/docs/introducing-jsx.md | 2 +- examples/hello-world.js | 4 ++ examples/index.html | 3 ++ examples/introducing-jsx.js | 19 ++++++++++ gatsby-node.js | 42 ++++++++++++++++----- package.json | 2 +- src/templates/codepen-example.js | 64 ++++++++++++++++++++++++++++++++ 9 files changed, 130 insertions(+), 13 deletions(-) create mode 100644 examples/hello-world.js create mode 100644 examples/index.html create mode 100644 examples/introducing-jsx.js create mode 100644 src/templates/codepen-example.js diff --git a/.eslintignore b/.eslintignore index ff8a5577f..942541715 100644 --- a/.eslintignore +++ b/.eslintignore @@ -4,4 +4,7 @@ node_modules/* content/* # Ignore built files -public/* \ No newline at end of file +public/* + +# Ignore examples +examples/* \ No newline at end of file diff --git a/content/docs/hello-world.md b/content/docs/hello-world.md index f9be3bde5..06087d7ba 100644 --- a/content/docs/hello-world.md +++ b/content/docs/hello-world.md @@ -11,7 +11,7 @@ redirect_from: - "docs/getting-started-zh-CN.html" --- -The easiest way to get started with React is to use [this Hello World example code on CodePen](http://codepen.io/gaearon/pen/ZpvBNJ?editors=0010). You don't need to install anything; you can just open it in another tab and follow along as we go through examples. If you'd rather use a local development environment, check out the [Installation](/docs/installation.html) page. +The easiest way to get started with React is to use this Hello World example code on CodePen. You don't need to install anything; you can just open it in another tab and follow along as we go through examples. If you'd rather use a local development environment, check out the [Installation](/docs/installation.html) page. The smallest React example looks like this: diff --git a/content/docs/introducing-jsx.md b/content/docs/introducing-jsx.md index 9830ddc93..e1e4d64d1 100644 --- a/content/docs/introducing-jsx.md +++ b/content/docs/introducing-jsx.md @@ -46,7 +46,7 @@ ReactDOM.render( ); ``` -[Try it on CodePen.](http://codepen.io/gaearon/pen/PGEjdG?editors=0010) +Try it on CodePen. We split JSX over multiple lines for readability. While it isn't required, when doing this, we also recommend wrapping it in parentheses to avoid the pitfalls of [automatic semicolon insertion](http://stackoverflow.com/q/2846283). diff --git a/examples/hello-world.js b/examples/hello-world.js new file mode 100644 index 000000000..d0f87a59b --- /dev/null +++ b/examples/hello-world.js @@ -0,0 +1,4 @@ +ReactDOM.render( +

Hello, world!

, + document.getElementById('root') +); diff --git a/examples/index.html b/examples/index.html new file mode 100644 index 000000000..461f11059 --- /dev/null +++ b/examples/index.html @@ -0,0 +1,3 @@ +
+ +
\ No newline at end of file diff --git a/examples/introducing-jsx.js b/examples/introducing-jsx.js new file mode 100644 index 000000000..adb326643 --- /dev/null +++ b/examples/introducing-jsx.js @@ -0,0 +1,19 @@ +function formatName(user) { + return user.firstName + ' ' + user.lastName; +} + +const user = { + firstName: 'Harper', + lastName: 'Perez', +}; + +const element = ( +

+ Hello, {formatName(user)}! +

+); + +ReactDOM.render( + element, + document.getElementById('root') +); diff --git a/gatsby-node.js b/gatsby-node.js index 0fc10c4f1..5a078ffc0 100644 --- a/gatsby-node.js +++ b/gatsby-node.js @@ -8,6 +8,7 @@ const {resolve} = require('path'); const webpack = require('webpack'); +const fs = require('fs'); exports.modifyWebpackConfig = ({config, stage}) => { // See https://github.com/FormidableLabs/react-live/issues/5 @@ -74,11 +75,11 @@ exports.createPages = async ({graphql, boundActionCreators}) => { // (which gets created by Gatsby during a separate phase). } else if ( slug.includes('blog/') || - slug.includes('community/') || - slug.includes('contributing/') || - slug.includes('docs/') || - slug.includes('tutorial/') || - slug.includes('warnings/') + slug.includes('community/') || + slug.includes('contributing/') || + slug.includes('docs/') || + slug.includes('tutorial/') || + slug.includes('warnings/') ) { let template; if (slug.includes('blog/')) { @@ -87,8 +88,8 @@ exports.createPages = async ({graphql, boundActionCreators}) => { template = communityTemplate; } else if ( slug.includes('contributing/') || - slug.includes('docs/') || - slug.includes('warnings/') + slug.includes('docs/') || + slug.includes('warnings/') ) { template = docsTemplate; } else if (slug.includes('tutorial/')) { @@ -117,8 +118,8 @@ exports.createPages = async ({graphql, boundActionCreators}) => { redirect.forEach(fromPath => { if (redirectToSlugMap[fromPath] != null) { console.error(`Duplicate redirect detected from "${fromPath}" to:\n` + - `* ${redirectToSlugMap[fromPath]}\n` + - `* ${slug}\n` + `* ${redirectToSlugMap[fromPath]}\n` + + `* ${slug}\n` ); process.exit(1); } @@ -161,6 +162,29 @@ exports.createPages = async ({graphql, boundActionCreators}) => { redirectInBrowser: true, toPath: newestBlogNode.fields.slug, }); + + // Create Codepen example pages + const htmlTemplate = fs.readFileSync('./examples/index.html', 'utf8'); + fs.readdirSync('./examples').forEach(file => { + // Only create pages for the JS files + if (file.toLowerCase().split('.').pop() === 'js') { + const slug = file.substring(0, file.length - 3); + const jsTemplate = fs.readFileSync(`./examples/${file}`, 'utf8'); + + createPage({ + path: `/examples/${slug}`, + component: resolve('./src/templates/codepen-example.js'), + context: { + slug, + payload: { + html: htmlTemplate, + js: jsTemplate, + }, + }, + }); + } + }); + }; // Parse date information out of blog post filename. diff --git a/package.json b/package.json index 1d214c9f0..b6106a109 100644 --- a/package.json +++ b/package.json @@ -84,4 +84,4 @@ "devDependencies": { "eslint-config-prettier": "^2.6.0" } -} +} \ No newline at end of file diff --git a/src/templates/codepen-example.js b/src/templates/codepen-example.js new file mode 100644 index 000000000..7079cc537 --- /dev/null +++ b/src/templates/codepen-example.js @@ -0,0 +1,64 @@ +'use strict'; + +import React, {Component} from 'react'; +import Container from 'components/Container'; +import {colors} from 'theme'; +// import {version} from '../site-constants'; + +// Copied over styles from ButtonLink for the submit btn +const primaryStyle = { + backgroundColor: colors.brand, + color: colors.black, + padding: '10px 25px', + whiteSpace: 'nowrap', + transition: 'background-color 0.2s ease-out', + outline: 0, + border: 'none', + cursor: 'pointer', + + ':hover': { + backgroundColor: colors.white, + }, + + display: 'inline-block', + fontSize: 16, +}; + +class CodepenExample extends Component { + componentDidMount() { + this.codepenForm.submit(); + } + + render() { + const {payload} = this.props.pathContext; + // Set codepen options + payload.js_pre_processor = 'babel'; + // Only have the JS editor open (default for all examples) + payload.editors = '0010'; + // We can pass @version in the URL for version locking, if desired. + payload.js_external = `https://unpkg.com/react/umd/react.development.js;https://unpkg.com/react-dom/umd/react-dom.development.js`; + + return ( + +

Redirecting to Codepen...

+
{ + this.codepenForm = form; + }} + action="https://codepen.io/pen/define" + method="POST"> + + + +
+
+ ); + } +} + +export default CodepenExample; From b809279c8338e73a6181c87b2d2d9da2c79f5a8c Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Mon, 6 Nov 2017 13:32:39 +0000 Subject: [PATCH 2/8] Replaced string.split().pop() with string.endsWith() --- gatsby-node.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/gatsby-node.js b/gatsby-node.js index 942c24c54..4b250d382 100644 --- a/gatsby-node.js +++ b/gatsby-node.js @@ -171,12 +171,7 @@ exports.createPages = async ({graphql, boundActionCreators}) => { const htmlTemplate = fs.readFileSync('./examples/index.html', 'utf8'); fs.readdirSync('./examples').forEach(file => { // Only create pages for the JS files - if ( - file - .toLowerCase() - .split('.') - .pop() === 'js' - ) { + if (file.endsWith('.js')) { const slug = file.substring(0, file.length - 3); const jsTemplate = fs.readFileSync(`./examples/${file}`, 'utf8'); From f14eb61e5f3ce175202c0f91fd2a41d58fe45f49 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Mon, 6 Nov 2017 13:37:45 +0000 Subject: [PATCH 3/8] Removed HTML boilerplate from examples folder to simplify build script --- examples/index.html | 3 --- gatsby-node.js | 28 ++++++++++++---------------- 2 files changed, 12 insertions(+), 19 deletions(-) delete mode 100644 examples/index.html diff --git a/examples/index.html b/examples/index.html deleted file mode 100644 index 461f11059..000000000 --- a/examples/index.html +++ /dev/null @@ -1,3 +0,0 @@ -
- -
\ No newline at end of file diff --git a/gatsby-node.js b/gatsby-node.js index 4b250d382..0003ccb70 100644 --- a/gatsby-node.js +++ b/gatsby-node.js @@ -168,25 +168,21 @@ exports.createPages = async ({graphql, boundActionCreators}) => { }); // Create Codepen example pages - const htmlTemplate = fs.readFileSync('./examples/index.html', 'utf8'); fs.readdirSync('./examples').forEach(file => { - // Only create pages for the JS files - if (file.endsWith('.js')) { - const slug = file.substring(0, file.length - 3); - const jsTemplate = fs.readFileSync(`./examples/${file}`, 'utf8'); + const slug = file.substring(0, file.length - 3); // Trim extension + const jsTemplate = fs.readFileSync(`./examples/${file}`, 'utf8'); - createPage({ - path: `/examples/${slug}`, - component: resolve('./src/templates/codepen-example.js'), - context: { - slug, - payload: { - html: htmlTemplate, - js: jsTemplate, - }, + createPage({ + path: `/examples/${slug}`, + component: resolve('./src/templates/codepen-example.js'), + context: { + slug, + payload: { + html: '
', + js: jsTemplate, }, - }); - } + }, + }); }); }; From 64e0aade1ef68e624adefcfcb25afe9582b0fd70 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Mon, 6 Nov 2017 13:44:45 +0000 Subject: [PATCH 4/8] Refactor Codepen payload a little --- gatsby-node.js | 9 +++------ src/templates/codepen-example.js | 23 ++++++++++++++--------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/gatsby-node.js b/gatsby-node.js index 0003ccb70..b43387127 100644 --- a/gatsby-node.js +++ b/gatsby-node.js @@ -167,20 +167,17 @@ exports.createPages = async ({graphql, boundActionCreators}) => { toPath: newestBlogNode.fields.slug, }); - // Create Codepen example pages + // Create Codepen example pages. fs.readdirSync('./examples').forEach(file => { const slug = file.substring(0, file.length - 3); // Trim extension - const jsTemplate = fs.readFileSync(`./examples/${file}`, 'utf8'); + const code = fs.readFileSync(`./examples/${file}`, 'utf8'); createPage({ path: `/examples/${slug}`, component: resolve('./src/templates/codepen-example.js'), context: { + code, slug, - payload: { - html: '
', - js: jsTemplate, - }, }, }); }); diff --git a/src/templates/codepen-example.js b/src/templates/codepen-example.js index 7079cc537..117682baf 100644 --- a/src/templates/codepen-example.js +++ b/src/templates/codepen-example.js @@ -3,7 +3,11 @@ import React, {Component} from 'react'; import Container from 'components/Container'; import {colors} from 'theme'; -// import {version} from '../site-constants'; + +const EXTERNALS = [ + 'https://unpkg.com/react/umd/react.development.js', + 'https://unpkg.com/react-dom/umd/react-dom.development.js', +]; // Copied over styles from ButtonLink for the submit btn const primaryStyle = { @@ -30,13 +34,14 @@ class CodepenExample extends Component { } render() { - const {payload} = this.props.pathContext; - // Set codepen options - payload.js_pre_processor = 'babel'; - // Only have the JS editor open (default for all examples) - payload.editors = '0010'; - // We can pass @version in the URL for version locking, if desired. - payload.js_external = `https://unpkg.com/react/umd/react.development.js;https://unpkg.com/react-dom/umd/react-dom.development.js`; + // Codepen configuration + const payload = JSON.stringify({ + editors: '0010', // Open JS editor by default + html: '
', + js: this.props.pathContext.code, + js_external: EXTERNALS.join(';'), + js_pre_processor: 'babel', + }); return ( @@ -48,7 +53,7 @@ class CodepenExample extends Component { }} action="https://codepen.io/pen/define" method="POST"> - + Date: Mon, 6 Nov 2017 13:51:45 +0000 Subject: [PATCH 5/8] Tweaked Codepen configuration/options slightly --- src/templates/codepen-example.js | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/templates/codepen-example.js b/src/templates/codepen-example.js index 117682baf..e3a7eb95e 100644 --- a/src/templates/codepen-example.js +++ b/src/templates/codepen-example.js @@ -34,13 +34,16 @@ class CodepenExample extends Component { } render() { - // Codepen configuration + // Codepen configuration. + // https://blog.codepen.io/documentation/api/prefill/ const payload = JSON.stringify({ - editors: '0010', // Open JS editor by default + editors: '0010', html: '
', js: this.props.pathContext.code, js_external: EXTERNALS.join(';'), js_pre_processor: 'babel', + layout: 'left', + title: 'reactjs.org example', }); return ( @@ -55,11 +58,17 @@ class CodepenExample extends Component { method="POST"> - +

+ Not automatically redirecting? + +

+ + +

); From 56735c44a05b45a685797100ac9609faa615f2d3 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Mon, 6 Nov 2017 13:59:25 +0000 Subject: [PATCH 6/8] Prettier --- gatsby-node.js | 10 ++++++---- package.json | 2 +- src/templates/codepen-example.js | 11 +++-------- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/gatsby-node.js b/gatsby-node.js index b43387127..19ac95ed2 100644 --- a/gatsby-node.js +++ b/gatsby-node.js @@ -6,9 +6,9 @@ 'use strict'; +const {readdirSync, readFileSync} = require('fs'); const {resolve} = require('path'); const webpack = require('webpack'); -const fs = require('fs'); exports.modifyWebpackConfig = ({config, stage}) => { // See https://github.com/FormidableLabs/react-live/issues/5 @@ -167,10 +167,12 @@ exports.createPages = async ({graphql, boundActionCreators}) => { toPath: newestBlogNode.fields.slug, }); - // Create Codepen example pages. - fs.readdirSync('./examples').forEach(file => { + // Create Codepen redirects. + // These use the Codepen prefill API to JIT-create Pens. + // https://blog.codepen.io/documentation/api/prefill/ + readdirSync('./examples').forEach(file => { const slug = file.substring(0, file.length - 3); // Trim extension - const code = fs.readFileSync(`./examples/${file}`, 'utf8'); + const code = readFileSync(`./examples/${file}`, 'utf8'); createPage({ path: `/examples/${slug}`, diff --git a/package.json b/package.json index 573b6e785..9adc56da2 100644 --- a/package.json +++ b/package.json @@ -86,4 +86,4 @@ "devDependencies": { "eslint-config-prettier": "^2.6.0" } -} \ No newline at end of file +} diff --git a/src/templates/codepen-example.js b/src/templates/codepen-example.js index e3a7eb95e..615b8f7ef 100644 --- a/src/templates/codepen-example.js +++ b/src/templates/codepen-example.js @@ -60,14 +60,9 @@ class CodepenExample extends Component {

Not automatically redirecting? - -

- - +
+
+

From 6eef46d1cba2d3da0da29b2dcd6313aa7b9b9b42 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Mon, 6 Nov 2017 14:05:14 +0000 Subject: [PATCH 7/8] Renamed 'examples' folder to 'codepen' for clarity --- {examples => codepen}/hello-world.js | 0 {examples => codepen}/introducing-jsx.js | 0 content/docs/hello-world.md | 2 +- content/docs/introducing-jsx.md | 2 +- gatsby-node.js | 6 +++--- 5 files changed, 5 insertions(+), 5 deletions(-) rename {examples => codepen}/hello-world.js (100%) rename {examples => codepen}/introducing-jsx.js (100%) diff --git a/examples/hello-world.js b/codepen/hello-world.js similarity index 100% rename from examples/hello-world.js rename to codepen/hello-world.js diff --git a/examples/introducing-jsx.js b/codepen/introducing-jsx.js similarity index 100% rename from examples/introducing-jsx.js rename to codepen/introducing-jsx.js diff --git a/content/docs/hello-world.md b/content/docs/hello-world.md index 8dba86b3c..29c0f839d 100644 --- a/content/docs/hello-world.md +++ b/content/docs/hello-world.md @@ -12,7 +12,7 @@ redirect_from: - "docs/getting-started-zh-CN.html" --- -The easiest way to get started with React is to use this Hello World example code on CodePen. You don't need to install anything; you can just open it in another tab and follow along as we go through examples. If you'd rather use a local development environment, check out the [Installation](/docs/installation.html) page. +The easiest way to get started with React is to use this Hello World example code on CodePen. You don't need to install anything; you can just open it in another tab and follow along as we go through examples. If you'd rather use a local development environment, check out the [Installation](/docs/installation.html) page. The smallest React example looks like this: diff --git a/content/docs/introducing-jsx.md b/content/docs/introducing-jsx.md index 85fb02629..11dabf044 100644 --- a/content/docs/introducing-jsx.md +++ b/content/docs/introducing-jsx.md @@ -46,7 +46,7 @@ ReactDOM.render( ); ``` -Try it on CodePen. +Try it on CodePen. We split JSX over multiple lines for readability. While it isn't required, when doing this, we also recommend wrapping it in parentheses to avoid the pitfalls of [automatic semicolon insertion](http://stackoverflow.com/q/2846283). diff --git a/gatsby-node.js b/gatsby-node.js index 19ac95ed2..381595491 100644 --- a/gatsby-node.js +++ b/gatsby-node.js @@ -170,12 +170,12 @@ exports.createPages = async ({graphql, boundActionCreators}) => { // Create Codepen redirects. // These use the Codepen prefill API to JIT-create Pens. // https://blog.codepen.io/documentation/api/prefill/ - readdirSync('./examples').forEach(file => { + readdirSync('./codepen').forEach(file => { const slug = file.substring(0, file.length - 3); // Trim extension - const code = readFileSync(`./examples/${file}`, 'utf8'); + const code = readFileSync(`./codepen/${file}`, 'utf8'); createPage({ - path: `/examples/${slug}`, + path: `/codepen/${slug}`, component: resolve('./src/templates/codepen-example.js'), context: { code, From cadaeae62b83b313019af8e719f977828b537ae3 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Mon, 6 Nov 2017 14:22:49 +0000 Subject: [PATCH 8/8] Reorganized codepen folder further to support nesting --- .eslintignore | 2 +- .../composing-components.js | 18 +++++++ .../extracting-components-continued.js | 52 +++++++++++++++++++ .../extracting-components.js | 40 ++++++++++++++ .../rendering-a-component.js | 9 ++++ content/docs/components-and-props.md | 8 +-- gatsby-node.js | 10 ++-- package.json | 3 +- yarn.lock | 14 ++++- 9 files changed, 145 insertions(+), 11 deletions(-) create mode 100644 codepen/components-and-props/composing-components.js create mode 100644 codepen/components-and-props/extracting-components-continued.js create mode 100644 codepen/components-and-props/extracting-components.js create mode 100644 codepen/components-and-props/rendering-a-component.js diff --git a/.eslintignore b/.eslintignore index 942541715..705b4afee 100644 --- a/.eslintignore +++ b/.eslintignore @@ -7,4 +7,4 @@ content/* public/* # Ignore examples -examples/* \ No newline at end of file +codepen/* \ No newline at end of file diff --git a/codepen/components-and-props/composing-components.js b/codepen/components-and-props/composing-components.js new file mode 100644 index 000000000..9158dd0cf --- /dev/null +++ b/codepen/components-and-props/composing-components.js @@ -0,0 +1,18 @@ +function Welcome(props) { + return

Hello, {props.name}

; +} + +function App() { + return ( +
+ + + +
+ ); +} + +ReactDOM.render( + , + document.getElementById('root') +); \ No newline at end of file diff --git a/codepen/components-and-props/extracting-components-continued.js b/codepen/components-and-props/extracting-components-continued.js new file mode 100644 index 000000000..bcb6547b1 --- /dev/null +++ b/codepen/components-and-props/extracting-components-continued.js @@ -0,0 +1,52 @@ +function formatDate(date) { + return date.toLocaleDateString(); +} + +function Avatar(props) { + return ( + {props.user.name} + ); +} + +function UserInfo(props) { + return ( +
+ +
+ {props.user.name} +
+
+ ); +} + +function Comment(props) { + return ( +
+ +
+ {props.text} +
+
+ {formatDate(props.date)} +
+
+ ); +} + +const comment = { + date: new Date(), + text: 'I hope you enjoy learning React!', + author: { + name: 'Hello Kitty', + avatarUrl: 'http://placekitten.com/g/64/64' + } +}; +ReactDOM.render( + , + document.getElementById('root') +); \ No newline at end of file diff --git a/codepen/components-and-props/extracting-components.js b/codepen/components-and-props/extracting-components.js new file mode 100644 index 000000000..720624ea7 --- /dev/null +++ b/codepen/components-and-props/extracting-components.js @@ -0,0 +1,40 @@ +function formatDate(date) { + return date.toLocaleDateString(); +} + +function Comment(props) { + return ( +
+
+ {props.author.name} +
+ {props.author.name} +
+
+
+ {props.text} +
+
+ {formatDate(props.date)} +
+
+ ); +} + +const comment = { + date: new Date(), + text: 'I hope you enjoy learning React!', + author: { + name: 'Hello Kitty', + avatarUrl: 'http://placekitten.com/g/64/64' + } +}; +ReactDOM.render( + , + document.getElementById('root') +); \ No newline at end of file diff --git a/codepen/components-and-props/rendering-a-component.js b/codepen/components-and-props/rendering-a-component.js new file mode 100644 index 000000000..d42e1681c --- /dev/null +++ b/codepen/components-and-props/rendering-a-component.js @@ -0,0 +1,9 @@ +function Welcome(props) { + return

Hello, {props.name}

; +} + +const element = ; +ReactDOM.render( + element, + document.getElementById('root') +); \ No newline at end of file diff --git a/content/docs/components-and-props.md b/content/docs/components-and-props.md index 1bd1f7a65..b6e967c75 100644 --- a/content/docs/components-and-props.md +++ b/content/docs/components-and-props.md @@ -76,7 +76,7 @@ ReactDOM.render( ); ``` -[Try it on CodePen.](http://codepen.io/gaearon/pen/YGYmEG?editors=0010) +Try it on CodePen. Let's recap what happens in this example: @@ -118,7 +118,7 @@ ReactDOM.render( ); ``` -[Try it on CodePen.](http://codepen.io/gaearon/pen/KgQKPr?editors=0010) +Try it on CodePen. Typically, new React apps have a single `App` component at the very top. However, if you integrate React into an existing app, you might start bottom-up with a small component like `Button` and gradually work your way to the top of the view hierarchy. @@ -152,7 +152,7 @@ function Comment(props) { } ``` -[Try it on CodePen.](http://codepen.io/gaearon/pen/VKQwEo?editors=0010) +Try it on CodePen. It accepts `author` (an object), `text` (a string), and `date` (a date) as props, and describes a comment on a social media website. @@ -231,7 +231,7 @@ function Comment(props) { } ``` -[Try it on CodePen.](http://codepen.io/gaearon/pen/rrJNJY?editors=0010) +Try it on CodePen. Extracting components might seem like grunt work at first, but having a palette of reusable components pays off in larger apps. A good rule of thumb is that if a part of your UI is used several times (`Button`, `Panel`, `Avatar`), or is complex enough on its own (`App`, `FeedStory`, `Comment`), it is a good candidate to be a reusable component. diff --git a/gatsby-node.js b/gatsby-node.js index 381595491..359be8011 100644 --- a/gatsby-node.js +++ b/gatsby-node.js @@ -6,7 +6,8 @@ 'use strict'; -const {readdirSync, readFileSync} = require('fs'); +const recursiveReaddir = require('recursive-readdir'); +const {readFileSync} = require('fs'); const {resolve} = require('path'); const webpack = require('webpack'); @@ -170,12 +171,13 @@ exports.createPages = async ({graphql, boundActionCreators}) => { // Create Codepen redirects. // These use the Codepen prefill API to JIT-create Pens. // https://blog.codepen.io/documentation/api/prefill/ - readdirSync('./codepen').forEach(file => { + const files = await recursiveReaddir('./codepen'); + files.forEach(file => { const slug = file.substring(0, file.length - 3); // Trim extension - const code = readFileSync(`./codepen/${file}`, 'utf8'); + const code = readFileSync(file, 'utf8'); createPage({ - path: `/codepen/${slug}`, + path: slug, component: resolve('./src/templates/codepen-example.js'), context: { code, diff --git a/package.json b/package.json index 9adc56da2..82a862d66 100644 --- a/package.json +++ b/package.json @@ -84,6 +84,7 @@ "reset": "rimraf ./.cache" }, "devDependencies": { - "eslint-config-prettier": "^2.6.0" + "eslint-config-prettier": "^2.6.0", + "recursive-readdir": "^2.2.1" } } diff --git a/yarn.lock b/yarn.lock index a7cd0524b..c527a80a1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1535,7 +1535,7 @@ bowser@^1.6.0: version "1.7.1" resolved "https://registry.yarnpkg.com/bowser/-/bowser-1.7.1.tgz#a4de8f18a1a0dc9531eb2a92a1521fb6a9ba96a5" -brace-expansion@^1.1.7: +brace-expansion@^1.0.0, brace-expansion@^1.1.7: version "1.1.8" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292" dependencies: @@ -6367,6 +6367,12 @@ minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: dependencies: brace-expansion "^1.1.7" +minimatch@3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.3.tgz#2a4e4090b96b2db06a9d7df01055a62a77c9b774" + dependencies: + brace-expansion "^1.0.0" + minimist@0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" @@ -8054,6 +8060,12 @@ rechoir@^0.6.2: dependencies: resolve "^1.1.6" +recursive-readdir@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/recursive-readdir/-/recursive-readdir-2.2.1.tgz#90ef231d0778c5ce093c9a48d74e5c5422d13a99" + dependencies: + minimatch "3.0.3" + redbox-react@^1.3.6: version "1.5.0" resolved "https://registry.yarnpkg.com/redbox-react/-/redbox-react-1.5.0.tgz#04dab11557d26651bf3562a67c22ace56c5d3967"