diff --git a/docs/api-pages.md b/docs/api-pages.md
index 7f4821d15ae4..f80aeb6ea3bc 100644
--- a/docs/api-pages.md
+++ b/docs/api-pages.md
@@ -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.
+## 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
{siteConfig.title}
;
+ }
+}
+
+module.exports = MyPage;
+```
+
## 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).
@@ -31,7 +50,7 @@ module.exports = MyPage;
## 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:
@@ -138,7 +157,7 @@ A React component to organize text and images.
className="myCustomClass"
contents={[
{
- title: `[Learn](${siteConfig.baseUrl}docs/tutorial.html)`,
+ title: `[Learn](${siteConfig.baseUrl}${siteConfig.docsUrl}/tutorial.html)`,
content: 'Learn how to use this project',
image: siteConfig.baseUrl + 'img/learn.png',
imageAlt: 'Learn how to use this project',
diff --git a/docs/api-site-config.md b/docs/api-site-config.md
index 667c93353364..fe18a7ceac59 100644
--- a/docs/api-site-config.md
+++ b/docs/api-site-config.md
@@ -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]
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.
+#### `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]
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 ` `
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]
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.
diff --git a/docs/guides-translation.md b/docs/guides-translation.md
index 14c57d45fa8a..d915a1f77d1b 100644
--- a/docs/guides-translation.md
+++ b/docs/guides-translation.md
@@ -65,7 +65,7 @@ You can also include an optional description attribute to give more context to a
```
-> The `` 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 `` 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 `` tags amongst the pure substrings of that string.
+> The `` 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 `` 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 `` tags amongst the pure substrings of that string.
## Gathering Strings to Translate
diff --git a/v1/examples/basics/core/Footer.js b/v1/examples/basics/core/Footer.js
index a9595b6f3eb6..7a13d73275c9 100644
--- a/v1/examples/basics/core/Footer.js
+++ b/v1/examples/basics/core/Footer.js
@@ -10,7 +10,10 @@ const React = require('react');
class Footer extends React.Component {
docUrl(doc, language) {
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) {
diff --git a/v1/examples/basics/pages/en/help.js b/v1/examples/basics/pages/en/help.js
index 512e335a99cb..2b790e46037f 100755
--- a/v1/examples/basics/pages/en/help.js
+++ b/v1/examples/basics/pages/en/help.js
@@ -12,47 +12,43 @@ const CompLibrary = require('../../core/CompLibrary.js');
const Container = CompLibrary.Container;
const GridBlock = CompLibrary.GridBlock;
-const siteConfig = require(`${process.cwd()}/siteConfig.js`);
-
-function docUrl(doc, language) {
- return `${siteConfig.baseUrl}docs/${language ? `${language}/` : ''}${doc}`;
-}
-
-class Help extends React.Component {
- render() {
- const language = this.props.language || '';
- const supportLinks = [
- {
- content: `Learn more using the [documentation on this site.](${docUrl(
- 'doc1.html',
- language,
- )})`,
- 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',
- },
- ];
-
- return (
-
-
-
-
-
This project is maintained by a dedicated group of people.
-
-
-
-
- );
- }
+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}`;
+
+ const supportLinks = [
+ {
+ 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',
+ },
+ ];
+
+ return (
+
+
+
+
+
This project is maintained by a dedicated group of people.
+
+
+
+
+ );
}
module.exports = Help;
diff --git a/v1/examples/basics/pages/en/index.js b/v1/examples/basics/pages/en/index.js
index 62f284c8a462..8f61cde6aa23 100755
--- a/v1/examples/basics/pages/en/index.js
+++ b/v1/examples/basics/pages/en/index.js
@@ -13,77 +13,60 @@ const MarkdownBlock = CompLibrary.MarkdownBlock; /* Used to read markdown */
const Container = CompLibrary.Container;
const GridBlock = CompLibrary.GridBlock;
-const siteConfig = require(`${process.cwd()}/siteConfig.js`);
+class HomeSplash extends React.Component {
+ render() {
+ 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 => (
+
+ );
-function imgUrl(img) {
- return `${siteConfig.baseUrl}img/${img}`;
-}
+ const Logo = props => (
+
+
+
+ );
-function docUrl(doc, language) {
- return `${siteConfig.baseUrl}docs/${language ? `${language}/` : ''}${doc}`;
-}
+ const ProjectTitle = () => (
+
+ {siteConfig.title}
+ {siteConfig.tagline}
+
+ );
-function pageUrl(page, language) {
- return siteConfig.baseUrl + (language ? `${language}/` : '') + page;
-}
+ const PromoSection = props => (
+
+ );
-class Button extends React.Component {
- render() {
- return (
+ const Button = props => (
);
- }
-}
-
-Button.defaultProps = {
- target: '_self',
-};
-
-const SplashContainer = props => (
-
-);
-
-const Logo = props => (
-
-
-
-);
-
-const ProjectTitle = () => (
-
- {siteConfig.title}
- {siteConfig.tagline}
-
-);
-
-const PromoSection = props => (
-
-);
-class HomeSplash extends React.Component {
- render() {
- const language = this.props.language || '';
return (
-
+
-
+
Try It Out
- Example Link
- Example Link 2
+ Example Link
+ Example Link 2
@@ -91,121 +74,131 @@ class HomeSplash extends React.Component {
}
}
-const Block = props => (
-
-
-
-);
-
-const Features = () => (
-
- {[
- {
- 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',
- },
- ]}
-
-);
-
-const FeatureCallout = () => (
-
-
Feature Callout
- These are features of this project
-
-);
-
-const LearnHow = () => (
-
- {[
- {
- content: 'Talk about learning how to use this',
- image: imgUrl('docusaurus.svg'),
- imageAlign: 'right',
- title: 'Learn How',
- },
- ]}
-
-);
-
-const TryOut = () => (
-
- {[
- {
- content: 'Talk about trying this out',
- image: imgUrl('docusaurus.svg'),
- imageAlign: 'left',
- title: 'Try it Out',
- },
- ]}
-
-);
-
-const Description = () => (
-
- {[
- {
- content: 'This is another description of how this project is useful',
- image: imgUrl('docusaurus.svg'),
- imageAlign: 'right',
- title: 'Description',
- },
- ]}
-
-);
-
-const Showcase = props => {
- if ((siteConfig.users || []).length === 0) {
- return null;
- }
+class Index extends React.Component {
+ render() {
+ const {config: siteConfig, language = ''} = this.props;
+ const {baseUrl} = siteConfig;
+
+ const Block = props => (
+
+
+
+ );
- const showcase = siteConfig.users.filter(user => user.pinned).map(user => (
-
-
-
- ));
-
- return (
-
-
Who is Using This?
-
This project is used by all these people
-
{showcase}
-
- );
-};
+ );
-class Index extends React.Component {
- render() {
- const language = this.props.language || '';
+ const TryOut = () => (
+
+ {[
+ {
+ content: 'Talk about trying this out',
+ image: `${baseUrl}img/docusaurus.svg`,
+ imageAlign: 'left',
+ title: 'Try it Out',
+ },
+ ]}
+
+ );
+
+ const Description = () => (
+
+ {[
+ {
+ content:
+ 'This is another description of how this project is useful',
+ image: `${baseUrl}img/docusaurus.svg`,
+ imageAlign: 'right',
+ title: 'Description',
+ },
+ ]}
+
+ );
+
+ const LearnHow = () => (
+
+ {[
+ {
+ content: 'Talk about learning how to use this',
+ image: `${baseUrl}img/docusaurus.svg`,
+ imageAlign: 'right',
+ title: 'Learn How',
+ },
+ ]}
+
+ );
+
+ const Features = () => (
+
+ {[
+ {
+ 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',
+ },
+ ]}
+
+ );
+
+ const Showcase = () => {
+ if ((siteConfig.users || []).length === 0) {
+ return null;
+ }
+
+ const showcase = siteConfig.users
+ .filter(user => user.pinned)
+ .map(user => (
+
+
+
+ ));
+
+ const pageUrl = page => baseUrl + (language ? `${language}/` : '') + page;
+
+ return (
+
+
Who is Using This?
+
This project is used by all these people
+
{showcase}
+
+
+ );
+ };
return (
);
diff --git a/v1/examples/basics/pages/en/users.js b/v1/examples/basics/pages/en/users.js
index b03fb812addf..039dc39ffa50 100644
--- a/v1/examples/basics/pages/en/users.js
+++ b/v1/examples/basics/pages/en/users.js
@@ -11,10 +11,9 @@ const CompLibrary = require('../../core/CompLibrary.js');
const Container = CompLibrary.Container;
-const siteConfig = require(`${process.cwd()}/siteConfig.js`);
-
class Users extends React.Component {
render() {
+ const {config: siteConfig} = this.props;
if ((siteConfig.users || []).length === 0) {
return null;
}
diff --git a/v1/examples/versions/pages/en/versions.js b/v1/examples/versions/pages/en/versions.js
index 286464fc177c..b3f7e8fbdd4a 100644
--- a/v1/examples/versions/pages/en/versions.js
+++ b/v1/examples/versions/pages/en/versions.js
@@ -13,10 +13,10 @@ const Container = CompLibrary.Container;
const CWD = process.cwd();
-const siteConfig = require(`${CWD}/siteConfig.js`);
const versions = require(`${CWD}/versions.json`);
-function Versions() {
+function Versions(props) {
+ const {config: siteConfig} = props;
const latestVersion = versions[0];
const repoUrl = `https://github.com/${siteConfig.organizationName}/${
siteConfig.projectName
diff --git a/v1/lib/__tests__/build-files.test.js b/v1/lib/__tests__/build-files.test.js
index 127f87fe3217..2d08755879ef 100644
--- a/v1/lib/__tests__/build-files.test.js
+++ b/v1/lib/__tests__/build-files.test.js
@@ -17,7 +17,9 @@ const CWD = process.cwd();
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 docsDir = `${CWD}/docs`;
const staticCSSDir = `${CWD}/website/static/css`;
diff --git a/v1/lib/core/nav/HeaderNav.js b/v1/lib/core/nav/HeaderNav.js
index 177a84ce0f5b..aa49f0542e5b 100644
--- a/v1/lib/core/nav/HeaderNav.js
+++ b/v1/lib/core/nav/HeaderNav.js
@@ -11,7 +11,9 @@ const React = require('react');
const fs = require('fs');
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 env = require('../../server/env.js');
@@ -33,6 +35,7 @@ class LanguageDropDown extends React.Component {
const helpTranslateString = translate(
'Help Translate|recruit community translators for your project',
);
+ const docsPart = `${siteConfig.docsUrl ? `${siteConfig.docsUrl}/` : ''}`;
// add all enabled languages to dropdown
const enabledLanguages = env.translation
.enabledLanguages()
@@ -48,8 +51,8 @@ class LanguageDropDown extends React.Component {
href =
siteConfig.baseUrl +
this.props.current.permalink.replace(
- `/${this.props.language}/`,
- `/${lang.tag}/`,
+ new RegExp(`^${docsPart}${this.props.language}/`),
+ `${docsPart}${lang.tag}/`,
);
} else if (this.props.current.id && this.props.current.id !== 'index') {
href = `${siteConfig.baseUrl + lang.tag}/${this.props.current.id}`;
diff --git a/v1/lib/server/__tests__/docs.test.js b/v1/lib/server/__tests__/docs.test.js
index 38d2664273a2..0b496175549b 100644
--- a/v1/lib/server/__tests__/docs.test.js
+++ b/v1/lib/server/__tests__/docs.test.js
@@ -66,7 +66,11 @@ const rawContent3 = metadataUtils.extractMetadata(doc3).rawContent;
const rawContentRefLinks = metadataUtils.extractMetadata(refLinks).rawContent;
describe('mdToHtmlify', () => {
- const mdToHtml = metadataUtils.mdToHtml(Metadata, '/');
+ const siteConfig = {
+ baseUrl: '/',
+ docsUrl: 'docs',
+ };
+ const mdToHtml = metadataUtils.mdToHtml(Metadata, siteConfig);
test('transform nothing', () => {
const content1 = docs.mdToHtmlify(
@@ -100,7 +104,7 @@ describe('mdToHtmlify', () => {
language: 'en',
},
};
- const customMdToHtml = metadataUtils.mdToHtml(customMetadata, '/');
+ const customMdToHtml = metadataUtils.mdToHtml(customMetadata, siteConfig);
const content3 = docs.mdToHtmlify(
rawContent3,
customMdToHtml,
diff --git a/v1/lib/server/__tests__/readMetadata.test.js b/v1/lib/server/__tests__/readMetadata.test.js
index 97ec2702a0bc..c89bf237da39 100644
--- a/v1/lib/server/__tests__/readMetadata.test.js
+++ b/v1/lib/server/__tests__/readMetadata.test.js
@@ -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});
describe('readMetadata', () => {
diff --git a/v1/lib/server/__tests__/routing.test.js b/v1/lib/server/__tests__/routing.test.js
index 594c5206ab6a..ae3d2b06417b 100644
--- a/v1/lib/server/__tests__/routing.test.js
+++ b/v1/lib/server/__tests__/routing.test.js
@@ -4,11 +4,11 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
-const routing = require('../routing');
+const routing = require('../routing.js');
describe('Blog routing', () => {
- const blogRegex = routing.blog('/');
- const blogRegex2 = routing.blog('/react/');
+ const blogRegex = routing.blog({baseUrl: '/'});
+ const blogRegex2 = routing.blog({baseUrl: '/react/'});
test('valid blog', () => {
expect('/blog/test.html').toMatch(blogRegex);
@@ -34,8 +34,8 @@ describe('Blog routing', () => {
});
describe('Docs routing', () => {
- const docsRegex = routing.docs('/');
- const docsRegex2 = routing.docs('/reason/');
+ const docsRegex = routing.docs({baseUrl: '/', docsUrl: 'docs'});
+ const docsRegex2 = routing.docs({baseUrl: '/reason/', docsUrl: 'docs'});
test('valid docs', () => {
expect('/docs/en/test.html').toMatch(docsRegex);
@@ -87,8 +87,8 @@ describe('Dot routing', () => {
});
describe('Feed routing', () => {
- const feedRegex = routing.feed('/');
- const feedRegex2 = routing.feed('/reason/');
+ const feedRegex = routing.feed({baseUrl: '/'});
+ const feedRegex2 = routing.feed({baseUrl: '/reason/'});
test('valid feed url', () => {
expect('/blog/atom.xml').toMatch(feedRegex);
@@ -137,8 +137,8 @@ describe('Extension-less url routing', () => {
});
describe('Page routing', () => {
- const pageRegex = routing.page('/');
- const pageRegex2 = routing.page('/reason/');
+ const pageRegex = routing.page({baseUrl: '/', docsUrl: 'docs'});
+ const pageRegex2 = routing.page({baseUrl: '/reason/', docsUrl: 'docs'});
test('valid page url', () => {
expect('/index.html').toMatch(pageRegex);
@@ -164,8 +164,8 @@ describe('Page routing', () => {
});
describe('Sitemap routing', () => {
- const sitemapRegex = routing.sitemap('/');
- const sitemapRegex2 = routing.sitemap('/reason/');
+ const sitemapRegex = routing.sitemap({baseUrl: '/'});
+ const sitemapRegex2 = routing.sitemap({baseUrl: '/reason/'});
test('valid sitemap url', () => {
expect('/sitemap.xml').toMatch(sitemapRegex);
diff --git a/v1/lib/server/config.js b/v1/lib/server/config.js
new file mode 100644
index 000000000000..714d76fd0589
--- /dev/null
+++ b/v1/lib/server/config.js
@@ -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;
+};
diff --git a/v1/lib/server/docs.js b/v1/lib/server/docs.js
index 3371a7928829..825cb1474aae 100644
--- a/v1/lib/server/docs.js
+++ b/v1/lib/server/docs.js
@@ -5,16 +5,20 @@
* LICENSE file in the root directory of this source tree.
*/
const CWD = process.cwd();
-const siteConfig = require(`${CWD}/siteConfig.js`);
const {join} = require('path');
const fs = require('fs-extra');
const React = require('react');
+const loadConfig = require('./config');
+
+const siteConfig = loadConfig(`${CWD}/siteConfig.js`);
const env = require('./env.js');
const {renderToStaticMarkupWithDoctype} = require('./renderUtils');
const readMetadata = require('./readMetadata.js');
const {insertTOC} = require('../core/toc.js');
const {getPath} = require('../core/utils.js');
+const docsPart = `${siteConfig.docsUrl ? `${siteConfig.docsUrl}/` : ''}`;
+
function getFilePath(metadata) {
if (!metadata) {
return null;
@@ -125,7 +129,10 @@ function replaceAssetsLink(oldContent) {
}
return fencedBlock
? line
- : line.replace(/\]\(assets\//g, `](${siteConfig.baseUrl}docs/assets/`);
+ : line.replace(
+ /\]\(assets\//g,
+ `](${siteConfig.baseUrl}${docsPart}assets/`,
+ );
});
return lines.join('\n');
}
@@ -152,7 +159,10 @@ function getMarkup(rawContent, mdToHtml, metadata) {
}
function getRedirectMarkup(metadata) {
- if (!env.translation.enabled || !metadata.permalink.includes('docs/en')) {
+ if (
+ !env.translation.enabled ||
+ !metadata.permalink.includes(`${docsPart}en`)
+ ) {
return null;
}
const Redirect = require('../core/Redirect.js');
diff --git a/v1/lib/server/generate.js b/v1/lib/server/generate.js
index 7160d0c315b8..ff3019843788 100644
--- a/v1/lib/server/generate.js
+++ b/v1/lib/server/generate.js
@@ -21,7 +21,8 @@ async function execute() {
const chalk = require('chalk');
const Site = require('../core/Site.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 feed = require('./feed.js');
const sitemap = require('./sitemap.js');
@@ -68,7 +69,7 @@ async function execute() {
fs.removeSync(join(CWD, 'build'));
// 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 => {
const metadata = Metadata[id];
const file = docs.getFile(metadata);
@@ -85,9 +86,13 @@ async function execute() {
if (!redirectMarkup) {
return;
}
+ const docsPart = `${siteConfig.docsUrl ? `${siteConfig.docsUrl}/` : ''}`;
const redirectFile = join(
buildDir,
- metadata.permalink.replace('docs/en', 'docs'),
+ metadata.permalink.replace(
+ new RegExp(`^${docsPart}en`),
+ siteConfig.docsUrl,
+ ),
);
writeFileAndCreateFolder(redirectFile, redirectMarkup);
});
@@ -332,7 +337,7 @@ async function execute() {
title={ReactComp.title}
description={ReactComp.description}
metadata={{id: pageID}}>
-
+
,
);
writeFileAndCreateFolder(
@@ -353,7 +358,7 @@ async function execute() {
config={siteConfig}
description={ReactComp.description}
metadata={{id: pageID}}>
-
+
,
);
writeFileAndCreateFolder(
@@ -371,7 +376,7 @@ async function execute() {
config={siteConfig}
description={ReactComp.description}
metadata={{id: pageID}}>
-
+
,
);
writeFileAndCreateFolder(
diff --git a/v1/lib/server/metadataUtils.js b/v1/lib/server/metadataUtils.js
index c868379f1dc4..77eaff9ec672 100644
--- a/v1/lib/server/metadataUtils.js
+++ b/v1/lib/server/metadataUtils.js
@@ -65,7 +65,8 @@ function extractMetadata(content) {
// 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
-function mdToHtml(Metadata, baseUrl) {
+function mdToHtml(Metadata, siteConfig) {
+ const {baseUrl, docsUrl} = siteConfig;
const result = {};
Object.keys(Metadata).forEach(id => {
const metadata = Metadata[id];
@@ -73,10 +74,15 @@ function mdToHtml(Metadata, baseUrl) {
return;
}
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 {
- htmlLink = htmlLink.replace('/docs/', '/docs/VERSION/');
+ htmlLink = htmlLink.replace(docsRegex, `${baseDocsPart}VERSION/`);
}
result[metadata.source] = htmlLink;
});
diff --git a/v1/lib/server/readMetadata.js b/v1/lib/server/readMetadata.js
index c916809b8c2b..7c2048dd44af 100644
--- a/v1/lib/server/readMetadata.js
+++ b/v1/lib/server/readMetadata.js
@@ -16,10 +16,14 @@ const metadataUtils = require('./metadataUtils');
const env = require('./env.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 utils = require('./utils.js');
+const docsPart = `${siteConfig.docsUrl ? `${siteConfig.docsUrl}/` : ''}`;
+
const SupportedHeaderFields = new Set([
'id',
'title',
@@ -166,7 +170,9 @@ function processMetadata(file, refDir) {
versionPart = 'next/';
}
- metadata.permalink = `docs/${langPart}${versionPart}${metadata.id}.html`;
+ metadata.permalink = `${docsPart}${langPart}${versionPart}${
+ metadata.id
+ }.html`;
// change ids previous, next
metadata.localized_id = metadata.id;
@@ -238,18 +244,24 @@ function generateMetadataDocs() {
baseMetadata.id = baseMetadata.id
.toString()
.replace(/^en-/, `${currentLanguage}-`);
- if (baseMetadata.permalink)
+ if (baseMetadata.permalink) {
baseMetadata.permalink = baseMetadata.permalink
.toString()
- .replace(/^docs\/en\//, `docs/${currentLanguage}/`);
- if (baseMetadata.next)
+ .replace(
+ new RegExp(`^${docsPart}en/`),
+ `${docsPart}${currentLanguage}/`,
+ );
+ }
+ if (baseMetadata.next) {
baseMetadata.next = baseMetadata.next
.toString()
.replace(/^en-/, `${currentLanguage}-`);
- if (baseMetadata.previous)
+ }
+ if (baseMetadata.previous) {
baseMetadata.previous = baseMetadata.previous
.toString()
.replace(/^en-/, `${currentLanguage}-`);
+ }
baseMetadata.language = currentLanguage;
defaultMetadatas[baseMetadata.id] = baseMetadata;
});
diff --git a/v1/lib/server/routing.js b/v1/lib/server/routing.js
index cd5d99815d1c..156ccec3ce15 100644
--- a/v1/lib/server/routing.js
+++ b/v1/lib/server/routing.js
@@ -4,35 +4,44 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
-function blog(baseUrl) {
- return new RegExp(`^${baseUrl}blog/.*html$`);
+function blog(siteConfig) {
+ return new RegExp(`^${siteConfig.baseUrl}blog/.*html$`);
}
-function docs(baseUrl) {
- return new RegExp(`^${baseUrl}docs/.*html$`);
+function docs(siteConfig) {
+ const docsPart = `${siteConfig.docsUrl ? `${siteConfig.docsUrl}/` : ''}`;
+ return new RegExp(`^${siteConfig.baseUrl}${docsPart}.*html$`);
}
function dotfiles() {
return /(?!.*html$)^\/.*\.[^\n/]+$/;
}
-function feed(baseUrl) {
- return new RegExp(`^${baseUrl}blog/(feed.xml|atom.xml)$`);
+function feed(siteConfig) {
+ return new RegExp(`^${siteConfig.baseUrl}blog/(feed.xml|atom.xml)$`);
}
function noExtension() {
return /\/[^.]*\/?$/;
}
-function page(baseUrl) {
+function page(siteConfig) {
const gr = regex => regex.toString().replace(/(^\/|\/$)/gm, '');
+
+ if (siteConfig.docsUrl === '') {
+ return new RegExp(
+ `(?!${gr(blog(siteConfig))})^${siteConfig.baseUrl}.*.html$`,
+ );
+ }
return new RegExp(
- `(?!${gr(docs(baseUrl))}|${gr(blog(baseUrl))})^${baseUrl}.*.html$`,
+ `(?!${gr(blog(siteConfig))}|${gr(docs(siteConfig))})^${
+ siteConfig.baseUrl
+ }.*.html$`,
);
}
-function sitemap(baseUrl) {
- return new RegExp(`^${baseUrl}sitemap.xml$`);
+function sitemap(siteConfig) {
+ return new RegExp(`^${siteConfig.baseUrl}sitemap.xml$`);
}
module.exports = {
diff --git a/v1/lib/server/server.js b/v1/lib/server/server.js
index 8b0d14e83dbe..4273e988f370 100644
--- a/v1/lib/server/server.js
+++ b/v1/lib/server/server.js
@@ -27,6 +27,7 @@ function execute(port) {
const feed = require('./feed');
const sitemap = require('./sitemap');
const routing = require('./routing');
+ const loadConfig = require('./config');
const CWD = process.cwd();
const join = path.join;
const sep = path.sep;
@@ -76,12 +77,9 @@ function execute(port) {
}
function reloadSiteConfig() {
- removeModuleAndChildrenFromCache(join(CWD, 'siteConfig.js'));
- siteConfig = require(join(CWD, 'siteConfig.js'));
-
- if (siteConfig.highlight && siteConfig.highlight.hljs) {
- siteConfig.highlight.hljs(require('highlight.js'));
- }
+ const siteConfigPath = join(CWD, 'siteConfig.js');
+ removeModuleAndChildrenFromCache(siteConfigPath);
+ siteConfig = loadConfig(siteConfigPath);
}
function requestFile(url, res, notFoundCallback) {
@@ -109,12 +107,13 @@ function execute(port) {
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 metadata =
Metadata[
Object.keys(Metadata).find(id => Metadata[id].permalink === url)
];
+
const file = docs.getFile(metadata);
if (!file) {
next();
@@ -122,11 +121,11 @@ function execute(port) {
}
const rawContent = metadataUtils.extractMetadata(file).rawContent;
removeModuleAndChildrenFromCache('../core/DocsLayout.js');
- const mdToHtml = metadataUtils.mdToHtml(Metadata, siteConfig.baseUrl);
+ const mdToHtml = metadataUtils.mdToHtml(Metadata, siteConfig);
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) => {
if (err) {
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');
const file = req.path
.toString()
@@ -151,7 +150,7 @@ function execute(port) {
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
// this to regenerate on file save rather than on page request.
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.
let htmlFile = req.path.toString().replace(siteConfig.baseUrl, '');
htmlFile = join(CWD, 'pages', htmlFile);
@@ -232,6 +231,7 @@ function execute(port) {
language = parts[i];
}
}
+
let englishFile = join(CWD, 'pages', file);
if (language && language !== 'en') {
englishFile = englishFile.replace(sep + language + sep, `${sep}en${sep}`);
@@ -272,7 +272,7 @@ function execute(port) {
title={ReactComp.title}
description={ReactComp.description}
metadata={{id: path.basename(userFile, '.js')}}>
-
+
,
);
@@ -339,7 +339,9 @@ function execute(port) {
// serve static assets from these locations
app.use(
- `${siteConfig.baseUrl}docs/assets`,
+ `${siteConfig.baseUrl}${
+ siteConfig.docsUrl ? `${siteConfig.docsUrl}/` : ''
+ }assets`,
express.static(join(CWD, '..', readMetadata.getDocsPath(), 'assets')),
);
app.use(
diff --git a/v1/lib/server/sitemap.js b/v1/lib/server/sitemap.js
index 1ae41a5fd554..7da57493bea0 100644
--- a/v1/lib/server/sitemap.js
+++ b/v1/lib/server/sitemap.js
@@ -14,7 +14,9 @@ const CWD = process.cwd();
const sitemap = require('sitemap');
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');
@@ -70,8 +72,12 @@ module.exports = function(callback) {
.forEach(key => {
const doc = Metadata[key];
const docUrl = utils.getPath(doc.permalink, siteConfig.cleanUrl);
+ const docsPart = `${siteConfig.docsUrl ? `${siteConfig.docsUrl}/` : ''}`;
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};
});
urls.push({
diff --git a/v1/lib/server/versionFallback.js b/v1/lib/server/versionFallback.js
index 914325fb54b9..cf001a4d3490 100644
--- a/v1/lib/server/versionFallback.js
+++ b/v1/lib/server/versionFallback.js
@@ -14,8 +14,9 @@ const metadataUtils = require('./metadataUtils');
const env = require('./env.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`);
@@ -187,14 +188,16 @@ function processVersionMetadata(file, version, useVersion, language) {
const latestVersion = versions[0];
+ const docsPart = `${siteConfig.docsUrl ? `${siteConfig.docsUrl}/` : ''}`;
+ const versionPart = `${version !== latestVersion ? `${version}/` : ''}`;
if (!ENABLE_TRANSLATION && !siteConfig.useEnglishUrl) {
- metadata.permalink = `docs/${
- version !== latestVersion ? `${version}/` : ''
- }${metadata.original_id}.html`;
+ metadata.permalink = `${docsPart}${versionPart}${
+ metadata.original_id
+ }.html`;
} else {
- metadata.permalink = `docs/${language}/${
- version !== latestVersion ? `${version}/` : ''
- }${metadata.original_id}.html`;
+ metadata.permalink = `${docsPart}${language}/${versionPart}${
+ metadata.original_id
+ }.html`;
}
metadata.id = metadata.id.replace(
`version-${useVersion}-`,
diff --git a/v1/website/core/Footer.js b/v1/website/core/Footer.js
index e78beb52d712..eaf9f2b05bac 100644
--- a/v1/website/core/Footer.js
+++ b/v1/website/core/Footer.js
@@ -58,6 +58,9 @@ SocialFooter.propTypes = {
class Footer extends React.Component {
render() {
+ const docsPart = `${
+ this.props.config.docsUrl ? `${this.props.config.docsUrl}/` : ''
+ }`;
return (