mirror of
https://github.com/facebook/docusaurus.git
synced 2025-06-02 10:52:35 +02:00
Merge branch 'master' of https://github.com/facebookexperimental/Docusaurus into new-docs
This commit is contained in:
commit
c182a96be9
31 changed files with 1098 additions and 233 deletions
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
node_modules
|
||||
.DS_Store
|
||||
lib/core/metadata.js
|
||||
yarn.lock
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
id: doc-markdown
|
||||
title: Documentation Markdown Features
|
||||
title: Markdown Features
|
||||
---
|
||||
|
||||
Docusaurus supports some extra features when writing documentation in markdown.
|
||||
|
|
32
docs/getting-started-installation.md
Normal file
32
docs/getting-started-installation.md
Normal file
|
@ -0,0 +1,32 @@
|
|||
---
|
||||
id: installation
|
||||
title: Installation
|
||||
---
|
||||
|
||||
Docusaurus was designed from the ground up to be easily installed and used to get your website up an running quickly. To install Docusaurus, follow these steps:
|
||||
|
||||
1. Create a `website` folder in the root of your GitHub repo.
|
||||
1. `cd website`
|
||||
1. Create a `package.json` file with the following scripts that will be used when developing documentation with Docusaurus:
|
||||
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"start": "docusaurus-start",
|
||||
"build": "docusaurus-build",
|
||||
"publish-gh-pages": "docusaurus-publish",
|
||||
"examples": "docusaurus-examples"
|
||||
}
|
||||
}
|
||||
```
|
||||
1. Install Docusaurus with `yarn` or `npm`
|
||||
|
||||
```
|
||||
yarn add docusaurus -dev
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```
|
||||
npm install --save-dev docusaurus
|
||||
```
|
69
docs/getting-started-preparation.md
Normal file
69
docs/getting-started-preparation.md
Normal file
|
@ -0,0 +1,69 @@
|
|||
---
|
||||
id: site-preparation
|
||||
title: Site Preparation
|
||||
---
|
||||
|
||||
After [installing Docusaurus](./getting-started-installation.md), you will want to install and run the example site included. This serves dual purposes.
|
||||
|
||||
1. Verifying that Docusaurus was installed correctly.
|
||||
1. Providing you the skeleton to create your site.
|
||||
|
||||
## Verifying Installation
|
||||
|
||||
1. Generate the files for the example site by running `examples` using `yarn` or `npm`.
|
||||
|
||||
```
|
||||
npm run examples
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```
|
||||
yarn run examples
|
||||
```
|
||||
|
||||
> If any of the files created by `[yarn | npm] run examples` already exists, Docusaurus will not overwrite them.
|
||||
|
||||
1. Run the server.
|
||||
|
||||
```
|
||||
npm run start
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```
|
||||
yarn run start
|
||||
```
|
||||
|
||||
1. Load the example site at http://localhost:3000. You should see the example site loaded in your web browser.
|
||||
|
||||
## Example Site Configuration
|
||||
|
||||
Loading the example site will create the following files/folders:
|
||||
|
||||
```
|
||||
website/core/Footer.js
|
||||
website/pages/...
|
||||
website/static/img/...
|
||||
website/siteConfig.js
|
||||
website/blog-examples-from-docusaurus/...
|
||||
docs-examples-from-docusaurus/...
|
||||
```
|
||||
|
||||
> The `docs-examples-from-docusaurus` folder will be at the same directory level as `website`, not in it.
|
||||
|
||||
The provided example files contain configurations that can be used as starting points for your site:
|
||||
|
||||
- The `website/core/Footer.js` file is a React component that acts as the footer for the site generated by Docusaurus and should be customized by the user.
|
||||
- The `website/blog-examples-from-docusaurus` folder contains examples of blog posts written in markdown.
|
||||
- The `docs-examples-from-docusaurus` folder contains example documentation files written in markdown.
|
||||
- The `website/pages` folder contains example top-level pages for the site.
|
||||
- The `website/static` folder contains static assets used by the example site.
|
||||
- The `website/siteConfig.js` file is the main configuration file used by Docusaurus.
|
||||
|
||||
You will need to keep the `website/siteConfig.js` and `website/core/Footer.js` files, but may edit them as you wish.
|
||||
|
||||
You should keep the `website/pages` and `website/static` folders, but may change the content inside them as you wish. At the bare minimum you should have an `en/index.js` or `en/index.html` file inside `website/pages` and an image to use as your header icon inside `website/static`.
|
||||
|
||||
The `website/blog-examples-from-docusaurus` and `docs-examples-from-docusaurus` folders contain example blog and document markdown files. If you wish to run Docusaurus with these files, you need to rename the folders to `website/blog` and `docs`, respectively.
|
52
docs/getting-started-site-creation.md
Normal file
52
docs/getting-started-site-creation.md
Normal file
|
@ -0,0 +1,52 @@
|
|||
---
|
||||
id: site-creation
|
||||
title: Creating your site
|
||||
---
|
||||
|
||||
Docusaurus' primary purpose of existence is to make it super simple for you to create documentation for your project and have a site to house those docs.
|
||||
|
||||
After [installation](./getting-started-installation.md) and [preparation](./getting-started-preparation.md), much of the work to create a basic site for your docs is already complete.
|
||||
|
||||
## Load the Example Site
|
||||
|
||||
[Preparation](./getting-started-preparation.md) created a sample site for you to see Docusaurus in action. However, it also provided the infrastructure that will be used as you are developing your own site.
|
||||
|
||||
## Site Structure
|
||||
|
||||
After loading the example site, you should see a structure in your repo that looks similar to:
|
||||
|
||||
```
|
||||
project-repo/
|
||||
blog/
|
||||
2017-05-06-blog-post.md
|
||||
docs/
|
||||
en/
|
||||
doc1.md
|
||||
website/
|
||||
blog/
|
||||
2017-05-06-blog-post.md
|
||||
```
|
||||
|
||||
All of your documentation files should be placed inside the `docs` folder as markdown `.md` files. Any blog posts should be inside the `blog` folder.
|
||||
|
||||
> The blog posts must be formatted as yyyy-mm-dd-your-file-name.md
|
||||
|
||||
## Create Your Basic Site
|
||||
|
||||
To create a fully functional site, you only need to do a few steps:
|
||||
|
||||
1. Add your documentation to the `/docs` folder as `.md` files, ensuring you have the proper [header](LINK HERE) in each file.
|
||||
1. Add zero or more docs to the [`sidebars.json`](LINK HERE) file so that your documentation is rendered in a sidebar, if you choose them to be.
|
||||
|
||||
> If you do not add your documentation to the `sidebars.json` file, the docs will be rendered, but they can only be linked to from other documentation and visited with the known URL.
|
||||
|
||||
1. Modify the `website/siteConfig.js` file to [configure your site](LINK_HERE), following the comments included in that file to guide you.
|
||||
1. [Customize](LINK_HERE_TO_CUSTOMIZATION) the `website/core/Footer.js` file that provides the footer for your site.
|
||||
1. Place assets, such as images, in the `website/static/` folder.
|
||||
1. Run the site to see the results of your changes.
|
||||
|
||||
```
|
||||
cd website
|
||||
yarn run start # or - npm run start
|
||||
# navigate to http://localhost:3000
|
||||
```
|
|
@ -1,8 +1,3 @@
|
|||
---
|
||||
id: getting-started
|
||||
title: Docusaurus
|
||||
---
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Project Structure
|
||||
|
@ -226,5 +221,5 @@ DEPLOY_USER=deltice GIT_USER=test-site-bot CIRCLE_PROJECT_USERNAME=deltice CIRCL
|
|||
|
||||
## More Information
|
||||
|
||||
For details on how to set up translation support, read [here](/docs/en/translation.md).
|
||||
For details on how to set up documentation search, read [here](/docs/en/search.md).
|
||||
For details on how to set up translation support, read [here](translation.md).
|
||||
For details on how to set up documentation search, read [here](search.md).
|
||||
|
|
115
docs/guides-navigation.md
Normal file
115
docs/guides-navigation.md
Normal file
|
@ -0,0 +1,115 @@
|
|||
---
|
||||
id: navigation
|
||||
title: Navigation and Sidebars
|
||||
---
|
||||
|
||||
## New Hidden Docs
|
||||
|
||||
New markdown files within `docs` will show up as pages on the website. Creating a file such as "docs/getting-started.md" will enable the new page `/docs/getting-started.html`.
|
||||
|
||||
To change the id (link name) of the file, set the `id` field in the markdown header. At the top of `getting-started.md`:
|
||||
|
||||
```
|
||||
---
|
||||
id: intro
|
||||
title: Getting Started
|
||||
---
|
||||
|
||||
My *new content* here..
|
||||
```
|
||||
|
||||
Now, the doc can be accessed from `/docs/intro.html`.
|
||||
|
||||
|
||||
## Adding Docs to a Sidebar
|
||||
|
||||
Now we want our new page to show up on the sidebar. We configure the order of the sidebar in `website/sidebars.json`.
|
||||
|
||||
Within `sidebars.json`, add the doc ID within an existing sidebar/category:
|
||||
|
||||
```
|
||||
{
|
||||
"docs": {
|
||||
"Getting Started": [
|
||||
"getting-started"
|
||||
```
|
||||
|
||||
Or you can create a new category within the sidebar:
|
||||
|
||||
```
|
||||
{
|
||||
"docs": {
|
||||
...
|
||||
"My New Sidebar Category": [
|
||||
"getting-started"
|
||||
],
|
||||
...
|
||||
```
|
||||
|
||||
## New Hidden Sections
|
||||
|
||||
You can also put the doc in a new sidebar. In this case we are creating a `intro` section within `sidebars.json`.
|
||||
|
||||
```
|
||||
{
|
||||
"intro": {
|
||||
"My Sidebar Category": [
|
||||
"getting-started"
|
||||
],
|
||||
},
|
||||
...
|
||||
```
|
||||
|
||||
Keep in mind, until you add the section to the nav bar (below), this new "intro" section of the site will be hidden with no links going to it.
|
||||
|
||||
|
||||
|
||||
## Adding doc to site nav bar
|
||||
|
||||
After creating a new section of the site by adding to `sidebars.json`, you can link to the new doc from the top navigation bar by editing the `headerLinks` field of `siteConfig.js`.
|
||||
|
||||
```
|
||||
headerLinks: [
|
||||
...
|
||||
{doc: 'intro', label: 'Getting Started'},
|
||||
...
|
||||
],
|
||||
```
|
||||
|
||||
## Custom page links in nav bar
|
||||
|
||||
To add custom pages to the navigation bar, entries can be added to the `headerLinks` of `siteConfig.js`. For example, if we have a page within `website/pages/help.js`, we can link to it by adding the following:
|
||||
|
||||
```
|
||||
headerLinks: [
|
||||
...
|
||||
{page: 'help', label: 'Help'},
|
||||
...
|
||||
],
|
||||
```
|
||||
|
||||
## External links in nav bar
|
||||
|
||||
Custom links can be added to the nav bar with the following entry in `siteConfig.js`:
|
||||
|
||||
```
|
||||
headerLinks: [
|
||||
...
|
||||
{href: 'https://github.com/facebookexperimental/Docusaurus', label: 'GitHub'},
|
||||
...
|
||||
],
|
||||
```
|
||||
|
||||
To open external links in a new tab, provide an `external: true` flag within the header link config.
|
||||
|
||||
## Search bar position in nav bar
|
||||
|
||||
If search is enabled on your site, your search bar will appear to the right of your links. If you want to put the search bar between links in the header, add a search entry in the `headerLinks` config array:
|
||||
|
||||
```
|
||||
headerLinks: [
|
||||
{doc: 'foo', label: 'Foo'},
|
||||
{search: true},
|
||||
{doc: 'bar', label: 'Bar'},
|
||||
],
|
||||
```
|
47
docs/guides-search.md
Normal file
47
docs/guides-search.md
Normal file
|
@ -0,0 +1,47 @@
|
|||
---
|
||||
id: search
|
||||
title: Enabling Search
|
||||
---
|
||||
|
||||
Docusaurus supports search using [Algolia DocSearch](https://community.algolia.com/docsearch/). Once you have set up your site, [enter your site information](https://community.algolia.com/docsearch/) to have Algolia crawl your website's documentation pages. Algolia will then send you an API key and index name for your site.
|
||||
|
||||
### Enabling the Search Bar
|
||||
|
||||
Enter your search-only API key and index name into `siteConfig.js` in the `algolia` section to enable search for your site.
|
||||
|
||||
```js
|
||||
const siteConfig = {
|
||||
...
|
||||
algolia: {
|
||||
apiKey: "my-search-only-api-key-1234",
|
||||
indexName: "my-index-name"
|
||||
},
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
### Controlling the Location of the Search Bar
|
||||
|
||||
By default, the search bar will be the rightmost element in the top navigation bar.
|
||||
|
||||
If you want to change the default location, add the `searchBar` flag in the `headerLinks` field of `siteConfig.js` in your desired location. For example, you may want the search bar in between your internal and external links.
|
||||
|
||||
```js
|
||||
const siteConfig = {
|
||||
...
|
||||
headerLinks: [
|
||||
{...}
|
||||
{...}
|
||||
{ search: true }
|
||||
{...}
|
||||
{...}
|
||||
],
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
### Disabling the Search Bar
|
||||
|
||||
To disable the search bar, comment out (recommended) or delete the `algolia` section in the `siteConfig.js` file.
|
||||
|
||||
Also, if you have customized the location of the search bar in `headerLinks`, set `search: false`.
|
|
@ -1,22 +0,0 @@
|
|||
---
|
||||
id: search
|
||||
title: Documentation Search
|
||||
---
|
||||
|
||||
## Algolia Search Integration
|
||||
|
||||
Docusaurus supports search using [Algolia DocSearch](https://community.algolia.com/docsearch/). Once you have set up your site, you can use the link above to have Algolia crawl your website's documentation pages. Algolia will then send you an API key and index name for your site.
|
||||
|
||||
Enter your search-only API key and index name into `siteConfig.js` in the `algolia` section to enable search for your site. The search bar will be in the header navigation bar in between internal links and external links. To disable the search bar, delete the `algolia` section in the `siteConfig.js` file.
|
||||
|
||||
```js
|
||||
const siteConfig = {
|
||||
...
|
||||
algolia: {
|
||||
apiKey: "my-search-only-api-key-1234",
|
||||
indexName: "my-index-name"
|
||||
},
|
||||
...
|
||||
}
|
||||
```
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
id: site-config
|
||||
title: Customizing siteConfig
|
||||
title: siteConfig.js
|
||||
---
|
||||
|
||||
A large part of site configuration is done by editing the `siteConfig.js` file.
|
||||
|
@ -122,10 +122,9 @@ const siteConfig = {
|
|||
"0f9f28b9ab9efae89810921a351753b5",
|
||||
indexName: "github"
|
||||
}
|
||||
gaTrackingId: "U-A2352"
|
||||
gaTrackingId: "U-A2352"
|
||||
};
|
||||
|
||||
module.exports = siteConfig;
|
||||
|
||||
```
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
id: translation
|
||||
title: Translations with Docusaurus
|
||||
title: Translations
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
|
|
@ -13,21 +13,18 @@ const Marked = require("./Marked.js");
|
|||
class Doc extends React.Component {
|
||||
render() {
|
||||
let editLink =
|
||||
!this.props.version &&
|
||||
this.props.config.editUrl &&
|
||||
<a
|
||||
className="edit-page-link button"
|
||||
href={
|
||||
this.props.config.editUrl +
|
||||
this.props.language +
|
||||
"/" +
|
||||
this.props.source
|
||||
}
|
||||
href={this.props.config.editUrl + this.props.source}
|
||||
target="_blank"
|
||||
>
|
||||
Edit this Doc
|
||||
</a>;
|
||||
if (this.props.language != "en") {
|
||||
editLink =
|
||||
!this.props.version &&
|
||||
this.props.config.recruitingLink &&
|
||||
<a
|
||||
className="edit-page-link button"
|
||||
|
|
|
@ -33,6 +33,7 @@ class DocsLayout extends React.Component {
|
|||
}
|
||||
description={content.trim().split("\n")[0]}
|
||||
language={metadata.language}
|
||||
version={metadata.version}
|
||||
>
|
||||
<div className="docMainWrapper wrapper">
|
||||
<DocsSidebar metadata={metadata} />
|
||||
|
|
|
@ -18,6 +18,10 @@ class DocsSidebar extends React.Component {
|
|||
render() {
|
||||
let sidebar = this.props.metadata.sidebar;
|
||||
let docsCategories = readCategories(sidebar);
|
||||
const categoryName = docsCategories[this.props.metadata.language][0].name;
|
||||
if (!categoryName) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<Container className="docsNavContainer" id="docsNav" wrapper={false}>
|
||||
<SideNav
|
||||
|
|
|
@ -8,40 +8,25 @@
|
|||
*/
|
||||
|
||||
const React = require("react");
|
||||
const fs = require("fs");
|
||||
const HeaderNav = require("./nav/HeaderNav.js");
|
||||
const Head = require("./Head.js");
|
||||
const Footer = require(process.cwd() + "/core/Footer.js");
|
||||
const translation = require("../server/translation.js");
|
||||
|
||||
const CWD = process.cwd();
|
||||
|
||||
class Site extends React.Component {
|
||||
/*
|
||||
goes in body after navPusher
|
||||
|
||||
<div id="fb-root" />
|
||||
<script
|
||||
type="text/javascript"
|
||||
src="https://cdn.jsdelivr.net/docsearch.js/1/docsearch.min.js"
|
||||
/>
|
||||
<script
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
|
||||
|
||||
ga('create', 'UA-44373548-17', 'auto');
|
||||
ga('send', 'pageview');
|
||||
|
||||
|
||||
!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)
|
||||
){js=d.createElement(s);js.id=id;js.src="https://platform.twitter.com/widgets.js";
|
||||
fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");
|
||||
|
||||
docsearch({
|
||||
apiKey: '833906d7486e4059359fa58823c4ef56',
|
||||
indexName: 'jest',
|
||||
inputSelector: '#search_input_react'
|
||||
});
|
||||
`,
|
||||
}}
|
||||
/>
|
||||
|
@ -62,7 +47,10 @@ class Site extends React.Component {
|
|||
this.props.config.url +
|
||||
this.props.config.baseUrl +
|
||||
(this.props.url || "index.html");
|
||||
|
||||
let latestVersion;
|
||||
if (fs.existsSync(CWD + "/versions.json")) {
|
||||
latestVersion = require(CWD + "/versions.json")[0];
|
||||
}
|
||||
return (
|
||||
<html>
|
||||
<Head
|
||||
|
@ -78,6 +66,7 @@ class Site extends React.Component {
|
|||
section={this.props.section}
|
||||
title={this.props.config.title}
|
||||
language={this.props.language}
|
||||
version={this.props.version}
|
||||
/>
|
||||
<div className="navPusher">
|
||||
{this.props.children}
|
||||
|
@ -103,17 +92,32 @@ class Site extends React.Component {
|
|||
}}
|
||||
/>}
|
||||
{this.props.config.algolia &&
|
||||
<script
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
(this.props.config.algolia.algoliaOptions
|
||||
? <script
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
var search = docsearch({
|
||||
apiKey: '${this.props.config.algolia.apiKey}',
|
||||
indexName: '${this.props.config.algolia.indexName}',
|
||||
inputSelector: '#search_input_react',
|
||||
algoliaOptions: '${this.props.config.algolia.algoliaOptions
|
||||
.replace("VERSION", this.props.version || latestVersion)
|
||||
.replace("LANGUAGE", this.props.language)}'
|
||||
});
|
||||
`
|
||||
}}
|
||||
/>
|
||||
: <script
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
var search = docsearch({
|
||||
apiKey: '${this.props.config.algolia.apiKey}',
|
||||
indexName: '${this.props.config.algolia.indexName}',
|
||||
inputSelector: '#search_input_react'
|
||||
});
|
||||
`
|
||||
}}
|
||||
/>}
|
||||
}}
|
||||
/>)}
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
|
|
|
@ -7,11 +7,20 @@
|
|||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
const React = require("react");
|
||||
const CWD = process.cwd();
|
||||
|
||||
const siteConfig = require(process.cwd() + "/siteConfig.js");
|
||||
const React = require("react");
|
||||
const fs = require("fs");
|
||||
const siteConfig = require(CWD + "/siteConfig.js");
|
||||
const translation = require("../../server/translation.js");
|
||||
|
||||
const ENABLE_TRANSLATION = fs.existsSync(CWD + "/languages.js");
|
||||
const ENABLE_VERSIONING = fs.existsSync(CWD + "/versions.json");
|
||||
let versions;
|
||||
if (ENABLE_VERSIONING) {
|
||||
versions = require(CWD + "/versions.json");
|
||||
}
|
||||
|
||||
class LanguageDropDown extends React.Component {
|
||||
render() {
|
||||
const enabledLanguages = [];
|
||||
|
@ -92,14 +101,22 @@ class HeaderNav extends React.Component {
|
|||
}
|
||||
|
||||
makeInternalLinks(link) {
|
||||
const linkWithLang = link.href.replace(
|
||||
let updatedLink = link.href.replace(
|
||||
/\/LANGUAGE\//,
|
||||
"/" + this.props.language + "/"
|
||||
);
|
||||
if (this.props.version) {
|
||||
updatedLink = updatedLink.replace(
|
||||
/\/VERSION\//,
|
||||
"/" + this.props.version + "/"
|
||||
);
|
||||
} else {
|
||||
updatedLink = updatedLink.replace(/\/VERSION\//, "/");
|
||||
}
|
||||
return (
|
||||
<li key={link.section}>
|
||||
<a
|
||||
href={linkWithLang}
|
||||
href={updatedLink}
|
||||
className={link.section === this.props.section ? "active" : ""}
|
||||
>
|
||||
{translation[this.props.language]
|
||||
|
@ -111,14 +128,10 @@ class HeaderNav extends React.Component {
|
|||
}
|
||||
|
||||
makeExternalLinks(link) {
|
||||
const linkWithLang = link.href.replace(
|
||||
/\/LANGUAGE\//,
|
||||
"/" + this.props.language + "/"
|
||||
);
|
||||
return (
|
||||
<li key={link.section}>
|
||||
<a
|
||||
href={linkWithLang}
|
||||
href={link.href}
|
||||
className={link.section === this.props.section ? "active" : ""}
|
||||
target={siteConfig.externalLinkTarget || "_self"}
|
||||
>
|
||||
|
@ -131,6 +144,11 @@ class HeaderNav extends React.Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
const versionsLink =
|
||||
this.props.baseUrl +
|
||||
(ENABLE_TRANSLATION
|
||||
? this.props.language + "/versions.html"
|
||||
: "versions.html");
|
||||
return (
|
||||
<div className="fixedHeaderContainer">
|
||||
<div className="headerWrapper wrapper">
|
||||
|
@ -142,6 +160,12 @@ class HeaderNav extends React.Component {
|
|||
{this.props.title}
|
||||
</h2>}
|
||||
</a>
|
||||
{ENABLE_VERSIONING &&
|
||||
<a href={versionsLink}>
|
||||
<h3>
|
||||
{this.props.version || versions[0]}
|
||||
</h3>
|
||||
</a>}
|
||||
{this.renderResponsiveNav()}
|
||||
</header>
|
||||
</div>
|
||||
|
|
|
@ -78,8 +78,9 @@ class SideNav extends React.Component {
|
|||
? i18n["localized-strings"][sbTitle] || sbTitle
|
||||
: sbTitle;
|
||||
} else {
|
||||
const id = metadata.original_id || metadata.localized_id;
|
||||
localizedString = i18n
|
||||
? i18n["localized-strings"][metadata.localized_id] || metadata.title
|
||||
? i18n["localized-strings"][id] || metadata.title
|
||||
: metadata.title;
|
||||
}
|
||||
return localizedString;
|
||||
|
|
|
@ -20,8 +20,13 @@ function execute() {
|
|||
const Site = require("../core/Site.js");
|
||||
const siteConfig = require(CWD + "/siteConfig.js");
|
||||
const translate = require("./translate.js");
|
||||
const versionFallback = require("./versionFallback.js");
|
||||
|
||||
const ENABLE_TRANSLATION = fs.existsSync(CWD + "/languages.js");
|
||||
const ENABLE_VERSIONING = fs.existsSync(CWD + "/versions.json");
|
||||
|
||||
let languages;
|
||||
if (fs.existsSync(CWD + "/languages.js")) {
|
||||
if (ENABLE_TRANSLATION) {
|
||||
languages = require(CWD + "/languages.js");
|
||||
} else {
|
||||
languages = [
|
||||
|
@ -77,114 +82,98 @@ function execute() {
|
|||
|
||||
readMetadata.generateDocsMetadata();
|
||||
const Metadata = require("../core/metadata.js");
|
||||
let mdToHtml = {};
|
||||
for (let i = 0; i < Metadata.length; i++) {
|
||||
const metadata = Metadata[i];
|
||||
if (metadata.language !== "en") {
|
||||
continue;
|
||||
|
||||
const mdToHtml = {};
|
||||
Object.keys(Metadata).forEach(id => {
|
||||
const metadata = Metadata[id];
|
||||
if (metadata.language !== "en" || metadata.original_id) {
|
||||
return;
|
||||
}
|
||||
mdToHtml[metadata.source] = siteConfig.baseUrl + metadata.permalink;
|
||||
}
|
||||
let htmlLink =
|
||||
siteConfig.baseUrl + metadata.permalink.replace("/next/", "/");
|
||||
if (htmlLink.includes("/docs/en/")) {
|
||||
htmlLink = htmlLink.replace("/docs/en/", "/docs/en/VERSION/");
|
||||
} else {
|
||||
htmlLink = htmlLink.replace("/docs/", "/docs/VERSION/");
|
||||
}
|
||||
mdToHtml[metadata.source] = htmlLink;
|
||||
});
|
||||
|
||||
const DocsLayout = require("../core/DocsLayout.js");
|
||||
|
||||
fs.removeSync(CWD + "/build");
|
||||
|
||||
// create html files for all English docs
|
||||
let files = glob.sync(CWD + "/../docs/**");
|
||||
files.forEach(file => {
|
||||
// console.log(file);
|
||||
let language = "en";
|
||||
// create html files for all docs
|
||||
Object.keys(Metadata).forEach(id => {
|
||||
const metadata = Metadata[id];
|
||||
|
||||
const extension = path.extname(file);
|
||||
|
||||
if (extension === ".md" || extension === ".markdown") {
|
||||
const result = readMetadata.processMetadata(file);
|
||||
if (!result) {
|
||||
return;
|
||||
let file;
|
||||
if (metadata.original_id) {
|
||||
if (ENABLE_TRANSLATION && metadata.language !== "en") {
|
||||
file =
|
||||
CWD + "/translated_docs/" + metadata.language + "/" + metadata.source;
|
||||
} else {
|
||||
file = CWD + "/versioned_docs/" + metadata.source;
|
||||
}
|
||||
|
||||
const metadata = result.metadata;
|
||||
let rawContent = result.rawContent;
|
||||
|
||||
/* generate table of contents if appropriate */
|
||||
if (rawContent && rawContent.indexOf(TABLE_OF_CONTENTS_TOKEN) != -1) {
|
||||
rawContent = insertTableOfContents(rawContent);
|
||||
} else {
|
||||
if (metadata.language === "en") {
|
||||
file = CWD + "/../docs/" + metadata.source;
|
||||
} else {
|
||||
file =
|
||||
CWD + "/translated_docs/" + metadata.language + "/" + metadata.source;
|
||||
}
|
||||
|
||||
/* replace any links to markdown files to their website html links */
|
||||
Object.keys(mdToHtml).forEach(function(key, index) {
|
||||
rawContent = rawContent.replace(
|
||||
new RegExp(key, "g"),
|
||||
mdToHtml[key].replace("/en/", "/" + language + "/")
|
||||
);
|
||||
});
|
||||
|
||||
const docComp = (
|
||||
<DocsLayout metadata={metadata} language={language} config={siteConfig}>
|
||||
{rawContent}
|
||||
</DocsLayout>
|
||||
);
|
||||
const str = renderToStaticMarkup(docComp);
|
||||
|
||||
let targetFile =
|
||||
CWD + "/build/" + siteConfig.projectName + "/" + metadata.permalink;
|
||||
// console.log(targetFile);
|
||||
writeFileAndCreateFolder(targetFile, str);
|
||||
}
|
||||
});
|
||||
|
||||
// create html files for all non-English docs
|
||||
if (languages.length > 1) {
|
||||
files = glob.sync(CWD + "/translated_docs/**");
|
||||
files.forEach(file => {
|
||||
let language = "en";
|
||||
if (!fs.existsSync(file)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const regexSubFolder = /translated_docs\/(.*)\/.*/;
|
||||
const match = regexSubFolder.exec(file);
|
||||
if (match) {
|
||||
language = match[1];
|
||||
}
|
||||
let rawContent = readMetadata.extractMetadata(fs.readFileSync(file, "utf8"))
|
||||
.rawContent;
|
||||
|
||||
if (enabledLanguages.indexOf(language) === -1) {
|
||||
return;
|
||||
}
|
||||
const language = metadata.language;
|
||||
|
||||
const extension = path.extname(file);
|
||||
if (extension !== ".md" && extension !== ".markdown") {
|
||||
return;
|
||||
}
|
||||
/* generate table of contents if appropriate */
|
||||
if (rawContent && rawContent.indexOf(TABLE_OF_CONTENTS_TOKEN) != -1) {
|
||||
rawContent = insertTableOfContents(rawContent);
|
||||
}
|
||||
|
||||
const result = readMetadata.processMetadata(file);
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
let latestVersion;
|
||||
if (ENABLE_VERSIONING) {
|
||||
latestVersion = JSON.parse(
|
||||
fs.readFileSync(CWD + "/versions.json", "utf8")
|
||||
)[0];
|
||||
}
|
||||
|
||||
const metadata = result.metadata;
|
||||
let rawContent = result.rawContent;
|
||||
|
||||
/* generate table of contents if appropriate */
|
||||
if (rawContent && rawContent.indexOf(TABLE_OF_CONTENTS_TOKEN) != -1) {
|
||||
rawContent = insertTableOfContents(rawContent);
|
||||
}
|
||||
|
||||
/* replace any links to markdown files to their website html links */
|
||||
Object.keys(mdToHtml).forEach(function(key, index) {
|
||||
rawContent = rawContent.replace(new RegExp(key, "g"), mdToHtml[key]);
|
||||
});
|
||||
|
||||
const docComp = (
|
||||
<DocsLayout metadata={metadata} language={language} config={siteConfig}>
|
||||
{rawContent}
|
||||
</DocsLayout>
|
||||
/* replace any links to markdown files to their website html links */
|
||||
Object.keys(mdToHtml).forEach(function(key, index) {
|
||||
let link = mdToHtml[key];
|
||||
link = link.replace("/en/", "/" + language + "/");
|
||||
link = link.replace(
|
||||
"/VERSION/",
|
||||
metadata.version && (metadata.version !== latestVersion)
|
||||
? "/" + metadata.version + "/"
|
||||
: "/"
|
||||
);
|
||||
const str = renderToStaticMarkup(docComp);
|
||||
let targetFile =
|
||||
CWD + "/build/" + siteConfig.projectName + "/" + metadata.permalink;
|
||||
// console.log(targetFile);
|
||||
writeFileAndCreateFolder(targetFile, str);
|
||||
rawContent = rawContent.replace(new RegExp(key, "g"), link);
|
||||
});
|
||||
}
|
||||
|
||||
rawContent = rawContent.replace(
|
||||
/\]\(assets\//g,
|
||||
"](" + siteConfig.baseUrl + "docs/assets/"
|
||||
);
|
||||
|
||||
const docComp = (
|
||||
<DocsLayout metadata={metadata} language={language} config={siteConfig}>
|
||||
{rawContent}
|
||||
</DocsLayout>
|
||||
);
|
||||
const str = renderToStaticMarkup(docComp);
|
||||
const targetFile =
|
||||
CWD + "/build/" + siteConfig.projectName + "/" + metadata.permalink;
|
||||
|
||||
writeFileAndCreateFolder(targetFile, str);
|
||||
});
|
||||
|
||||
/* copy docs assets if they exist */
|
||||
if (fs.existsSync(CWD + "/../docs/assets")) {
|
||||
|
|
|
@ -34,8 +34,12 @@ function readCategories(sidebar) {
|
|||
for (let k = 0; k < enabledLanguages.length; ++k) {
|
||||
const language = enabledLanguages[k];
|
||||
|
||||
const metadatas = Metadata.filter(metadata => {
|
||||
return metadata.sidebar === sidebar && metadata.language === language;
|
||||
const metadatas = [];
|
||||
Object.keys(Metadata).forEach(id => {
|
||||
const metadata = Metadata[id];
|
||||
if (metadata.sidebar === sidebar && metadata.language === language) {
|
||||
metadatas.push(metadata);
|
||||
}
|
||||
});
|
||||
|
||||
// Build a hashmap of article_id -> metadata
|
||||
|
|
|
@ -14,6 +14,10 @@ const fs = require("fs");
|
|||
const os = require("os");
|
||||
const glob = require("glob");
|
||||
const siteConfig = require(CWD + "/siteConfig.js");
|
||||
const versionFallback = require("./versionFallback.js");
|
||||
|
||||
const ENABLE_VERSIONING = fs.existsSync(CWD + "/versions.json");
|
||||
|
||||
let languages;
|
||||
if (fs.existsSync(CWD + "/languages.js")) {
|
||||
languages = require(CWD + "/languages.js");
|
||||
|
@ -28,7 +32,9 @@ if (fs.existsSync(CWD + "/languages.js")) {
|
|||
}
|
||||
|
||||
function readSidebar() {
|
||||
const allSidebars = require(CWD + "/sidebar.json");
|
||||
let allSidebars = require(CWD + "/sidebars.json");
|
||||
Object.assign(allSidebars, versionFallback.sidebarData());
|
||||
|
||||
const order = {};
|
||||
|
||||
Object.keys(allSidebars).forEach(sidebar => {
|
||||
|
@ -91,6 +97,7 @@ function extractMetadata(content) {
|
|||
return { metadata, rawContent: both.content };
|
||||
}
|
||||
|
||||
// process the metadata for a document found in the docs folder
|
||||
function processMetadata(file) {
|
||||
const result = extractMetadata(fs.readFileSync(file, "utf8"));
|
||||
if (!result.metadata || !result.rawContent) {
|
||||
|
@ -115,6 +122,18 @@ function processMetadata(file) {
|
|||
metadata.permalink = "docs/" + language + "/" + metadata.id + ".html";
|
||||
}
|
||||
|
||||
if (ENABLE_VERSIONING) {
|
||||
metadata.version = "next";
|
||||
if (languages.length === 1 && !siteConfig.useEnglishUrl) {
|
||||
metadata.permalink = metadata.permalink.replace("docs/", "docs/next/");
|
||||
} else {
|
||||
metadata.permalink = metadata.permalink.replace(
|
||||
"docs/" + language + "/",
|
||||
"docs/" + language + "/next/"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// change ids previous, next
|
||||
metadata.localized_id = metadata.id;
|
||||
metadata.id = language + "-" + metadata.id;
|
||||
|
@ -123,18 +142,20 @@ function processMetadata(file) {
|
|||
const order = readSidebar();
|
||||
const id = metadata.localized_id;
|
||||
|
||||
metadata.sidebar = order[id].sidebar;
|
||||
metadata.category = order[id].category;
|
||||
if (order[id]) {
|
||||
metadata.sidebar = order[id].sidebar;
|
||||
metadata.category = order[id].category;
|
||||
|
||||
if (order[id].next) {
|
||||
metadata.next_id = order[id].next;
|
||||
metadata.next = language + "-" + order[id].next;
|
||||
if (order[id].next) {
|
||||
metadata.next_id = order[id].next;
|
||||
metadata.next = language + "-" + order[id].next;
|
||||
}
|
||||
if (order[id].previous) {
|
||||
metadata.previous_id = order[id].previous;
|
||||
metadata.previous = language + "-" + order[id].previous;
|
||||
}
|
||||
}
|
||||
if (order[id].previous) {
|
||||
metadata.previous_id = order[id].previous;
|
||||
metadata.previous = language + "-" + order[id].previous;
|
||||
}
|
||||
|
||||
|
||||
return { metadata, rawContent: rawContent };
|
||||
}
|
||||
|
||||
|
@ -148,7 +169,7 @@ function generateDocsMetadata() {
|
|||
enabledLanguages.push(lang.tag);
|
||||
});
|
||||
|
||||
const metadatas = [];
|
||||
const metadatas = {};
|
||||
|
||||
/* metadata for english files */
|
||||
let files = glob.sync(CWD + "/../docs/**");
|
||||
|
@ -163,7 +184,7 @@ function generateDocsMetadata() {
|
|||
return;
|
||||
}
|
||||
let metadata = res.metadata;
|
||||
metadatas.push(metadata);
|
||||
metadatas[metadata.id] = metadata;
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -188,10 +209,32 @@ function generateDocsMetadata() {
|
|||
return;
|
||||
}
|
||||
let metadata = res.metadata;
|
||||
metadatas.push(metadata);
|
||||
metadatas[metadata.id] = metadata;
|
||||
}
|
||||
});
|
||||
|
||||
const versionData = versionFallback.docData();
|
||||
versionData.forEach(metadata => {
|
||||
const id = metadata.localized_id;
|
||||
metadata.sidebar = order[id].sidebar;
|
||||
metadata.category = order[id].category;
|
||||
if (order[id].next) {
|
||||
metadata.next_id = order[id].next.replace(
|
||||
"version-" + metadata.version + "-",
|
||||
""
|
||||
);
|
||||
metadata.next = metadata.language + "-" + order[id].next;
|
||||
}
|
||||
if (order[id].previous) {
|
||||
metadata.previous_id = order[id].previous.replace(
|
||||
"version-" + metadata.version + "-",
|
||||
""
|
||||
);
|
||||
metadata.previous = metadata.language + "-" + order[id].previous;
|
||||
}
|
||||
metadatas[metadata.id] = metadata;
|
||||
});
|
||||
|
||||
fs.writeFileSync(
|
||||
__dirname + "/../core/metadata.js",
|
||||
"/**\n" +
|
||||
|
@ -245,6 +288,7 @@ function generateBlogMetadata() {
|
|||
}
|
||||
|
||||
module.exports = {
|
||||
readSidebar,
|
||||
extractMetadata,
|
||||
processMetadata,
|
||||
generateDocsMetadata,
|
||||
|
|
|
@ -20,9 +20,11 @@ function execute(port) {
|
|||
const mkdirp = require("mkdirp");
|
||||
const glob = require("glob");
|
||||
const translate = require("./translate.js");
|
||||
const versionFallback = require("./versionFallback");
|
||||
|
||||
const CWD = process.cwd();
|
||||
const ENABLE_TRANSLATION = fs.existsSync(CWD + "/languages.js");
|
||||
const ENABLE_VERSIONING = fs.existsSync(CWD + "/versions.json");
|
||||
|
||||
let siteConfig = require(CWD + "/siteConfig.js");
|
||||
|
||||
|
@ -127,49 +129,84 @@ function execute(port) {
|
|||
purgeCache(CWD + "/siteConfig.js");
|
||||
siteConfig = require(CWD + "/siteConfig.js");
|
||||
|
||||
let url = req.path.toString().replace(siteConfig.baseUrl, "");
|
||||
|
||||
reloadMetadata();
|
||||
|
||||
// links is a map from a permalink to an id
|
||||
let links = {};
|
||||
for (let i = 0; i < Metadata.length; i++) {
|
||||
const metadata = Metadata[i];
|
||||
if (metadata.language === "en") {
|
||||
links[metadata.permalink] = CWD + "/../docs/" + metadata.source;
|
||||
Object.keys(Metadata).forEach(id => {
|
||||
const metadata = Metadata[id];
|
||||
links[metadata.permalink] = id;
|
||||
});
|
||||
|
||||
const mdToHtml = {};
|
||||
Object.keys(Metadata).forEach(id => {
|
||||
const metadata = Metadata[id];
|
||||
if (metadata.language !== "en" || metadata.original_id) {
|
||||
return;
|
||||
}
|
||||
let htmlLink =
|
||||
siteConfig.baseUrl + metadata.permalink.replace("/next/", "/");
|
||||
if (htmlLink.includes("/docs/en/")) {
|
||||
htmlLink = htmlLink.replace("/docs/en/", "/docs/en/VERSION/");
|
||||
} else {
|
||||
links[metadata.permalink] =
|
||||
htmlLink = htmlLink.replace("/docs/", "/docs/VERSION/");
|
||||
}
|
||||
mdToHtml[metadata.source] = htmlLink;
|
||||
});
|
||||
|
||||
const metadata = Metadata[links[url]];
|
||||
const language = metadata.language;
|
||||
|
||||
let file;
|
||||
if (metadata.original_id) {
|
||||
if (ENABLE_TRANSLATION && metadata.language !== "en") {
|
||||
file =
|
||||
CWD + "/translated_docs/" + metadata.language + "/" + metadata.source;
|
||||
} else {
|
||||
file = CWD + "/versioned_docs/" + metadata.source;
|
||||
}
|
||||
} else {
|
||||
if (metadata.language === "en") {
|
||||
file = CWD + "/../docs/" + metadata.source;
|
||||
} else {
|
||||
file =
|
||||
CWD + "/translated_docs/" + metadata.language + "/" + metadata.source;
|
||||
}
|
||||
}
|
||||
let mdToHtml = {};
|
||||
for (let i = 0; i < Metadata.length; i++) {
|
||||
const metadata = Metadata[i];
|
||||
if (metadata.language !== "en") {
|
||||
continue;
|
||||
}
|
||||
mdToHtml[metadata.source] = siteConfig.baseUrl + metadata.permalink;
|
||||
}
|
||||
|
||||
let file = links[req.path.toString().replace(siteConfig.baseUrl, "")];
|
||||
|
||||
if (!fs.existsSync(file)) {
|
||||
next();
|
||||
return;
|
||||
}
|
||||
const result = readMetadata.processMetadata(file);
|
||||
|
||||
const metadata = result.metadata;
|
||||
const language = metadata.language;
|
||||
let rawContent = result.rawContent;
|
||||
let rawContent = readMetadata.extractMetadata(fs.readFileSync(file, "utf8"))
|
||||
.rawContent;
|
||||
|
||||
/* generate table of contents if appropriate */
|
||||
if (rawContent && rawContent.indexOf(TABLE_OF_CONTENTS_TOKEN) !== -1) {
|
||||
rawContent = insertTableOfContents(rawContent);
|
||||
}
|
||||
|
||||
let latestVersion;
|
||||
if (ENABLE_VERSIONING) {
|
||||
latestVersion = JSON.parse(
|
||||
fs.readFileSync(CWD + "/versions.json", "utf8")
|
||||
)[0];
|
||||
}
|
||||
|
||||
/* replace any links to markdown files to their website html links */
|
||||
Object.keys(mdToHtml).forEach(function(key, index) {
|
||||
rawContent = rawContent.replace(
|
||||
new RegExp(key, "g"),
|
||||
mdToHtml[key].replace("/en/", "/" + language + "/")
|
||||
let link = mdToHtml[key];
|
||||
link = link.replace("/en/", "/" + language + "/");
|
||||
link = link.replace(
|
||||
"/VERSION/",
|
||||
metadata.version && (metadata.version !== latestVersion)
|
||||
? "/" + metadata.version + "/"
|
||||
: "/"
|
||||
);
|
||||
rawContent = rawContent.replace(new RegExp(key, "g"), link);
|
||||
});
|
||||
|
||||
rawContent = rawContent.replace(
|
||||
|
@ -187,6 +224,7 @@ function execute(port) {
|
|||
|
||||
res.send(renderToStaticMarkup(docComp));
|
||||
});
|
||||
|
||||
/* handle all requests for blog pages and posts */
|
||||
app.get(/blog\/.*html$/, (req, res) => {
|
||||
purgeCache(CWD + "/siteConfig.js");
|
||||
|
|
308
lib/server/versionFallback.js
Normal file
308
lib/server/versionFallback.js
Normal file
|
@ -0,0 +1,308 @@
|
|||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
const CWD = process.cwd();
|
||||
const glob = require("glob");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const diff = require("diff");
|
||||
const assert = require("assert");
|
||||
|
||||
const siteConfig = require(CWD + "/siteConfig.js");
|
||||
|
||||
const ENABLE_TRANSLATION = fs.existsSync(CWD + "/languages.js");
|
||||
|
||||
let versions;
|
||||
if (fs.existsSync(CWD + "/versions.json")) {
|
||||
versions = require(CWD + "/versions.json");
|
||||
} else {
|
||||
versions = [];
|
||||
}
|
||||
|
||||
let languages;
|
||||
if (fs.existsSync(CWD + "/languages.js")) {
|
||||
languages = require(CWD + "/languages.js");
|
||||
} else {
|
||||
languages = [
|
||||
{
|
||||
enabled: true,
|
||||
name: "English",
|
||||
tag: "en"
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/*****************************************************************/
|
||||
|
||||
// included to prevent cyclical dependency with readMetadata.js
|
||||
|
||||
function splitHeader(content) {
|
||||
const lines = content.split("\n");
|
||||
let i = 1;
|
||||
for (; i < lines.length - 1; ++i) {
|
||||
if (lines[i] === "---") {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return {
|
||||
header: lines.slice(1, i + 1).join("\n"),
|
||||
content: lines.slice(i + 1).join("\n")
|
||||
};
|
||||
}
|
||||
|
||||
// Extract markdown metadata header
|
||||
function extractMetadata(content) {
|
||||
const metadata = {};
|
||||
const both = splitHeader(content);
|
||||
const lines = both.header.split("\n");
|
||||
for (let i = 0; i < lines.length - 1; ++i) {
|
||||
const keyvalue = lines[i].split(":");
|
||||
const key = keyvalue[0].trim();
|
||||
let value = keyvalue.slice(1).join(":").trim();
|
||||
try {
|
||||
value = JSON.parse(value);
|
||||
} catch (e) {}
|
||||
metadata[key] = value;
|
||||
}
|
||||
return { metadata, rawContent: both.content };
|
||||
}
|
||||
|
||||
/*****************************************************************/
|
||||
|
||||
const versionFolder = CWD + "/versioned_docs/";
|
||||
|
||||
// available stores doc ids of documents that are available for
|
||||
// each version
|
||||
const available = {};
|
||||
// versionFiles is used to keep track of what file to use with a
|
||||
// given version/id of a document
|
||||
const versionFiles = {};
|
||||
let files = glob.sync(versionFolder + "**");
|
||||
files.forEach(file => {
|
||||
const ext = path.extname(file);
|
||||
if (ext !== ".md" && ext !== ".markdown") {
|
||||
return;
|
||||
}
|
||||
const res = extractMetadata(fs.readFileSync(file, "utf8"));
|
||||
const metadata = res.metadata;
|
||||
|
||||
if (!(metadata.original_id in available)) {
|
||||
available[metadata.original_id] = new Set();
|
||||
}
|
||||
const version = metadata.id.split("-")[1];
|
||||
available[metadata.original_id].add(version);
|
||||
|
||||
if (!(version in versionFiles)) {
|
||||
versionFiles[version] = {};
|
||||
}
|
||||
versionFiles[version][metadata.original_id] = file;
|
||||
});
|
||||
|
||||
// returns the version to use for a document based on its id and
|
||||
// what the requested version is
|
||||
function docVersion(id, req_version) {
|
||||
// iterate through versions until a version less than or equal to the requested
|
||||
// is found, then check if that verison has an available file to use
|
||||
let requestedFound = false;
|
||||
for (let i = 0; i < versions.length; i++) {
|
||||
if (versions[i] === req_version) {
|
||||
requestedFound = true;
|
||||
}
|
||||
if (!requestedFound) {
|
||||
continue;
|
||||
}
|
||||
if (!available[id]) {
|
||||
return null;
|
||||
}
|
||||
if (available[id].has(versions[i])) {
|
||||
return versions[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// returns whether a given file has content that differ from the
|
||||
// document with the given id
|
||||
function diffLatestDoc(file, id) {
|
||||
if (versions.length === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const latest = versions[0];
|
||||
|
||||
const version = docVersion(id, latest);
|
||||
if (!version) {
|
||||
return true;
|
||||
}
|
||||
const latestFile = versionFiles[version][id];
|
||||
|
||||
if (!latestFile || !fs.existsSync(latestFile)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const diffs = diff.diffChars(
|
||||
extractMetadata(fs.readFileSync(latestFile, "utf8")).rawContent,
|
||||
extractMetadata(fs.readFileSync(file, "utf8")).rawContent
|
||||
);
|
||||
diffs.forEach(part => {
|
||||
if (part.added || part.removed) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
// return metadata for a versioned file given the file, its version (requested),
|
||||
// the version of the file to be used, and its language
|
||||
function processVersionMetadata(file, version, useVersion, language) {
|
||||
const metadata = extractMetadata(fs.readFileSync(file, "utf8")).metadata;
|
||||
metadata.source = "version-" + useVersion + "/" + path.basename(file);
|
||||
|
||||
const latestVersion = versions[0];
|
||||
|
||||
if (!ENABLE_TRANSLATION && !siteConfig.useEnglishUrl) {
|
||||
metadata.permalink =
|
||||
"docs/" +
|
||||
(version !== latestVersion ? version + "/" : "") +
|
||||
metadata.original_id +
|
||||
".html";
|
||||
} else {
|
||||
metadata.permalink =
|
||||
"docs/" +
|
||||
language +
|
||||
"/" +
|
||||
(version !== latestVersion ? version + "/" : "") +
|
||||
metadata.original_id +
|
||||
".html";
|
||||
}
|
||||
metadata.id = metadata.id.replace(
|
||||
"version-" + useVersion + "-",
|
||||
"version-" + version + "-"
|
||||
);
|
||||
metadata.localized_id = metadata.id;
|
||||
metadata.id = language + "-" + metadata.id;
|
||||
metadata.language = language;
|
||||
metadata.version = version;
|
||||
|
||||
return metadata;
|
||||
}
|
||||
|
||||
// return all metadata of versioned documents
|
||||
function docData() {
|
||||
const allIds = new Set();
|
||||
Object.keys(versionFiles).forEach(version => {
|
||||
Object.keys(versionFiles[version]).forEach(id => {
|
||||
allIds.add(id);
|
||||
});
|
||||
});
|
||||
|
||||
const metadatas = [];
|
||||
|
||||
languages.filter(language => language.enabled).forEach(language => {
|
||||
versions.forEach(version => {
|
||||
allIds.forEach(id => {
|
||||
const useVersion = docVersion(id, version);
|
||||
if (!useVersion) {
|
||||
return;
|
||||
}
|
||||
const file = versionFiles[useVersion][id];
|
||||
|
||||
metadatas.push(
|
||||
processVersionMetadata(file, version, useVersion, language.tag)
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return metadatas;
|
||||
}
|
||||
|
||||
// return the version of the sidebar to use given a requested version
|
||||
function sidebarVersion(req_version) {
|
||||
// iterate through versions until a version less than or equal to the requested
|
||||
// is found, then check if that verison has an available file to use
|
||||
let requestedFound = false;
|
||||
for (let i = 0; i < versions.length; i++) {
|
||||
if (versions[i] === req_version) {
|
||||
requestedFound = true;
|
||||
}
|
||||
if (!requestedFound) {
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
fs.existsSync(
|
||||
CWD + "/versioned_sidebars/version-" + versions[i] + "-sidebar.json"
|
||||
)
|
||||
) {
|
||||
return versions[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// return whether or not the current sidebar.json file differs from the
|
||||
// latest versioned one
|
||||
function diffLatestSidebar() {
|
||||
if (versions.length === 0) {
|
||||
return true;
|
||||
}
|
||||
const latest = versions[0];
|
||||
|
||||
const version = sidebarVersion(latest);
|
||||
const latestSidebar =
|
||||
CWD + "/versioned_sidebars/version-" + version + "-sidebar.json";
|
||||
if (!fs.existsSync(latestSidebar)) {
|
||||
return true;
|
||||
}
|
||||
const currentSidebar = CWD + "/sidebar.json";
|
||||
if (!fs.existsSync(currentSidebar)) {
|
||||
// TO DO: error message
|
||||
}
|
||||
|
||||
// compare for equality between latest version sidebar with version prefixes
|
||||
// stripped and current sidebar
|
||||
return (
|
||||
JSON.stringify(JSON.parse(fs.readFileSync(latestSidebar, "utf8"))).replace(
|
||||
new RegExp("version-" + version + "-", "g"),
|
||||
""
|
||||
) !== JSON.stringify(JSON.parse(fs.readFileSync(currentSidebar, "utf8")))
|
||||
);
|
||||
}
|
||||
|
||||
// return all versioned sidebar data
|
||||
function sidebarData() {
|
||||
const allSidebars = {};
|
||||
|
||||
for (let i = 0; i < versions.length; i++) {
|
||||
const version = sidebarVersion(versions[i]);
|
||||
const sidebar = JSON.parse(
|
||||
fs
|
||||
.readFileSync(
|
||||
CWD + "/versioned_sidebars/version-" + version + "-sidebar.json",
|
||||
"utf8"
|
||||
)
|
||||
.replace(
|
||||
new RegExp("version-" + version + "-", "g"),
|
||||
"version-" + versions[i] + "-"
|
||||
)
|
||||
);
|
||||
Object.assign(allSidebars, sidebar);
|
||||
}
|
||||
return allSidebars;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
docVersion,
|
||||
diffLatestDoc,
|
||||
processVersionMetadata,
|
||||
docData,
|
||||
sidebarVersion,
|
||||
diffLatestSidebar,
|
||||
sidebarData
|
||||
};
|
|
@ -512,6 +512,13 @@ header h2 {
|
|||
position: relative;
|
||||
z-index: 9999;
|
||||
}
|
||||
.fixedHeaderContainer header h3 {
|
||||
text-decoration: underline;
|
||||
font-family: "Helvetica Neue", Arial, sans-serif;
|
||||
color: white;
|
||||
margin-left: 10px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.promoSection {
|
||||
display: flex;
|
||||
|
|
122
lib/version.js
Normal file
122
lib/version.js
Normal file
|
@ -0,0 +1,122 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
const glob = require("glob");
|
||||
const fs = require("fs-extra");
|
||||
const path = require("path");
|
||||
const mkdirp = require("mkdirp");
|
||||
const readMetadata = require("./server/readMetadata.js");
|
||||
const versionFallback = require("./server/versionFallback.js");
|
||||
|
||||
const CWD = process.cwd();
|
||||
let versions;
|
||||
if (fs.existsSync(CWD + "/versions.json")) {
|
||||
versions = require(CWD + "/versions.json");
|
||||
} else {
|
||||
versions = [];
|
||||
}
|
||||
|
||||
let version;
|
||||
|
||||
const program = require("commander");
|
||||
program
|
||||
.arguments("<version>")
|
||||
.action(ver => {
|
||||
version = ver;
|
||||
})
|
||||
.parse(process.argv);
|
||||
|
||||
if (typeof version === "undefined") {
|
||||
console.error(
|
||||
"No version number specified!\nPass the version you wish to create as an argument.\nEx: 1.0.0"
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (versions.includes(version)) {
|
||||
console.error(
|
||||
"This verison already exists!\nSpecify a new version to create that does not already exist."
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
function makeHeader(metadata) {
|
||||
let header = "---\n";
|
||||
Object.keys(metadata).forEach(key => {
|
||||
header += key + ": " + metadata[key] + "\n";
|
||||
});
|
||||
header += "---\n";
|
||||
return header;
|
||||
}
|
||||
|
||||
const versionFolder = CWD + "/versioned_docs/version-" + version;
|
||||
|
||||
mkdirp.sync(versionFolder);
|
||||
|
||||
// copy necessary files to new version, changing some of its metadata to reflect the versioning
|
||||
let files = glob.sync(CWD + "/../docs/*");
|
||||
files.forEach(file => {
|
||||
const ext = path.extname(file);
|
||||
if (ext !== ".md" && ext !== ".markdown") {
|
||||
return;
|
||||
}
|
||||
|
||||
const res = readMetadata.extractMetadata(fs.readFileSync(file, "utf8"));
|
||||
let metadata = res.metadata;
|
||||
let rawContent = res.rawContent;
|
||||
if (!metadata.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!versionFallback.diffLatestDoc(file, metadata.id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
metadata.original_id = metadata.id;
|
||||
metadata.id = "version-" + version + "-" + metadata.id;
|
||||
|
||||
const targetFile =
|
||||
CWD + "/versioned_docs/version-" + version + "/" + path.basename(file);
|
||||
|
||||
fs.writeFileSync(targetFile, makeHeader(metadata) + rawContent, "utf8");
|
||||
});
|
||||
|
||||
// copy sidebar if necessary
|
||||
if (versionFallback.diffLatestSidebar()) {
|
||||
mkdirp(CWD + "/versioned_sidebars");
|
||||
const sidebar = JSON.parse(fs.readFileSync(CWD + "/sidebars.json", "utf8"));
|
||||
const versioned = {};
|
||||
|
||||
Object.keys(sidebar).forEach(sb => {
|
||||
const version_sb = "version-" + version + "-" + sb;
|
||||
versioned[version_sb] = {};
|
||||
|
||||
const categories = sidebar[sb];
|
||||
Object.keys(categories).forEach(category => {
|
||||
versioned[version_sb][category] = [];
|
||||
|
||||
const ids = categories[category];
|
||||
ids.forEach((id, index) => {
|
||||
versioned[version_sb][category].push("version-" + version + "-" + id);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
fs.writeFileSync(
|
||||
CWD + "/versioned_sidebars/version-" + version + "-sidebars.json",
|
||||
JSON.stringify(versioned, null, 2),
|
||||
"utf8"
|
||||
);
|
||||
}
|
||||
|
||||
// update versions.json file
|
||||
versions.unshift(version);
|
||||
fs.writeFileSync(CWD + "/versions.json", JSON.stringify(versions, null, 2));
|
|
@ -20,7 +20,7 @@ const path = require("path");
|
|||
const siteConfig = require(CWD + "/siteConfig.js");
|
||||
const babylon = require("babylon");
|
||||
const traverse = require("babel-traverse").default;
|
||||
const sidebar = require(CWD + "/sidebar.json");
|
||||
const sidebars = require(CWD + "/sidebars.json");
|
||||
|
||||
function writeFileAndCreateFolder(file, content) {
|
||||
mkdirp.sync(file.replace(new RegExp("/[^/]*$"), ""));
|
||||
|
@ -67,13 +67,27 @@ function execute() {
|
|||
}
|
||||
|
||||
/* find sidebar category titles to translate */
|
||||
Object.keys(sidebar).forEach(sb => {
|
||||
const categories = sidebar[sb];
|
||||
Object.keys(sidebars).forEach(sb => {
|
||||
const categories = sidebars[sb];
|
||||
Object.keys(categories).forEach(category => {
|
||||
translations["localized-strings"][category] = category;
|
||||
});
|
||||
});
|
||||
|
||||
files = glob.sync(CWD + "/versioned_sidebars/*");
|
||||
files.forEach(file => {
|
||||
if (!file.endsWith("-sidebars.json")) {
|
||||
return;
|
||||
}
|
||||
sidebarContent = JSON.parse(fs.readFileSync(file, "utf8"));
|
||||
Object.keys(sidebarContent).forEach(sb => {
|
||||
const categories = sidebarContent[sb];
|
||||
Object.keys(categories).forEach(category => {
|
||||
translations["localized-strings"][category] = category;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/* go through pages to look for text inside translate tags */
|
||||
files = glob.sync(CWD + "/pages/en/**");
|
||||
files.forEach(file => {
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
"babylon": "^6.17.4",
|
||||
"classnames": "^2.2.5",
|
||||
"commander": "^2.11.0",
|
||||
"diff": "^3.3.0",
|
||||
"express": "^4.15.3",
|
||||
"fs-extra": "^3.0.1",
|
||||
"glob": "^7.1.2",
|
||||
|
@ -30,6 +31,7 @@
|
|||
"docusaurus-build": "./lib/build-files.js",
|
||||
"docusaurus-publish": "./lib/publish-gh-pages.js",
|
||||
"docusaurus-examples": "./lib/copy-examples.js",
|
||||
"docusaurus-write-translations": "./lib/write-translations.js"
|
||||
"docusaurus-write-translations": "./lib/write-translations.js",
|
||||
"docusaurus-version": "./lib/version.js"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -94,24 +94,24 @@ class Index extends React.Component {
|
|||
contents={[
|
||||
{
|
||||
content:
|
||||
"Write all of your documentation and blog posts in Markdown and have it built into a website you can publish",
|
||||
"Save time and focus on your project's documentation. Simply write docs and blog posts with Markdown and Docusaurus will publish a set of static html files ready to serve.",
|
||||
image: `${siteConfig.baseUrl}img/markdown.png`,
|
||||
imageAlign: "top",
|
||||
title: "Markdown Documentation"
|
||||
title: "Powered by Markdown"
|
||||
},
|
||||
{
|
||||
content:
|
||||
"Write the content of your main pages as React components that automatically share a header and footer",
|
||||
"Extend or customize your project's layout by reusing React. Docusaurus can be extended while reusing the same header and footer.",
|
||||
image: `${siteConfig.baseUrl}img/react.svg`,
|
||||
imageAlign: "top",
|
||||
title: "React Main Pages"
|
||||
title: "Built Using React"
|
||||
},
|
||||
{
|
||||
content:
|
||||
"Translate your docs and your website using Crowdin integration",
|
||||
"Localization comes pre-configured. Use Crowdin to translate your docs into over 70 languages.",
|
||||
image: `${siteConfig.baseUrl}img/translation.svg`,
|
||||
imageAlign: "top",
|
||||
title: "Translations"
|
||||
title: "Ready for Translations"
|
||||
}
|
||||
]}
|
||||
layout="threeColumn"
|
||||
|
@ -123,14 +123,14 @@ class Index extends React.Component {
|
|||
contents={[
|
||||
{
|
||||
content:
|
||||
"Support users of all versions by easily providing documentation for each version of your program",
|
||||
"Support users on all versions of your project. Document Versioning helps you keep documentation in sync with project releases.",
|
||||
image: `${siteConfig.baseUrl}img/docusaurus.svg`,
|
||||
imageAlign: "top",
|
||||
title: "Versioning"
|
||||
title: "Document Versioning"
|
||||
},
|
||||
{
|
||||
content:
|
||||
"Provide search for your documentation using Algolia DocSearch integration",
|
||||
"Make it easy for your community to find what they need in your documentation. Currently supports Algolia DocSearch.",
|
||||
image: `${siteConfig.baseUrl}img/docusaurus.svg`,
|
||||
imageAlign: "top",
|
||||
title: "Document Search"
|
||||
|
@ -144,7 +144,7 @@ class Index extends React.Component {
|
|||
contents={[
|
||||
{
|
||||
content:
|
||||
"The provided site template lets you get a website for your project up and running quickly without having having to worry about all the site design. Provided example files help you configure your site.",
|
||||
"Get up and running quickly without having having to worry about site design.",
|
||||
imageAlign: "right",
|
||||
image: `${siteConfig.baseUrl}img/docusaurus.svg`,
|
||||
title: "Quick Setup"
|
||||
|
@ -158,10 +158,10 @@ class Index extends React.Component {
|
|||
contents={[
|
||||
{
|
||||
content:
|
||||
"Use a local server to see how file changes affect your website without having to reload the server. Publish your site to GitHub pages manually using a script or with continuous integration like CircleCI.",
|
||||
"Make design and documentation changes by using the included live server. Publish your site to GitHub pages or other static file hosts manually, using a script, or with continuous integration like CircleCI.",
|
||||
imageAlign: "left",
|
||||
image: `${siteConfig.baseUrl}img/docusaurus.svg`,
|
||||
title: "Development and Deployment"
|
||||
image: `${siteConfig.baseUrl}img/docusaurus_live.webp`,
|
||||
title: "Develop and Deploy"
|
||||
}
|
||||
]}
|
||||
layout="twoColumn"
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
{
|
||||
"docs": {
|
||||
"Docusaurus": [
|
||||
"getting-started",
|
||||
"site-config",
|
||||
"doc-markdown",
|
||||
"translation",
|
||||
"search"
|
||||
]
|
||||
}
|
||||
}
|
18
website/sidebars.json
Normal file
18
website/sidebars.json
Normal file
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"docs": {
|
||||
"Getting Started": [
|
||||
"installation",
|
||||
"site-preparation",
|
||||
"site-creation"
|
||||
],
|
||||
"Guides": [
|
||||
"navigation",
|
||||
"translation",
|
||||
"search"
|
||||
],
|
||||
"API": [
|
||||
"site-config",
|
||||
"doc-markdown"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -29,7 +29,7 @@ const siteConfig = {
|
|||
headerLinksInternal: [
|
||||
{
|
||||
section: "docs",
|
||||
href: "/docusaurus/docs/getting-started.html",
|
||||
href: "/docusaurus/docs/installation.html",
|
||||
text: "Docs"
|
||||
},
|
||||
{ section: "help", href: "/docusaurus/LANGUAGE/help.html", text: "Help" }
|
||||
|
@ -44,6 +44,14 @@ const siteConfig = {
|
|||
headerIcon: "img/docusaurus.svg",
|
||||
footerIcon: "img/docusaurus.svg",
|
||||
favicon: "img/favicon.png",
|
||||
// See https://docusaurus.io/docs/search for more information about Aloglia
|
||||
// search
|
||||
/*
|
||||
algolia: {
|
||||
apiKey: "my-search-only-api-key-1234",
|
||||
indexName: "my-index-name"
|
||||
},
|
||||
*/
|
||||
colors: {
|
||||
primaryColor: "#2E8555",
|
||||
secondaryColor: "#205C3B",
|
||||
|
|
BIN
website/static/img/docusaurus_live.webp
Normal file
BIN
website/static/img/docusaurus_live.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 78 KiB |
Loading…
Add table
Add a link
Reference in a new issue