mirror of
https://github.com/facebook/docusaurus.git
synced 2025-06-02 10:52:35 +02:00
[markdown] Switch to Remarkable (#153)
* Switch to Remarkable * Clean up references to custom code blocks * Remove valdiateDOMNesting warning * Add syntax highlighting * Add Reason support * Breaking change: prismColor to codeColor, remove CompLibrary.Prism, expose hljs * Completely remove Prism and associated CSS rules * Support loading plugins and scripts * Remove CSS rules, allowing Highlight.js theme to be used entirely * Remove unnecessary webplayer script
This commit is contained in:
parent
58613545b6
commit
b832176dc6
18 changed files with 231 additions and 2744 deletions
|
@ -8,12 +8,10 @@
|
|||
const Marked = require("./Marked.js");
|
||||
const Container = require("./Container.js");
|
||||
const GridBlock = require("./GridBlock.js");
|
||||
const Prism = require("./Prism.js");
|
||||
|
||||
// collection of other components to provide to users
|
||||
module.exports = {
|
||||
Marked: Marked,
|
||||
Container: Container,
|
||||
GridBlock: GridBlock,
|
||||
Prism: Prism
|
||||
GridBlock: GridBlock
|
||||
};
|
||||
|
|
|
@ -10,15 +10,24 @@ const React = require("react");
|
|||
// html head for each page
|
||||
class Head extends React.Component {
|
||||
render() {
|
||||
let links = this.props.config.headerLinks;
|
||||
const links = this.props.config.headerLinks;
|
||||
let hasBlog = false;
|
||||
links.map(link => {
|
||||
if (link.blog) hasBlog = true;
|
||||
});
|
||||
let sourceCodeButton = this.props.config.sourceCodeButton;
|
||||
// defaults to github, but other values may be allowed in the future
|
||||
let includeGithubButton =
|
||||
const sourceCodeButton = this.props.config.sourceCodeButton;
|
||||
// defaults to GitHub, but other values may be allowed in the future
|
||||
const includeGitHubButton =
|
||||
sourceCodeButton === "github" || sourceCodeButton == null;
|
||||
|
||||
const highlightDefaultVersion = '9.12.0';
|
||||
const highlightConfig = this.props.config.highlight
|
||||
|| { version: highlightDefaultVersion, theme: 'default' };
|
||||
const highlightVersion = highlightConfig.version || highlightDefaultVersion;
|
||||
const highlightTheme = highlightConfig.theme || 'default';
|
||||
|
||||
const hasCustomScripts = this.props.config.scripts;
|
||||
|
||||
return (
|
||||
<head>
|
||||
<meta charSet="utf-8" />
|
||||
|
@ -64,7 +73,7 @@ class Head extends React.Component {
|
|||
href={this.props.config.url + "/blog/atom.xml"}
|
||||
title={this.props.config.title + " Blog ATOM Feed"}
|
||||
/>
|
||||
)}{" "}
|
||||
)}
|
||||
{hasBlog && (
|
||||
<link
|
||||
rel="alternate"
|
||||
|
@ -73,14 +82,18 @@ class Head extends React.Component {
|
|||
title={this.props.config.title + " Blog RSS Feed"}
|
||||
/>
|
||||
)}
|
||||
{includeGithubButton && (
|
||||
{includeGitHubButton && (
|
||||
<script async defer src="https://buttons.github.io/buttons.js" />
|
||||
)}
|
||||
<script
|
||||
type="text/javascript"
|
||||
src={this.props.config.baseUrl + "js/webplayer.js"}
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href={`//cdnjs.cloudflare.com/ajax/libs/highlight.js/${highlightVersion}/styles/${highlightTheme}.min.css`}
|
||||
/>
|
||||
<script type="text/javascript" src="https://snack.expo.io/embed.js" />
|
||||
{hasCustomScripts && this.props.config.scripts.map(function(source) {
|
||||
return (
|
||||
<script type="text/javascript" src={source} />
|
||||
);
|
||||
})}
|
||||
</head>
|
||||
);
|
||||
}
|
||||
|
|
1110
lib/core/Marked.js
1110
lib/core/Marked.js
File diff suppressed because it is too large
Load diff
1161
lib/core/Prism.js
1161
lib/core/Prism.js
File diff suppressed because it is too large
Load diff
93
lib/core/Remarkable.js
Normal file
93
lib/core/Remarkable.js
Normal file
|
@ -0,0 +1,93 @@
|
|||
'use strict';
|
||||
|
||||
const React = require('react');
|
||||
const hljs = require('highlight.js')
|
||||
const Markdown = require('Remarkable');
|
||||
const toSlug = require("./toSlug.js");
|
||||
|
||||
const CWD = process.cwd();
|
||||
|
||||
function anchors(md) {
|
||||
md.renderer.rules.heading_open = function(tokens, idx /*, options, env */) {
|
||||
return '<h' + tokens[idx].hLevel + '>' + '<a class="anchor" name="' + toSlug(tokens[idx+1].content) + '"></a>';
|
||||
};
|
||||
md.renderer.rules.heading_close = function(tokens, idx /*, options, env */) {
|
||||
return ' <a class="hash-link" href="#' + toSlug(tokens[idx-1].content) + '">#</a>' + '</h' + tokens[idx].hLevel + '>\n';
|
||||
};
|
||||
}
|
||||
|
||||
class Remarkable extends React.Component {
|
||||
|
||||
render() {
|
||||
var Container = this.props.container;
|
||||
|
||||
return (
|
||||
<Container>
|
||||
{this.content()}
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
componentWillUpdate(nextProps, nextState) {
|
||||
if (nextProps.options !== this.props.options) {
|
||||
this.md = new Markdown(nextProps.options);
|
||||
}
|
||||
}
|
||||
|
||||
content() {
|
||||
if (this.props.source) {
|
||||
return <span dangerouslySetInnerHTML={{ __html: this.renderMarkdown(this.props.source) }} />;
|
||||
}
|
||||
else {
|
||||
return React.Children.map(this.props.children, child => {
|
||||
if (typeof child === 'string') {
|
||||
return <span dangerouslySetInnerHTML={{ __html: this.renderMarkdown(child) }} />;
|
||||
}
|
||||
else {
|
||||
return child;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
renderMarkdown(source) {
|
||||
if (!this.md) {
|
||||
this.md = new Markdown({
|
||||
highlight: function (str, lang) {
|
||||
if (lang && hljs.getLanguage(lang)) {
|
||||
try {
|
||||
return hljs.highlight(lang, str).value;
|
||||
} catch (err) {}
|
||||
}
|
||||
|
||||
try {
|
||||
return hljs.highlightAuto(str).value;
|
||||
} catch (err) {}
|
||||
|
||||
return ''; // use external default escaping
|
||||
}
|
||||
});
|
||||
|
||||
// Register anchors plugin
|
||||
this.md.use(anchors);
|
||||
|
||||
// Allow client sites to register their own plugins
|
||||
const siteConfig = require(CWD + "/siteConfig.js");
|
||||
if (siteConfig.markdownPlugins) {
|
||||
siteConfig.markdownPlugins.forEach(function(plugin) {
|
||||
this.md.use(plugin);
|
||||
}, this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return this.md.render(source);
|
||||
}
|
||||
}
|
||||
|
||||
Remarkable.defaultProps = {
|
||||
container: 'div',
|
||||
options: {},
|
||||
};
|
||||
|
||||
module.exports = Remarkable;
|
|
@ -32,6 +32,10 @@ class Site extends React.Component {
|
|||
(this.props.url || "index.html");
|
||||
let latestVersion;
|
||||
|
||||
const highlightDefaultVersion = '9.12.0';
|
||||
const highlightConfig = this.props.config.highlight
|
||||
|| { version: highlightDefaultVersion, theme: 'default' };
|
||||
const highlightVersion = highlightConfig.version || highlightDefaultVersion;
|
||||
if (fs.existsSync(CWD + "/versions.json")) {
|
||||
latestVersion = require(CWD + "/versions.json")[0];
|
||||
}
|
||||
|
@ -124,6 +128,12 @@ class Site extends React.Component {
|
|||
}}
|
||||
/>
|
||||
))}
|
||||
<script src={`//cdnjs.cloudflare.com/ajax/libs/highlight.js/${highlightVersion}/highlight.min.js`}></script>
|
||||
<script
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `hljs.initHighlightingOnLoad();`
|
||||
}}
|
||||
/>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
|
|
|
@ -1,139 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
*
|
||||
* @providesModule SnackPlayer
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var Prism = require('./Prism');
|
||||
var React = require('react');
|
||||
|
||||
const PropTypes = require('prop-types');
|
||||
|
||||
const LatestSDKVersion = '21.0.0';
|
||||
var ReactNativeToExpoSDKVersionMap = {
|
||||
'0.48': '21.0.0',
|
||||
'0.47': '20.0.0',
|
||||
'0.46': '19.0.0',
|
||||
'0.45': '18.0.0',
|
||||
'0.44': '17.0.0',
|
||||
'0.43': '16.0.0',
|
||||
'0.42': '15.0.0',
|
||||
'0.41': '14.0.0',
|
||||
};
|
||||
|
||||
/**
|
||||
* Use the SnackPlayer by including a ```SnackPlayer``` block in markdown.
|
||||
*
|
||||
* Optionally, include url parameters directly after the block's language.
|
||||
* Valid options are name, description, and platform.
|
||||
*
|
||||
* E.g.
|
||||
* ```SnackPlayer?platform=android&name=Hello%20world!
|
||||
* import React from 'react';
|
||||
* import { Text } from 'react-native';
|
||||
*
|
||||
* export default class App extends React.Component {
|
||||
* render() {
|
||||
* return <Text>Hello World!</Text>;
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class SnackPlayer extends React.Component {
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
this.parseParams = this.parseParams.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
window.ExpoSnack && window.ExpoSnack.initialize();
|
||||
}
|
||||
|
||||
render() {
|
||||
var code = encodeURIComponent(this.props.children);
|
||||
var params = this.parseParams(this.props.params);
|
||||
var platform = params.platform
|
||||
? params.platform
|
||||
: 'ios';
|
||||
var name = params.name
|
||||
? decodeURIComponent(params.name)
|
||||
: 'Example';
|
||||
var description = params.description
|
||||
? decodeURIComponent(params.description)
|
||||
: 'Example usage';
|
||||
|
||||
var optionalProps = {};
|
||||
var { version } = this.context;
|
||||
if (version === 'next') {
|
||||
optionalProps[
|
||||
'data-snack-sdk-version'
|
||||
] = LatestSDKVersion;
|
||||
} else {
|
||||
optionalProps[
|
||||
'data-snack-sdk-version'
|
||||
] = ReactNativeToExpoSDKVersionMap[version] ||
|
||||
LatestSDKVersion;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="snack-player">
|
||||
<div
|
||||
className="mobile-friendly-snack"
|
||||
style={{ display: 'none' }}
|
||||
>
|
||||
<Prism>
|
||||
{this.props.children}
|
||||
</Prism>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="desktop-friendly-snack"
|
||||
style={{ marginTop: 15, marginBottom: 15 }}
|
||||
>
|
||||
<div
|
||||
data-snack-name={name}
|
||||
data-snack-description={description}
|
||||
data-snack-code={code}
|
||||
data-snack-platform={platform}
|
||||
data-snack-preview="true"
|
||||
{...optionalProps}
|
||||
style={{
|
||||
overflow: 'hidden',
|
||||
background: '#fafafa',
|
||||
border: '1px solid rgba(0,0,0,.16)',
|
||||
borderRadius: '4px',
|
||||
height: '514px',
|
||||
width: '880px',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
parseParams(paramString) {
|
||||
var params = {};
|
||||
|
||||
if (paramString) {
|
||||
var pairs = paramString.split('&');
|
||||
for (var i = 0; i < pairs.length; i++) {
|
||||
var pair = pairs[i].split('=');
|
||||
params[pair[0]] = pair[1];
|
||||
}
|
||||
}
|
||||
|
||||
return params;
|
||||
}
|
||||
}
|
||||
|
||||
SnackPlayer.contextTypes = {
|
||||
version: PropTypes.number.isRequired,
|
||||
};
|
||||
|
||||
module.exports = SnackPlayer;
|
|
@ -1,75 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @providesModule WebPlayer
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var Prism = require('./Prism');
|
||||
var React = require('react');
|
||||
|
||||
var WEB_PLAYER_VERSION = '1.10.0';
|
||||
|
||||
/**
|
||||
* Use the WebPlayer by including a ```ReactNativeWebPlayer``` block in markdown.
|
||||
*
|
||||
* Optionally, include url parameters directly after the block's language. For
|
||||
* the complete list of url parameters, see: https://github.com/dabbott/react-native-web-player
|
||||
*
|
||||
* E.g.
|
||||
* ```ReactNativeWebPlayer?platform=android
|
||||
* import React from 'react';
|
||||
* import { AppRegistry, Text } from 'react-native';
|
||||
*
|
||||
* const App = () => <Text>Hello World!</Text>;
|
||||
*
|
||||
* AppRegistry.registerComponent('MyApp', () => App);
|
||||
* ```
|
||||
*/
|
||||
class WebPlayer extends React.Component {
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
this.parseParams = this.parseParams.bind(this);
|
||||
}
|
||||
|
||||
parseParams(paramString) {
|
||||
var params = {};
|
||||
|
||||
if (paramString) {
|
||||
var pairs = paramString.split('&');
|
||||
for (var i = 0; i < pairs.length; i++) {
|
||||
var pair = pairs[i].split('=');
|
||||
params[pair[0]] = pair[1];
|
||||
}
|
||||
}
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
render() {
|
||||
var hash = `#code=${encodeURIComponent(this.props.children)}`;
|
||||
|
||||
if (this.props.params) {
|
||||
hash += `&${this.props.params}`;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={'web-player'}>
|
||||
<Prism>{this.props.children}</Prism>
|
||||
<iframe
|
||||
style={{marginTop: 4}}
|
||||
width="880"
|
||||
height={this.parseParams(this.props.params).platform === 'android' ? '425' : '420'}
|
||||
data-src={`//cdn.rawgit.com/dabbott/react-native-web-player/gh-v${WEB_PLAYER_VERSION}/index.html${hash}`}
|
||||
frameBorder="0"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = WebPlayer;
|
Loading…
Add table
Add a link
Reference in a new issue