diff --git a/gatsby-config.js b/gatsby-config.js
index 7f4982aec..ed0799f34 100644
--- a/gatsby-config.js
+++ b/gatsby-config.js
@@ -33,14 +33,14 @@ module.exports = {
{
resolve: 'gatsby-source-filesystem',
options: {
- path: `${__dirname}/src/pages`,
name: 'pages',
+ path: `${__dirname}/src/pages`,
},
},
{
resolve: 'gatsby-source-filesystem',
options: {
- name: 'packages',
+ name: 'content',
path: `${__dirname}/content/`,
},
},
diff --git a/plugins/gatsby-transformer-home-example-code/gatsby-node.js b/plugins/gatsby-transformer-home-example-code/gatsby-node.js
index e8d813313..5965a3ba7 100644
--- a/plugins/gatsby-transformer-home-example-code/gatsby-node.js
+++ b/plugins/gatsby-transformer-home-example-code/gatsby-node.js
@@ -1,28 +1,33 @@
-const {readdirSync, readFileSync} = require('fs');
-const {join, resolve} = require('path');
+const crypto = require(`crypto`);
+
+const createContentDigest = obj =>
+ crypto
+ .createHash(`md5`)
+ .update(obj)
+ .digest(`hex`);
// Store code snippets in GraphQL for the home page examples.
// Snippets will be matched with markdown templates of the same name.
-exports.sourceNodes = ({graphql, actions}) => {
+exports.onCreateNode = async ({actions, node, loadNodeContent}) => {
const {createNode} = actions;
+ const {absolutePath, ext, name, relativeDirectory, sourceInstanceName} = node;
- const path = resolve(__dirname, '../../content/home/examples');
- const files = readdirSync(path);
-
- files.forEach(file => {
- if (file.match(/\.js$/)) {
- const code = readFileSync(join(path, file), 'utf8');
- const id = file.replace(/\.js$/, '');
-
- createNode({
- id,
- children: [],
- parent: 'EXAMPLES',
- internal: {
- type: 'ExampleCode',
- contentDigest: JSON.stringify(code),
- },
- });
- }
- });
+ if (
+ sourceInstanceName === 'content' &&
+ relativeDirectory === 'home/examples' &&
+ ext === '.js'
+ ) {
+ const code = await loadNodeContent(node);
+ createNode({
+ id: name,
+ children: [],
+ parent: node.id,
+ code,
+ mdAbsolutePath: absolutePath.replace(/\.js$/, '.md'),
+ internal: {
+ type: 'ExampleCode',
+ contentDigest: createContentDigest(JSON.stringify(code)),
+ },
+ });
+ }
};
diff --git a/src/components/CodeEditor/CodeEditor.js b/src/components/CodeEditor/CodeEditor.js
index f5ef723a7..9c1aab34e 100644
--- a/src/components/CodeEditor/CodeEditor.js
+++ b/src/components/CodeEditor/CodeEditor.js
@@ -38,6 +38,12 @@ class CodeEditor extends Component {
}
}
+ UNSAFE_componentWillReceiveProps(nextProps) {
+ if (this.props.code !== nextProps.code) {
+ this.setState(this._updateState(nextProps.code));
+ }
+ }
+
render() {
const {children} = this.props;
const {
@@ -68,211 +74,169 @@ class CodeEditor extends Component {
- {children && (
-
- {children}
-
- )}
-
+
+ Live JSX Editor
+
+
+
+
+
+
+
+ {error && (
+
-
- Live JSX Editor
-
-
-
-
-
-
-
- {error && (
-
-
-
- Error
-
-
-
- {errorMessage}
-
+ Error
+
- )}
- {!error && (
+
+ {errorMessage}
+
+
+ )}
+ {!error && (
+
+
+
+ )}
);
diff --git a/src/components/CodeExample/CodeExample.js b/src/components/CodeExample/CodeExample.js
new file mode 100644
index 000000000..31fe7e2c6
--- /dev/null
+++ b/src/components/CodeExample/CodeExample.js
@@ -0,0 +1,73 @@
+import React, {Component} from 'react';
+import PropTypes from 'prop-types';
+
+import {colors, media} from 'theme';
+import CodeEditor from '../CodeEditor/CodeEditor';
+
+class CodeExample extends Component {
+ render() {
+ const {children, code, id, loaded} = this.props;
+ return (
+
+ {children && (
+
+ {children}
+
+ )}
+ {loaded ?
:
Loading code example...
}
+
+ );
+ }
+}
+
+CodeExample.propTypes = {
+ children: PropTypes.node,
+ code: PropTypes.string.isRequired,
+ loaded: PropTypes.bool.isRequired,
+};
+
+export default CodeExample;
diff --git a/src/components/CodeExample/index.js b/src/components/CodeExample/index.js
new file mode 100644
index 000000000..6bff868c1
--- /dev/null
+++ b/src/components/CodeExample/index.js
@@ -0,0 +1,3 @@
+import CodeExample from './CodeExample';
+
+export default CodeExample;
diff --git a/src/pages/index.js b/src/pages/index.js
index 480d8b0a9..786e37336 100644
--- a/src/pages/index.js
+++ b/src/pages/index.js
@@ -7,72 +7,46 @@
import ButtonLink from 'components/ButtonLink';
import Container from 'components/Container';
import Flex from 'components/Flex';
-import mountCodeExample from 'utils/mountCodeExample';
+import CodeExample from 'components/CodeExample';
import PropTypes from 'prop-types';
import React, {Component} from 'react';
import {graphql} from 'gatsby';
import TitleAndMetaTags from 'components/TitleAndMetaTags';
import Layout from 'components/Layout';
import {colors, media, sharedStyles} from 'theme';
-import createOgUrl from 'utils/createOgUrl';
import loadScript from 'utils/loadScript';
+import createOgUrl from 'utils/createOgUrl';
import {babelURL} from 'site-constants';
import ReactDOM from 'react-dom';
import logoWhiteSvg from 'icons/logo-white.svg';
class Home extends Component {
- constructor(props, context) {
- super(props, context);
-
- const {data} = props;
-
- const code = data.code.edges.reduce((map, {node}) => {
- map[node.id] = JSON.parse(node.internal.contentDigest);
-
- return map;
- }, {});
-
- const examples = data.examples.edges.map(({node}) => ({
- content: node.html,
- id: node.fields.slug.replace(/^.+\//, '').replace('.html', ''),
- title: node.frontmatter.title,
- }));
-
- const marketing = data.marketing.edges.map(({node}) => ({
- title: node.frontmatter.title,
- content: node.html,
- }));
-
- this.state = {
- code,
- examples,
- marketing,
- };
- }
+ state = {
+ babelLoaded: false,
+ };
componentDidMount() {
- const {code, examples} = this.state;
-
- examples.forEach(({id}) => {
- renderExamplePlaceholder(id);
- });
-
- function mountCodeExamples() {
- examples.forEach(({id}) => {
- mountCodeExample(id, code[id]);
- });
- }
-
- loadScript(babelURL).then(mountCodeExamples, error => {
- console.error('Babel failed to load.');
-
- mountCodeExamples();
- });
+ loadScript(babelURL).then(
+ () => {
+ this.setState({
+ babelLoaded: true,
+ });
+ },
+ error => {
+ console.error('Babel failed to load.');
+ },
+ );
}
render() {
- const {examples, marketing} = this.state;
- const {location} = this.props;
+ const {babelLoaded} = this.state;
+ const {data, location} = this.props;
+ const {codeExamples, examples, marketing} = data;
+
+ const code = codeExamples.edges.reduce((lookup, {node}) => {
+ lookup[node.mdAbsolutePath] = node;
+ return lookup;
+ }, {});
return (
@@ -217,7 +191,7 @@ class Home extends Component {
whiteSpace: 'nowrap',
},
}}>
- {marketing.map((column, index) => (
+ {marketing.edges.map(({node: column}, index) => (
- {column.title}
+ {column.frontmatter.title}
-
+
))}
@@ -283,27 +257,19 @@ class Home extends Component {
/>
- {examples.map((example, index) => (
-
- ))}
+ {examples.edges.map(({node}, index) => {
+ const snippet = code[node.fileAbsolutePath];
+ return (
+
+ {node.frontmatter.title}
+
+
+ );
+ })}
@@ -339,7 +305,6 @@ class Home extends Component {
Home.propTypes = {
data: PropTypes.shape({
- code: PropTypes.object.isRequired,
examples: PropTypes.object.isRequired,
marketing: PropTypes.object.isRequired,
}).isRequired,
@@ -382,22 +347,23 @@ const CtaItem = ({children, primary = false}) => (
export const pageQuery = graphql`
query IndexMarkdown {
- code: allExampleCode {
+ codeExamples: allExampleCode {
edges {
node {
id
- internal {
- contentDigest
- }
+ code
+ mdAbsolutePath
}
}
}
+
examples: allMarkdownRemark(
filter: {fileAbsolutePath: {regex: "//home/examples//"}}
sort: {fields: [frontmatter___order], order: ASC}
) {
edges {
node {
+ fileAbsolutePath
fields {
slug
}
diff --git a/src/utils/mountCodeExample.js b/src/utils/mountCodeExample.js
deleted file mode 100644
index 41c35eef8..000000000
--- a/src/utils/mountCodeExample.js
+++ /dev/null
@@ -1,36 +0,0 @@
-/**
- * Copyright (c) 2013-present, Facebook, Inc.
- *
- * @emails react-core
- */
-
-import CodeEditor from '../components/CodeEditor';
-import React from 'react';
-import ReactDOM from 'react-dom';
-
-// TODO This is a huge hack.
-// Remark transform this template to split code examples and their targets apart.
-const mountCodeExample = (containerId, code) => {
- const container = document.getElementById(containerId);
- const parent = container.parentElement;
-
- const children = Array.prototype.filter.call(
- parent.children,
- child => child !== container,
- );
- children.forEach(child => parent.removeChild(child));
-
- const description = children
- .map(child => child.outerHTML)
- .join('')
- .replace(/`([^`]+)`/g, '$1');
-
- ReactDOM.render(
-
- {}
- ,
- container,
- );
-};
-
-export default mountCodeExample;