From 6fd0d7950e2fa5864644b9db52a7ef5564d3bd4c Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Mon, 6 Nov 2017 23:00:26 +0000 Subject: [PATCH 1/5] Added custom remark transform for Codepen example links --- content/docs/components-and-props.md | 8 ++-- content/docs/hello-world.md | 2 +- content/docs/introducing-jsx.md | 3 +- gatsby-config.js | 1 + package.json | 3 +- .../gatsby-remark-codepen-examples/index.js | 45 +++++++++++++++++++ .../package.json | 4 ++ yarn.lock | 6 +++ 8 files changed, 65 insertions(+), 7 deletions(-) create mode 100644 plugins/gatsby-remark-codepen-examples/index.js create mode 100644 plugins/gatsby-remark-codepen-examples/package.json diff --git a/content/docs/components-and-props.md b/content/docs/components-and-props.md index b6e967c7..2be8b748 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. +[](codepen:components-and-props/rendering-a-component). Let's recap what happens in this example: @@ -118,7 +118,7 @@ ReactDOM.render( ); ``` -Try it on CodePen. +[](codepen:components-and-props/composing-components). 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. +[](codepen:components-and-props/extracting-components). 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. +[](codepen:components-and-props/extracting-components-continued). 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/content/docs/hello-world.md b/content/docs/hello-world.md index 29c0f839..b4f5e692 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](codepen:hello-world). 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 11dabf04..e92e9025 100644 --- a/content/docs/introducing-jsx.md +++ b/content/docs/introducing-jsx.md @@ -6,6 +6,7 @@ prev: hello-world.html next: rendering-elements.html --- + Consider this variable declaration: ```js @@ -46,7 +47,7 @@ ReactDOM.render( ); ``` -Try it on CodePen. +[](codepen:introducing-jsx). 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-config.js b/gatsby-config.js index 01961f1e..60be836c 100644 --- a/gatsby-config.js +++ b/gatsby-config.js @@ -56,6 +56,7 @@ module.exports = { }, }, 'gatsby-remark-autolink-headers', + 'gatsby-remark-codepen-examples', 'gatsby-remark-use-jsx', { resolve: 'gatsby-remark-prismjs', diff --git a/package.json b/package.json index 82a862d6..8501d1de 100644 --- a/package.json +++ b/package.json @@ -85,6 +85,7 @@ }, "devDependencies": { "eslint-config-prettier": "^2.6.0", - "recursive-readdir": "^2.2.1" + "recursive-readdir": "^2.2.1", + "unist-util-map": "^1.0.3" } } diff --git a/plugins/gatsby-remark-codepen-examples/index.js b/plugins/gatsby-remark-codepen-examples/index.js new file mode 100644 index 00000000..f00fb76b --- /dev/null +++ b/plugins/gatsby-remark-codepen-examples/index.js @@ -0,0 +1,45 @@ +const map = require('unist-util-map'); + +const DEFAULT_LINK_TEXT = 'Try it on CodePen'; + +// TODO target="_blank" +module.exports = ({markdownAST}) => { + map(markdownAST, (node, index, parent) => { + // eg convert + // from: [](codepen:introducing-jsx) + // to: Try it on CodePen + // from: [Try the Hello World example on CodePen](codepen:hello-world) + // to: Try the Hello World example on CodePen + if (node.type === 'link' && node.url.startsWith('codepen:')) { + const href = node.url.replace('codepen:', '/codepen/'); + const text = + node.children.length === 0 ? DEFAULT_LINK_TEXT : node.children[0].value; + + const anchorOpenNode = { + type: 'html', + value: ``, + }; + + const textNode = { + type: 'text', + value: text, + }; + + const anchorCloseNode = { + type: 'html', + value: '', + }; + + parent.children.splice( + index, + 1, + anchorOpenNode, + textNode, + anchorCloseNode, + ); + } + + // No change + return node; + }); +}; diff --git a/plugins/gatsby-remark-codepen-examples/package.json b/plugins/gatsby-remark-codepen-examples/package.json new file mode 100644 index 00000000..f6017a58 --- /dev/null +++ b/plugins/gatsby-remark-codepen-examples/package.json @@ -0,0 +1,4 @@ +{ + "name": "gatsby-remark-codepen-examples", + "version": "0.0.1" +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index c527a80a..9640d346 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9795,6 +9795,12 @@ unist-util-is@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-2.1.1.tgz#0c312629e3f960c66e931e812d3d80e77010947b" +unist-util-map@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/unist-util-map/-/unist-util-map-1.0.3.tgz#26a913d7cddb3cd3e9a886d135d37a3d1f54e514" + dependencies: + object-assign "^4.0.1" + unist-util-modify-children@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/unist-util-modify-children/-/unist-util-modify-children-1.1.1.tgz#66d7e6a449e6f67220b976ab3cb8b5ebac39e51d" From b9616f1bdf45df70d5dfd8980a080e47290e9685 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Mon, 6 Nov 2017 23:23:45 +0000 Subject: [PATCH 2/5] Added check to ensure linked Codepen file exists --- plugins/gatsby-remark-codepen-examples/index.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/plugins/gatsby-remark-codepen-examples/index.js b/plugins/gatsby-remark-codepen-examples/index.js index f00fb76b..e5e1ee6c 100644 --- a/plugins/gatsby-remark-codepen-examples/index.js +++ b/plugins/gatsby-remark-codepen-examples/index.js @@ -1,3 +1,5 @@ +const {existsSync} = require('fs'); +const {join} = require('path'); const map = require('unist-util-map'); const DEFAULT_LINK_TEXT = 'Try it on CodePen'; @@ -15,6 +17,15 @@ module.exports = ({markdownAST}) => { const text = node.children.length === 0 ? DEFAULT_LINK_TEXT : node.children[0].value; + // Verify that the specified example file exists. + const filePath = join(__dirname, `../../${href}.js`); + if (!existsSync(filePath)) { + console.log( + `Invalid Codepen link specified; no such file "${filePath}"`, + ); + process.exit(1); + } + const anchorOpenNode = { type: 'html', value: ``, From 3f6854d0814a681e36dcf41be8f51c90a6c957b1 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Tue, 7 Nov 2017 10:35:49 +0000 Subject: [PATCH 3/5] Changed Codepen link protocol from 'codepen:' to 'codepen://' for improved clarity --- content/docs/components-and-props.md | 8 ++++---- content/docs/hello-world.md | 2 +- content/docs/introducing-jsx.md | 2 +- plugins/gatsby-remark-codepen-examples/index.js | 5 +++-- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/content/docs/components-and-props.md b/content/docs/components-and-props.md index 2be8b748..505ef3c1 100644 --- a/content/docs/components-and-props.md +++ b/content/docs/components-and-props.md @@ -76,7 +76,7 @@ ReactDOM.render( ); ``` -[](codepen:components-and-props/rendering-a-component). +[](codepen://components-and-props/rendering-a-component). Let's recap what happens in this example: @@ -118,7 +118,7 @@ ReactDOM.render( ); ``` -[](codepen:components-and-props/composing-components). +[](codepen://components-and-props/composing-components). 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) { } ``` -[](codepen:components-and-props/extracting-components). +[](codepen://components-and-props/extracting-components). 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) { } ``` -[](codepen:components-and-props/extracting-components-continued). +[](codepen://components-and-props/extracting-components-continued). 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/content/docs/hello-world.md b/content/docs/hello-world.md index b4f5e692..adf4db48 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](codepen:hello-world). 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](codepen://hello-world). 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 e92e9025..674859f9 100644 --- a/content/docs/introducing-jsx.md +++ b/content/docs/introducing-jsx.md @@ -47,7 +47,7 @@ ReactDOM.render( ); ``` -[](codepen:introducing-jsx). +[](codepen://introducing-jsx). 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/plugins/gatsby-remark-codepen-examples/index.js b/plugins/gatsby-remark-codepen-examples/index.js index e5e1ee6c..2a73894e 100644 --- a/plugins/gatsby-remark-codepen-examples/index.js +++ b/plugins/gatsby-remark-codepen-examples/index.js @@ -2,6 +2,7 @@ const {existsSync} = require('fs'); const {join} = require('path'); const map = require('unist-util-map'); +const CODEPEN_PROTOCOL = 'codepen://'; const DEFAULT_LINK_TEXT = 'Try it on CodePen'; // TODO target="_blank" @@ -12,8 +13,8 @@ module.exports = ({markdownAST}) => { // to: Try it on CodePen // from: [Try the Hello World example on CodePen](codepen:hello-world) // to: Try the Hello World example on CodePen - if (node.type === 'link' && node.url.startsWith('codepen:')) { - const href = node.url.replace('codepen:', '/codepen/'); + if (node.type === 'link' && node.url.startsWith(CODEPEN_PROTOCOL)) { + const href = node.url.replace(CODEPEN_PROTOCOL, '/codepen/'); const text = node.children.length === 0 ? DEFAULT_LINK_TEXT : node.children[0].value; From 17a126e5e1951fdfe8b3e2564e621148461adc5a Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Tue, 7 Nov 2017 10:36:23 +0000 Subject: [PATCH 4/5] Replaced console.log with console.error --- plugins/gatsby-remark-codepen-examples/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/gatsby-remark-codepen-examples/index.js b/plugins/gatsby-remark-codepen-examples/index.js index 2a73894e..d3872849 100644 --- a/plugins/gatsby-remark-codepen-examples/index.js +++ b/plugins/gatsby-remark-codepen-examples/index.js @@ -21,7 +21,7 @@ module.exports = ({markdownAST}) => { // Verify that the specified example file exists. const filePath = join(__dirname, `../../${href}.js`); if (!existsSync(filePath)) { - console.log( + console.error( `Invalid Codepen link specified; no such file "${filePath}"`, ); process.exit(1); From 4614ab0791a78fa89e5a1d66f3bdc9017e22fc84 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Tue, 7 Nov 2017 10:39:53 +0000 Subject: [PATCH 5/5] Whitespace --- content/docs/introducing-jsx.md | 1 - 1 file changed, 1 deletion(-) diff --git a/content/docs/introducing-jsx.md b/content/docs/introducing-jsx.md index 674859f9..d636e0c5 100644 --- a/content/docs/introducing-jsx.md +++ b/content/docs/introducing-jsx.md @@ -6,7 +6,6 @@ prev: hello-world.html next: rendering-elements.html --- - Consider this variable declaration: ```js