Skip to content

Commit

Permalink
Add experimental react support within markdown
Browse files Browse the repository at this point in the history
Also added fronmatter for interactive: true to turn on React on a file by file
basis and prevent slowing down the builds for non-react files
  • Loading branch information
chiedo committed Oct 18, 2020
1 parent 0547215 commit 83a81e7
Show file tree
Hide file tree
Showing 15 changed files with 32,677 additions and 619 deletions.
25,147 changes: 25,147 additions & 0 deletions assets/js/react-dom.development.js

Large diffs are not rendered by default.

3,318 changes: 3,318 additions & 0 deletions assets/js/react.development.js

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,40 @@ intro: 'While you can grant read/write access to collaborators on a personal rep
versions:
free-pro-team: '*'
enterprise-server: '*'
interactive: true
---

### Personal user accounts

A repository owned by a user account has two permission levels: the *repository owner* and *collaborators*. For more information, see "[Permission levels for a user account repository](/articles/permission-levels-for-a-user-account-repository)."

### Organization accounts
<!--react-->
<BlueContent>
{`var i;
for (i = 0; i < cars.length; i++) {
text += cars[i] + "<br>";
}`}
</BlueContent>
<!--end-react-->

Organization members can have *owner*{% if currentVersion == "free-pro-team@latest" %}, *billing manager*,{% endif %} or *member* roles. Owners have complete administrative access to your organization{% if currentVersion == "free-pro-team@latest" %}, while billing managers can manage billing settings{% endif %}. Member is the default role for everyone else. You can manage access permissions for multiple members at a time with teams. For more information, see:
- "[Permission levels for an organization](/articles/permission-levels-for-an-organization)"
- "[Project board permissions for an organization](/articles/project-board-permissions-for-an-organization)"
- "[Repository permission levels for an organization](/articles/repository-permission-levels-for-an-organization)"
- "[About teams](/articles/about-teams)"

<!--react--><BlueContent>More blue content!</BlueContent><!--end-react-->

<!--react--><RedContent>Red content!</RedContent><!--end-react-->

<!--react--><Timer /><!--end-react-->

{% if currentVersion == "free-pro-team@latest" %}

### Enterprise accounts


*Enterprise owners* have ultimate power over the enterprise account and can take every action in the enterprise account. *Billing managers* can manage your enterprise account's billing settings. Members and outside collaborators of organizations owned by your enterprise account are automatically members of the enterprise account, although they have no access to the enterprise account itself or its settings. For more information, see "[Roles for an enterprise account](/articles/roles-for-an-enterprise-account)."

{% data reusables.gated-features.enterprise-accounts %}
Expand Down
2 changes: 2 additions & 0 deletions includes/head.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,6 @@
<link rel="stylesheet" href="/dist/index.css">
<link rel="alternate icon" type="image/png" href="/assets/images/site/favicon.png">
<link rel="icon" type="image/svg+xml" href="/assets/images/site/favicon.svg">
<script src="/assets/js/react.development.js" crossorigin></script>
<script src="/assets/js/react-dom.development.js" crossorigin></script>
</head>
9 changes: 9 additions & 0 deletions javascripts/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ import experiment from './experiment'
import copyCode from './copy-code'
import { fillCsrf } from './get-csrf'
import initializeEvents from './events'
import BlueContent from '../react/BlueContent'
import RedContent from '../react/RedContent'
import Timer from '../react/Timer'

document.addEventListener('DOMContentLoaded', async () => {
displayPlatformSpecificContent()
Expand All @@ -35,3 +38,9 @@ document.addEventListener('DOMContentLoaded', async () => {
copyCode()
initializeEvents()
})

module.export = {
BlueContent,
RedContent,
Timer
}
3 changes: 3 additions & 0 deletions lib/frontmatter.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ const schema = {
quickstart: { type: 'string' },
learn: { type: 'string' }
}
},
interactive: {
type: 'boolean'
}
}
}
Expand Down
27 changes: 26 additions & 1 deletion lib/page.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@ const pathUtils = require('./path-utils')
const Permalink = require('./permalink')
const languages = require('./languages')
const renderContent = require('./render-content')
const renderReact = require('./react-engine')
const frontmatter = require('./frontmatter')
const products = require('./all-products')
const slash = require('slash')


class Page {
constructor (opts) {
assert(opts.relativePath, 'relativePath is required')
Expand Down Expand Up @@ -119,9 +121,30 @@ class Page {
this.title = await renderContent(this.rawTitle, context, { textOnly: true, encodeEntities: true })
this.shortTitle = await renderContent(this.shortTitle, context, { textOnly: true, encodeEntities: true })

const markdown = this.mapTopic
let markdown = this.mapTopic
? getMapTopicContent(this, context.pages, context.redirects)
: this.markdown

// If the article is interactive parse the React!
if (this.interactive) {
// Search for the react code comments to find the react components
const reactComponents = markdown.match(/<!--react-->(.*?)<!--end-react-->/gms)

// Render each of the react components in the markdown
for (const index in reactComponents) {
let componentStr = reactComponents[index]

// Remove the React comment indicators
componentStr = componentStr.replace('<!--react-->\n', '').replace('<!--react-->', '')
componentStr = componentStr.replace('\n<!--end-react-->', '').replace('<!--end-react-->', '')

// Get the rendered component
const renderedComponent = await renderReact(componentStr)

// Replace the react component with the rendered markdown
markdown = markdown.replace(reactComponents[index], renderedComponent)
}
}

const html = await renderContent(markdown, context)

Expand Down Expand Up @@ -163,6 +186,7 @@ class Page {
return cleanedHTML
}


// Allow other modules (like custom liquid tags) to make one-off requests
// for a page's rendered properties like `title` and `intro`
async renderProp (propName, context, opts = { unwrap: false }) {
Expand All @@ -179,6 +203,7 @@ class Page {

const html = await renderContent(prop, context, opts)


if (!opts.unwrap) return html

// The unwrap option removes surrounding tags from a string, preserving any inner HTML
Expand Down
30 changes: 30 additions & 0 deletions lib/react-engine.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
const babel = require('@babel/core')
const React = require('react')
const { renderToString } = require('react-dom/server')
const mdx = require('@mdx-js/mdx')
const { MDXProvider, mdx: createElement } = require('@mdx-js/react')
const BlueContent = require('../dist/react/BlueContent')
const RedContent = require('../dist/react/RedContent')
const Timer = require('../dist/react/Timer')

const transform = code =>
babel.transform(code, {
plugins: [
'@babel/plugin-transform-react-jsx',
'@babel/plugin-proposal-object-rest-spread'
]
}).code

const renderReact = async componentStr => {
let componentName
if (componentStr.trim().startsWith('<BlueContent')) componentName = 'bluecontent'
if (componentStr.trim().startsWith('<RedContent')) componentName = 'redcontent'
if (componentStr.trim().startsWith('<Timer')) componentName = 'timer'

const jsx = `<div className="react-component-${componentName}">\n${componentStr}\n</div>`
const component = transform(jsx)

return renderToString(eval(component))
}

module.exports = renderReact
Loading

0 comments on commit 83a81e7

Please sign in to comment.