Skip to content

Commit 7c69169

Browse files
authored
Merge branch 'main' into repository-https
2 parents cc4b5d6 + 1f3eadb commit 7c69169

9 files changed

Lines changed: 3225 additions & 622 deletions

File tree

lib/frontmatter.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,9 @@ const schema = {
8989
href: { type: 'string' }
9090
}
9191
}
92+
},
93+
interactive: {
94+
type: 'boolean'
9295
}
9396
}
9497
}

lib/page.js

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ const pathUtils = require('./path-utils')
1616
const Permalink = require('./permalink')
1717
const languages = require('./languages')
1818
const renderContent = require('./render-content')
19+
const { renderReact } = require('./react/engine')
1920
const frontmatter = require('./frontmatter')
2021
const products = require('./all-products')
2122
const slash = require('slash')
@@ -133,10 +134,31 @@ class Page {
133134
this.title = await renderContent(this.rawTitle, context, { textOnly: true, encodeEntities: true })
134135
this.shortTitle = await renderContent(this.shortTitle, context, { textOnly: true, encodeEntities: true })
135136

136-
const markdown = this.mapTopic
137+
let markdown = this.mapTopic
137138
? getMapTopicContent(this, context.pages, context.redirects)
138139
: this.markdown
139140

141+
// If the article is interactive parse the React!
142+
if (this.interactive) {
143+
// Search for the react code comments to find the react components
144+
const reactComponents = markdown.match(/<!--react-->(.*?)<!--end-react-->/gs)
145+
146+
// Render each of the react components in the markdown
147+
await Promise.all(reactComponents.map(async (reactComponent) => {
148+
let componentStr = reactComponent
149+
150+
// Remove the React comment indicators
151+
componentStr = componentStr.replace('<!--react-->\n', '').replace('<!--react-->', '')
152+
componentStr = componentStr.replace('\n<!--end-react-->', '').replace('<!--end-react-->', '')
153+
154+
// Get the rendered component
155+
const renderedComponent = await renderReact(componentStr)
156+
157+
// Replace the react component with the rendered markdown
158+
markdown = markdown.replace(reactComponent, renderedComponent)
159+
}))
160+
}
161+
140162
const html = await renderContent(markdown, context)
141163

142164
// product frontmatter may contain liquid

lib/react/babel.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
const babel = require('@babel/core')
2+
3+
const reactBabelOptions = {
4+
presets: [
5+
'@babel/preset-env',
6+
'@babel/preset-react'
7+
],
8+
plugins: [
9+
'@babel/plugin-transform-react-jsx',
10+
'@babel/plugin-proposal-object-rest-spread',
11+
'@babel/plugin-proposal-class-properties',
12+
'@babel/transform-runtime'
13+
]
14+
}
15+
16+
const transform = code =>
17+
babel.transform(code, reactBabelOptions).code
18+
19+
module.exports = {
20+
transform: transform,
21+
reactBabelOptions: reactBabelOptions
22+
}

lib/react/engine.js

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
const { renderToString } = require('react-dom/server')
2+
const { transform } = require('./babel')
3+
const React = require('react')
4+
const fs = require('fs')
5+
const path = require('path')
6+
const dirTree = require('directory-tree')
7+
8+
// Name of directory for saving transformed components that should be gitignored
9+
const dist = 'dist'
10+
11+
// Build React components
12+
// This loops through the react components and transpiles them to /dist
13+
// so they can be used by Node.js when we do server side rendering
14+
const tree = dirTree('./react/')
15+
if (tree) {
16+
for (const index in tree.children) {
17+
const file = tree.children[index]
18+
if (file.type === 'file' && file.extension === '.js') {
19+
if (!fs.existsSync(path.join(dist, 'react'))) {
20+
fs.mkdirSync(path.join(dist, 'react'), { recursive: true })
21+
}
22+
const content = transform(fs.readFileSync(file.path, 'utf8'))
23+
fs.writeFileSync(path.join(dist, file.path), content)
24+
}
25+
}
26+
}
27+
// End Build React Components
28+
29+
// Register components
30+
const components = {
31+
// CodeBlock: require('../../dist/react/CodeBlock'),
32+
// CodeEditor: require('../../dist/react/CodeEditor')
33+
}
34+
35+
const renderReact = async componentStr => {
36+
// Get component name as string so we can use it in the class name
37+
// which will be needed later if we choose to do client side React hydration
38+
const componentName = componentStr.match(/<([a-zA-Z]+)\s/)[1]
39+
// Add the wrapper and class name so we can later use React hydration on the client
40+
// side
41+
const jsx = `<div className="react-component-${componentName}">\n${componentStr}\n</div>`
42+
43+
const component = transform(jsx)
44+
45+
// eslint-disable-next-line
46+
const getComponent = new Function(
47+
'React',
48+
...Object.keys(components),
49+
`${component.replace('React', 'return React')}`
50+
)
51+
52+
return renderToString(getComponent(React, ...Object.values(components)))
53+
}
54+
55+
module.exports = {
56+
renderReact
57+
}

0 commit comments

Comments
 (0)