This commit is contained in:
Octomerger Bot 2020-12-03 13:02:53 -08:00 коммит произвёл GitHub
Родитель c7870f44b7 218d08f38d
Коммит 25eeefbee6
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
11 изменённых файлов: 3127 добавлений и 528 удалений

Просмотреть файл

@ -9,11 +9,7 @@ versions:
github-ae: '*'
---
There are two stable versions of the GitHub API: the [REST API](/rest) and the [GraphQL API](/graphql).
When using the REST API, we encourage you to [request v3 via the `Accept` header](/rest/overview/media-types#request-specific-version).
For information on using the GraphQL API, see the [v4 docs](/graphql).
There are two stable versions of the GitHub API: the [REST API](/rest) and the [GraphQL API](/graphql). When using the REST API, we encourage you to [request v3 via the `Accept` header](/v3/media/#request-specific-version). For information on using the GraphQL API, see the [v4 docs](/graphql).
## Deprecated versions

Просмотреть файл

@ -3,7 +3,7 @@
<ul class="d-flex list-style-none flex-wrap flex-justify-center flex-xl-justify-start">
<li class="d-flex mr-xl-3 text-gray">
{% octicon "mark-github" height="20" class="mr-2 mr-xl-3" %}
<span>{{ "now" | date: "%Y" }} GitHub, Inc.</span>
<span>&copy; {{ "now" | date: "%Y" }} GitHub, Inc.</span>
</li>
<li class="ml-3"><a href="/github/site-policy/github-terms-of-service">{% data ui.footer.terms %} </a></li>
<li class="ml-3"><a href="/github/site-policy/github-privacy-statement">{% data ui.footer.privacy %} </a></li>

Просмотреть файл

@ -87,6 +87,9 @@ const schema = {
href: { type: 'string' }
}
}
},
interactive: {
type: 'boolean'
}
}
}

Просмотреть файл

@ -16,6 +16,7 @@ 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')
@ -133,10 +134,31 @@ 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-->/gs)
// Render each of the react components in the markdown
await Promise.all(reactComponents.map(async (reactComponent) => {
let componentStr = reactComponent
// 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(reactComponent, renderedComponent)
}))
}
const html = await renderContent(markdown, context)
// product frontmatter may contain liquid

22
lib/react/babel.js Normal file
Просмотреть файл

@ -0,0 +1,22 @@
const babel = require('@babel/core')
const reactBabelOptions = {
presets: [
'@babel/preset-env',
'@babel/preset-react'
],
plugins: [
'@babel/plugin-transform-react-jsx',
'@babel/plugin-proposal-object-rest-spread',
'@babel/plugin-proposal-class-properties',
'@babel/transform-runtime'
]
}
const transform = code =>
babel.transform(code, reactBabelOptions).code
module.exports = {
transform: transform,
reactBabelOptions: reactBabelOptions
}

57
lib/react/engine.js Normal file
Просмотреть файл

@ -0,0 +1,57 @@
const { renderToString } = require('react-dom/server')
const { transform } = require('./babel')
const React = require('react')
const fs = require('fs')
const path = require('path')
const dirTree = require('directory-tree')
// Name of directory for saving transformed components that should be gitignored
const dist = 'dist'
// Build React components
// This loops through the react components and transpiles them to /dist
// so they can be used by Node.js when we do server side rendering
const tree = dirTree('./react/')
if (tree) {
for (const index in tree.children) {
const file = tree.children[index]
if (file.type === 'file' && file.extension === '.js') {
if (!fs.existsSync(path.join(dist, 'react'))) {
fs.mkdirSync(path.join(dist, 'react'), { recursive: true })
}
const content = transform(fs.readFileSync(file.path, 'utf8'))
fs.writeFileSync(path.join(dist, file.path), content)
}
}
}
// End Build React Components
// Register components
const components = {
// CodeBlock: require('../../dist/react/CodeBlock'),
// CodeEditor: require('../../dist/react/CodeEditor')
}
const renderReact = async componentStr => {
// Get component name as string so we can use it in the class name
// which will be needed later if we choose to do client side React hydration
const componentName = componentStr.match(/<([a-zA-Z]+)\s/)[1]
// Add the wrapper and class name so we can later use React hydration on the client
// side
const jsx = `<div className="react-component-${componentName}">\n${componentStr}\n</div>`
const component = transform(jsx)
// eslint-disable-next-line
const getComponent = new Function(
'React',
...Object.keys(components),
`${component.replace('React', 'return React')}`
)
return renderToString(getComponent(React, ...Object.values(components)))
}
module.exports = {
renderReact
}

3469
package-lock.json сгенерированный

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -9,9 +9,15 @@
},
"license": "(MIT AND CC-BY-4.0)",
"dependencies": {
"@babel/core": "^7.8.3",
"@babel/core": "^7.12.9",
"@babel/plugin-proposal-class-properties": "^7.12.1",
"@babel/plugin-syntax-class-properties": "^7.12.1",
"@babel/plugin-transform-modules-amd": "^7.12.1",
"@babel/plugin-transform-modules-commonjs": "^7.12.1",
"@babel/plugin-transform-react-jsx": "^7.12.7",
"@babel/plugin-transform-runtime": "^7.11.0",
"@babel/preset-env": "^7.8.4",
"@babel/preset-env": "^7.12.7",
"@babel/preset-react": "^7.12.7",
"@babel/runtime": "^7.11.2",
"@github-docs/data-directory": "^1.2.0",
"@github-docs/frontmatter": "^1.3.1",
@ -22,6 +28,7 @@
"@primer/octicons": "^11.0.0",
"algoliasearch": "^3.35.1",
"babel-loader": "^8.1.0",
"babel-preset-env": "^1.7.0",
"browser-date-formatter": "^3.0.3",
"change-case": "^3.1.0",
"cheerio": "^1.0.0-rc.3",
@ -32,8 +39,9 @@
"copy-webpack-plugin": "^6.0.3",
"cors": "^2.8.5",
"cross-env": "^7.0.2",
"css-loader": "^4.0.0",
"css-loader": "^4.3.0",
"csurf": "^1.11.0",
"directory-tree": "^2.2.5",
"dotenv": "^8.2.0",
"express": "^4.17.1",
"express-rate-limit": "^5.1.3",
@ -65,6 +73,8 @@
"port-used": "^2.0.8",
"querystring": "^0.2.0",
"rate-limit-redis": "^2.0.0",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"readline-sync": "^1.4.10",
"remark-code-extra": "^1.0.1",
"resolve-url-loader": "^3.1.2",

38
react/README.md Normal file
Просмотреть файл

@ -0,0 +1,38 @@
# React (Experimental)
The files in this directory are React components. We can use these components within Markdown files by adding `interactive: true` frontmatter to a Markdown file. Then, using the following syntax to embed the component:
```
<!--react-->
<ReactComponentHere some='prop' other={`prop`} />
<!--end-react-->
```
Theoretically React can be embedded anywhere on the site with a little bit of creativity but that is yet to be tested.
## Defining new components
Start by adding frontmatter `interactive:true` to any Markdown file that you want to support React. This prevents React render time from slowing down any other page and keeping the impact on performance very low.
1. Create the component in the `./react` directory
2. Register the component for webpack in `./javascripts/index.js` so the component works with client side rendering
3. Register the component in `./lib/react-engine.js` so the component works with server side rendering
4. If the component needs to be evaluated client side, see [this example](https://github.com/github/docs/blob/a48998c7890b71c8f58eda1fa31b50df348a0042/react/CodeEditor.js) for how to ensure the client side component rendered by the server gets [Hydrated](https://reactjs.org/docs/react-dom.html#hydrate).
## A Complete Example
The following example demonstrates the addition of an interactive CodeEditor on one of our pages:
- [Defining the CodeEditor React Component](https://github.com/github/docs/blob/a48998c7890b71c8f58eda1fa31b50df348a0042/react/CodeEditor.js)
- [Configuring the CodeEditor for client-side rendering](https://github.com/github/docs/blob/a48998c7890b71c8f58eda1fa31b50df348a0042/javascripts/index.js#L45)
- [Configuring the CodeEditor for server-side rendering](https://github.com/github/docs/blob/a48998c7890b71c8f58eda1fa31b50df348a0042/lib/react/engine.js#L30)
- CodeEditor added to a markdown file
- [Enabling React components on a page](https://github.com/github/docs/blame/a48998c7890b71c8f58eda1fa31b50df348a0042/content/github/getting-started-with-github/access-permissions-on-github.md#L12)
- [Adding the CodeEditor to a page](https://github.com/github/docs/blame/a48998c7890b71c8f58eda1fa31b50df348a0042/content/github/getting-started-with-github/access-permissions-on-github.md#L47)
## Gotchas
- When requiring React components from npm you will often need to explicitly call `require('component').default` to make sure you get the component
- Some code examples you get from external React packages won't work out of the box, you often need to dig into the module and determine whether it's exporting to default or not which will in many cases cause you to need to `require('package').default`
- `import` doesn't always work. Use `require` for a more consistent experience with React in this codebase.
- If components require you to load CSS, you need to load that CSS in the `javascripts/index.js` file as shown [here](https://github.com/github/docs/blob/a48998c7890b71c8f58eda1fa31b50df348a0042/javascripts/index.js#L22)

Просмотреть файл

@ -30,6 +30,7 @@ const main = async () => {
excludeDev: true,
ignore: [
'@babel/*',
'babel-preset-env',
'@primer/*',
'instantsearch.js',
'querystring',

Просмотреть файл

@ -2,6 +2,7 @@ const path = require('path')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const { EnvironmentPlugin } = require('webpack')
const { reactBabelOptions } = require('./lib/react/babel')
module.exports = {
devtool: 'source-map', // this prevents webpack from using eval
@ -13,9 +14,19 @@ module.exports = {
},
module: {
rules: [
{
test: /\.js$/,
include: [
path.resolve(__dirname, 'react')
],
use: {
loader: 'babel-loader',
options: reactBabelOptions
}
},
{
test: /\.m?js$/,
exclude: /(node_modules|bower_components)/,
exclude: /(node_modules|bower_components|react)/,
use: {
loader: 'babel-loader',
options: {
@ -29,6 +40,10 @@ module.exports = {
}
}
},
{
test: /\.css$/i,
use: ['style-loader', 'css-loader']
},
{
test: /\.s[ac]ss$/i,
use: [