feat: Allow modifying docs url prefix (#914)

* Allow other routes than /docs in the URL

siteConfig.js has a new mandatory field named *docsRoute* which default
value is 'docs' and that can be customized by the user.

This change will allow users who uses the library to host guides and
tutorials to customize their websites by assign 'docsRoute' values
like 'tutorials' or 'guides'.

Fixes #879

* Make "docsRoute" field optional

* Isolate docsRoute login in getDocsRoute function

* Rename docsRoute to docsUrl

* Run prettier

* Remove old folders

* fix: Restore docusaurus reference link

* fix: Add `docsUrl` param fallback. Refactor multiple function calls

* Fix linting errors

* Update description for docsUrl field

* Reduce redundant calls to getDocsUrl

* Replace a missed use case for `docsUrl` instead of the function call

* Move `getDocsUrl` out from `server/routing.js` to `server/utils.js`

**Why?**
Because `routing.js` is exporting all router RegEx's, and the
`getDocsUrl` suffices more as a util

* WiP: Align leading slashes and fix routing around `docsUrl`

Checklist:
- [x] Added `removeDuplicateLeadingSlashes` util to make sure there is only
one leading slash
- [-] Fix edge cases for routing:
  - [x] `docsUrl: ''`
  - [ ] `docsUrl: '/'`
  - [ ] make it work with languages
  - [ ] make it work with versioning

* Make leading slashes canonical cross routing and generated links

This ensures correct routing for customized `baseUrl` and `docsUrl`.

- Changed all routing functions to take `siteConfig` instead of
`siteConfig.baseUrl`
- Updated tests accordingly

* Alternative fallback for `docsUrl`

* rework/ fix implementation

* cleanup

* refactor and add docs for config props

* fix typo

* fix broken url
This commit is contained in:
Dom Corvasce 2018-11-28 08:34:16 +01:00 committed by Endilie Yacop Sucipto
parent ff22074ff7
commit 61078e38a9
28 changed files with 423 additions and 319 deletions

View file

@ -5,6 +5,25 @@ title: Pages and Styles
Docusaurus provides support for writing pages as React components inside the `website/pages` directory which will share the same header, footer, and styles as the rest of the site. Docusaurus provides support for writing pages as React components inside the `website/pages` directory which will share the same header, footer, and styles as the rest of the site.
## Provided Props
Docusaurus provides your [siteConfig.js](api-site-config.md) as a `config` props. Hence, you can access `baseUrl` or `title` through this props.
Example
```js
const React = require('react');
class MyPage extends React.Component {
render() {
const siteConfig = this.props.config;
return <div>{siteConfig.title}</div>;
}
}
module.exports = MyPage;
```
## URLs for Pages ## URLs for Pages
Any `.js` files in `website/pages` will be rendered to static HTML using the path of the file after `pages`. Files in `website/pages/en` will also get copied out into `pages` and will OVERRIDE any files of the same name in `pages`. For example, the page for the `website/pages/en/help.js` file will be found at the URL `${baseUrl}en/help.js` as well as the URL `${baseUrl}help.js`, where `${baseUrl}` is the `baseUrl` field set in your [siteConfig.js file](api-site-config.md). Any `.js` files in `website/pages` will be rendered to static HTML using the path of the file after `pages`. Files in `website/pages/en` will also get copied out into `pages` and will OVERRIDE any files of the same name in `pages`. For example, the page for the `website/pages/en/help.js` file will be found at the URL `${baseUrl}en/help.js` as well as the URL `${baseUrl}help.js`, where `${baseUrl}` is the `baseUrl` field set in your [siteConfig.js file](api-site-config.md).
@ -31,7 +50,7 @@ module.exports = MyPage;
## Description for Pages ## Description for Pages
By default, the description your page is `tagline` set in [`siteConfig.js`](api-site-config.md). If you want to set a specific description for your custom pages, add a `description` class property on your exported React component. By default, the description your page is `tagline` set in [`siteConfig.js`](api-site-config.md). If you want to set a specific description for your custom pages, add a `description` class property on your exported React component.
Example: Example:
@ -138,7 +157,7 @@ A React component to organize text and images.
className="myCustomClass" className="myCustomClass"
contents={[ contents={[
{ {
title: `[Learn](${siteConfig.baseUrl}docs/tutorial.html)`, title: `[Learn](${siteConfig.baseUrl}${siteConfig.docsUrl}/tutorial.html)`,
content: 'Learn how to use this project', content: 'Learn how to use this project',
image: siteConfig.baseUrl + 'img/learn.png', image: siteConfig.baseUrl + 'img/learn.png',
imageAlt: 'Learn how to use this project', imageAlt: 'Learn how to use this project',

View file

@ -64,10 +64,6 @@ headerLinks: [
], ],
``` ```
#### `noIndex` [boolean]
Boolean. If true, Docusaurus will politely ask crawlers and search engines to avoid indexing your site. This is done with a header tag and so only applies to docs and pages. Will not attempt to hide static resources. This is a best effort request. Malicious crawlers can and will still index your site.
#### `organizationName` [string] #### `organizationName` [string]
GitHub username of the organization or user hosting this project. This is used by the publishing script to determine where your GitHub pages website will be hosted. GitHub username of the organization or user hosting this project. This is used by the publishing script to determine where your GitHub pages website will be hosted.
@ -130,6 +126,11 @@ customDocsPath: 'website-docs';
The default version for the site to be shown. If this is not set, the latest version will be shown. The default version for the site to be shown. If this is not set, the latest version will be shown.
#### `docsUrl` [string]
The base url for all docs file. Set this field to `''` to remove the `docs` prefix of the documentation URL.
If unset, it is defaulted to `docs`.
#### `disableHeaderTitle` [boolean] #### `disableHeaderTitle` [boolean]
An option to disable showing the title in the header next to the header icon. Exclude this field to keep the header as normal, otherwise set to `true`. An option to disable showing the title in the header next to the header icon. Exclude this field to keep the header as normal, otherwise set to `true`.
@ -246,6 +247,10 @@ Path to your web app manifest (e.g., `manifest.json`). This will add a `<link>`
An array of plugins to be loaded by Remarkable, the markdown parser and renderer used by Docusaurus. The plugin will receive a reference to the Remarkable instance, allowing custom parsing and rendering rules to be defined. An array of plugins to be loaded by Remarkable, the markdown parser and renderer used by Docusaurus. The plugin will receive a reference to the Remarkable instance, allowing custom parsing and rendering rules to be defined.
#### `noIndex` [boolean]
Boolean. If true, Docusaurus will politely ask crawlers and search engines to avoid indexing your site. This is done with a header tag and so only applies to docs and pages. Will not attempt to hide static resources. This is a best effort request. Malicious crawlers can and will still index your site.
#### `ogImage` [string] #### `ogImage` [string]
Local path to an Open Graph image (e.g., `img/myImage.png`). This image will show up when your site is shared on Facebook and other websites/apps where the Open Graph protocol is supported. Local path to an Open Graph image (e.g., `img/myImage.png`). This image will show up when your site is shared on Facebook and other websites/apps where the Open Graph protocol is supported.

View file

@ -65,7 +65,7 @@ You can also include an optional description attribute to give more context to a
<p> <p>
``` ```
> The `<translate>` tag generally works well on pure strings. If you have a string like "Docusaurus currently provides support to help your website use [translations](${siteConfig.baseUrl}docs/${this.props.language}/translation.html)", wrapping the `<translation>` tag around that entire string will cause issues because of the markdown linking, etc. Your options are to not translate those strings, or spread a bunch of `<translate>` tags amongst the pure substrings of that string. > The `<translate>` tag generally works well on pure strings. If you have a string like "Docusaurus currently provides support to help your website use [translations](${siteConfig.baseUrl}${siteConfig.docsUrl}/${this.props.language}/translation.html)", wrapping the `<translation>` tag around that entire string will cause issues because of the markdown linking, etc. Your options are to not translate those strings, or spread a bunch of `<translate>` tags amongst the pure substrings of that string.
## Gathering Strings to Translate ## Gathering Strings to Translate

View file

@ -10,7 +10,10 @@ const React = require('react');
class Footer extends React.Component { class Footer extends React.Component {
docUrl(doc, language) { docUrl(doc, language) {
const baseUrl = this.props.config.baseUrl; const baseUrl = this.props.config.baseUrl;
return `${baseUrl}docs/${language ? `${language}/` : ''}${doc}`; const docsUrl = this.props.config.docsUrl;
const docsPart = `${docsUrl ? `${docsUrl}/` : ''}`;
const langPart = `${language ? `${language}/` : ''}`;
return `${baseUrl}${docsPart}${langPart}${doc}`;
} }
pageUrl(doc, language) { pageUrl(doc, language) {

View file

@ -12,47 +12,43 @@ const CompLibrary = require('../../core/CompLibrary.js');
const Container = CompLibrary.Container; const Container = CompLibrary.Container;
const GridBlock = CompLibrary.GridBlock; const GridBlock = CompLibrary.GridBlock;
const siteConfig = require(`${process.cwd()}/siteConfig.js`); function Help(props) {
const {config: siteConfig, language = ''} = props;
const {baseUrl, docsUrl} = siteConfig;
const docsPart = `${docsUrl ? `${docsUrl}/` : ''}`;
const langPart = `${language ? `${language}/` : ''}`;
const docUrl = doc => `${baseUrl}${docsPart}${langPart}${doc}`;
function docUrl(doc, language) { const supportLinks = [
return `${siteConfig.baseUrl}docs/${language ? `${language}/` : ''}${doc}`; {
} content: `Learn more using the [documentation on this site.](${docUrl(
'doc1.html',
)})`,
title: 'Browse Docs',
},
{
content: 'Ask questions about the documentation and project',
title: 'Join the community',
},
{
content: "Find out what's new with this project",
title: 'Stay up to date',
},
];
class Help extends React.Component { return (
render() { <div className="docMainWrapper wrapper">
const language = this.props.language || ''; <Container className="mainContainer documentContainer postContainer">
const supportLinks = [ <div className="post">
{ <header className="postHeader">
content: `Learn more using the [documentation on this site.](${docUrl( <h1>Need help?</h1>
'doc1.html', </header>
language, <p>This project is maintained by a dedicated group of people.</p>
)})`, <GridBlock contents={supportLinks} layout="threeColumn" />
title: 'Browse Docs', </div>
}, </Container>
{ </div>
content: 'Ask questions about the documentation and project', );
title: 'Join the community',
},
{
content: "Find out what's new with this project",
title: 'Stay up to date',
},
];
return (
<div className="docMainWrapper wrapper">
<Container className="mainContainer documentContainer postContainer">
<div className="post">
<header className="postHeader">
<h1>Need help?</h1>
</header>
<p>This project is maintained by a dedicated group of people.</p>
<GridBlock contents={supportLinks} layout="threeColumn" />
</div>
</Container>
</div>
);
}
} }
module.exports = Help; module.exports = Help;

View file

@ -13,77 +13,60 @@ const MarkdownBlock = CompLibrary.MarkdownBlock; /* Used to read markdown */
const Container = CompLibrary.Container; const Container = CompLibrary.Container;
const GridBlock = CompLibrary.GridBlock; const GridBlock = CompLibrary.GridBlock;
const siteConfig = require(`${process.cwd()}/siteConfig.js`); class HomeSplash extends React.Component {
function imgUrl(img) {
return `${siteConfig.baseUrl}img/${img}`;
}
function docUrl(doc, language) {
return `${siteConfig.baseUrl}docs/${language ? `${language}/` : ''}${doc}`;
}
function pageUrl(page, language) {
return siteConfig.baseUrl + (language ? `${language}/` : '') + page;
}
class Button extends React.Component {
render() { render() {
return ( const {siteConfig, language = ''} = this.props;
const {baseUrl, docsUrl} = siteConfig;
const docsPart = `${docsUrl ? `${docsUrl}/` : ''}`;
const langPart = `${language ? `${language}/` : ''}`;
const docUrl = doc => `${baseUrl}${docsPart}${langPart}${doc}`;
const SplashContainer = props => (
<div className="homeContainer">
<div className="homeSplashFade">
<div className="wrapper homeWrapper">{props.children}</div>
</div>
</div>
);
const Logo = props => (
<div className="projectLogo">
<img src={props.img_src} alt="Project Logo" />
</div>
);
const ProjectTitle = () => (
<h2 className="projectTitle">
{siteConfig.title}
<small>{siteConfig.tagline}</small>
</h2>
);
const PromoSection = props => (
<div className="section promoSection">
<div className="promoRow">
<div className="pluginRowBlock">{props.children}</div>
</div>
</div>
);
const Button = props => (
<div className="pluginWrapper buttonWrapper"> <div className="pluginWrapper buttonWrapper">
<a className="button" href={this.props.href} target={this.props.target}> <a className="button" href={props.href} target={props.target}>
{this.props.children} {props.children}
</a> </a>
</div> </div>
); );
}
}
Button.defaultProps = {
target: '_self',
};
const SplashContainer = props => (
<div className="homeContainer">
<div className="homeSplashFade">
<div className="wrapper homeWrapper">{props.children}</div>
</div>
</div>
);
const Logo = props => (
<div className="projectLogo">
<img src={props.img_src} alt="Project Logo" />
</div>
);
const ProjectTitle = () => (
<h2 className="projectTitle">
{siteConfig.title}
<small>{siteConfig.tagline}</small>
</h2>
);
const PromoSection = props => (
<div className="section promoSection">
<div className="promoRow">
<div className="pluginRowBlock">{props.children}</div>
</div>
</div>
);
class HomeSplash extends React.Component {
render() {
const language = this.props.language || '';
return ( return (
<SplashContainer> <SplashContainer>
<Logo img_src={imgUrl('docusaurus.svg')} /> <Logo img_src={`${baseUrl}img/docusaurus.svg`} />
<div className="inner"> <div className="inner">
<ProjectTitle /> <ProjectTitle siteConfig={siteConfig} />
<PromoSection> <PromoSection>
<Button href="#try">Try It Out</Button> <Button href="#try">Try It Out</Button>
<Button href={docUrl('doc1.html', language)}>Example Link</Button> <Button href={docUrl('doc1.html')}>Example Link</Button>
<Button href={docUrl('doc2.html', language)}>Example Link 2</Button> <Button href={docUrl('doc2.html')}>Example Link 2</Button>
</PromoSection> </PromoSection>
</div> </div>
</SplashContainer> </SplashContainer>
@ -91,121 +74,131 @@ class HomeSplash extends React.Component {
} }
} }
const Block = props => (
<Container
padding={['bottom', 'top']}
id={props.id}
background={props.background}>
<GridBlock align="center" contents={props.children} layout={props.layout} />
</Container>
);
const Features = () => (
<Block layout="fourColumn">
{[
{
content: 'This is the content of my feature',
image: imgUrl('docusaurus.svg'),
imageAlign: 'top',
title: 'Feature One',
},
{
content: 'The content of my second feature',
image: imgUrl('docusaurus.svg'),
imageAlign: 'top',
title: 'Feature Two',
},
]}
</Block>
);
const FeatureCallout = () => (
<div
className="productShowcaseSection paddingBottom"
style={{textAlign: 'center'}}>
<h2>Feature Callout</h2>
<MarkdownBlock>These are features of this project</MarkdownBlock>
</div>
);
const LearnHow = () => (
<Block background="light">
{[
{
content: 'Talk about learning how to use this',
image: imgUrl('docusaurus.svg'),
imageAlign: 'right',
title: 'Learn How',
},
]}
</Block>
);
const TryOut = () => (
<Block id="try">
{[
{
content: 'Talk about trying this out',
image: imgUrl('docusaurus.svg'),
imageAlign: 'left',
title: 'Try it Out',
},
]}
</Block>
);
const Description = () => (
<Block background="dark">
{[
{
content: 'This is another description of how this project is useful',
image: imgUrl('docusaurus.svg'),
imageAlign: 'right',
title: 'Description',
},
]}
</Block>
);
const Showcase = props => {
if ((siteConfig.users || []).length === 0) {
return null;
}
const showcase = siteConfig.users.filter(user => user.pinned).map(user => (
<a href={user.infoLink} key={user.infoLink}>
<img src={user.image} alt={user.caption} title={user.caption} />
</a>
));
return (
<div className="productShowcaseSection paddingBottom">
<h2>Who is Using This?</h2>
<p>This project is used by all these people</p>
<div className="logos">{showcase}</div>
<div className="more-users">
<a className="button" href={pageUrl('users.html', props.language)}>
More {siteConfig.title} Users
</a>
</div>
</div>
);
};
class Index extends React.Component { class Index extends React.Component {
render() { render() {
const language = this.props.language || ''; const {config: siteConfig, language = ''} = this.props;
const {baseUrl} = siteConfig;
const Block = props => (
<Container
padding={['bottom', 'top']}
id={props.id}
background={props.background}>
<GridBlock
align="center"
contents={props.children}
layout={props.layout}
/>
</Container>
);
const FeatureCallout = () => (
<div
className="productShowcaseSection paddingBottom"
style={{textAlign: 'center'}}>
<h2>Feature Callout</h2>
<MarkdownBlock>These are features of this project</MarkdownBlock>
</div>
);
const TryOut = () => (
<Block id="try">
{[
{
content: 'Talk about trying this out',
image: `${baseUrl}img/docusaurus.svg`,
imageAlign: 'left',
title: 'Try it Out',
},
]}
</Block>
);
const Description = () => (
<Block background="dark">
{[
{
content:
'This is another description of how this project is useful',
image: `${baseUrl}img/docusaurus.svg`,
imageAlign: 'right',
title: 'Description',
},
]}
</Block>
);
const LearnHow = () => (
<Block background="light">
{[
{
content: 'Talk about learning how to use this',
image: `${baseUrl}img/docusaurus.svg`,
imageAlign: 'right',
title: 'Learn How',
},
]}
</Block>
);
const Features = () => (
<Block layout="fourColumn">
{[
{
content: 'This is the content of my feature',
image: `${baseUrl}img/docusaurus.svg`,
imageAlign: 'top',
title: 'Feature One',
},
{
content: 'The content of my second feature',
image: `${baseUrl}img/docusaurus.svg`,
imageAlign: 'top',
title: 'Feature Two',
},
]}
</Block>
);
const Showcase = () => {
if ((siteConfig.users || []).length === 0) {
return null;
}
const showcase = siteConfig.users
.filter(user => user.pinned)
.map(user => (
<a href={user.infoLink} key={user.infoLink}>
<img src={user.image} alt={user.caption} title={user.caption} />
</a>
));
const pageUrl = page => baseUrl + (language ? `${language}/` : '') + page;
return (
<div className="productShowcaseSection paddingBottom">
<h2>Who is Using This?</h2>
<p>This project is used by all these people</p>
<div className="logos">{showcase}</div>
<div className="more-users">
<a className="button" href={pageUrl('users.html')}>
More {siteConfig.title} Users
</a>
</div>
</div>
);
};
return ( return (
<div> <div>
<HomeSplash language={language} /> <HomeSplash siteConfig={siteConfig} language={language} />
<div className="mainContainer"> <div className="mainContainer">
<Features /> <Features />
<FeatureCallout /> <FeatureCallout />
<LearnHow /> <LearnHow />
<TryOut /> <TryOut />
<Description /> <Description />
<Showcase language={language} /> <Showcase />
</div> </div>
</div> </div>
); );

View file

@ -11,10 +11,9 @@ const CompLibrary = require('../../core/CompLibrary.js');
const Container = CompLibrary.Container; const Container = CompLibrary.Container;
const siteConfig = require(`${process.cwd()}/siteConfig.js`);
class Users extends React.Component { class Users extends React.Component {
render() { render() {
const {config: siteConfig} = this.props;
if ((siteConfig.users || []).length === 0) { if ((siteConfig.users || []).length === 0) {
return null; return null;
} }

View file

@ -13,10 +13,10 @@ const Container = CompLibrary.Container;
const CWD = process.cwd(); const CWD = process.cwd();
const siteConfig = require(`${CWD}/siteConfig.js`);
const versions = require(`${CWD}/versions.json`); const versions = require(`${CWD}/versions.json`);
function Versions() { function Versions(props) {
const {config: siteConfig} = props;
const latestVersion = versions[0]; const latestVersion = versions[0];
const repoUrl = `https://github.com/${siteConfig.organizationName}/${ const repoUrl = `https://github.com/${siteConfig.organizationName}/${
siteConfig.projectName siteConfig.projectName

View file

@ -17,7 +17,9 @@ const CWD = process.cwd();
const utils = require('../server/utils'); const utils = require('../server/utils');
const siteConfig = require(`${CWD}/website/siteConfig.js`); const loadConfig = require('../server/config');
const siteConfig = loadConfig(`${CWD}/website/siteConfig.js`);
const buildDir = `${CWD}/website/build`; const buildDir = `${CWD}/website/build`;
const docsDir = `${CWD}/docs`; const docsDir = `${CWD}/docs`;
const staticCSSDir = `${CWD}/website/static/css`; const staticCSSDir = `${CWD}/website/static/css`;

View file

@ -11,7 +11,9 @@ const React = require('react');
const fs = require('fs'); const fs = require('fs');
const classNames = require('classnames'); const classNames = require('classnames');
const siteConfig = require(`${CWD}/siteConfig.js`); const loadConfig = require('../../server/config');
const siteConfig = loadConfig(`${CWD}/siteConfig.js`);
const translation = require('../../server/translation.js'); const translation = require('../../server/translation.js');
const env = require('../../server/env.js'); const env = require('../../server/env.js');
@ -33,6 +35,7 @@ class LanguageDropDown extends React.Component {
const helpTranslateString = translate( const helpTranslateString = translate(
'Help Translate|recruit community translators for your project', 'Help Translate|recruit community translators for your project',
); );
const docsPart = `${siteConfig.docsUrl ? `${siteConfig.docsUrl}/` : ''}`;
// add all enabled languages to dropdown // add all enabled languages to dropdown
const enabledLanguages = env.translation const enabledLanguages = env.translation
.enabledLanguages() .enabledLanguages()
@ -48,8 +51,8 @@ class LanguageDropDown extends React.Component {
href = href =
siteConfig.baseUrl + siteConfig.baseUrl +
this.props.current.permalink.replace( this.props.current.permalink.replace(
`/${this.props.language}/`, new RegExp(`^${docsPart}${this.props.language}/`),
`/${lang.tag}/`, `${docsPart}${lang.tag}/`,
); );
} else if (this.props.current.id && this.props.current.id !== 'index') { } else if (this.props.current.id && this.props.current.id !== 'index') {
href = `${siteConfig.baseUrl + lang.tag}/${this.props.current.id}`; href = `${siteConfig.baseUrl + lang.tag}/${this.props.current.id}`;

View file

@ -66,7 +66,11 @@ const rawContent3 = metadataUtils.extractMetadata(doc3).rawContent;
const rawContentRefLinks = metadataUtils.extractMetadata(refLinks).rawContent; const rawContentRefLinks = metadataUtils.extractMetadata(refLinks).rawContent;
describe('mdToHtmlify', () => { describe('mdToHtmlify', () => {
const mdToHtml = metadataUtils.mdToHtml(Metadata, '/'); const siteConfig = {
baseUrl: '/',
docsUrl: 'docs',
};
const mdToHtml = metadataUtils.mdToHtml(Metadata, siteConfig);
test('transform nothing', () => { test('transform nothing', () => {
const content1 = docs.mdToHtmlify( const content1 = docs.mdToHtmlify(
@ -100,7 +104,7 @@ describe('mdToHtmlify', () => {
language: 'en', language: 'en',
}, },
}; };
const customMdToHtml = metadataUtils.mdToHtml(customMetadata, '/'); const customMdToHtml = metadataUtils.mdToHtml(customMetadata, siteConfig);
const content3 = docs.mdToHtmlify( const content3 = docs.mdToHtmlify(
rawContent3, rawContent3,
customMdToHtml, customMdToHtml,

View file

@ -30,7 +30,7 @@ jest.mock('../env', () => ({
}, },
})); }));
jest.mock(`${process.cwd()}/siteConfig.js`, () => true, {virtual: true}); jest.mock(`${process.cwd()}/siteConfig.js`, () => ({}), {virtual: true});
jest.mock(`${process.cwd()}/sidebar.json`, () => true, {virtual: true}); jest.mock(`${process.cwd()}/sidebar.json`, () => true, {virtual: true});
describe('readMetadata', () => { describe('readMetadata', () => {

View file

@ -4,11 +4,11 @@
* This source code is licensed under the MIT license found in the * This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
const routing = require('../routing'); const routing = require('../routing.js');
describe('Blog routing', () => { describe('Blog routing', () => {
const blogRegex = routing.blog('/'); const blogRegex = routing.blog({baseUrl: '/'});
const blogRegex2 = routing.blog('/react/'); const blogRegex2 = routing.blog({baseUrl: '/react/'});
test('valid blog', () => { test('valid blog', () => {
expect('/blog/test.html').toMatch(blogRegex); expect('/blog/test.html').toMatch(blogRegex);
@ -34,8 +34,8 @@ describe('Blog routing', () => {
}); });
describe('Docs routing', () => { describe('Docs routing', () => {
const docsRegex = routing.docs('/'); const docsRegex = routing.docs({baseUrl: '/', docsUrl: 'docs'});
const docsRegex2 = routing.docs('/reason/'); const docsRegex2 = routing.docs({baseUrl: '/reason/', docsUrl: 'docs'});
test('valid docs', () => { test('valid docs', () => {
expect('/docs/en/test.html').toMatch(docsRegex); expect('/docs/en/test.html').toMatch(docsRegex);
@ -87,8 +87,8 @@ describe('Dot routing', () => {
}); });
describe('Feed routing', () => { describe('Feed routing', () => {
const feedRegex = routing.feed('/'); const feedRegex = routing.feed({baseUrl: '/'});
const feedRegex2 = routing.feed('/reason/'); const feedRegex2 = routing.feed({baseUrl: '/reason/'});
test('valid feed url', () => { test('valid feed url', () => {
expect('/blog/atom.xml').toMatch(feedRegex); expect('/blog/atom.xml').toMatch(feedRegex);
@ -137,8 +137,8 @@ describe('Extension-less url routing', () => {
}); });
describe('Page routing', () => { describe('Page routing', () => {
const pageRegex = routing.page('/'); const pageRegex = routing.page({baseUrl: '/', docsUrl: 'docs'});
const pageRegex2 = routing.page('/reason/'); const pageRegex2 = routing.page({baseUrl: '/reason/', docsUrl: 'docs'});
test('valid page url', () => { test('valid page url', () => {
expect('/index.html').toMatch(pageRegex); expect('/index.html').toMatch(pageRegex);
@ -164,8 +164,8 @@ describe('Page routing', () => {
}); });
describe('Sitemap routing', () => { describe('Sitemap routing', () => {
const sitemapRegex = routing.sitemap('/'); const sitemapRegex = routing.sitemap({baseUrl: '/'});
const sitemapRegex2 = routing.sitemap('/reason/'); const sitemapRegex2 = routing.sitemap({baseUrl: '/reason/'});
test('valid sitemap url', () => { test('valid sitemap url', () => {
expect('/sitemap.xml').toMatch(sitemapRegex); expect('/sitemap.xml').toMatch(sitemapRegex);

31
v1/lib/server/config.js Normal file
View file

@ -0,0 +1,31 @@
/**
* 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.
*/
const fs = require('fs-extra');
module.exports = function loadConfig(configPath, deleteCache = true) {
if (deleteCache) {
delete require.cache[configPath];
}
let config = {};
if (fs.existsSync(configPath)) {
config = require(configPath); // eslint-disable-line
}
/* Fill default value */
const defaultConfig = {
customDocsPath: 'docs',
docsUrl: 'docs',
};
Object.keys(defaultConfig).forEach(field => {
if (!(field in config)) {
config[field] = defaultConfig[field];
}
});
return config;
};

View file

@ -5,16 +5,20 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
const CWD = process.cwd(); const CWD = process.cwd();
const siteConfig = require(`${CWD}/siteConfig.js`);
const {join} = require('path'); const {join} = require('path');
const fs = require('fs-extra'); const fs = require('fs-extra');
const React = require('react'); const React = require('react');
const loadConfig = require('./config');
const siteConfig = loadConfig(`${CWD}/siteConfig.js`);
const env = require('./env.js'); const env = require('./env.js');
const {renderToStaticMarkupWithDoctype} = require('./renderUtils'); const {renderToStaticMarkupWithDoctype} = require('./renderUtils');
const readMetadata = require('./readMetadata.js'); const readMetadata = require('./readMetadata.js');
const {insertTOC} = require('../core/toc.js'); const {insertTOC} = require('../core/toc.js');
const {getPath} = require('../core/utils.js'); const {getPath} = require('../core/utils.js');
const docsPart = `${siteConfig.docsUrl ? `${siteConfig.docsUrl}/` : ''}`;
function getFilePath(metadata) { function getFilePath(metadata) {
if (!metadata) { if (!metadata) {
return null; return null;
@ -125,7 +129,10 @@ function replaceAssetsLink(oldContent) {
} }
return fencedBlock return fencedBlock
? line ? line
: line.replace(/\]\(assets\//g, `](${siteConfig.baseUrl}docs/assets/`); : line.replace(
/\]\(assets\//g,
`](${siteConfig.baseUrl}${docsPart}assets/`,
);
}); });
return lines.join('\n'); return lines.join('\n');
} }
@ -152,7 +159,10 @@ function getMarkup(rawContent, mdToHtml, metadata) {
} }
function getRedirectMarkup(metadata) { function getRedirectMarkup(metadata) {
if (!env.translation.enabled || !metadata.permalink.includes('docs/en')) { if (
!env.translation.enabled ||
!metadata.permalink.includes(`${docsPart}en`)
) {
return null; return null;
} }
const Redirect = require('../core/Redirect.js'); const Redirect = require('../core/Redirect.js');

View file

@ -21,7 +21,8 @@ async function execute() {
const chalk = require('chalk'); const chalk = require('chalk');
const Site = require('../core/Site.js'); const Site = require('../core/Site.js');
const env = require('./env.js'); const env = require('./env.js');
const siteConfig = require(`${CWD}/siteConfig.js`); const loadConfig = require('./config.js');
const siteConfig = loadConfig(`${CWD}/siteConfig.js`);
const translate = require('./translate.js'); const translate = require('./translate.js');
const feed = require('./feed.js'); const feed = require('./feed.js');
const sitemap = require('./sitemap.js'); const sitemap = require('./sitemap.js');
@ -68,7 +69,7 @@ async function execute() {
fs.removeSync(join(CWD, 'build')); fs.removeSync(join(CWD, 'build'));
// create html files for all docs by going through all doc ids // create html files for all docs by going through all doc ids
const mdToHtml = metadataUtils.mdToHtml(Metadata, siteConfig.baseUrl); const mdToHtml = metadataUtils.mdToHtml(Metadata, siteConfig);
Object.keys(Metadata).forEach(id => { Object.keys(Metadata).forEach(id => {
const metadata = Metadata[id]; const metadata = Metadata[id];
const file = docs.getFile(metadata); const file = docs.getFile(metadata);
@ -85,9 +86,13 @@ async function execute() {
if (!redirectMarkup) { if (!redirectMarkup) {
return; return;
} }
const docsPart = `${siteConfig.docsUrl ? `${siteConfig.docsUrl}/` : ''}`;
const redirectFile = join( const redirectFile = join(
buildDir, buildDir,
metadata.permalink.replace('docs/en', 'docs'), metadata.permalink.replace(
new RegExp(`^${docsPart}en`),
siteConfig.docsUrl,
),
); );
writeFileAndCreateFolder(redirectFile, redirectMarkup); writeFileAndCreateFolder(redirectFile, redirectMarkup);
}); });
@ -332,7 +337,7 @@ async function execute() {
title={ReactComp.title} title={ReactComp.title}
description={ReactComp.description} description={ReactComp.description}
metadata={{id: pageID}}> metadata={{id: pageID}}>
<ReactComp language={language} /> <ReactComp config={siteConfig} language={language} />
</Site>, </Site>,
); );
writeFileAndCreateFolder( writeFileAndCreateFolder(
@ -353,7 +358,7 @@ async function execute() {
config={siteConfig} config={siteConfig}
description={ReactComp.description} description={ReactComp.description}
metadata={{id: pageID}}> metadata={{id: pageID}}>
<ReactComp language={language} /> <ReactComp config={siteConfig} language={language} />
</Site>, </Site>,
); );
writeFileAndCreateFolder( writeFileAndCreateFolder(
@ -371,7 +376,7 @@ async function execute() {
config={siteConfig} config={siteConfig}
description={ReactComp.description} description={ReactComp.description}
metadata={{id: pageID}}> metadata={{id: pageID}}>
<ReactComp language={language} /> <ReactComp config={siteConfig} language={language} />
</Site>, </Site>,
); );
writeFileAndCreateFolder( writeFileAndCreateFolder(

View file

@ -65,7 +65,8 @@ function extractMetadata(content) {
// mdToHtml is a map from a markdown file name to its html link, used to // mdToHtml is a map from a markdown file name to its html link, used to
// change relative markdown links that work on GitHub into actual site links // change relative markdown links that work on GitHub into actual site links
function mdToHtml(Metadata, baseUrl) { function mdToHtml(Metadata, siteConfig) {
const {baseUrl, docsUrl} = siteConfig;
const result = {}; const result = {};
Object.keys(Metadata).forEach(id => { Object.keys(Metadata).forEach(id => {
const metadata = Metadata[id]; const metadata = Metadata[id];
@ -73,10 +74,15 @@ function mdToHtml(Metadata, baseUrl) {
return; return;
} }
let htmlLink = baseUrl + metadata.permalink.replace('/next/', '/'); let htmlLink = baseUrl + metadata.permalink.replace('/next/', '/');
if (htmlLink.includes('/docs/en/')) {
htmlLink = htmlLink.replace('/docs/en/', '/docs/en/VERSION/'); const baseDocsPart = `${baseUrl}${docsUrl ? `${docsUrl}/` : ''}`;
const i18nDocsRegex = new RegExp(`^${baseDocsPart}en/`);
const docsRegex = new RegExp(`^${baseDocsPart}`);
if (i18nDocsRegex.test(htmlLink)) {
htmlLink = htmlLink.replace(i18nDocsRegex, `${baseDocsPart}en/VERSION/`);
} else { } else {
htmlLink = htmlLink.replace('/docs/', '/docs/VERSION/'); htmlLink = htmlLink.replace(docsRegex, `${baseDocsPart}VERSION/`);
} }
result[metadata.source] = htmlLink; result[metadata.source] = htmlLink;
}); });

View file

@ -16,10 +16,14 @@ const metadataUtils = require('./metadataUtils');
const env = require('./env.js'); const env = require('./env.js');
const blog = require('./blog.js'); const blog = require('./blog.js');
const siteConfig = require(`${CWD}/siteConfig.js`); const loadConfig = require('./config');
const siteConfig = loadConfig(`${CWD}/siteConfig.js`);
const versionFallback = require('./versionFallback.js'); const versionFallback = require('./versionFallback.js');
const utils = require('./utils.js'); const utils = require('./utils.js');
const docsPart = `${siteConfig.docsUrl ? `${siteConfig.docsUrl}/` : ''}`;
const SupportedHeaderFields = new Set([ const SupportedHeaderFields = new Set([
'id', 'id',
'title', 'title',
@ -166,7 +170,9 @@ function processMetadata(file, refDir) {
versionPart = 'next/'; versionPart = 'next/';
} }
metadata.permalink = `docs/${langPart}${versionPart}${metadata.id}.html`; metadata.permalink = `${docsPart}${langPart}${versionPart}${
metadata.id
}.html`;
// change ids previous, next // change ids previous, next
metadata.localized_id = metadata.id; metadata.localized_id = metadata.id;
@ -238,18 +244,24 @@ function generateMetadataDocs() {
baseMetadata.id = baseMetadata.id baseMetadata.id = baseMetadata.id
.toString() .toString()
.replace(/^en-/, `${currentLanguage}-`); .replace(/^en-/, `${currentLanguage}-`);
if (baseMetadata.permalink) if (baseMetadata.permalink) {
baseMetadata.permalink = baseMetadata.permalink baseMetadata.permalink = baseMetadata.permalink
.toString() .toString()
.replace(/^docs\/en\//, `docs/${currentLanguage}/`); .replace(
if (baseMetadata.next) new RegExp(`^${docsPart}en/`),
`${docsPart}${currentLanguage}/`,
);
}
if (baseMetadata.next) {
baseMetadata.next = baseMetadata.next baseMetadata.next = baseMetadata.next
.toString() .toString()
.replace(/^en-/, `${currentLanguage}-`); .replace(/^en-/, `${currentLanguage}-`);
if (baseMetadata.previous) }
if (baseMetadata.previous) {
baseMetadata.previous = baseMetadata.previous baseMetadata.previous = baseMetadata.previous
.toString() .toString()
.replace(/^en-/, `${currentLanguage}-`); .replace(/^en-/, `${currentLanguage}-`);
}
baseMetadata.language = currentLanguage; baseMetadata.language = currentLanguage;
defaultMetadatas[baseMetadata.id] = baseMetadata; defaultMetadatas[baseMetadata.id] = baseMetadata;
}); });

View file

@ -4,35 +4,44 @@
* This source code is licensed under the MIT license found in the * This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
function blog(baseUrl) { function blog(siteConfig) {
return new RegExp(`^${baseUrl}blog/.*html$`); return new RegExp(`^${siteConfig.baseUrl}blog/.*html$`);
} }
function docs(baseUrl) { function docs(siteConfig) {
return new RegExp(`^${baseUrl}docs/.*html$`); const docsPart = `${siteConfig.docsUrl ? `${siteConfig.docsUrl}/` : ''}`;
return new RegExp(`^${siteConfig.baseUrl}${docsPart}.*html$`);
} }
function dotfiles() { function dotfiles() {
return /(?!.*html$)^\/.*\.[^\n/]+$/; return /(?!.*html$)^\/.*\.[^\n/]+$/;
} }
function feed(baseUrl) { function feed(siteConfig) {
return new RegExp(`^${baseUrl}blog/(feed.xml|atom.xml)$`); return new RegExp(`^${siteConfig.baseUrl}blog/(feed.xml|atom.xml)$`);
} }
function noExtension() { function noExtension() {
return /\/[^.]*\/?$/; return /\/[^.]*\/?$/;
} }
function page(baseUrl) { function page(siteConfig) {
const gr = regex => regex.toString().replace(/(^\/|\/$)/gm, ''); const gr = regex => regex.toString().replace(/(^\/|\/$)/gm, '');
if (siteConfig.docsUrl === '') {
return new RegExp(
`(?!${gr(blog(siteConfig))})^${siteConfig.baseUrl}.*.html$`,
);
}
return new RegExp( return new RegExp(
`(?!${gr(docs(baseUrl))}|${gr(blog(baseUrl))})^${baseUrl}.*.html$`, `(?!${gr(blog(siteConfig))}|${gr(docs(siteConfig))})^${
siteConfig.baseUrl
}.*.html$`,
); );
} }
function sitemap(baseUrl) { function sitemap(siteConfig) {
return new RegExp(`^${baseUrl}sitemap.xml$`); return new RegExp(`^${siteConfig.baseUrl}sitemap.xml$`);
} }
module.exports = { module.exports = {

View file

@ -27,6 +27,7 @@ function execute(port) {
const feed = require('./feed'); const feed = require('./feed');
const sitemap = require('./sitemap'); const sitemap = require('./sitemap');
const routing = require('./routing'); const routing = require('./routing');
const loadConfig = require('./config');
const CWD = process.cwd(); const CWD = process.cwd();
const join = path.join; const join = path.join;
const sep = path.sep; const sep = path.sep;
@ -76,12 +77,9 @@ function execute(port) {
} }
function reloadSiteConfig() { function reloadSiteConfig() {
removeModuleAndChildrenFromCache(join(CWD, 'siteConfig.js')); const siteConfigPath = join(CWD, 'siteConfig.js');
siteConfig = require(join(CWD, 'siteConfig.js')); removeModuleAndChildrenFromCache(siteConfigPath);
siteConfig = loadConfig(siteConfigPath);
if (siteConfig.highlight && siteConfig.highlight.hljs) {
siteConfig.highlight.hljs(require('highlight.js'));
}
} }
function requestFile(url, res, notFoundCallback) { function requestFile(url, res, notFoundCallback) {
@ -109,12 +107,13 @@ function execute(port) {
const app = express(); const app = express();
app.get(routing.docs(siteConfig.baseUrl), (req, res, next) => { app.get(routing.docs(siteConfig), (req, res, next) => {
const url = decodeURI(req.path.toString().replace(siteConfig.baseUrl, '')); const url = decodeURI(req.path.toString().replace(siteConfig.baseUrl, ''));
const metadata = const metadata =
Metadata[ Metadata[
Object.keys(Metadata).find(id => Metadata[id].permalink === url) Object.keys(Metadata).find(id => Metadata[id].permalink === url)
]; ];
const file = docs.getFile(metadata); const file = docs.getFile(metadata);
if (!file) { if (!file) {
next(); next();
@ -122,11 +121,11 @@ function execute(port) {
} }
const rawContent = metadataUtils.extractMetadata(file).rawContent; const rawContent = metadataUtils.extractMetadata(file).rawContent;
removeModuleAndChildrenFromCache('../core/DocsLayout.js'); removeModuleAndChildrenFromCache('../core/DocsLayout.js');
const mdToHtml = metadataUtils.mdToHtml(Metadata, siteConfig.baseUrl); const mdToHtml = metadataUtils.mdToHtml(Metadata, siteConfig);
res.send(docs.getMarkup(rawContent, mdToHtml, metadata)); res.send(docs.getMarkup(rawContent, mdToHtml, metadata));
}); });
app.get(routing.sitemap(siteConfig.baseUrl), (req, res) => { app.get(routing.sitemap(siteConfig), (req, res) => {
sitemap((err, xml) => { sitemap((err, xml) => {
if (err) { if (err) {
res.status(500).send('Sitemap error'); res.status(500).send('Sitemap error');
@ -137,7 +136,7 @@ function execute(port) {
}); });
}); });
app.get(routing.feed(siteConfig.baseUrl), (req, res, next) => { app.get(routing.feed(siteConfig), (req, res, next) => {
res.set('Content-Type', 'application/rss+xml'); res.set('Content-Type', 'application/rss+xml');
const file = req.path const file = req.path
.toString() .toString()
@ -151,7 +150,7 @@ function execute(port) {
next(); next();
}); });
app.get(routing.blog(siteConfig.baseUrl), (req, res, next) => { app.get(routing.blog(siteConfig), (req, res, next) => {
// Regenerate the blog metadata in case it has changed. Consider improving // Regenerate the blog metadata in case it has changed. Consider improving
// this to regenerate on file save rather than on page request. // this to regenerate on file save rather than on page request.
reloadMetadataBlog(); reloadMetadataBlog();
@ -177,7 +176,7 @@ function execute(port) {
} }
}); });
app.get(routing.page(siteConfig.baseUrl), (req, res, next) => { app.get(routing.page(siteConfig), (req, res, next) => {
// Look for user-provided HTML file first. // Look for user-provided HTML file first.
let htmlFile = req.path.toString().replace(siteConfig.baseUrl, ''); let htmlFile = req.path.toString().replace(siteConfig.baseUrl, '');
htmlFile = join(CWD, 'pages', htmlFile); htmlFile = join(CWD, 'pages', htmlFile);
@ -232,6 +231,7 @@ function execute(port) {
language = parts[i]; language = parts[i];
} }
} }
let englishFile = join(CWD, 'pages', file); let englishFile = join(CWD, 'pages', file);
if (language && language !== 'en') { if (language && language !== 'en') {
englishFile = englishFile.replace(sep + language + sep, `${sep}en${sep}`); englishFile = englishFile.replace(sep + language + sep, `${sep}en${sep}`);
@ -272,7 +272,7 @@ function execute(port) {
title={ReactComp.title} title={ReactComp.title}
description={ReactComp.description} description={ReactComp.description}
metadata={{id: path.basename(userFile, '.js')}}> metadata={{id: path.basename(userFile, '.js')}}>
<ReactComp language={language} /> <ReactComp config={siteConfig} language={language} />
</Site>, </Site>,
); );
@ -339,7 +339,9 @@ function execute(port) {
// serve static assets from these locations // serve static assets from these locations
app.use( app.use(
`${siteConfig.baseUrl}docs/assets`, `${siteConfig.baseUrl}${
siteConfig.docsUrl ? `${siteConfig.docsUrl}/` : ''
}assets`,
express.static(join(CWD, '..', readMetadata.getDocsPath(), 'assets')), express.static(join(CWD, '..', readMetadata.getDocsPath(), 'assets')),
); );
app.use( app.use(

View file

@ -14,7 +14,9 @@ const CWD = process.cwd();
const sitemap = require('sitemap'); const sitemap = require('sitemap');
const utils = require('../core/utils'); const utils = require('../core/utils');
const siteConfig = require(`${CWD}/siteConfig.js`); const loadConfig = require('./config');
const siteConfig = loadConfig(`${CWD}/siteConfig.js`);
const readMetadata = require('./readMetadata.js'); const readMetadata = require('./readMetadata.js');
@ -70,8 +72,12 @@ module.exports = function(callback) {
.forEach(key => { .forEach(key => {
const doc = Metadata[key]; const doc = Metadata[key];
const docUrl = utils.getPath(doc.permalink, siteConfig.cleanUrl); const docUrl = utils.getPath(doc.permalink, siteConfig.cleanUrl);
const docsPart = `${siteConfig.docsUrl ? `${siteConfig.docsUrl}/` : ''}`;
const links = enabledLanguages.map(lang => { const links = enabledLanguages.map(lang => {
const langUrl = docUrl.replace('docs/en/', `docs/${lang.tag}/`); const langUrl = docUrl.replace(
new RegExp(`^${docsPart}en/`),
`${docsPart}${lang.tag}/`,
);
return {lang: lang.tag, url: langUrl}; return {lang: lang.tag, url: langUrl};
}); });
urls.push({ urls.push({

View file

@ -14,8 +14,9 @@ const metadataUtils = require('./metadataUtils');
const env = require('./env.js'); const env = require('./env.js');
const utils = require('./utils.js'); const utils = require('./utils.js');
const loadConfig = require('./config');
const siteConfig = require(`${CWD}/siteConfig.js`); const siteConfig = loadConfig(`${CWD}/siteConfig.js`);
const ENABLE_TRANSLATION = fs.existsSync(`${CWD}/languages.js`); const ENABLE_TRANSLATION = fs.existsSync(`${CWD}/languages.js`);
@ -187,14 +188,16 @@ function processVersionMetadata(file, version, useVersion, language) {
const latestVersion = versions[0]; const latestVersion = versions[0];
const docsPart = `${siteConfig.docsUrl ? `${siteConfig.docsUrl}/` : ''}`;
const versionPart = `${version !== latestVersion ? `${version}/` : ''}`;
if (!ENABLE_TRANSLATION && !siteConfig.useEnglishUrl) { if (!ENABLE_TRANSLATION && !siteConfig.useEnglishUrl) {
metadata.permalink = `docs/${ metadata.permalink = `${docsPart}${versionPart}${
version !== latestVersion ? `${version}/` : '' metadata.original_id
}${metadata.original_id}.html`; }.html`;
} else { } else {
metadata.permalink = `docs/${language}/${ metadata.permalink = `${docsPart}${language}/${versionPart}${
version !== latestVersion ? `${version}/` : '' metadata.original_id
}${metadata.original_id}.html`; }.html`;
} }
metadata.id = metadata.id.replace( metadata.id = metadata.id.replace(
`version-${useVersion}-`, `version-${useVersion}-`,

View file

@ -58,6 +58,9 @@ SocialFooter.propTypes = {
class Footer extends React.Component { class Footer extends React.Component {
render() { render() {
const docsPart = `${
this.props.config.docsUrl ? `${this.props.config.docsUrl}/` : ''
}`;
return ( return (
<footer className="nav-footer" id="footer"> <footer className="nav-footer" id="footer">
<section className="sitemap"> <section className="sitemap">
@ -77,28 +80,28 @@ class Footer extends React.Component {
<h5>Docs</h5> <h5>Docs</h5>
<a <a
href={` href={`
${this.props.config.baseUrl}docs/${ ${this.props.config.baseUrl}${docsPart}${
this.props.language this.props.language
}/installation`}> }/installation`}>
Getting Started Getting Started
</a> </a>
<a <a
href={` href={`
${this.props.config.baseUrl}docs/${ ${this.props.config.baseUrl}${docsPart}${
this.props.language this.props.language
}/versioning`}> }/versioning`}>
Versioning Versioning
</a> </a>
<a <a
href={` href={`
${this.props.config.baseUrl}docs/${ ${this.props.config.baseUrl}${docsPart}${
this.props.language this.props.language
}/translation`}> }/translation`}>
Localization Localization
</a> </a>
<a <a
href={` href={`
${this.props.config.baseUrl}docs/${ ${this.props.config.baseUrl}${docsPart}${
this.props.language this.props.language
}/search`}> }/search`}>
Adding Search Adding Search

View file

@ -11,11 +11,11 @@ const React = require('react');
const CompLibrary = require('../../core/CompLibrary.js'); const CompLibrary = require('../../core/CompLibrary.js');
const Container = CompLibrary.Container; const Container = CompLibrary.Container;
const siteConfig = require(`${process.cwd()}/siteConfig.js`);
const translate = require('../../server/translate.js').translate; const translate = require('../../server/translate.js').translate;
class AboutSlash extends React.Component { class AboutSlash extends React.Component {
render() { render() {
const {config: siteConfig} = this.props;
return ( return (
<div className="pageContainer"> <div className="pageContainer">
<Container className="mainContainer documentContainer postContainer"> <Container className="mainContainer documentContainer postContainer">

View file

@ -10,17 +10,19 @@ const CompLibrary = require('../../core/CompLibrary.js');
const Container = CompLibrary.Container; const Container = CompLibrary.Container;
const GridBlock = CompLibrary.GridBlock; const GridBlock = CompLibrary.GridBlock;
const siteConfig = require(`${process.cwd()}/siteConfig.js`);
const translate = require('../../server/translate.js').translate; const translate = require('../../server/translate.js').translate;
class Help extends React.Component { class Help extends React.Component {
render() { render() {
const {config: siteConfig} = this.props;
const supportLinks = [ const supportLinks = [
{ {
title: <translate>Browse the docs</translate>, title: <translate>Browse the docs</translate>,
content: `Learn more about Docusaurus using the [official documentation](${ content: `Learn more about Docusaurus using the [official documentation](${
siteConfig.baseUrl siteConfig.baseUrl
}docs/${this.props.language}/installation).`, }${siteConfig.docsUrl ? `${siteConfig.docsUrl}/` : ''}${
this.props.language
}/installation).`,
}, },
{ {
title: <translate>Discord</translate>, title: <translate>Discord</translate>,

View file

@ -12,27 +12,20 @@ const CompLibrary = require('../../core/CompLibrary.js');
const Container = CompLibrary.Container; const Container = CompLibrary.Container;
const GridBlock = CompLibrary.GridBlock; const GridBlock = CompLibrary.GridBlock;
const Showcase = require(`${process.cwd()}/core/Showcase.js`); const Showcase = require(`${process.cwd()}/core/Showcase.js`);
const siteConfig = require(`${process.cwd()}/siteConfig.js`);
const translate = require('../../server/translate.js').translate; const translate = require('../../server/translate.js').translate;
class Button extends React.Component {
render() {
return (
<div className="pluginWrapper buttonWrapper">
<a className="button" href={this.props.href} target={this.props.target}>
{this.props.children}
</a>
</div>
);
}
}
Button.defaultProps = {
target: '_self',
};
class HomeSplash extends React.Component { class HomeSplash extends React.Component {
render() { render() {
const {siteConfig, language} = this.props;
const Button = props => (
<div className="pluginWrapper buttonWrapper">
<a className="button" href={props.href} target={props.target}>
{props.children}
</a>
</div>
);
return ( return (
<div className="homeContainer"> <div className="homeContainer">
<div className="homeSplashFade"> <div className="homeSplashFade">
@ -53,9 +46,7 @@ class HomeSplash extends React.Component {
<div className="pluginRowBlock"> <div className="pluginRowBlock">
<Button <Button
href={` href={`
${siteConfig.baseUrl}docs/${ ${siteConfig.baseUrl}docs/${language}/installation
this.props.language
}/installation
`}> `}>
<translate>Get Started</translate> <translate>Get Started</translate>
</Button> </Button>
@ -75,12 +66,12 @@ class HomeSplash extends React.Component {
class Index extends React.Component { class Index extends React.Component {
render() { render() {
const language = this.props.language || 'en'; const {config: siteConfig, language = 'en'} = this.props;
const pinnedUsersToShowcase = siteConfig.users.filter(user => user.pinned); const pinnedUsersToShowcase = siteConfig.users.filter(user => user.pinned);
return ( return (
<div> <div>
<HomeSplash language={language} /> <HomeSplash siteConfig={siteConfig} language={language} />
<div className="mainContainer"> <div className="mainContainer">
<Container padding={['bottom', 'top']} background="light"> <Container padding={['bottom', 'top']} background="light">
<GridBlock <GridBlock

View file

@ -10,11 +10,11 @@ const CompLibrary = require('../../core/CompLibrary.js');
const Container = CompLibrary.Container; const Container = CompLibrary.Container;
const Showcase = require(`${process.cwd()}/core/Showcase.js`); const Showcase = require(`${process.cwd()}/core/Showcase.js`);
const siteConfig = require(`${process.cwd()}/siteConfig.js`);
const translate = require('../../server/translate.js').translate; const translate = require('../../server/translate.js').translate;
class Users extends React.Component { class Users extends React.Component {
render() { render() {
const {config: siteConfig} = this.props;
const fbUsersToShowcase = siteConfig.users.filter( const fbUsersToShowcase = siteConfig.users.filter(
user => user.fbOpenSource, user => user.fbOpenSource,
); );

View file

@ -13,10 +13,10 @@ const Container = CompLibrary.Container;
const CWD = process.cwd(); const CWD = process.cwd();
const siteConfig = require(`${CWD}/siteConfig.js`);
const versions = require(`${CWD}/versions.json`); const versions = require(`${CWD}/versions.json`);
function Versions(props) { function Versions(props) {
const {config: siteConfig} = props;
const latestVersion = versions[0]; const latestVersion = versions[0];
const repoUrl = `https://github.com/${siteConfig.organizationName}/${ const repoUrl = `https://github.com/${siteConfig.organizationName}/${
siteConfig.projectName siteConfig.projectName
@ -36,7 +36,7 @@ function Versions(props) {
<th>{latestVersion}</th> <th>{latestVersion}</th>
<td> <td>
<a <a
href={`${siteConfig.baseUrl}docs/${ href={`${siteConfig.baseUrl}${siteConfig.docsUrl}/${
props.language props.language
}/installation`}> }/installation`}>
Documentation Documentation
@ -58,7 +58,7 @@ function Versions(props) {
<th>master</th> <th>master</th>
<td> <td>
<a <a
href={`${siteConfig.baseUrl}docs/${ href={`${siteConfig.baseUrl}${siteConfig.docsUrl}/${
props.language props.language
}/next/installation`}> }/next/installation`}>
Documentation Documentation
@ -83,7 +83,7 @@ function Versions(props) {
<th>{version}</th> <th>{version}</th>
<td> <td>
<a <a
href={`${siteConfig.baseUrl}docs/${ href={`${siteConfig.baseUrl}${siteConfig.docsUrl}/${
props.language props.language
}/${version}/installation`}> }/${version}/installation`}>
Documentation Documentation