initial commit
|
|
@ -0,0 +1,131 @@
|
|||
# Docusaurus
|
||||
|
||||
## Getting Started
|
||||
|
||||
In your project repo, make sure you have a `docs` folder containing all of your documentation files written in markdown. Create a `website` folder inside which you will install and run docusaurus. If you wish, you can also include a `blog` folder in your project repo at the same level as the `docs` folder for blog posts.
|
||||
|
||||
Install Docusaurus using `npm`:
|
||||
|
||||
```
|
||||
cd website
|
||||
npm install --save-dev docusaurus
|
||||
```
|
||||
|
||||
In your `package.json` file, add the following scripts for docusaurus:
|
||||
|
||||
```json
|
||||
"scripts": {
|
||||
"docusaurus-start": "docusaurus-start",
|
||||
"docusaurus-build": "docusaurus-build",
|
||||
"docusaurus-publish": "docusaurus-publish",
|
||||
"docusaurus-examples": "docusaurus-examples"
|
||||
}
|
||||
```
|
||||
|
||||
To create example files for configuration, run `docusaurus-examples` using npm:
|
||||
|
||||
```
|
||||
npm run docusaurus-examples
|
||||
```
|
||||
|
||||
This will create the following files in your website folder:
|
||||
|
||||
```
|
||||
core/Footer.js
|
||||
example-docs/en/doc1.md
|
||||
example-docs/en/doc2.md
|
||||
example-docs/en/doc3.md
|
||||
example-docs/en/exampledoc4.md
|
||||
example-docs/en/exampledoc5.md
|
||||
example-blog/2016-03-11-blog-post.md
|
||||
example-blog/2017-04-10-blog-post-two.md
|
||||
i18n/en.js
|
||||
src/en/help.js
|
||||
src/en/index.js
|
||||
src/en/users.js
|
||||
languages.js
|
||||
siteConfig.js
|
||||
```
|
||||
|
||||
`docusaurus-examples` will not overwrite any existing files of the same name in your website folder.
|
||||
|
||||
## Configuration
|
||||
|
||||
The provided example files contain configurations for an example project `deltice/test-site` and the documents in `example-docs/`. These are provided for your reference to help you configure your project. Documents in `example-docs/` are provided for your reference for how to write markdown files, including necessary front matter information, and are not necessary while all other generated files are needed to build and publish your website.
|
||||
|
||||
Blog posts should be written as markdown files with the following front matter:
|
||||
```
|
||||
---
|
||||
title: Blog Post Title
|
||||
author: Author Name
|
||||
authorURL: http://twitter.com/author <!-- (or some other link) -->
|
||||
authorFBID: 21315325 <!-- id to get author's picture -->
|
||||
---
|
||||
```
|
||||
In the blog post you should include a line `<!--truncate-->`. This will determine under which point text will be ignored when generating the preview of your blog post. Blog posts should have the file name format: `yyyy-mm-dd-your-file-name.md`.
|
||||
|
||||
First, configure the siteConfig.js file which has comments guiding you through what needs to be done and how each configuration affects your website.
|
||||
|
||||
Keep languages.js as is with just English enabled. Enter English strings for your website in i18n/en.js manually. In future updates, these files will be used by docusaurus to support translations/localization.
|
||||
|
||||
Next, customize core/Footer.js which will serve as the footer for each page on your website.
|
||||
|
||||
Include your own top-level pages as React components in `src/en/`. Any `.js` files at `src/en/` will be copied to `src/` as well, so `your-site/index.html` will be the same as `your-site/en/index.html`. Three pages are provided for your reference and to use as templates if you so desire. They also contain examples of React components that are available for your use. Currently, if you want to add other React components to a file, you must include all of it inside that file due to how `require` paths are currently set-up. This may be changed in future updates.
|
||||
|
||||
All images and other files you wish to include should be placed inside the `src` folder. Currently, docusaurus will attempt to compile any `.js` files into React pages if it is not in `src/js/`.
|
||||
|
||||
## Using Docusaurus
|
||||
|
||||
### Run the Server
|
||||
|
||||
To run your website locally run the script:
|
||||
|
||||
```
|
||||
npm run docusaurus-start
|
||||
```
|
||||
|
||||
This will start a server hosting your website locally at `localhost:3000`. This server will ignore any occurences `siteConfig.baseUrl` in URLs, e.g. `localhost:3000/your-site/index.html` will be the same as `localhost:3000/index.html`. Any changes to configured files will be reflected by refreshing the page, i.e. the server does not need to be restarted to show changes.
|
||||
|
||||
|
||||
### Build Static Pages
|
||||
|
||||
To create a static build of your website, run the script:
|
||||
|
||||
```
|
||||
npm run docusaurus-build
|
||||
```
|
||||
|
||||
This will generate `.html` files from all of your docs and other pages included in `src`. This allows you to check whether or not all your files are being generated correctly. The build folder is inside Docusaurus's directory inside `node_modules`.
|
||||
|
||||
### Publishing Your Website
|
||||
|
||||
Use CircleCI to publish your website whenever your project repo is updated. Configure your circle.yml file in your project repo to run commands to publish to GitHub Pages. An example is shown here:
|
||||
|
||||
```yaml
|
||||
machine:
|
||||
node:
|
||||
version: 6.10.3
|
||||
npm:
|
||||
version: 3.10.10
|
||||
|
||||
test:
|
||||
override:
|
||||
- "true"
|
||||
|
||||
deployment:
|
||||
website:
|
||||
branch: master
|
||||
commands:
|
||||
- git config --global user.email "test-site-bot@users.noreply.github.com"
|
||||
- git config --global user.name "Website Deployment Script"
|
||||
- echo "machine github.com login test-site-bot password $GITHUB_TOKEN" > ~/.netrc
|
||||
- cd website && npm install && GIT_USER=test-site-bot npm run docusaurus-publish
|
||||
```
|
||||
|
||||
Note that in this case a GitHub user `test-site-bot` is created to use just for publishing. Make sure to give your Git user push permissions for your project and to set a GITHUB_TOKEN environment variable in Circle if you choose to publish this way.
|
||||
|
||||
If you wish to manually publish your website with the `docusaurus-publish` script, run the following example command with the appropriate variables for your project:
|
||||
|
||||
```
|
||||
DEPLOY_USER=deltice GIT_USER=test-site-bot CIRCLE_PROJECT_USERNAME=deltice CIRCLE_PROJECT_REPONAME=test-site npm run docusaurus-publish
|
||||
```
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
const React = require('react');
|
||||
|
||||
const githubButton = (
|
||||
<a
|
||||
className="github-button"
|
||||
href="https://github.com/deltice/test-site"
|
||||
data-icon="octicon-star"
|
||||
data-count-href="/deltice/test-site/stargazers"
|
||||
data-count-api="/repos/deltice/test-site#stargazers_count"
|
||||
data-count-aria-label="# stargazers on GitHub"
|
||||
aria-label="Star this project on GitHub">
|
||||
Star
|
||||
</a>
|
||||
);
|
||||
|
||||
class Footer extends React.Component {
|
||||
render() {
|
||||
const currentYear = new Date().getFullYear();
|
||||
return (
|
||||
<footer className="nav-footer" id="footer">
|
||||
<section className="sitemap">
|
||||
<a href={this.props.config.baseUrl} className="nav-home">
|
||||
<img
|
||||
src={this.props.config.baseUrl + this.props.config.footerIcon}
|
||||
alt={this.props.config.title}
|
||||
width="66"
|
||||
height="58"
|
||||
/>
|
||||
</a>
|
||||
<div>
|
||||
<h5>Docs</h5>
|
||||
<a
|
||||
href={
|
||||
this.props.config.baseUrl + 'docs/' + this.props.language + '/doc1.html'
|
||||
}
|
||||
>
|
||||
Getting Started (or other categories)
|
||||
</a>
|
||||
<a
|
||||
href={
|
||||
this.props.config.baseUrl + 'docs/' + this.props.language + '/doc2.html'
|
||||
}
|
||||
>
|
||||
Guides (or other categories)
|
||||
</a>
|
||||
<a
|
||||
href={
|
||||
this.props.config.baseUrl + 'docs/' + this.props.language + '/doc3.html'
|
||||
}
|
||||
>
|
||||
API Reference (or other categories)
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<h5>Community</h5>
|
||||
<a href={this.props.config.baseUrl + this.props.language + '/users.html'}>
|
||||
User Showcase
|
||||
</a>
|
||||
<a
|
||||
href="http://stackoverflow.com/questions/tagged/"
|
||||
target="_blank"
|
||||
>
|
||||
Stack Overflow
|
||||
</a>
|
||||
<a
|
||||
href="https://discordapp.com/"
|
||||
>
|
||||
Project Chat
|
||||
</a>
|
||||
<a href="https://twitter.com/" target="_blank">Twitter</a>
|
||||
</div>
|
||||
<div>
|
||||
<h5>More</h5>
|
||||
<a href={this.props.config.baseUrl + "blog"}>Blog</a>
|
||||
<a href="https://github.com/">GitHub</a>
|
||||
{githubButton}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<a
|
||||
href="https://code.facebook.com/projects/"
|
||||
target="_blank"
|
||||
className="fbOpenSource"
|
||||
>
|
||||
<img
|
||||
src={this.props.config.baseUrl + "img/oss_logo.png"}
|
||||
alt="Facebook Open Source"
|
||||
width="170"
|
||||
height="45"
|
||||
/>
|
||||
</a>
|
||||
<section className="copyright">
|
||||
Copyright © {currentYear} Facebook Inc.
|
||||
</section>
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
module.exports = Footer;
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
---
|
||||
title: Blog Title
|
||||
author: Blog Author
|
||||
authorURL: http://twitter.com/
|
||||
authorFBID: 0
|
||||
---
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus elementum massa eget nulla aliquet sagittis. Proin odio tortor, vulputate ut odio in, ultrices ultricies augue. Cras ornare ultrices lorem malesuada iaculis. Etiam sit amet libero tempor, pulvinar mauris sed, sollicitudin sapien.
|
||||
|
||||
<!--truncate-->
|
||||
|
||||
Mauris vestibulum ullamcorper nibh, ut semper purus pulvinar ut. Donec volutpat orci sit amet mauris malesuada, non pulvinar augue aliquam. Vestibulum ultricies at urna ut suscipit. Morbi iaculis, erat at imperdiet semper, ipsum nulla sodales erat, eget tincidunt justo dui quis justo. Pellentesque dictum bibendum diam at aliquet. Sed pulvinar, dolor quis finibus ornare, eros odio facilisis erat, eu rhoncus nunc dui sed ex. Nunc gravida dui massa, sed ornare arcu tincidunt sit amet. Maecenas efficitur sapien neque, a laoreet libero feugiat ut.
|
||||
|
||||
Nulla facilisi. Maecenas sodales nec purus eget posuere. Sed sapien quam, pretium a risus in, porttitor dapibus erat. Sed sit amet fringilla ipsum, eget iaculis augue. Integer sollicitudin tortor quis ultricies aliquam. Suspendisse fringilla nunc in tellus cursus, at placerat tellus scelerisque. Sed tempus elit a sollicitudin rhoncus. Nulla facilisi. Morbi nec dolor dolor. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Cras et aliquet lectus. Pellentesque sit amet eros nisi. Quisque ac sapien in sapien congue accumsan. Nullam in posuere ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Proin lacinia leo a nibh fringilla pharetra.
|
||||
|
||||
Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Proin venenatis lectus dui, vel ultrices ante bibendum hendrerit. Aenean egestas feugiat dui id hendrerit. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Curabitur in tellus laoreet, eleifend nunc id, viverra leo. Proin vulputate non dolor vel vulputate. Curabitur pretium lobortis felis, sit amet finibus lorem suscipit ut. Sed non mollis risus. Duis sagittis, mi in euismod tincidunt, nunc mauris vestibulum urna, at euismod est elit quis erat. Phasellus accumsan vitae neque eu placerat. In elementum arcu nec tellus imperdiet, eget maximus nulla sodales. Curabitur eu sapien eget nisl sodales fermentum.
|
||||
|
||||
Phasellus pulvinar ex id commodo imperdiet. Praesent odio nibh, sollicitudin sit amet faucibus id, placerat at metus. Donec vitae eros vitae tortor hendrerit finibus. Interdum et malesuada fames ac ante ipsum primis in faucibus. Quisque vitae purus dolor. Duis suscipit ac nulla et finibus. Phasellus ac sem sed dui dictum gravida. Phasellus eleifend vestibulum facilisis. Integer pharetra nec enim vitae mattis. Duis auctor, lectus quis condimentum bibendum, nunc dolor aliquam massa, id bibendum orci velit quis magna. Ut volutpat nulla nunc, sed interdum magna condimentum non. Sed urna metus, scelerisque vitae consectetur a, feugiat quis magna. Donec dignissim ornare nisl, eget tempor risus malesuada quis.
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
---
|
||||
title: New Blog Post
|
||||
author: Blog Author
|
||||
authorURL: http://twitter.com/
|
||||
authorFBID: 0
|
||||
---
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus elementum massa eget nulla aliquet sagittis. Proin odio tortor, vulputate ut odio in, ultrices ultricies augue. Cras ornare ultrices lorem malesuada iaculis. Etiam sit amet libero tempor, pulvinar mauris sed, sollicitudin sapien.
|
||||
|
||||
<!--truncate-->
|
||||
|
||||
Mauris vestibulum ullamcorper nibh, ut semper purus pulvinar ut. Donec volutpat orci sit amet mauris malesuada, non pulvinar augue aliquam. Vestibulum ultricies at urna ut suscipit. Morbi iaculis, erat at imperdiet semper, ipsum nulla sodales erat, eget tincidunt justo dui quis justo. Pellentesque dictum bibendum diam at aliquet. Sed pulvinar, dolor quis finibus ornare, eros odio facilisis erat, eu rhoncus nunc dui sed ex. Nunc gravida dui massa, sed ornare arcu tincidunt sit amet. Maecenas efficitur sapien neque, a laoreet libero feugiat ut.
|
||||
|
||||
Nulla facilisi. Maecenas sodales nec purus eget posuere. Sed sapien quam, pretium a risus in, porttitor dapibus erat. Sed sit amet fringilla ipsum, eget iaculis augue. Integer sollicitudin tortor quis ultricies aliquam. Suspendisse fringilla nunc in tellus cursus, at placerat tellus scelerisque. Sed tempus elit a sollicitudin rhoncus. Nulla facilisi. Morbi nec dolor dolor. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Cras et aliquet lectus. Pellentesque sit amet eros nisi. Quisque ac sapien in sapien congue accumsan. Nullam in posuere ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Proin lacinia leo a nibh fringilla pharetra.
|
||||
|
||||
Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Proin venenatis lectus dui, vel ultrices ante bibendum hendrerit. Aenean egestas feugiat dui id hendrerit. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Curabitur in tellus laoreet, eleifend nunc id, viverra leo. Proin vulputate non dolor vel vulputate. Curabitur pretium lobortis felis, sit amet finibus lorem suscipit ut. Sed non mollis risus. Duis sagittis, mi in euismod tincidunt, nunc mauris vestibulum urna, at euismod est elit quis erat. Phasellus accumsan vitae neque eu placerat. In elementum arcu nec tellus imperdiet, eget maximus nulla sodales. Curabitur eu sapien eget nisl sodales fermentum.
|
||||
|
||||
Phasellus pulvinar ex id commodo imperdiet. Praesent odio nibh, sollicitudin sit amet faucibus id, placerat at metus. Donec vitae eros vitae tortor hendrerit finibus. Interdum et malesuada fames ac ante ipsum primis in faucibus. Quisque vitae purus dolor. Duis suscipit ac nulla et finibus. Phasellus ac sem sed dui dictum gravida. Phasellus eleifend vestibulum facilisis. Integer pharetra nec enim vitae mattis. Duis auctor, lectus quis condimentum bibendum, nunc dolor aliquam massa, id bibendum orci velit quis magna. Ut volutpat nulla nunc, sed interdum magna condimentum non. Sed urna metus, scelerisque vitae consectetur a, feugiat quis magna. Donec dignissim ornare nisl, eget tempor risus malesuada quis.
|
||||
|
|
@ -0,0 +1,136 @@
|
|||
---
|
||||
id: doc1
|
||||
title: Docusaurus
|
||||
layout: docs
|
||||
category: Docusaurus
|
||||
permalink: docs/en/doc1.html
|
||||
next: doc2
|
||||
---
|
||||
|
||||
## Getting Started
|
||||
|
||||
In your project repo, make sure you have a `docs` folder containing all of your documentation files written in markdown. Create a `website` folder inside which you will install and run docusaurus. If you wish, you can also include a `blog` folder in your project repo at the same level as the `docs` folder for blog posts.
|
||||
|
||||
Install Docusaurus using `npm`:
|
||||
|
||||
```
|
||||
cd website
|
||||
npm install --save-dev docusaurus
|
||||
```
|
||||
|
||||
In your `package.json` file, add the following scripts for docusaurus:
|
||||
|
||||
```json
|
||||
"scripts": {
|
||||
"docusaurus-start": "docusaurus-start",
|
||||
"docusaurus-build": "docusaurus-build",
|
||||
"docusaurus-publish": "docusaurus-publish",
|
||||
"docusaurus-examples": "docusaurus-examples"
|
||||
}
|
||||
```
|
||||
|
||||
To create example files for configuration, run `docusaurus-examples` using npm:
|
||||
|
||||
```
|
||||
npm run docusaurus-examples
|
||||
```
|
||||
|
||||
This will create the following files in your website folder:
|
||||
|
||||
```
|
||||
core/Footer.js
|
||||
example-docs/en/doc1.md
|
||||
example-docs/en/doc2.md
|
||||
example-docs/en/doc3.md
|
||||
example-docs/en/exampledoc4.md
|
||||
example-docs/en/exampledoc5.md
|
||||
i18n/en.js
|
||||
src/en/help.js
|
||||
src/en/index.js
|
||||
src/en/users.js
|
||||
languages.js
|
||||
siteConfig.js
|
||||
```
|
||||
|
||||
`docusaurus-examples` will not overwrite any existing files of the same name in your website folder.
|
||||
|
||||
## Configuration
|
||||
|
||||
The provided example files contain configurations for an example project `deltice/test-site` and the documents in `example-docs/`. These are provided for your reference to help you configure your project. Documents in `example-docs/` are provided for your reference for how to write markdown files, including necessary front matter information, and are not necessary while all other generated files are needed to build and publish your website.
|
||||
|
||||
Blog posts should be written as markdown files with the following front matter:
|
||||
```
|
||||
---
|
||||
title: Blog Post Title
|
||||
author: Author Name
|
||||
authorURL: http://twitter.com/author <!-- (or some other link) -->
|
||||
authorFBID: 21315325 <!-- id to get author's picture -->
|
||||
---
|
||||
```
|
||||
In the blog post you should include a line `<!--truncate-->`. This will determine under which point text will be ignored when generating the preview of your blog post. Blog posts should have the file name format: `yyyy-mm-dd-your-file-name.md`.
|
||||
|
||||
First, configure the siteConfig.js file which has comments guiding you through what needs to be done and how each configuration affects your website.
|
||||
|
||||
Keep languages.js as is with just English enabled. Enter English strings for your website in i18n/en.js manually. In future updates, these files will be used by docusaurus to support translations/localization.
|
||||
|
||||
Next, customize core/Footer.js which will serve as the footer for each page on your website.
|
||||
|
||||
Include your own top-level pages as React components in `src/en/`. Any `.js` files at `src/en/` will be copied to `src/` as well, so `your-site/index.html` will be the same as `your-site/en/index.html`. Three pages are provided for your reference and to use as templates if you so desire. They also contain examples of React components that are available for your use. Currently, if you want to add other React components to a file, you must include all of it inside that file due to how `require` paths are currently set-up. This may be changed in future updates.
|
||||
|
||||
All images and other files you wish to include should be placed inside the `src` folder. Currently, docusaurus will attempt to compile any `.js` files into React pages if it is not in `src/js/`.
|
||||
|
||||
## Using Docusaurus
|
||||
|
||||
### Run the Server
|
||||
|
||||
To run your website locally run the script:
|
||||
|
||||
```
|
||||
npm run docusaurus-start
|
||||
```
|
||||
|
||||
This will start a server hosting your website locally at `localhost:3000`. This server will ignore any occurences `siteConfig.baseUrl` in URLs, e.g. `localhost:3000/your-site/index.html` will be the same as `localhost:3000/index.html`. Any changes to configured files will be reflected by refreshing the page, i.e. the server does not need to be restarted to show changes.
|
||||
|
||||
|
||||
### Build Static Pages
|
||||
|
||||
To create a static build of your website, run the script:
|
||||
|
||||
```
|
||||
npm run docusaurus-build
|
||||
```
|
||||
|
||||
This will generate `.html` files from all of your docs and other pages included in `src`. This allows you to check whether or not all your files are being generated correctly. The build folder is inside Docusaurus's directory inside `node_modules`.
|
||||
|
||||
### Publishing Your Website
|
||||
|
||||
Use CircleCI to publish your website whenever your project repo is updated. Configure your circle.yml file in your project repo to run commands to publish to GitHub Pages. An example is shown here:
|
||||
|
||||
```yaml
|
||||
machine:
|
||||
node:
|
||||
version: 6.10.3
|
||||
npm:
|
||||
version: 3.10.10
|
||||
|
||||
test:
|
||||
override:
|
||||
- "true"
|
||||
|
||||
deployment:
|
||||
website:
|
||||
branch: master
|
||||
commands:
|
||||
- git config --global user.email "test-site-bot@users.noreply.github.com"
|
||||
- git config --global user.name "Website Deployment Script"
|
||||
- echo "machine github.com login test-site-bot password $GITHUB_TOKEN" > ~/.netrc
|
||||
- cd website && GIT_USER=test-site-bot npm run docusaurus-publish
|
||||
```
|
||||
|
||||
Note that in this case a GitHub user `test-site-bot` is created to use just for publishing. Make sure to give your Git user push permissions for your project and to set a GITHUB_TOKEN environment variable in Circle if you choose to publish this way.
|
||||
|
||||
If you wish to manually publish your website with the `docusaurus-publish` script, run the following example command with the appropriate variables for your project:
|
||||
|
||||
```
|
||||
DEPLOY_USER=deltice GIT_USER=test-site-bot CIRCLE_PROJECT_USERNAME=deltice CIRCLE_PROJECT_REPONAME=test-site npm run docusaurus-publish
|
||||
```
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
id: doc2
|
||||
title: document number 2
|
||||
layout: docs
|
||||
category: First Category
|
||||
permalink: docs/en/doc2.html
|
||||
previous: doc1
|
||||
next: doc3
|
||||
---
|
||||
|
||||
This is a link to [another document.](/docs/en/otherdoc.md)
|
||||
This is a link to an [external page.](http://www.example.com)
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
---
|
||||
id: doc3
|
||||
title: This is document number 3
|
||||
layout: docs
|
||||
category: Second Category
|
||||
permalink: docs/en/doc3.html
|
||||
previous: doc2
|
||||
---
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. In ac euismod odio, eu consequat dui. Nullam molestie consectetur risus id imperdiet. Proin sodales ornare turpis, non mollis massa ultricies id. Nam at nibh scelerisque, feugiat ante non, dapibus tortor. Vivamus volutpat diam quis tellus elementum bibendum. Praesent semper gravida velit quis aliquam. Etiam in cursus neque. Nam lectus ligula, malesuada et mauris a, bibendum faucibus mi. Phasellus ut interdum felis. Phasellus in odio pulvinar, porttitor urna eget, fringilla lectus. Aliquam sollicitudin est eros. Mauris consectetur quam vitae mauris interdum hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
|
||||
Duis et egestas libero, imperdiet faucibus ipsum. Sed posuere eget urna vel feugiat. Vivamus a arcu sagittis, fermentum urna dapibus, congue lectus. Fusce vulputate porttitor nisl, ac cursus elit volutpat vitae. Nullam vitae ipsum egestas, convallis quam non, porta nibh. Morbi gravida erat nec neque bibendum, eu pellentesque velit posuere. Fusce aliquam erat eu massa eleifend tristique.
|
||||
|
||||
Sed consequat sollicitudin ipsum eget tempus. Integer a aliquet velit. In justo nibh, pellentesque non suscipit eget, gravida vel lacus. Donec odio ante, malesuada in massa quis, pharetra tristique ligula. Donec eros est, tristique eget finibus quis, semper non nisl. Vivamus et elit nec enim ornare placerat. Sed posuere odio a elit cursus sagittis.
|
||||
|
||||
Phasellus feugiat purus eu tortor ultrices finibus. Ut libero nibh, lobortis et libero nec, dapibus posuere eros. Sed sagittis euismod justo at consectetur. Nulla finibus libero placerat, cursus sapien at, eleifend ligula. Vivamus elit nisl, hendrerit ac nibh eu, ultrices tempus dui. Nam tellus neque, commodo non rhoncus eu, gravida in risus. Nullam id iaculis tortor.
|
||||
|
||||
Nullam at odio in sem varius tempor sit amet vel lorem. Etiam eu hendrerit nisl. Fusce nibh mauris, vulputate sit amet ex vitae, congue rhoncus nisl. Sed eget tellus purus. Nullam tempus commodo erat ut tristique. Cras accumsan massa sit amet justo consequat eleifend. Integer scelerisque vitae tellus id consectetur.
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
---
|
||||
id: doc4
|
||||
title: Other Document
|
||||
layout: docs-other
|
||||
category: First Category
|
||||
permalink: docs/en/doc4.html
|
||||
next: doc5
|
||||
---
|
||||
|
||||
this is another document
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
---
|
||||
id: doc5
|
||||
title: Fifth Document
|
||||
layout: docs-other
|
||||
category: First Category
|
||||
permalink: docs/en/doc5.html
|
||||
previous: doc4
|
||||
---
|
||||
|
||||
Another one
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
module.exports = {
|
||||
baseUrl: '/test-site/', /* base url for your repo */
|
||||
/* strings for belowFold section of index.js */
|
||||
belowFold: {
|
||||
learn: {
|
||||
content: 'Talk about learning how to use this',
|
||||
title: 'Learn How',
|
||||
},
|
||||
try: {
|
||||
content: 'Talk about trying this out',
|
||||
title: 'Try It Out',
|
||||
},
|
||||
description: {
|
||||
content: 'This is another description of how this project is useful',
|
||||
title: 'Description',
|
||||
},
|
||||
using: {
|
||||
button: 'More Docusaurus Users',
|
||||
content: 'This project is used by all these people',
|
||||
title: "Who's using this?",
|
||||
},
|
||||
},
|
||||
/* strings for featureCallout section of index.js */
|
||||
featureCallout: {
|
||||
content: 'This is my feature callout',
|
||||
title: 'Feature callout title',
|
||||
},
|
||||
/* strings for features section of index.js */
|
||||
features: [
|
||||
{
|
||||
content: 'This is the content of my feature',
|
||||
image: '/test-site/img/docusaurus.svg',
|
||||
imageAlign: 'top',
|
||||
title: 'Feature One',
|
||||
},
|
||||
{
|
||||
content: 'The content of my second feature',
|
||||
image: '/test-site/img/docusaurus.svg',
|
||||
imageAlign: 'top',
|
||||
title: 'Feature Two',
|
||||
},
|
||||
],
|
||||
/*
|
||||
-strings found in markdown front matter:
|
||||
id, previous, next, category
|
||||
-'text' strings in headerNav
|
||||
*/
|
||||
'localized-strings': {
|
||||
doc1: 'Docusaurus',
|
||||
doc2: 'The Second in a Series of Documents',
|
||||
doc3: 'The Third in a Series of Documents',
|
||||
doc4: 'Separate Sidebar Document 1',
|
||||
doc5: 'Separate Sidebar Document 2',
|
||||
'Docusaurus': 'Docusaurus Guide',
|
||||
'First Category': 'Example Category 1',
|
||||
'Second Category': 'Example Category 2',
|
||||
previous: 'Previous',
|
||||
next: 'Continue Reading',
|
||||
Docs: 'Docs',
|
||||
API: 'API',
|
||||
GitHub: 'GitHub',
|
||||
Help: 'Help',
|
||||
Blog: 'Blog'
|
||||
},
|
||||
/* strings for promo section of index.js */
|
||||
promo: {
|
||||
doc1: 'Example Link',
|
||||
doc2: 'Example Link 2',
|
||||
try: 'Try it out',
|
||||
},
|
||||
/* strings for help.js page */
|
||||
support: {
|
||||
browse: {
|
||||
content: 'Learn more using the [documentation on this site.](/test-site/docs/en/doc1.html)\n',
|
||||
title: 'Browse Docs',
|
||||
},
|
||||
header: {
|
||||
content: 'This project is maintained by a dedicated group of people\n',
|
||||
title: 'Need help?',
|
||||
},
|
||||
join: {
|
||||
content: 'Ask questions about the documentation and project\n',
|
||||
title: 'Join the community',
|
||||
},
|
||||
},
|
||||
tagline: 'My Tagline', /* tagline of site */
|
||||
url: 'https://deltice.github.io',
|
||||
/* strings for users.js page */
|
||||
using: {
|
||||
header: {
|
||||
content: 'This project is used by many folks',
|
||||
title: "Who's using this?",
|
||||
},
|
||||
prompt: 'Are you using this project?',
|
||||
prompt_cta: 'Add your company',
|
||||
},
|
||||
};
|
||||
|
|
@ -0,0 +1,178 @@
|
|||
/**
|
||||
* Copyright 2004-present Facebook. All Rights Reserved.
|
||||
*
|
||||
*/
|
||||
const languages = [
|
||||
{
|
||||
enabled: false,
|
||||
name: '日本語',
|
||||
tag: 'ja',
|
||||
},
|
||||
{
|
||||
enabled: true,
|
||||
name: 'English',
|
||||
tag: 'en',
|
||||
},
|
||||
{
|
||||
enabled: false,
|
||||
name: 'العربية',
|
||||
tag: 'ar',
|
||||
},
|
||||
{
|
||||
enabled: false,
|
||||
name: 'Bosanski',
|
||||
tag: 'bs-BA',
|
||||
},
|
||||
{
|
||||
enabled: false,
|
||||
name: 'Català',
|
||||
tag: 'ca',
|
||||
},
|
||||
{
|
||||
enabled: false,
|
||||
name: 'Čeština',
|
||||
tag: 'cs',
|
||||
},
|
||||
{
|
||||
enabled: false,
|
||||
name: 'Dansk',
|
||||
tag: 'da',
|
||||
},
|
||||
{
|
||||
enabled: false,
|
||||
name: 'Deutsch',
|
||||
tag: 'de',
|
||||
},
|
||||
{
|
||||
enabled: false,
|
||||
name: 'Ελληνικά',
|
||||
tag: 'el',
|
||||
},
|
||||
{
|
||||
enabled: false,
|
||||
name: 'Español',
|
||||
tag: 'es-ES',
|
||||
},
|
||||
{
|
||||
enabled: false,
|
||||
name: 'فارسی',
|
||||
tag: 'fa-IR',
|
||||
},
|
||||
{
|
||||
enabled: false,
|
||||
name: 'Suomi',
|
||||
tag: 'fi',
|
||||
},
|
||||
{
|
||||
enabled: false,
|
||||
name: 'Français',
|
||||
tag: 'fr',
|
||||
},
|
||||
{
|
||||
enabled: false,
|
||||
name: 'עִברִית',
|
||||
tag: 'he',
|
||||
},
|
||||
{
|
||||
enabled: false,
|
||||
name: 'Magyar',
|
||||
tag: 'hu',
|
||||
},
|
||||
{
|
||||
enabled: false,
|
||||
name: 'Bahasa Indonesia',
|
||||
tag: 'id-ID',
|
||||
},
|
||||
{
|
||||
enabled: false,
|
||||
name: 'Italiano',
|
||||
tag: 'it',
|
||||
},
|
||||
{
|
||||
enabled: false,
|
||||
name: 'Afrikaans',
|
||||
tag: 'af',
|
||||
},
|
||||
{
|
||||
enabled: false,
|
||||
name: '한국어',
|
||||
tag: 'ko',
|
||||
},
|
||||
{
|
||||
enabled: false,
|
||||
name: 'मराठी',
|
||||
tag: 'mr-IN',
|
||||
},
|
||||
{
|
||||
enabled: false,
|
||||
name: 'Nederlands',
|
||||
tag: 'nl',
|
||||
},
|
||||
{
|
||||
enabled: false,
|
||||
name: 'Norsk',
|
||||
tag: 'no-NO',
|
||||
},
|
||||
{
|
||||
enabled: false,
|
||||
name: 'Polskie',
|
||||
tag: 'pl',
|
||||
},
|
||||
{
|
||||
enabled: false,
|
||||
name: 'Português',
|
||||
tag: 'pt-PT',
|
||||
},
|
||||
{
|
||||
enabled: false,
|
||||
name: 'Português (Brasil)',
|
||||
tag: 'pt-BR',
|
||||
},
|
||||
{
|
||||
enabled: false,
|
||||
name: 'Română',
|
||||
tag: 'ro',
|
||||
},
|
||||
{
|
||||
enabled: false,
|
||||
name: 'Русский',
|
||||
tag: 'ru',
|
||||
},
|
||||
{
|
||||
enabled: false,
|
||||
name: 'Slovenský',
|
||||
tag: 'sk-SK',
|
||||
},
|
||||
{
|
||||
enabled: false,
|
||||
name: 'Српски језик (Ћирилица)',
|
||||
tag: 'sr',
|
||||
},
|
||||
{
|
||||
enabled: false,
|
||||
name: 'Svenska',
|
||||
tag: 'sv-SE',
|
||||
},
|
||||
{
|
||||
enabled: false,
|
||||
name: 'Türkçe',
|
||||
tag: 'tr',
|
||||
},
|
||||
{
|
||||
enabled: false,
|
||||
name: 'Українська',
|
||||
tag: 'uk',
|
||||
},
|
||||
{
|
||||
enabled: false,
|
||||
name: 'Tiếng Việt',
|
||||
tag: 'vi',
|
||||
},
|
||||
{
|
||||
enabled: false,
|
||||
name: '中文',
|
||||
tag: 'zh-Hans',
|
||||
},
|
||||
{enabled: false, name: '繁體中文', tag: 'zh-Hant'},
|
||||
];
|
||||
module.exports = languages;
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
/* List of projects/orgs using your project for the users page */
|
||||
const users = [
|
||||
{
|
||||
caption: 'User1',
|
||||
image: '/test-site/img/docusaurus.svg',
|
||||
infoLink: 'https://www.example.com',
|
||||
pinned: true,
|
||||
},
|
||||
]
|
||||
|
||||
const siteConfig = {
|
||||
title: 'Test Site', /* title for your website */
|
||||
url: 'https://deltice.github.io', /* your github url */
|
||||
baseUrl: '/test-site/', /* base url for your project */
|
||||
repo: 'deltice/test-site', /* repo for your project */
|
||||
users,
|
||||
/* base url for editing docs, usage example: editUrl + 'en/doc1.md' */
|
||||
editUrl: 'https://github.com/deltice/test-site/edit/master/docs/',
|
||||
/* header links for links on this site, 'LANGUAGE' will be replaced by whatever
|
||||
language the page is for, ex: 'en' */
|
||||
headerLinksInternal: [
|
||||
{
|
||||
section: 'docs',
|
||||
href: '/test-site/docs/LANGUAGE/doc1.html',
|
||||
text: 'Docs',
|
||||
},
|
||||
{section: 'api', href: '/test-site/docs/LANGUAGE/doc4.html', text: 'API'},
|
||||
{section: 'help', href: '/test-site/LANGUAGE/help.html', text: 'Help'},
|
||||
{section: 'blog', href: '/test-site/blog', text: 'Blog'}
|
||||
],
|
||||
/* header links for links outside the site */
|
||||
headerLinksExternal: [
|
||||
{
|
||||
section: 'github',
|
||||
href: 'https://github.com/deltice/test-site',
|
||||
text: 'GitHub',
|
||||
},
|
||||
],
|
||||
/* path to images for header/footer */
|
||||
headerIcon: 'img/docusaurus.svg',
|
||||
footerIcon: 'img/docusaurus.svg',
|
||||
/* default link for docsSidebar */
|
||||
docsSidebarDefaults: {
|
||||
layout: 'docs',
|
||||
root: '/test-site/docs/en/doc1.html',
|
||||
title: 'Docs',
|
||||
},
|
||||
/* colors for website */
|
||||
colors: {
|
||||
primaryColor: '#2E8555',
|
||||
secondaryColor: '#205C3B',
|
||||
prismColor: 'rgba(46, 133, 85, 0.03)', /* primaryColor in rgba form, with 0.03 alpha */
|
||||
},
|
||||
};
|
||||
|
||||
const languages = require('./languages.js');
|
||||
|
||||
const enabledLanguages = [];
|
||||
languages.filter(lang => lang.enabled).map(lang => {
|
||||
enabledLanguages.push(lang);
|
||||
});
|
||||
|
||||
siteConfig['languages'] = enabledLanguages;
|
||||
|
||||
siteConfig['en'] = require('./i18n/en.js');
|
||||
|
||||
/* INJECT LOCALIZED FILES BEGIN */
|
||||
/* INJECT LOCALIZED FILES END */
|
||||
|
||||
module.exports = siteConfig;
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
const React = require('react');
|
||||
|
||||
const CompLibrary = require('../../core/CompLibrary.js');
|
||||
const Container = CompLibrary.Container;
|
||||
const GridBlock = CompLibrary.GridBlock;
|
||||
|
||||
const siteConfig = require(process.cwd() + '/siteConfig.js');
|
||||
|
||||
class Help extends React.Component {
|
||||
render() {
|
||||
const supportLinks = [
|
||||
siteConfig[this.props.language].support.browse,
|
||||
siteConfig[this.props.language].support.join,
|
||||
];
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="docMainWrapper wrapper">
|
||||
<Container className="mainContainer documentContainer postContainer">
|
||||
<div className="post">
|
||||
<header className="postHeader">
|
||||
<h2>{siteConfig[this.props.language].support.header.title}</h2>
|
||||
</header>
|
||||
<p>
|
||||
{siteConfig[this.props.language].support.header.content}
|
||||
</p>
|
||||
<GridBlock contents={supportLinks} layout="threeColumn" />
|
||||
</div>
|
||||
</Container>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Help.defaultProps = {
|
||||
language: 'en',
|
||||
};
|
||||
|
||||
module.exports = Help;
|
||||
|
|
@ -0,0 +1,178 @@
|
|||
const React = require('react');
|
||||
|
||||
const CompLibrary = require('../../core/CompLibrary.js');
|
||||
const Marked = CompLibrary.Marked; /* Used to read markdown */
|
||||
const Container = CompLibrary.Container;
|
||||
const GridBlock = CompLibrary.GridBlock;
|
||||
|
||||
const siteConfig = require(process.cwd() + '/siteConfig.js');
|
||||
|
||||
class Button extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<div className="pluginWrapper buttonWrapper">
|
||||
<a
|
||||
className="button"
|
||||
href={this.props.href}
|
||||
target={this.props.target}
|
||||
>
|
||||
{this.props.children}
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Button.defaultProps = {
|
||||
target: '_self',
|
||||
};
|
||||
|
||||
class HomeSplash extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<div className="homeContainer">
|
||||
<div className="homeSplashFade">
|
||||
<div className="wrapper homeWrapper">
|
||||
<div className="projectLogo">
|
||||
<img src="/test-site/img/docusaurus.svg" alt="Jest" />
|
||||
</div>
|
||||
<div className="inner">
|
||||
<h2 className="projectTitle">
|
||||
{siteConfig.title}
|
||||
<small>{siteConfig[this.props.language].tagline}</small>
|
||||
</h2>
|
||||
<div className="section promoSection">
|
||||
<div className="promoRow">
|
||||
<div className="pluginRowBlock">
|
||||
<Button href="#try">
|
||||
{siteConfig[this.props.language].promo.try}
|
||||
</Button>
|
||||
<Button
|
||||
href={
|
||||
'/test-site/docs/' +
|
||||
this.props.language +
|
||||
'/doc1.html'
|
||||
}
|
||||
>
|
||||
{siteConfig[this.props.language].promo.doc1}
|
||||
</Button>
|
||||
<Button
|
||||
href={
|
||||
'/test-site/docs/' +
|
||||
this.props.language +
|
||||
'/doc2.html'
|
||||
}
|
||||
>
|
||||
{siteConfig[this.props.language].promo.doc2}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class Index extends React.Component {
|
||||
render() {
|
||||
let language = this.props.language;
|
||||
if(typeof language == 'undefined') {
|
||||
language = 'en';
|
||||
}
|
||||
const showcase = siteConfig.users
|
||||
.filter(user => {
|
||||
return user.pinned;
|
||||
})
|
||||
.map(user => {
|
||||
return(
|
||||
<a href={user.infoLink}>
|
||||
<img src={user.image} title={user.caption} />
|
||||
</a>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<HomeSplash
|
||||
language={language}
|
||||
/>
|
||||
<div className="mainContainer">
|
||||
<Container padding={['bottom', 'top']}>
|
||||
<GridBlock
|
||||
align="center"
|
||||
contents={siteConfig[language].features}
|
||||
layout="fourColumn"
|
||||
/>
|
||||
</Container>
|
||||
|
||||
<div
|
||||
className="productShowcaseSection paddingBottom"
|
||||
style={{textAlign: 'center'}}>
|
||||
<h2>{siteConfig[language].featureCallout.title}</h2>
|
||||
<Marked>
|
||||
{siteConfig[language].featureCallout.content}
|
||||
</Marked>
|
||||
</div>
|
||||
|
||||
<Container padding={['bottom', 'top']} background="light">
|
||||
<GridBlock
|
||||
contents={[
|
||||
{
|
||||
content: siteConfig[language].belowFold.learn.content,
|
||||
image: '/test-site/img/docusaurus.svg',
|
||||
imageAlign: 'right',
|
||||
title: siteConfig[language].belowFold.learn.title,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Container>
|
||||
|
||||
<Container padding={['bottom', 'top']} id="try">
|
||||
<GridBlock
|
||||
contents={[
|
||||
{
|
||||
content: siteConfig[language].belowFold.try.content,
|
||||
image: '/test-site/img/docusaurus.svg',
|
||||
imageAlign: 'left',
|
||||
title: siteConfig[language].belowFold.try.title,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Container>
|
||||
|
||||
<Container padding={['bottom', 'top']} background="dark">
|
||||
<GridBlock
|
||||
contents={[
|
||||
{
|
||||
content: siteConfig[language].belowFold.description.content,
|
||||
image: '/test-site/img/docusaurus.svg',
|
||||
imageAlign: 'right',
|
||||
title: siteConfig[language].belowFold.description.title,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Container>
|
||||
|
||||
|
||||
<div className="productShowcaseSection paddingBottom">
|
||||
<h2>{siteConfig[language].belowFold.using.title}</h2>
|
||||
<p>{siteConfig[language].belowFold.using.content}</p>
|
||||
<div className="logos">
|
||||
{showcase}
|
||||
</div>
|
||||
<div className="more-users">
|
||||
<a className="button" href={siteConfig.baseUrl + "users.html"} target="_self">
|
||||
{siteConfig[language].belowFold.using.button}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Index;
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
const React = require('react');
|
||||
|
||||
const CompLibrary = require('../../core/CompLibrary.js');
|
||||
const Container = CompLibrary.Container;
|
||||
|
||||
const siteConfig = require(process.cwd() + '/siteConfig.js');
|
||||
|
||||
class Users extends React.Component {
|
||||
render() {
|
||||
const showcase = siteConfig.users.map(user => {
|
||||
return (
|
||||
<a href={user.infoLink}>
|
||||
<img src={user.image} title={user.caption} />
|
||||
</a>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="mainContainer">
|
||||
<Container padding={['bottom', 'top']}>
|
||||
<div className="showcaseSection">
|
||||
<div className="prose">
|
||||
<h1>{siteConfig[this.props.language].using.header.title}</h1>
|
||||
<p>{siteConfig[this.props.language].using.header.content}</p>
|
||||
</div>
|
||||
<div className="logos">
|
||||
{showcase}
|
||||
</div>
|
||||
<p>{siteConfig[this.props.language].using.prompt}</p>
|
||||
<a
|
||||
href="https://github.com/deltice/test-site/edit/master/website/siteConfig.js"
|
||||
className="button"
|
||||
>
|
||||
{siteConfig[this.props.language].using.prompt_cta}
|
||||
</a>
|
||||
</div>
|
||||
</Container>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Users.defaultProps = {
|
||||
language: 'en',
|
||||
};
|
||||
|
||||
module.exports = Users;
|
||||
|
After Width: | Height: | Size: 76 KiB |
|
After Width: | Height: | Size: 984 B |
|
After Width: | Height: | Size: 9.4 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
|
@ -0,0 +1,9 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
require('babel-register') ({
|
||||
ignore: false,
|
||||
"presets": ["react"]
|
||||
});
|
||||
|
||||
const server = require('./server/generate.js');
|
||||
server();
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
const CWD = process.cwd();
|
||||
const fs = require('fs-extra');
|
||||
|
||||
fs.copySync(__dirname + '/../examples/', CWD, {overwrite: false});
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
const BlogPost = require('./BlogPost.js');
|
||||
const BlogSidebar = require('./BlogSidebar.js');
|
||||
const Container = require('./Container.js');
|
||||
const MetadataBlog = require('./MetadataBlog.js');
|
||||
const React = require('react');
|
||||
const Site = require('./Site.js');
|
||||
|
||||
const BlogPageLayout = React.createClass({
|
||||
getPageURL(page) {
|
||||
let url = this.props.config.baseUrl + 'blog/';
|
||||
if (page > 0) {
|
||||
url += 'page' + (page + 1) + '/';
|
||||
}
|
||||
return url + '#content';
|
||||
},
|
||||
|
||||
render() {
|
||||
const perPage = this.props.metadata.perPage;
|
||||
const page = this.props.metadata.page;
|
||||
return (
|
||||
<Site section="blog" title="Blog" language="en" config={this.props.config} >
|
||||
<div className="docMainWrapper wrapper">
|
||||
<BlogSidebar language={this.props.language} config={this.props.config} />
|
||||
<Container className="mainContainer documentContainer postContainer blogContainer">
|
||||
<div className="posts">
|
||||
{MetadataBlog
|
||||
.slice(page * perPage, (page + 1) * perPage)
|
||||
.map(post => {
|
||||
return (
|
||||
<BlogPost
|
||||
post={post}
|
||||
content={post.content}
|
||||
truncate={true}
|
||||
key={post.path + post.title}
|
||||
config={this.props.config}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
<div className="docs-prevnext">
|
||||
{page > 0 &&
|
||||
<a className="docs-prev" href={this.getPageURL(page - 1)}>
|
||||
← Prev
|
||||
</a>}
|
||||
{MetadataBlog.length > (page + 1) * perPage &&
|
||||
<a className="docs-next" href={this.getPageURL(page + 1)}>
|
||||
Next →
|
||||
</a>}
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</div>
|
||||
</Site>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = BlogPageLayout;
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
const Marked = require('./Marked.js');
|
||||
const React = require('react');
|
||||
|
||||
class BlogPost extends React.Component {
|
||||
renderContent() {
|
||||
let content = this.props.content;
|
||||
if (this.props.truncate) {
|
||||
content = content.split('<!--truncate-->')[0];
|
||||
return (
|
||||
<article className="post-content">
|
||||
<Marked>{content}</Marked>
|
||||
<div className="read-more">
|
||||
<a className="button" href={this.props.config.baseUrl + 'blog/' + this.props.post.path}>
|
||||
Read More
|
||||
</a>
|
||||
</div>
|
||||
</article>
|
||||
);
|
||||
}
|
||||
return <Marked>{content}</Marked>;
|
||||
}
|
||||
|
||||
renderAuthorPhoto() {
|
||||
const post = this.props.post;
|
||||
if (post.authorFBID) {
|
||||
return (
|
||||
<div className="authorPhoto">
|
||||
<a href={post.authorURL} target="_blank">
|
||||
<img
|
||||
src={
|
||||
'https://graph.facebook.com/' +
|
||||
post.authorFBID +
|
||||
'/picture/?height=200&width=200'
|
||||
}
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
renderTitle() {
|
||||
const post = this.props.post;
|
||||
return (
|
||||
<h1>
|
||||
<a href={this.props.config.baseUrl + 'blog/' + post.path}>{post.title}</a>
|
||||
</h1>
|
||||
);
|
||||
}
|
||||
|
||||
renderPostHeader() {
|
||||
const post = this.props.post;
|
||||
const match = post.path.match(/([0-9]+)\/([0-9]+)\/([0-9]+)/);
|
||||
// Because JavaScript sucks at date handling :(
|
||||
const year = match[1];
|
||||
const month = [
|
||||
'January',
|
||||
'February',
|
||||
'March',
|
||||
'April',
|
||||
'May',
|
||||
'June',
|
||||
'July',
|
||||
'August',
|
||||
'September',
|
||||
'October',
|
||||
'November',
|
||||
'December',
|
||||
][parseInt(match[2], 10) - 1];
|
||||
const day = parseInt(match[3], 10);
|
||||
|
||||
return (
|
||||
<header className="postHeader">
|
||||
{this.renderAuthorPhoto()}
|
||||
<p className="post-authorName">
|
||||
<a href={post.authorURL} target="_blank">{post.author}</a>
|
||||
</p>
|
||||
{this.renderTitle()}
|
||||
<p className="post-meta">
|
||||
{month} {day}, {year}
|
||||
</p>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="post">
|
||||
{this.renderPostHeader()}
|
||||
{this.renderContent()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = BlogPost;
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
const React = require('react');
|
||||
const BlogPost = require('./BlogPost.js');
|
||||
const BlogSidebar = require('./BlogSidebar.js');
|
||||
const Container = require('./Container.js');
|
||||
const Site = require('./Site.js');
|
||||
|
||||
class BlogPostLayout extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<Site
|
||||
className="sideNavVisible"
|
||||
section="blog"
|
||||
url={'blog/' + this.props.metadata.path}
|
||||
title={this.props.metadata.title}
|
||||
language={'en'}
|
||||
description={this.props.children.trim().split('\n')[0]}
|
||||
config={this.props.config}
|
||||
>
|
||||
<div className="docMainWrapper wrapper">
|
||||
<BlogSidebar language={'en'} current={this.props.metadata} config={this.props.config}/>
|
||||
<Container className="mainContainer documentContainer postContainer blogContainer">
|
||||
<div className="lonePost">
|
||||
<BlogPost
|
||||
post={this.props.metadata}
|
||||
content={this.props.children}
|
||||
language={'en'}
|
||||
config={this.props.config}
|
||||
/>
|
||||
</div>
|
||||
</Container>
|
||||
</div>
|
||||
</Site>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = BlogPostLayout;
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
const React = require('react');
|
||||
const Container = require('./Container.js');
|
||||
const SideNav = require('./nav/SideNav.js');
|
||||
|
||||
const MetadataBlog = require('./MetadataBlog.js');
|
||||
|
||||
class BlogSidebar extends React.Component {
|
||||
render() {
|
||||
const contents = [{
|
||||
name: 'Recent Posts',
|
||||
links: MetadataBlog,
|
||||
}];
|
||||
const title = this.props.current && this.props.current.title;
|
||||
const current = {
|
||||
id: title || '',
|
||||
category: 'Recent Posts',
|
||||
};
|
||||
return (
|
||||
<Container className="docsNavContainer" id="docsNav" wrapper={false}>
|
||||
<SideNav
|
||||
language={this.props.language}
|
||||
root={this.props.config.baseUrl + "blog/"}
|
||||
title="Blog"
|
||||
contents={contents}
|
||||
current={current}
|
||||
/>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = BlogSidebar;
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
const Marked = require('./Marked.js');
|
||||
const Container = require('./Container.js');
|
||||
const GridBlock = require('./GridBlock.js');
|
||||
|
||||
module.exports = {
|
||||
Marked: Marked,
|
||||
Container: Container,
|
||||
GridBlock: GridBlock
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
const React = require('react');
|
||||
const classNames = require('classnames');
|
||||
|
||||
class Container extends React.Component {
|
||||
render() {
|
||||
const containerClasses = classNames('container', this.props.className, {
|
||||
'darkBackground': this.props.background === 'dark',
|
||||
'highlightBackground': this.props.background === 'highlight',
|
||||
'lightBackground': this.props.background === 'light',
|
||||
'paddingAll': this.props.padding.indexOf('all') >= 0,
|
||||
'paddingBottom': this.props.padding.indexOf('bottom') >= 0,
|
||||
'paddingLeft': this.props.padding.indexOf('left') >= 0,
|
||||
'paddingRight': this.props.padding.indexOf('right') >= 0,
|
||||
'paddingTop': this.props.padding.indexOf('top') >= 0,
|
||||
});
|
||||
let wrappedChildren;
|
||||
|
||||
if (this.props.wrapper) {
|
||||
wrappedChildren =
|
||||
<div className="wrapper">{this.props.children}</div>;
|
||||
} else {
|
||||
wrappedChildren = this.props.children;
|
||||
}
|
||||
return (
|
||||
<div className={containerClasses} id={this.props.id}>
|
||||
{wrappedChildren}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Container.defaultProps = {
|
||||
background: 'transparent',
|
||||
padding: [],
|
||||
wrapper: true,
|
||||
};
|
||||
|
||||
module.exports = Container;
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
const React = require('react');
|
||||
const Marked = require('./Marked.js');
|
||||
|
||||
class Doc extends React.Component {
|
||||
render() {
|
||||
let editLink = (
|
||||
<a
|
||||
className="edit-page-link button"
|
||||
href={
|
||||
this.props.config.editUrl +
|
||||
this.props.language +
|
||||
'/' +
|
||||
this.props.source
|
||||
}
|
||||
target="_blank"
|
||||
>
|
||||
Edit this Doc
|
||||
</a>
|
||||
);
|
||||
return (
|
||||
<div className="post">
|
||||
<header className="postHeader">
|
||||
{editLink}
|
||||
<h1>{this.props.title}</h1>
|
||||
</header>
|
||||
<article>
|
||||
<Marked>{this.props.content}</Marked>
|
||||
</article>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Doc;
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
const React = require('react');
|
||||
const Container = require('./Container.js');
|
||||
const Doc = require('./Doc.js');
|
||||
const DocsSidebar = require('./DocsSidebar.js');
|
||||
const Site = require('./Site.js');
|
||||
|
||||
class DocsLayout extends React.Component {
|
||||
render() {
|
||||
const metadata = this.props.metadata;
|
||||
const content = this.props.children;
|
||||
return (
|
||||
<Site
|
||||
config={this.props.config}
|
||||
className="sideNavVisible"
|
||||
section="docs"
|
||||
title={
|
||||
this.props.config[this.props.metadata.language]['localized-strings'][
|
||||
this.props.metadata.localized_id
|
||||
]
|
||||
}
|
||||
description={content.trim().split('\n')[0]}
|
||||
language={metadata.language}
|
||||
>
|
||||
<div className="docMainWrapper wrapper">
|
||||
<DocsSidebar metadata={metadata} />
|
||||
<Container className="mainContainer">
|
||||
<Doc
|
||||
content={content}
|
||||
config={this.props.config}
|
||||
source={metadata.source}
|
||||
title={
|
||||
this.props.config[this.props.metadata.language]['localized-strings'][
|
||||
this.props.metadata.localized_id
|
||||
]
|
||||
}
|
||||
language={metadata.language}
|
||||
/>
|
||||
<div className="docs-prevnext">
|
||||
{metadata.previous_id &&
|
||||
<a
|
||||
className="docs-prev button"
|
||||
href={metadata.previous_id + '.html#content'}
|
||||
>
|
||||
←
|
||||
{' '}
|
||||
{
|
||||
this.props.config[this.props.metadata.language][
|
||||
'localized-strings'
|
||||
]['previous']
|
||||
}
|
||||
</a>}
|
||||
{metadata.next_id &&
|
||||
<a
|
||||
className="docs-next button"
|
||||
href={metadata.next_id + '.html#content'}
|
||||
>
|
||||
{
|
||||
this.props.config[this.props.metadata.language][
|
||||
'localized-strings'
|
||||
]['next']
|
||||
}
|
||||
{' '}
|
||||
→
|
||||
</a>}
|
||||
</div>
|
||||
</Container>
|
||||
</div>
|
||||
</Site>
|
||||
);
|
||||
}
|
||||
}
|
||||
module.exports = DocsLayout;
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
const Metadata = require('./metadata.js');
|
||||
const React = require('react');
|
||||
const Container = require('./Container.js');
|
||||
const SideNav = require('./nav/SideNav.js');
|
||||
const siteConfig = require(process.cwd() + '/siteConfig.js');
|
||||
|
||||
class DocsSidebar extends React.Component {
|
||||
render() {
|
||||
let layout = this.props.metadata.layout;
|
||||
let docsCategories = require('./' + layout + 'Categories.js');
|
||||
return (
|
||||
<Container className="docsNavContainer" id="docsNav" wrapper={false}>
|
||||
<SideNav
|
||||
language={this.props.metadata.language}
|
||||
root={this.props.root}
|
||||
title={this.props.title}
|
||||
contents={docsCategories[this.props.metadata.language]}
|
||||
current={this.props.metadata}
|
||||
/>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
DocsSidebar.propTypes = {
|
||||
layout: React.PropTypes.string,
|
||||
root: React.PropTypes.string,
|
||||
title: React.PropTypes.string,
|
||||
};
|
||||
|
||||
DocsSidebar.defaultProps = siteConfig.docsSidebarDefaults;
|
||||
|
||||
module.exports = DocsSidebar;
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
const React = require('react');
|
||||
|
||||
const githubButton = (
|
||||
<a
|
||||
className="github-button"
|
||||
href="https://github.com/deltice/test-site"
|
||||
data-icon="octicon-star"
|
||||
data-count-href="/deltice/test-site/stargazers"
|
||||
data-count-api="/repos/deltice/test-site#stargazers_count"
|
||||
data-count-aria-label="# stargazers on GitHub"
|
||||
aria-label="Star this project on GitHub">
|
||||
Star
|
||||
</a>
|
||||
);
|
||||
|
||||
class Footer extends React.Component {
|
||||
render() {
|
||||
const currentYear = new Date().getFullYear();
|
||||
return (
|
||||
<footer className="nav-footer" id="footer">
|
||||
<section className="sitemap">
|
||||
<a href={this.props.config.baseUrl} className="nav-home">
|
||||
<img
|
||||
src={this.props.config.baseUrl + this.props.config.footerIcon}
|
||||
alt={this.props.config.title}
|
||||
width="66"
|
||||
height="58"
|
||||
/>
|
||||
</a>
|
||||
<div>
|
||||
<h5>Docs</h5>
|
||||
<a
|
||||
href={
|
||||
this.props.config.baseUrl + 'docs/' + this.props.language + '/doc1.html'
|
||||
}
|
||||
>
|
||||
Getting Started (or other categories)
|
||||
</a>
|
||||
<a
|
||||
href={
|
||||
this.props.config.baseUrl + 'docs/' + this.props.language + '/doc2.html'
|
||||
}
|
||||
>
|
||||
Guides (or other categories)
|
||||
</a>
|
||||
<a
|
||||
href={
|
||||
this.props.config.baseUrl + 'docs/' + this.props.language + '/doc3.html'
|
||||
}
|
||||
>
|
||||
API Reference (or other categories)
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<h5>Community</h5>
|
||||
<a href={this.props.config.baseUrl + this.props.language + '/users.html'}>
|
||||
User Showcase
|
||||
</a>
|
||||
<a
|
||||
href="http://stackoverflow.com/questions/tagged/"
|
||||
target="_blank"
|
||||
>
|
||||
Stack Overflow
|
||||
</a>
|
||||
<a
|
||||
href="https://discordapp.com/"
|
||||
>
|
||||
Project Chat
|
||||
</a>
|
||||
<a href="https://twitter.com/" target="_blank">Twitter</a>
|
||||
</div>
|
||||
<div>
|
||||
<h5>More</h5>
|
||||
<a href={this.props.config.baseUrl + "blog"}>Blog</a>
|
||||
<a href="https://github.com/">GitHub</a>
|
||||
{githubButton}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<a
|
||||
href="https://code.facebook.com/projects/"
|
||||
target="_blank"
|
||||
className="fbOpenSource"
|
||||
>
|
||||
<img
|
||||
src={this.props.config.baseUrl + "img/oss_logo.png"}
|
||||
alt="Facebook Open Source"
|
||||
width="170"
|
||||
height="45"
|
||||
/>
|
||||
</a>
|
||||
<section className="copyright">
|
||||
Copyright © {currentYear} Facebook Inc.
|
||||
</section>
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
module.exports = Footer;
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
const React = require('react');
|
||||
const classNames = require('classnames');
|
||||
|
||||
const Marked = require('./Marked.js');
|
||||
|
||||
class GridBlock extends React.Component {
|
||||
renderBlock(block) {
|
||||
const blockClasses = classNames('blockElement', this.props.className, {
|
||||
'alignCenter': this.props.align === 'center',
|
||||
'alignRight': this.props.align === 'right',
|
||||
'fourByGridBlock': this.props.layout === 'fourColumn',
|
||||
'imageAlignBottom': (block.image && block.imageAlign === 'bottom'),
|
||||
'imageAlignSide': (block.image && (block.imageAlign === 'left' ||
|
||||
block.imageAlign === 'right')),
|
||||
'imageAlignTop': (block.image && block.imageAlign === 'top'),
|
||||
'threeByGridBlock': this.props.layout === 'threeColumn',
|
||||
'twoByGridBlock': this.props.layout === 'twoColumn',
|
||||
});
|
||||
|
||||
const topLeftImage = (block.imageAlign === 'top' ||
|
||||
block.imageAlign === 'left') &&
|
||||
this.renderBlockImage(block.image);
|
||||
|
||||
const bottomRightImage = (block.imageAlign === 'bottom' ||
|
||||
block.imageAlign === 'right') &&
|
||||
this.renderBlockImage(block.image);
|
||||
|
||||
return (
|
||||
<div className={blockClasses} key={block.title}>
|
||||
{topLeftImage}
|
||||
<div className="blockContent">
|
||||
{this.renderBlockTitle(block.title)}
|
||||
<Marked>{block.content}</Marked>
|
||||
</div>
|
||||
{bottomRightImage}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderBlockImage(image) {
|
||||
if (image) {
|
||||
return (
|
||||
<div className="blockImage"><img src={image} /></div>
|
||||
);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
renderBlockTitle(title) {
|
||||
if (title) {
|
||||
return <h2>{title}</h2>;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="gridBlock">
|
||||
{this.props.contents.map(this.renderBlock, this)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
GridBlock.defaultProps = {
|
||||
align: 'left',
|
||||
contents: [],
|
||||
imagealign: 'top',
|
||||
layout: 'twoColumn',
|
||||
};
|
||||
|
||||
module.exports = GridBlock;
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
const React = require('react');
|
||||
|
||||
class Head extends React.Component {
|
||||
render() {
|
||||
/*
|
||||
|
||||
<link rel="apple-touch-icon" sizes="180x180" href={this.props.config.baseUrl + "img/favicon/apple-touch-icon.png"} />
|
||||
<link rel="icon" type="image/png" sizes="32x32" href={this.props.config.baseUrl + "img/favicon/favicon-32x32.png"} />
|
||||
<link rel="icon" type="image/png" sizes="16x16" href={this.props.config.baseUrl + "img/favicon/favicon-16x16.png"} />
|
||||
<link rel="manifest" href={this.props.config.baseUrl + "img/favicon/manifest.json"} />
|
||||
<link rel="mask-icon" href={this.props.config.baseUrl + "img/favicon/safari-pinned-tab.svg"} color="#5bbad5" />
|
||||
<link rel="shortcut icon" href={this.props.config.baseUrl + "img/favicon/favicon.ico"} />
|
||||
<meta name="msapplication-config" content={this.props.config.baseUrl + "img/favicon/browserconfig.xml"} />
|
||||
<meta name="theme-color" content={this.props.config.primaryColor} />
|
||||
|
||||
*/
|
||||
|
||||
return (
|
||||
<head>
|
||||
<meta charSet="utf-8" />
|
||||
<meta httpEquiv="X-UA-Compatible" content="IE=edge, chrome=1" />
|
||||
<title>{this.props.title}</title>
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<meta property="og:title" content={this.props.title} />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:url" content={this.props.url} />
|
||||
<meta property="og:image" content={this.props.config.baseUrl + "img/opengraph.png"} />
|
||||
<meta property="og:description" content={this.props.description} />
|
||||
|
||||
|
||||
<link rel="shortcut icon" href="/img/favicon.png" />
|
||||
<link rel="stylesheet" href={this.props.config.baseUrl + "css/main.css"} />
|
||||
<script async defer src="https://buttons.github.io/buttons.js"></script>
|
||||
</head>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Head;
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
const React = require('react');
|
||||
const toSlug = require('./toSlug.js');
|
||||
|
||||
const Header = React.createClass({
|
||||
render() {
|
||||
const slug = toSlug(this.props.toSlug || this.props.children);
|
||||
const Heading = 'h' + this.props.level;
|
||||
|
||||
return (
|
||||
<Heading {...this.props}>
|
||||
<a className="anchor" name={slug}></a>
|
||||
{this.props.children}
|
||||
{' '}<a className="hash-link" href={'#' + slug}>#</a>
|
||||
</Heading>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = Header;
|
||||
|
|
@ -0,0 +1,560 @@
|
|||
const React = require('react');
|
||||
const unindent = require('./unindent.js');
|
||||
|
||||
/* http://prismjs.com/download.html?themes=prism&languages=markup+clike+javascript+jsx */
|
||||
/**
|
||||
* Prism: Lightweight, robust, elegant syntax highlighting
|
||||
* MIT license https://www.opensource.org/licenses/mit-license.php/
|
||||
* @author Lea Verou https://lea.verou.me
|
||||
*/
|
||||
|
||||
// Private helper vars
|
||||
const lang = /\blang(?:uage)?-(?!\*)(\w+)\b/i;
|
||||
|
||||
const _ = Prism = {
|
||||
util: {
|
||||
encode(tokens) {
|
||||
if (tokens instanceof Token) {
|
||||
return new Token(tokens.type, _.util.encode(tokens.content), tokens.alias);
|
||||
} else if (_.util.type(tokens) === 'Array') {
|
||||
return tokens.map(_.util.encode);
|
||||
} else {
|
||||
return tokens.replace(/&/g, '&').replace(/</g, '<').replace(/\u00a0/g, ' ');
|
||||
}
|
||||
},
|
||||
|
||||
type(o) {
|
||||
return Object.prototype.toString.call(o).match(/\[object (\w+)\]/)[1];
|
||||
},
|
||||
|
||||
// Deep clone a language definition (e.g. to extend it)
|
||||
clone(o) {
|
||||
const type = _.util.type(o);
|
||||
|
||||
switch (type) {
|
||||
case 'Object':
|
||||
var clone = {};
|
||||
|
||||
for (const key in o) {
|
||||
if (o.hasOwnProperty(key)) {
|
||||
clone[key] = _.util.clone(o[key]);
|
||||
}
|
||||
}
|
||||
|
||||
return clone;
|
||||
|
||||
case 'Array':
|
||||
// Check for existence for IE8
|
||||
return o.map && o.map(v => { return _.util.clone(v); });
|
||||
}
|
||||
|
||||
return o;
|
||||
},
|
||||
},
|
||||
|
||||
languages: {
|
||||
extend(id, redef) {
|
||||
const lang = _.util.clone(_.languages[id]);
|
||||
|
||||
for (const key in redef) {
|
||||
lang[key] = redef[key];
|
||||
}
|
||||
|
||||
return lang;
|
||||
},
|
||||
|
||||
/**
|
||||
* Insert a token before another token in a language literal
|
||||
* As this needs to recreate the object (we cannot actually insert before keys in object literals),
|
||||
* we cannot just provide an object, we need an object and a key.
|
||||
* @param inside The key (or language id) of the parent
|
||||
* @param before The key to insert before. If not provided, the function appends instead.
|
||||
* @param insert Object with the key/value pairs to insert
|
||||
* @param root The object that contains `inside`. If equal to Prism.languages, it can be omitted.
|
||||
*/
|
||||
insertBefore(inside, before, insert, root) {
|
||||
root = root || _.languages;
|
||||
const grammar = root[inside];
|
||||
|
||||
if (arguments.length == 2) {
|
||||
insert = arguments[1];
|
||||
|
||||
for (var newToken in insert) {
|
||||
if (insert.hasOwnProperty(newToken)) {
|
||||
grammar[newToken] = insert[newToken];
|
||||
}
|
||||
}
|
||||
|
||||
return grammar;
|
||||
}
|
||||
|
||||
const ret = {};
|
||||
|
||||
for (const token in grammar) {
|
||||
|
||||
if (grammar.hasOwnProperty(token)) {
|
||||
|
||||
if (token == before) {
|
||||
|
||||
for (var newToken in insert) {
|
||||
|
||||
if (insert.hasOwnProperty(newToken)) {
|
||||
ret[newToken] = insert[newToken];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret[token] = grammar[token];
|
||||
}
|
||||
}
|
||||
|
||||
// Update references in other language definitions
|
||||
_.languages.DFS(_.languages, function(key, value) {
|
||||
if (value === root[inside] && key != inside) {
|
||||
this[key] = ret;
|
||||
}
|
||||
});
|
||||
|
||||
return root[inside] = ret;
|
||||
},
|
||||
|
||||
// Traverse a language definition with Depth First Search
|
||||
DFS(o, callback, type) {
|
||||
for (const i in o) {
|
||||
if (o.hasOwnProperty(i)) {
|
||||
callback.call(o, i, o[i], type || i);
|
||||
|
||||
if (_.util.type(o[i]) === 'Object') {
|
||||
_.languages.DFS(o[i], callback);
|
||||
} else if (_.util.type(o[i]) === 'Array') {
|
||||
_.languages.DFS(o[i], callback, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
highlightAll(async, callback) {
|
||||
const elements = document.querySelectorAll('code[class*="language-"], [class*="language-"] code, code[class*="lang-"], [class*="lang-"] code');
|
||||
|
||||
for (let i = 0, element; element = elements[i++];) {
|
||||
_.highlightElement(element, async === true, callback);
|
||||
}
|
||||
},
|
||||
|
||||
highlightElement(element, async, callback) {
|
||||
// Find language
|
||||
let language, grammar, parent = element;
|
||||
|
||||
while (parent && !lang.test(parent.className)) {
|
||||
parent = parent.parentNode;
|
||||
}
|
||||
|
||||
if (parent) {
|
||||
language = (parent.className.match(lang) || [, ''])[1];
|
||||
grammar = _.languages[language];
|
||||
}
|
||||
|
||||
// Set language on the element, if not present
|
||||
element.className = element.className.replace(lang, '').replace(/\s+/g, ' ') + ' language-' + language;
|
||||
|
||||
// Set language on the parent, for styling
|
||||
parent = element.parentNode;
|
||||
|
||||
if (/pre/i.test(parent.nodeName)) {
|
||||
parent.className = parent.className.replace(lang, '').replace(/\s+/g, ' ') + ' language-' + language;
|
||||
}
|
||||
|
||||
if (!grammar) {
|
||||
return;
|
||||
}
|
||||
|
||||
let code = element.textContent;
|
||||
|
||||
if (!code) {
|
||||
return;
|
||||
}
|
||||
|
||||
code = code.replace(/^(?:\r?\n|\r)/, '');
|
||||
|
||||
const env = {
|
||||
element,
|
||||
language,
|
||||
grammar,
|
||||
code,
|
||||
};
|
||||
|
||||
_.hooks.run('before-highlight', env);
|
||||
|
||||
if (async && _self.Worker) {
|
||||
const worker = new Worker(_.filename);
|
||||
|
||||
worker.onmessage = function(evt) {
|
||||
env.highlightedCode = Token.stringify(JSON.parse(evt.data), language);
|
||||
|
||||
_.hooks.run('before-insert', env);
|
||||
|
||||
env.element.innerHTML = env.highlightedCode;
|
||||
|
||||
callback && callback.call(env.element);
|
||||
_.hooks.run('after-highlight', env);
|
||||
};
|
||||
|
||||
worker.postMessage(JSON.stringify({
|
||||
language: env.language,
|
||||
code: env.code,
|
||||
}));
|
||||
} else {
|
||||
env.highlightedCode = _.highlight(env.code, env.grammar, env.language);
|
||||
|
||||
_.hooks.run('before-insert', env);
|
||||
|
||||
env.element.innerHTML = env.highlightedCode;
|
||||
|
||||
callback && callback.call(element);
|
||||
|
||||
_.hooks.run('after-highlight', env);
|
||||
}
|
||||
},
|
||||
|
||||
highlight(text, grammar, language) {
|
||||
const tokens = _.tokenize(text, grammar);
|
||||
return Token.stringify(_.util.encode(tokens), language);
|
||||
},
|
||||
|
||||
tokenize(text, grammar, language) {
|
||||
const Token = _.Token;
|
||||
|
||||
const strarr = [text];
|
||||
|
||||
const rest = grammar.rest;
|
||||
|
||||
if (rest) {
|
||||
for (var token in rest) {
|
||||
grammar[token] = rest[token];
|
||||
}
|
||||
|
||||
delete grammar.rest;
|
||||
}
|
||||
|
||||
tokenloop: for (var token in grammar) {
|
||||
if (!grammar.hasOwnProperty(token) || !grammar[token]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let patterns = grammar[token];
|
||||
patterns = (_.util.type(patterns) === 'Array') ? patterns : [patterns];
|
||||
|
||||
for (let j = 0; j < patterns.length; ++j) {
|
||||
let pattern = patterns[j],
|
||||
inside = pattern.inside,
|
||||
lookbehind = !!pattern.lookbehind,
|
||||
lookbehindLength = 0,
|
||||
alias = pattern.alias;
|
||||
|
||||
pattern = pattern.pattern || pattern;
|
||||
|
||||
for (let i = 0; i < strarr.length; i++) { // Don't cache length as it changes during the loop
|
||||
|
||||
const str = strarr[i];
|
||||
|
||||
if (strarr.length > text.length) {
|
||||
// Something went terribly wrong, ABORT, ABORT!
|
||||
break tokenloop;
|
||||
}
|
||||
|
||||
if (str instanceof Token) {
|
||||
continue;
|
||||
}
|
||||
|
||||
pattern.lastIndex = 0;
|
||||
|
||||
var match = pattern.exec(str);
|
||||
|
||||
if (match) {
|
||||
if (lookbehind) {
|
||||
lookbehindLength = match[1].length;
|
||||
}
|
||||
|
||||
var from = match.index - 1 + lookbehindLength,
|
||||
match = match[0].slice(lookbehindLength),
|
||||
len = match.length,
|
||||
to = from + len,
|
||||
before = str.slice(0, from + 1),
|
||||
after = str.slice(to + 1);
|
||||
|
||||
const args = [i, 1];
|
||||
|
||||
if (before) {
|
||||
args.push(before);
|
||||
}
|
||||
|
||||
const wrapped = new Token(token, inside ? _.tokenize(match, inside) : match, alias);
|
||||
|
||||
args.push(wrapped);
|
||||
|
||||
if (after) {
|
||||
args.push(after);
|
||||
}
|
||||
|
||||
Array.prototype.splice.apply(strarr, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return strarr;
|
||||
},
|
||||
|
||||
hooks: {
|
||||
all: {},
|
||||
|
||||
add(name, callback) {
|
||||
const hooks = _.hooks.all;
|
||||
|
||||
hooks[name] = hooks[name] || [];
|
||||
|
||||
hooks[name].push(callback);
|
||||
},
|
||||
|
||||
run(name, env) {
|
||||
const callbacks = _.hooks.all[name];
|
||||
|
||||
if (!callbacks || !callbacks.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0, callback; callback = callbacks[i++];) {
|
||||
callback(env);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const Token = _.Token = function(type, content, alias) {
|
||||
this.type = type;
|
||||
this.content = content;
|
||||
this.alias = alias;
|
||||
};
|
||||
|
||||
Token.reactify = function(o, language, parent, key) {
|
||||
if (typeof o == 'string') {
|
||||
return o;
|
||||
}
|
||||
|
||||
if (_.util.type(o) === 'Array') {
|
||||
return o.map((element, i) => {
|
||||
return Token.reactify(element, language, o, i);
|
||||
});
|
||||
}
|
||||
|
||||
const env = {
|
||||
type: o.type,
|
||||
content: Token.reactify(o.content, language, parent),
|
||||
tag: 'span',
|
||||
classes: ['token', o.type],
|
||||
attributes: {key},
|
||||
language,
|
||||
parent,
|
||||
};
|
||||
|
||||
if (env.type == 'comment') {
|
||||
env.attributes.spellCheck = true;
|
||||
}
|
||||
|
||||
if (o.alias) {
|
||||
const aliases = _.util.type(o.alias) === 'Array' ? o.alias : [o.alias];
|
||||
Array.prototype.push.apply(env.classes, aliases);
|
||||
}
|
||||
|
||||
_.hooks.run('wrap', env);
|
||||
|
||||
env.attributes.className = env.classes.join(' ');
|
||||
|
||||
return React.DOM[env.tag](env.attributes, env.content);
|
||||
};
|
||||
|
||||
Prism.languages.markup = {
|
||||
'comment': /<!--[\w\W]*?-->/,
|
||||
'prolog': /<\?[\w\W]+?\?>/,
|
||||
'doctype': /<!DOCTYPE[\w\W]+?>/,
|
||||
'cdata': /<!\[CDATA\[[\w\W]*?]]>/i,
|
||||
'tag': {
|
||||
pattern: /<\/?[^\s>\/]+(?:\s+[^\s>\/=]+(?:=(?:("|')(?:\\\1|\\?(?!\1)[\w\W])*\1|[^\s'">=]+))?)*\s*\/?>/i,
|
||||
inside: {
|
||||
'tag': {
|
||||
pattern: /^<\/?[^\s>\/]+/i,
|
||||
inside: {
|
||||
'punctuation': /^<\/?/,
|
||||
'namespace': /^[^\s>\/:]+:/,
|
||||
},
|
||||
},
|
||||
'attr-value': {
|
||||
pattern: /=(?:('|")[\w\W]*?(\1)|[^\s>]+)/i,
|
||||
inside: {
|
||||
'punctuation': /[=>"']/,
|
||||
},
|
||||
},
|
||||
'punctuation': /\/?>/,
|
||||
'attr-name': {
|
||||
pattern: /[^\s>\/]+/,
|
||||
inside: {
|
||||
'namespace': /^[^\s>\/:]+:/,
|
||||
},
|
||||
},
|
||||
|
||||
},
|
||||
},
|
||||
'entity': /&#?[\da-z]{1,8};/i,
|
||||
};
|
||||
|
||||
// Plugin to make entity title show the real entity, idea by Roman Komarov
|
||||
Prism.hooks.add('wrap', env => {
|
||||
|
||||
if (env.type === 'entity') {
|
||||
env.attributes['title'] = env.content.replace(/&/, '&');
|
||||
}
|
||||
});
|
||||
|
||||
Prism.languages.clike = {
|
||||
'comment': [
|
||||
{
|
||||
pattern: /(^|[^\\])\/\*[\w\W]*?\*\//,
|
||||
lookbehind: true,
|
||||
},
|
||||
{
|
||||
pattern: /(^|[^\\:])\/\/.*/,
|
||||
lookbehind: true,
|
||||
},
|
||||
],
|
||||
'string': /("|')(\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,
|
||||
'class-name': {
|
||||
pattern: /((?:(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[a-z0-9_\.\\]+/i,
|
||||
lookbehind: true,
|
||||
inside: {
|
||||
punctuation: /(\.|\\)/,
|
||||
},
|
||||
},
|
||||
'keyword': /\b(if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/,
|
||||
'boolean': /\b(true|false)\b/,
|
||||
'function': /[a-z0-9_]+(?=\()/i,
|
||||
'number': /\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee]-?\d+)?)\b/,
|
||||
'operator': /[-+]{1,2}|!|<=?|>=?|={1,3}|&{1,2}|\|?\||\?|\*|\/|~|\^|%/,
|
||||
'punctuation': /[{}[\];(),.:]/,
|
||||
};
|
||||
|
||||
Prism.languages.javascript = Prism.languages.extend('clike', {
|
||||
'keyword': /\b(as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|false|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|true|try|typeof|var|void|while|with|yield)\b/,
|
||||
'number': /\b-?(0x[\dA-Fa-f]+|0b[01]+|0o[0-7]+|\d*\.?\d+([Ee][+-]?\d+)?|NaN|Infinity)\b/,
|
||||
'function': /(?!\d)[a-z0-9_$]+(?=\()/i,
|
||||
});
|
||||
|
||||
Prism.languages.insertBefore('javascript', 'keyword', {
|
||||
'regex': {
|
||||
pattern: /(^|[^/])\/(?!\/)(\[.+?]|\\.|[^/\\\r\n])+\/[gimyu]{0,5}(?=\s*($|[\r\n,.;})]))/,
|
||||
lookbehind: true,
|
||||
},
|
||||
});
|
||||
|
||||
Prism.languages.insertBefore('javascript', 'class-name', {
|
||||
'template-string': {
|
||||
pattern: /`(?:\\`|\\?[^`])*`/,
|
||||
inside: {
|
||||
'interpolation': {
|
||||
pattern: /\$\{[^}]+\}/,
|
||||
inside: {
|
||||
'interpolation-punctuation': {
|
||||
pattern: /^\$\{|\}$/,
|
||||
alias: 'punctuation',
|
||||
},
|
||||
rest: Prism.languages.javascript,
|
||||
},
|
||||
},
|
||||
'string': /[\s\S]+/,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (Prism.languages.markup) {
|
||||
Prism.languages.insertBefore('markup', 'tag', {
|
||||
'script': {
|
||||
pattern: /<script[\w\W]*?>[\w\W]*?<\/script>/i,
|
||||
inside: {
|
||||
'tag': {
|
||||
pattern: /<script[\w\W]*?>|<\/script>/i,
|
||||
inside: Prism.languages.markup.tag.inside,
|
||||
},
|
||||
rest: Prism.languages.javascript,
|
||||
},
|
||||
alias: 'language-javascript',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
(function(Prism) {
|
||||
|
||||
const javascript = Prism.util.clone(Prism.languages.javascript);
|
||||
|
||||
Prism.languages.jsx = Prism.languages.extend('markup', javascript);
|
||||
Prism.languages.jsx.tag.pattern = /<\/?[\w:-]+\s*(?:\s+[\w:-]+(?:=(?:("|')(\\?[\w\W])*?\1|[^\s'">=]+|(\{[\w\W]*?\})))?\s*)*\/?>/i;
|
||||
|
||||
Prism.languages.jsx.tag.inside['attr-value'].pattern = /=[^\{](?:('|")[\w\W]*?(\1)|[^\s>]+)/i;
|
||||
|
||||
Prism.languages.insertBefore('inside', 'attr-value', {
|
||||
'script': {
|
||||
pattern: /=(\{[\w\W]*?\})/i,
|
||||
inside: {
|
||||
'function' : Prism.languages.javascript.function,
|
||||
'punctuation': /[={}[\];(),.:]/,
|
||||
'keyword': Prism.languages.javascript.keyword,
|
||||
},
|
||||
'alias': 'language-javascript',
|
||||
},
|
||||
}, Prism.languages.jsx.tag);
|
||||
|
||||
}(Prism));
|
||||
|
||||
const PrismComponent = React.createClass({
|
||||
statics: {
|
||||
_,
|
||||
},
|
||||
getDefaultProps() {
|
||||
return {
|
||||
language: 'javascript',
|
||||
};
|
||||
},
|
||||
render() {
|
||||
const lines = [];
|
||||
if (this.props.line) {
|
||||
this.props.line.split(',').forEach(range => {
|
||||
const parts = range.split('-');
|
||||
if (parts.length === 1) {
|
||||
lines.push(parts[0].trim());
|
||||
} else {
|
||||
const start = parseInt(parts[0].trim(), 10);
|
||||
const end = parseInt(parts[1].trim(), 10);
|
||||
for (let ii = start; ii <= end; ii++) {
|
||||
lines.push(ii);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
const grammar = _.languages[this.props.language];
|
||||
return (
|
||||
<pre className={'prism language-' + this.props.language}>
|
||||
{Token.reactify(_.tokenize(this.props.children, grammar))}
|
||||
{lines.map((line, ii) => {
|
||||
return (
|
||||
<div
|
||||
className="line-highlight"
|
||||
key={ii}
|
||||
style={{height: 20, top: 20 * (line - 1)}}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</pre>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = PrismComponent;
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
const React = require('react');
|
||||
const HeaderNav = require('./nav/HeaderNav.js');
|
||||
const Head = require('./Head.js');
|
||||
const Footer = require(process.cwd() + '/core/Footer.js');
|
||||
|
||||
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'
|
||||
});
|
||||
`,
|
||||
}}
|
||||
/>
|
||||
<script async defer src="https://buttons.github.io/buttons.js" />
|
||||
*/
|
||||
|
||||
render() {
|
||||
const title = this.props.title
|
||||
? this.props.title + ' · ' + this.props.config.title
|
||||
: this.props.config.title + ' · ' + this.props.config[this.props.language].tagline;
|
||||
const description =
|
||||
this.props.description || this.props.config[this.props.language].tagline;
|
||||
const url =
|
||||
this.props.config.url + this.props.config.baseUrl + (this.props.url || 'index.html');
|
||||
return (
|
||||
<html>
|
||||
<Head config={this.props.config} description={description} title={title} url={url} />
|
||||
<body className={this.props.className}>
|
||||
<HeaderNav
|
||||
config={this.props.config}
|
||||
baseUrl={this.props.config.baseUrl}
|
||||
section={this.props.section}
|
||||
title={this.props.config.title}
|
||||
language={this.props.language}
|
||||
/>
|
||||
<div className="navPusher">
|
||||
{this.props.children}
|
||||
<Footer config={this.props.config} language={this.props.language} />
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
}
|
||||
module.exports = Site;
|
||||
|
|
@ -0,0 +1,139 @@
|
|||
const React = require('react');
|
||||
|
||||
const siteConfig = require(process.cwd() + '/siteConfig.js');
|
||||
|
||||
class LanguageDropDown extends React.Component {
|
||||
render() {
|
||||
const enabledLanguages = [];
|
||||
let currentLanguage = 'English';
|
||||
|
||||
siteConfig['languages'].map(lang => {
|
||||
if (lang.tag == this.props.language) {
|
||||
currentLanguage = lang.name;
|
||||
}
|
||||
if (lang.tag == this.props.language) {
|
||||
return;
|
||||
}
|
||||
enabledLanguages.push(
|
||||
<li key={lang.tag}>
|
||||
<a href={siteConfig.baseUrl + lang.tag}>
|
||||
{lang.name}
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
});
|
||||
|
||||
enabledLanguages.push(
|
||||
<li key="recruiting">
|
||||
<a href={siteConfig.recruitingLink} target="_blank">
|
||||
Help Translate
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
|
||||
return (
|
||||
<span>
|
||||
<li>
|
||||
<a id="languages-menu" href="#">
|
||||
<img
|
||||
className="languages-icon"
|
||||
src={this.props.baseUrl + 'img/language.svg'}
|
||||
/>
|
||||
{currentLanguage}
|
||||
</a>
|
||||
<div id="languages-dropdown" className="hide">
|
||||
<ul id="languages-dropdown-items">
|
||||
{enabledLanguages}
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<script
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
const languagesMenuItem = document.getElementById("languages-menu");
|
||||
const languagesDropDown = document.getElementById("languages-dropdown");
|
||||
languagesMenuItem.addEventListener("click", function(){
|
||||
if(languagesDropDown.className == "hide") {
|
||||
languagesDropDown.className = "visible";
|
||||
} else {
|
||||
languagesDropDown.className = "hide";
|
||||
}
|
||||
});
|
||||
`,
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class HeaderNav extends React.Component {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
slideoutActive: false,
|
||||
};
|
||||
}
|
||||
|
||||
makeLinks(link) {
|
||||
link.href = link.href.replace(
|
||||
/\/LANGUAGE\//,
|
||||
'\/' + this.props.language + '\/'
|
||||
);
|
||||
return (
|
||||
<li key={link.section}>
|
||||
<a
|
||||
href={link.href}
|
||||
className={link.section === this.props.section ? 'active' : ''}>
|
||||
{siteConfig[this.props.language]['localized-strings'][link.text]}
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="fixedHeaderContainer">
|
||||
<div className="headerWrapper wrapper">
|
||||
<header>
|
||||
<a href={this.props.baseUrl}>
|
||||
<img src={this.props.baseUrl + siteConfig.headerIcon} />
|
||||
<h2>{this.props.title}</h2>
|
||||
</a>
|
||||
{this.renderResponsiveNav()}
|
||||
</header>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
stick this between internal and external links
|
||||
|
||||
<LanguageDropDown
|
||||
baseUrl={this.props.baseUrl}
|
||||
language={this.props.language}
|
||||
/>
|
||||
|
||||
<li className="navSearchWrapper reactNavSearchWrapper">
|
||||
<input id="search_input_react" type="text" placeholder="Search" />
|
||||
</li>
|
||||
|
||||
*/
|
||||
renderResponsiveNav() {
|
||||
return (
|
||||
<div className="navigationWrapper navigationSlider">
|
||||
<nav className="slidingNav">
|
||||
<ul className="nav-site nav-site-internal">
|
||||
{this.props.config.headerLinksInternal.map(this.makeLinks, this)}
|
||||
|
||||
|
||||
{this.props.config.headerLinksExternal.map(this.makeLinks, this)}
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = HeaderNav;
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
const React = require('react');
|
||||
const classNames = require('classnames');
|
||||
|
||||
const siteConfig = require(process.cwd() + '/siteConfig.js');
|
||||
|
||||
class SideNav extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<nav className="toc">
|
||||
<div className="toggleNav">
|
||||
<section className="navWrapper wrapper">
|
||||
<div className="navBreadcrumb wrapper">
|
||||
<div className="navToggle" id="navToggler"><i /></div>
|
||||
<h2>
|
||||
<i>›</i>
|
||||
<span>{this.props.current.category}</span>
|
||||
</h2>
|
||||
</div>
|
||||
<div className="navGroups">
|
||||
{this.props.contents.map(this.renderCategory, this)}
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<script
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
var toggler = document.getElementById('navToggler');
|
||||
var nav = document.getElementById('docsNav');
|
||||
toggler.onclick = function() {
|
||||
nav.classList.toggle('docsSliderActive');
|
||||
};
|
||||
`,
|
||||
}}
|
||||
/>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
renderCategory(category) {
|
||||
return (
|
||||
<div className="navGroup navGroupActive" key={category.name}>
|
||||
<h3>{this.getLocalizedCategoryString(category.name)}</h3>
|
||||
<ul>
|
||||
{category.links.map(this.renderItemLink, this)}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
getLocalizedCategoryString(category) {
|
||||
let categoryString =
|
||||
siteConfig[this.props.language]['localized-strings'][category];
|
||||
if (typeof categoryString == 'undefined') {
|
||||
categoryString = category;
|
||||
}
|
||||
return categoryString;
|
||||
}
|
||||
getLocalizedString(metadata) {
|
||||
let localizedString = '';
|
||||
if (
|
||||
typeof metadata.localized_id == 'undefined' ||
|
||||
typeof siteConfig[this.props.language] == 'undefined' ||
|
||||
typeof siteConfig[this.props.language]['localized-strings'] == 'undefined'
|
||||
) {
|
||||
localizedString = metadata.title;
|
||||
} else {
|
||||
localizedString =
|
||||
siteConfig[this.props.language]['localized-strings'][
|
||||
metadata.localized_id
|
||||
];
|
||||
}
|
||||
return localizedString;
|
||||
}
|
||||
getLink(metadata) {
|
||||
if (metadata.permalink) {
|
||||
if (metadata.permalink.match(/^https?:/)) {
|
||||
return metadata.permalink;
|
||||
}
|
||||
return siteConfig.baseUrl + metadata.permalink + '#content';
|
||||
}
|
||||
if (metadata.path) {
|
||||
return siteConfig.baseUrl + 'blog/' + metadata.path;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
renderItemLink(link) {
|
||||
const itemClasses = classNames('navListItem', {
|
||||
navListItemActive: link.id === this.props.current.id,
|
||||
});
|
||||
const linkClasses = classNames('navItem', {
|
||||
navItemActive: link.id === this.props.current.id,
|
||||
});
|
||||
return (
|
||||
<li className={itemClasses} key={link.id}>
|
||||
<a className={linkClasses} href={this.getLink(link)}>
|
||||
{this.getLocalizedString(link)}
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
}
|
||||
SideNav.defaultProps = {
|
||||
contents: [],
|
||||
};
|
||||
module.exports = SideNav;
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
module.exports = string => {
|
||||
// var accents = "àáäâèéëêìíïîòóöôùúüûñç";
|
||||
const accents = '\u00e0\u00e1\u00e4\u00e2\u00e8'
|
||||
+ '\u00e9\u00eb\u00ea\u00ec\u00ed\u00ef'
|
||||
+ '\u00ee\u00f2\u00f3\u00f6\u00f4\u00f9'
|
||||
+ '\u00fa\u00fc\u00fb\u00f1\u00e7';
|
||||
|
||||
const without = 'aaaaeeeeiiiioooouuuunc';
|
||||
|
||||
let slug = string
|
||||
.toString()
|
||||
|
||||
// Handle uppercase characters
|
||||
.toLowerCase()
|
||||
|
||||
// Handle accentuated characters
|
||||
.replace(
|
||||
new RegExp('[' + accents + ']', 'g'),
|
||||
c => { return without.charAt(accents.indexOf(c)); })
|
||||
|
||||
// Replace `.`, `(` and `?` with blank string like Github does
|
||||
.replace(/\.|\(|\?/g, '')
|
||||
|
||||
// Dash special characters
|
||||
.replace(/[^a-z0-9]/g, '-')
|
||||
|
||||
// Compress multiple dash
|
||||
.replace(/-+/g, '-')
|
||||
|
||||
// Trim dashes
|
||||
.replace(/^-|-$/g, '');
|
||||
|
||||
// Add trailing `-` if string contains ` ...` in the end like Github does
|
||||
if (/\s[.]{1,}/.test(string)) {
|
||||
slug += '-';
|
||||
}
|
||||
|
||||
return slug;
|
||||
};
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
// Remove the indentation introduced by JSX
|
||||
function unindent(code) {
|
||||
const lines = code.split('\n');
|
||||
if (lines[0] === '') {
|
||||
lines.shift();
|
||||
}
|
||||
if (lines.length <= 1) {
|
||||
return code;
|
||||
}
|
||||
|
||||
const indent = lines[0].match(/^\s*/)[0];
|
||||
for (let i = 0; i < lines.length; ++i) {
|
||||
lines[i] = lines[i].replace(new RegExp('^' + indent), '');
|
||||
}
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
module.exports = unindent;
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
const shell = require('shelljs');
|
||||
|
||||
const GIT_USER = process.env.GIT_USER;
|
||||
const CIRCLE_BRANCH = process.env.CIRCLE_BRANCH;
|
||||
const CIRCLE_PROJECT_USERNAME = process.env.CIRCLE_PROJECT_USERNAME;
|
||||
const CIRCLE_PROJECT_REPONAME = process.env.CIRCLE_PROJECT_REPONAME;
|
||||
const CI_PULL_REQUEST = process.env.CI_PULL_REQUEST;
|
||||
const remoteBranch = `https://${GIT_USER}@github.com/${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME}.git`;
|
||||
|
||||
|
||||
if (!shell.which('git')) {
|
||||
shell.echo('Sorry, this script requires git');
|
||||
shell.exit(1);
|
||||
}
|
||||
|
||||
if (CI_PULL_REQUEST || CIRCLE_BRANCH !== `master`) {
|
||||
echo('Skipping deploy');
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (shell.exec('npm run docusaurus-build').code) {
|
||||
shell.echo('Error: generating html failed');
|
||||
shell.exit(1);
|
||||
}
|
||||
|
||||
shell.cd(__dirname);
|
||||
shell.cd('..');
|
||||
shell.cd('build');
|
||||
|
||||
if (shell.exec(`git clone ${remoteBranch} ${CIRCLE_PROJECT_REPONAME}-gh-pages`).code !== 0) {
|
||||
shell.echo('Error: git clone failed');
|
||||
shell.exit(1);
|
||||
}
|
||||
|
||||
shell.cd(`${CIRCLE_PROJECT_REPONAME}-gh-pages`);
|
||||
|
||||
if (shell.exec('git checkout origin/gh-pages').code +
|
||||
shell.exec('git checkout -b gh-pages').code +
|
||||
shell.exec('git branch --set-upstream-to=origin/gh-pages').code !== 0
|
||||
) {
|
||||
shell.echo('Error: Git checkout gh-pages failed');
|
||||
shell.exit(1);
|
||||
}
|
||||
|
||||
shell.exec('rm -rf *');
|
||||
|
||||
shell.cd('../..');
|
||||
|
||||
shell.cp('-R', `build/${CIRCLE_PROJECT_REPONAME}/*`, `build/${CIRCLE_PROJECT_REPONAME}-gh-pages/`);
|
||||
shell.cd(`build/${CIRCLE_PROJECT_REPONAME}-gh-pages`);
|
||||
|
||||
shell.exec('git add --all');
|
||||
shell.exec('git commit -m "update website [ci skip]"');
|
||||
if (shell.exec('git push origin gh-pages').code !== 0) {
|
||||
shell.echo('Error: Git push failed');
|
||||
shell.exit(1);
|
||||
} else {
|
||||
shell.echo(`Website is live at: https://${CIRCLE_PROJECT_USERNAME}.github.io/${CIRCLE_PROJECT_REPONAME}/`);
|
||||
shell.exit(0);
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,245 @@
|
|||
function execute() {
|
||||
|
||||
const CWD = process.cwd();
|
||||
const fs = require('fs-extra');
|
||||
const readMetadata = require('./readMetadata.js');
|
||||
const renderToStaticMarkup = require('react-dom/server').renderToStaticMarkup;
|
||||
const path = require('path');
|
||||
const toSlug = require('../core/toSlug.js');
|
||||
const React = require('react');
|
||||
const siteConfig = require(CWD + '/siteConfig.js');
|
||||
const mkdirp = require('mkdirp');
|
||||
const glob = require('glob');
|
||||
const languages = require(CWD + '/languages.js');
|
||||
const Site = require('../core/Site.js');
|
||||
|
||||
console.log('generate.js triggered...');
|
||||
|
||||
function writeFileAndCreateFolder(file, content) {
|
||||
mkdirp.sync(file.replace(new RegExp('/[^/]*$'), ''));
|
||||
|
||||
fs.writeFileSync(file, content);
|
||||
}
|
||||
|
||||
const TABLE_OF_CONTENTS_TOKEN = '<AUTOGENERATED_TABLE_OF_CONTENTS>';
|
||||
|
||||
const insertTableOfContents = rawContent => {
|
||||
const regexp = /\n###\s+(`.*`.*)\n/g;
|
||||
let match;
|
||||
const headers = [];
|
||||
while ((match = regexp.exec(rawContent))) {
|
||||
headers.push(match[1]);
|
||||
}
|
||||
|
||||
const tableOfContents = headers
|
||||
.map(header => ` - [${header}](#${toSlug(header)})`)
|
||||
.join('\n');
|
||||
|
||||
return rawContent.replace(TABLE_OF_CONTENTS_TOKEN, tableOfContents);
|
||||
};
|
||||
|
||||
const regexSubFolder = /docs\/(.*)\/.*/;
|
||||
|
||||
const enabledLanguages = [];
|
||||
languages.filter(lang => lang.enabled).map(lang => {
|
||||
enabledLanguages.push(lang.tag);
|
||||
});
|
||||
|
||||
readMetadata.generateDocsMetadata();
|
||||
const Metadata = require('../core/metadata.js');
|
||||
let mdToHtml = {};
|
||||
for (let i = 0; i < Metadata.length; i++) {
|
||||
const metadata = Metadata[i];
|
||||
mdToHtml['/docs/' + metadata.language + '/' + metadata.source] = siteConfig.baseUrl + metadata.permalink;
|
||||
}
|
||||
|
||||
const readCategories = require('./readCategories.js');
|
||||
let layouts = {};
|
||||
for (let i = 0; i < Metadata.length; i++) {
|
||||
let layout = Metadata[i].layout;
|
||||
if (layouts[layout] !== true) {
|
||||
layouts[layout] = true;
|
||||
readCategories(layout);
|
||||
}
|
||||
}
|
||||
|
||||
const DocsLayout = require('../core/DocsLayout.js');
|
||||
|
||||
fs.removeSync(__dirname + '/../../build');
|
||||
|
||||
|
||||
// create html files for all docs
|
||||
let files = glob.sync(CWD + '/../docs/**');
|
||||
files.forEach(file => {
|
||||
// console.log(file);
|
||||
let language = 'en';
|
||||
const match = regexSubFolder.exec(file);
|
||||
if (match) {
|
||||
language = match[1];
|
||||
}
|
||||
|
||||
if (enabledLanguages.indexOf(language) === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
const extension = path.extname(file);
|
||||
|
||||
if (extension === '.md' || extension === '.markdown') {
|
||||
const result = readMetadata.processMetadata(file);
|
||||
|
||||
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>;
|
||||
const str = renderToStaticMarkup(docComp);
|
||||
|
||||
let targetFile = __dirname + '/../../build' + siteConfig.baseUrl + metadata.permalink;
|
||||
// console.log(targetFile);
|
||||
writeFileAndCreateFolder(targetFile, str);
|
||||
}
|
||||
});
|
||||
|
||||
// create html files for all blog posts
|
||||
readMetadata.generateBlogMetadata();
|
||||
const MetadataBlog = require('../core/MetadataBlog.js');
|
||||
const BlogPostLayout = require('../core/BlogPostLayout.js');
|
||||
|
||||
files = glob.sync(CWD + '/../blog/**/*.*');
|
||||
files.sort().reverse().forEach(file => {
|
||||
/* convert filename ot use slashes */
|
||||
const filePath = path
|
||||
.basename(file)
|
||||
.replace('-', '/')
|
||||
.replace('-', '/')
|
||||
.replace('-', '/')
|
||||
.replace(/\./g, '-')
|
||||
.replace(/\-md$/, '.html');
|
||||
const result = readMetadata.extractMetadata(fs.readFileSync(file, {encoding: 'utf8'}));
|
||||
const rawContent = result.rawContent;
|
||||
const metadata = Object.assign(
|
||||
{path: filePath, content: rawContent},
|
||||
result.metadata
|
||||
);
|
||||
metadata.id = metadata.title;
|
||||
|
||||
let language = 'en';
|
||||
const blogPostComp = <BlogPostLayout metadata={metadata} language={language} config={siteConfig}>{rawContent}</BlogPostLayout>;
|
||||
const str = renderToStaticMarkup(blogPostComp);
|
||||
|
||||
let targetFile = __dirname + '/../../build' + siteConfig.baseUrl + 'blog/' + filePath;
|
||||
writeFileAndCreateFolder(targetFile, str);
|
||||
});
|
||||
// create html files for all blog pages
|
||||
const BlogPageLayout = require('../core/BlogPageLayout.js');
|
||||
const perPage = 10;
|
||||
for (
|
||||
let page = 0;
|
||||
page < Math.ceil(MetadataBlog.length / perPage);
|
||||
page++
|
||||
) {
|
||||
let language = 'en';
|
||||
const metadata = {page: page, perPage: perPage};
|
||||
const blogPageComp = <BlogPageLayout metadata={metadata} language={language} config={siteConfig}></BlogPageLayout>
|
||||
const str = renderToStaticMarkup(blogPageComp);
|
||||
|
||||
let targetFile = __dirname + '/../../build' + siteConfig.baseUrl + 'blog' + (page > 0 ? '/page' + (page + 1) : '') + '/index.html';
|
||||
writeFileAndCreateFolder(targetFile, str);
|
||||
}
|
||||
|
||||
/* compile/copy all files in src of docusaurus into build */
|
||||
files = glob.sync(__dirname + '/../src/**');
|
||||
files.forEach(file => {
|
||||
// console.log(file);
|
||||
let targetFile = file.replace('/lib/src/', '/build' + siteConfig.baseUrl);
|
||||
if (file.match(/\.js$/) && !file.match(/src\/js/)) {
|
||||
targetFile = targetFile.replace(/\.js$/, '.html');
|
||||
|
||||
let language = 'en';
|
||||
|
||||
const ReactComp = require(file);
|
||||
const str = renderToStaticMarkup(<Site language={language} config={siteConfig}><ReactComp /></Site>);
|
||||
writeFileAndCreateFolder(targetFile, str);
|
||||
}
|
||||
else if (file.match(/\.css$/)) {
|
||||
console.log(file);
|
||||
let cssContent = fs.readFileSync(file);
|
||||
cssContent = cssContent.toString().replace(new RegExp('{primaryColor}', 'g'), siteConfig.colors.primaryColor);
|
||||
cssContent = cssContent.replace(new RegExp('{secondaryColor}', 'g'), siteConfig.colors.secondaryColor);
|
||||
cssContent = cssContent.replace(new RegExp('{prismColor}', 'g'), siteConfig.colors.prismColor);
|
||||
|
||||
|
||||
mkdirp.sync(targetFile.replace(new RegExp('/[^/]*$'), ''));
|
||||
fs.writeFileSync(targetFile, cssContent);
|
||||
}
|
||||
else if (!fs.lstatSync(file).isDirectory()) {
|
||||
// console.log(file);
|
||||
mkdirp.sync(targetFile.replace(new RegExp('/[^/]*$'), ''));
|
||||
fs.copySync(file, targetFile);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/* copy all user provided files into build folder, compiling any js files in src
|
||||
by making a temporary copy in docusaurus's src folder so React components are
|
||||
required with corresponding paths */
|
||||
files = glob.sync(CWD + '/src/**');
|
||||
files.forEach(file => {
|
||||
if (file.match(/\.js$/) && !file.match(/src\/js/)) {
|
||||
let parts = file.split('src');
|
||||
let tempFile = __dirname +'/../src' + parts[1];
|
||||
tempFile = tempFile.replace(path.basename(file), 'temp' + path.basename(file));
|
||||
mkdirp.sync(tempFile.replace(new RegExp('/[^/]*$'), ''));
|
||||
fs.copySync(file, tempFile);
|
||||
|
||||
let language = 'en';
|
||||
|
||||
let targetFile = __dirname + '/../../build' + siteConfig.baseUrl + parts[1];
|
||||
targetFile = targetFile.replace(/\.js$/, '.html');
|
||||
const ReactComp = require(tempFile);
|
||||
const str = renderToStaticMarkup(<Site language={language} config={siteConfig}><ReactComp /></Site>);
|
||||
writeFileAndCreateFolder(targetFile, str);
|
||||
|
||||
fs.removeSync(tempFile);
|
||||
}
|
||||
else if (file.match(/\.css$/)) {
|
||||
console.log(file);
|
||||
let parts = file.split('src');
|
||||
let cssContent = fs.readFileSync(file);
|
||||
cssContent = cssContent.toString().replace(new RegExp('{primaryColor}', 'g'), siteConfig.colors.primaryColor);
|
||||
cssContent = cssContent.replace(new RegExp('{secondaryColor}', 'g'), siteConfig.colors.secondaryColor);
|
||||
cssContent = cssContent.replace(new RegExp('{prismColor}', 'g'), siteConfig.colors.prismColor);
|
||||
|
||||
|
||||
let targetFile = __dirname + '/../../build' + siteConfig.baseUrl + parts[1];
|
||||
mkdirp.sync(targetFile.replace(new RegExp('/[^/]*$'), ''));
|
||||
fs.writeFileSync(targetFile, cssContent);
|
||||
}
|
||||
else if (!fs.lstatSync(file).isDirectory()) {
|
||||
let parts = file.split('src');
|
||||
let targetFile = __dirname + '/../../build' + siteConfig.baseUrl + parts[1];
|
||||
mkdirp.sync(targetFile.replace(new RegExp('/[^/]*$'), ''));
|
||||
fs.copySync(file, targetFile);
|
||||
}
|
||||
});
|
||||
|
||||
/* copy html files in 'en' to base level as well */
|
||||
files = glob.sync(__dirname + '/../../build' + siteConfig.baseUrl +'en/**');
|
||||
files.forEach(file => {
|
||||
let targetFile = file.replace('en/', '');
|
||||
if (file.match(/\.html$/)) {
|
||||
fs.copySync(file, targetFile);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = execute;
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
const CWD = process.cwd();
|
||||
|
||||
const Metadata = require('../core/metadata.js');
|
||||
const fs = require('fs');
|
||||
const languages = require(CWD + '/languages.js');
|
||||
|
||||
function readCategories(layout) {
|
||||
|
||||
const enabledLanguages = [];
|
||||
languages.filter(lang => lang.enabled).map(lang => {
|
||||
enabledLanguages.push(lang.tag);
|
||||
});
|
||||
|
||||
const allCategories = {};
|
||||
|
||||
for (let k = 0; k < enabledLanguages.length; ++k) {
|
||||
const language = enabledLanguages[k];
|
||||
|
||||
const metadatas = Metadata.filter(metadata => {
|
||||
return metadata.layout === layout &&
|
||||
metadata.language === language;
|
||||
});
|
||||
|
||||
// Build a hashmap of article_id -> metadata
|
||||
const articles = {};
|
||||
for (let i = 0; i < metadatas.length; ++i) {
|
||||
const metadata = metadatas[i];
|
||||
articles[metadata.id] = metadata;
|
||||
}
|
||||
|
||||
// Build a hashmap of article_id -> previous_id
|
||||
const previous = {};
|
||||
for (let i = 0; i < metadatas.length; ++i) {
|
||||
const metadata = metadatas[i];
|
||||
if (metadata.next) {
|
||||
if (!articles[metadata.next]) {
|
||||
throw new Error(
|
||||
'`next: ' + metadata.next + '` in ' + metadata.id + " doesn't exist"
|
||||
);
|
||||
}
|
||||
previous[articles[metadata.next].id] = metadata.id;
|
||||
}
|
||||
}
|
||||
|
||||
// Find the first element which doesn't have any previous
|
||||
let first = null;
|
||||
for (let i = 0; i < metadatas.length; ++i) {
|
||||
const metadata = metadatas[i];
|
||||
if (!previous[metadata.id]) {
|
||||
first = metadata;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const categories = [];
|
||||
let currentCategory = null;
|
||||
|
||||
let metadata = first;
|
||||
let i = 0;
|
||||
while (metadata && i++ < 1000) {
|
||||
if (!currentCategory || metadata.category !== currentCategory.name) {
|
||||
currentCategory && categories.push(currentCategory);
|
||||
currentCategory = {
|
||||
name: metadata.category,
|
||||
links: [],
|
||||
};
|
||||
}
|
||||
currentCategory.links.push(metadata);
|
||||
metadata = articles[metadata.next];
|
||||
}
|
||||
categories.push(currentCategory);
|
||||
|
||||
allCategories[language] = categories;
|
||||
}
|
||||
|
||||
fs.writeFileSync(
|
||||
__dirname + '/../core/' + layout + 'Categories.js',
|
||||
'/**\n' +
|
||||
' * @generated\n' +
|
||||
' */\n' +
|
||||
'module.exports = ' +
|
||||
JSON.stringify(allCategories, null, 2) +
|
||||
';'
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
module.exports = readCategories;
|
||||
|
|
@ -0,0 +1,151 @@
|
|||
const CWD = process.cwd();
|
||||
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const os = require('os');
|
||||
const languages = require(CWD + '/languages.js');
|
||||
const glob = require('glob');
|
||||
|
||||
function splitHeader(content) {
|
||||
const lines = content.split(os.EOL);
|
||||
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();
|
||||
// Handle the case where you have "Community #10"
|
||||
try {
|
||||
value = JSON.parse(value);
|
||||
} catch (e) {}
|
||||
metadata[key] = value;
|
||||
}
|
||||
return {metadata, rawContent: both.content};
|
||||
}
|
||||
|
||||
function processMetadata(file) {
|
||||
const result = extractMetadata(fs.readFileSync(file, 'utf8'));
|
||||
|
||||
let language = 'en';
|
||||
|
||||
const metadata = result.metadata;
|
||||
const rawContent = result.rawContent;
|
||||
metadata.source = path.basename(file);
|
||||
|
||||
metadata.localized_id = metadata.id;
|
||||
metadata.id = language + '-' + metadata.id;
|
||||
if (metadata.previous) {
|
||||
metadata.previous_id = metadata.previous;
|
||||
metadata.previous = language + '-' + metadata.previous;
|
||||
}
|
||||
if (metadata.next) {
|
||||
metadata.next_id = metadata.next;
|
||||
metadata.next = language + '-' + metadata.next;
|
||||
}
|
||||
metadata.language = language;
|
||||
|
||||
return {metadata, rawContent: rawContent};
|
||||
}
|
||||
|
||||
function generateDocsMetadata() {
|
||||
|
||||
const regexSubFolder = /docs\/(.*)\/.*/;
|
||||
|
||||
const enabledLanguages = [];
|
||||
languages.filter(lang => lang.enabled).map(lang => {
|
||||
enabledLanguages.push(lang.tag);
|
||||
});
|
||||
|
||||
const metadatas = [];
|
||||
|
||||
const files = glob.sync(CWD + '/../docs/**');
|
||||
files.forEach(file => {
|
||||
let language = 'en';
|
||||
const match = regexSubFolder.exec(file);
|
||||
if (match) {
|
||||
language = match[1];
|
||||
}
|
||||
|
||||
if (enabledLanguages.indexOf(language) === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
const extension = path.extname(file);
|
||||
|
||||
if (extension === '.md' || extension === '.markdown') {
|
||||
const res = processMetadata(file);
|
||||
const metadata = res.metadata;
|
||||
metadatas.push(metadata);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
fs.writeFileSync(
|
||||
__dirname + '/../core/metadata.js',
|
||||
'/**\n' +
|
||||
' * @generated\n' +
|
||||
' */\n' +
|
||||
'module.exports = ' +
|
||||
JSON.stringify(metadatas, null, 2) +
|
||||
';'
|
||||
);
|
||||
}
|
||||
|
||||
function generateBlogMetadata() {
|
||||
const metadatas = [];
|
||||
|
||||
let files = glob.sync(CWD + '/../blog/**/*.*');
|
||||
files.sort().reverse().forEach(file => {
|
||||
// Transform
|
||||
// 2015-08-13-blog-post-name-0.5.md
|
||||
// into
|
||||
// 2015/08/13/blog-post-name-0-5.html
|
||||
const filePath = path
|
||||
.basename(file)
|
||||
.replace('-', '/')
|
||||
.replace('-', '/')
|
||||
.replace('-', '/')
|
||||
// react-middleware is broken with files that contains multiple .
|
||||
// like react-0.14.js
|
||||
.replace(/\./g, '-')
|
||||
.replace(/\-md$/, '.html');
|
||||
const result = extractMetadata(fs.readFileSync(file, {encoding: 'utf8'}));
|
||||
const rawContent = result.rawContent;
|
||||
const metadata = Object.assign(
|
||||
{path: filePath, content: rawContent},
|
||||
result.metadata
|
||||
);
|
||||
metadata.id = metadata.title;
|
||||
metadatas.push(metadata);
|
||||
|
||||
fs.writeFileSync(
|
||||
__dirname + '/../core/MetadataBlog.js',
|
||||
'/**\n' +
|
||||
' * @generated\n' +
|
||||
' */\n' +
|
||||
'module.exports = ' +
|
||||
JSON.stringify(metadatas, null, 2) +
|
||||
';'
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports.extractMetadata = extractMetadata;
|
||||
module.exports.processMetadata = processMetadata;
|
||||
module.exports.generateDocsMetadata = generateDocsMetadata;
|
||||
module.exports.generateBlogMetadata = generateBlogMetadata;
|
||||
|
|
@ -0,0 +1,315 @@
|
|||
function execute () {
|
||||
|
||||
const CWD = process.cwd();
|
||||
const express = require('express');
|
||||
const React = require('react');
|
||||
const renderToStaticMarkup = require('react-dom/server').renderToStaticMarkup;
|
||||
const fs = require('fs-extra');
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
const readMetadata = require('./readMetadata.js');
|
||||
const toSlug = require('../core/toSlug.js');
|
||||
const mkdirp = require('mkdirp');
|
||||
let siteConfig = require(CWD + '/siteConfig.js');
|
||||
|
||||
/**
|
||||
* Removes a module from the cache
|
||||
*/
|
||||
function purgeCache(moduleName) {
|
||||
// Traverse the cache looking for the files
|
||||
// loaded by the specified module name
|
||||
searchCache(moduleName, function (mod) {
|
||||
delete require.cache[mod.id];
|
||||
});
|
||||
|
||||
// Remove cached paths to the module.
|
||||
Object.keys(module.constructor._pathCache).forEach(function(cacheKey) {
|
||||
if (cacheKey.indexOf(moduleName)>0) {
|
||||
delete module.constructor._pathCache[cacheKey];
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Traverses the cache to search for all the cached
|
||||
* files of the specified module name
|
||||
*/
|
||||
function searchCache(moduleName, callback) {
|
||||
// Resolve the module identified by the specified name
|
||||
let mod = require.resolve(moduleName);
|
||||
|
||||
// Check if the module has been resolved and found within
|
||||
// the cache
|
||||
if (mod && ((mod = require.cache[mod]) !== undefined)) {
|
||||
// Recursively go over the results
|
||||
(function traverse(mod) {
|
||||
// Go over each of the module's children and
|
||||
// traverse them
|
||||
mod.children.forEach(function (child) {
|
||||
traverse(child);
|
||||
});
|
||||
|
||||
// Call the specified callback providing the
|
||||
// found cached module
|
||||
callback(mod);
|
||||
}(mod));
|
||||
}
|
||||
};
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
let Metadata;
|
||||
let readCategories;
|
||||
|
||||
function reloadMetadataCategories() {
|
||||
readMetadata.generateDocsMetadata();
|
||||
purgeCache('../core/metadata.js');
|
||||
Metadata = require('../core/metadata.js');
|
||||
purgeCache('./readCategories.js');
|
||||
readCategories = require('./readCategories.js');
|
||||
|
||||
let layouts = {};
|
||||
for (let i = 0; i < Metadata.length; i++) {
|
||||
let layout = Metadata[i].layout;
|
||||
if (layouts[layout] !== true) {
|
||||
layouts[layout] = true;
|
||||
readCategories(layout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
const TABLE_OF_CONTENTS_TOKEN = '<AUTOGENERATED_TABLE_OF_CONTENTS>';
|
||||
|
||||
const insertTableOfContents = rawContent => {
|
||||
const regexp = /\n###\s+(`.*`.*)\n/g;
|
||||
let match;
|
||||
const headers = [];
|
||||
while ((match = regexp.exec(rawContent))) {
|
||||
headers.push(match[1]);
|
||||
}
|
||||
|
||||
const tableOfContents = headers
|
||||
.map(header => ` - [${header}](#${toSlug(header)})`)
|
||||
.join('\n');
|
||||
|
||||
return rawContent.replace(TABLE_OF_CONTENTS_TOKEN, tableOfContents);
|
||||
};
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
console.log('server.js triggered...');
|
||||
|
||||
const port = 3000;
|
||||
|
||||
reloadMetadataCategories();
|
||||
|
||||
/* handle all requests for document pages */
|
||||
const app = express()
|
||||
.get(/docs\/[\s\S]*html$/, (req, res) => {
|
||||
purgeCache(CWD + '/siteConfig.js');
|
||||
siteConfig = require(CWD + '/siteConfig.js');
|
||||
|
||||
console.log(req.path);
|
||||
|
||||
reloadMetadataCategories();
|
||||
let links = {};
|
||||
for (let i = 0; i < Metadata.length; i++) {
|
||||
const metadata = Metadata[i];
|
||||
links[metadata.permalink] = 'docs/' + metadata.language + '/' + metadata.source;
|
||||
}
|
||||
let mdToHtml = {};
|
||||
for (let i = 0; i < Metadata.length; i++) {
|
||||
const metadata = Metadata[i];
|
||||
mdToHtml['/docs/' + metadata.language + '/' + metadata.source] = siteConfig.baseUrl + metadata.permalink;
|
||||
}
|
||||
|
||||
let file = links[req.path.toString().replace(siteConfig.baseUrl, '')];
|
||||
file = CWD + '/../' + file;
|
||||
console.log(file);
|
||||
const result = readMetadata.processMetadata(file);
|
||||
|
||||
let language = 'en';
|
||||
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]);
|
||||
});
|
||||
|
||||
purgeCache('../core/DocsLayout.js');
|
||||
const DocsLayout = require('../core/DocsLayout.js');
|
||||
const docComp = <DocsLayout metadata={metadata} language={language} config={siteConfig}>{rawContent}</DocsLayout>;
|
||||
|
||||
res.send(renderToStaticMarkup(docComp));
|
||||
});
|
||||
/* handle all requests for blog pages and posts */
|
||||
app.get(/blog\/[\s\S]*html$/, (req, res) => {
|
||||
purgeCache(CWD + '/siteConfig.js');
|
||||
siteConfig = require(CWD + '/siteConfig.js');
|
||||
|
||||
readMetadata.generateBlogMetadata();
|
||||
MetadataBlog = require('../core/MetadataBlog.js');
|
||||
purgeCache('../core/MetadataBlog.js')
|
||||
readMetadata.generateBlogMetadata();
|
||||
MetadataBlog = require('../core/MetadataBlog.js');
|
||||
|
||||
/* generate all of the blog pages */
|
||||
purgeCache('../core/BlogPageLayout.js');
|
||||
const BlogPageLayout = require('../core/BlogPageLayout.js');
|
||||
const blogPages = {};
|
||||
/* make blog pages with 10 posts per page */
|
||||
const perPage = 10;
|
||||
for (
|
||||
let page = 0;
|
||||
page < Math.ceil(MetadataBlog.length / perPage);
|
||||
page++
|
||||
) {
|
||||
let language = 'en';
|
||||
const metadata = {page: page, perPage: perPage};
|
||||
const blogPageComp = <BlogPageLayout metadata={metadata} language={language} config={siteConfig} />;
|
||||
const str = renderToStaticMarkup(blogPageComp);
|
||||
|
||||
let path = (page > 0 ? 'page' + (page + 1) : '') + '/index.html';
|
||||
blogPages[path] = str;
|
||||
}
|
||||
|
||||
let parts = req.path.toString().split('blog/');
|
||||
// send corresponding blog page if appropriate
|
||||
if (parts[1] === 'index.html') {
|
||||
res.send(blogPages['/index.html']);
|
||||
}
|
||||
else if (parts[1].endsWith('/index.html')) {
|
||||
res.send(blogPages[parts[1]]);
|
||||
}
|
||||
else if (parts[1].match(/page([0-9]+)/)) {
|
||||
if (parts[1].endsWith('/')) {
|
||||
res.send(blogPages[parts[1] + 'index.html']);
|
||||
} else {
|
||||
res.send(blogPages[parts[1] + '/index.html']);
|
||||
}
|
||||
}
|
||||
// else send corresponding blog post
|
||||
else {
|
||||
let file = parts[1];
|
||||
file = file.replace(/\.html$/, '.md');
|
||||
file = file.replace(new RegExp('/', 'g'), '-');
|
||||
file = CWD + '/../blog/' + file;
|
||||
|
||||
const result = readMetadata.extractMetadata(fs.readFileSync(file, {encoding: 'utf8'}));
|
||||
const rawContent = result.rawContent;
|
||||
const metadata = Object.assign(
|
||||
{path: req.path.toString().split('blog/')[1], content: rawContent},
|
||||
result.metadata
|
||||
);
|
||||
metadata.id = metadata.title;
|
||||
|
||||
let language = 'en';
|
||||
purgeCache('../core/BlogPostLayout.js')
|
||||
const BlogPostLayout = require('../core/BlogPostLayout.js');
|
||||
|
||||
const blogPostComp = <BlogPostLayout metadata={metadata} language={language} config={siteConfig}>{rawContent}</BlogPostLayout>;
|
||||
res.send(renderToStaticMarkup(blogPostComp));
|
||||
}
|
||||
});
|
||||
|
||||
/* redirect /blog and /blog/ to /blog/index.html */
|
||||
app.get(/blog\/?$/, (req, res) => {
|
||||
res.redirect('/blog/index.html');
|
||||
});
|
||||
|
||||
/* handle all other main pages */
|
||||
app.get('*.html', (req, res) => {
|
||||
purgeCache(CWD + '/siteConfig.js');
|
||||
siteConfig = require(CWD + '/siteConfig.js');
|
||||
|
||||
console.log(req.path);
|
||||
let file = req.path.toString().replace(/\.html$/, '.js');
|
||||
file = file.replace(siteConfig.baseUrl, '');
|
||||
userFile = CWD + '/src/' + file;
|
||||
defaultFile = __dirname + '/../src/' + file
|
||||
|
||||
let language = 'en';
|
||||
|
||||
/* look for user provided file either in specified path or in path for english files */
|
||||
if (fs.existsSync(userFile) ||
|
||||
fs.existsSync(userFile=userFile.replace(path.basename(userFile), 'en/' + path.basename(userFile)))) {
|
||||
|
||||
/* copy into docusaurus so require paths work */
|
||||
let parts = userFile.split('src/');
|
||||
let tempFile = __dirname + '/../src/' + parts[1];
|
||||
tempFile = tempFile.replace(path.basename(file), 'temp' + path.basename(file));
|
||||
mkdirp.sync(tempFile.replace(new RegExp('/[^/]*$'), ''));
|
||||
fs.copySync(userFile, tempFile);
|
||||
|
||||
/* render into a string */
|
||||
purgeCache(tempFile);
|
||||
const ReactComp = require(tempFile);
|
||||
purgeCache('../core/Site.js');
|
||||
const Site = require('../core/Site.js');
|
||||
const str = renderToStaticMarkup(<Site language={language} config={siteConfig}><ReactComp /></Site>);
|
||||
|
||||
fs.removeSync(tempFile);
|
||||
|
||||
res.send(str);
|
||||
}
|
||||
/* look for a default file */
|
||||
else if (fs.existsSync(defaultFile)) {
|
||||
purgeCache(defaultFile);
|
||||
const ReactComp = require(defaultFile);
|
||||
purgeCache('../core/Site.js');
|
||||
const Site = require('../core/Site.js');
|
||||
res.send(renderToStaticMarkup(<Site language={language} config={siteConfig}><ReactComp /></Site>));
|
||||
}
|
||||
else {
|
||||
console.log(req.path);
|
||||
res.send('No file found');
|
||||
}
|
||||
});
|
||||
|
||||
/* redirect baseUrl and / to /index.html */
|
||||
app.get('/', (req, res) => {
|
||||
res.redirect('/index.html');
|
||||
});
|
||||
app.get(siteConfig.baseUrl, (req, res) => {
|
||||
res.redirect('/index.html');
|
||||
});
|
||||
|
||||
/* generate the css file with the specified colors from siteConfig */
|
||||
app.get('*.css', (req, res) => {
|
||||
let filePath = req.path.toString().replace(siteConfig.baseUrl, '/');
|
||||
let cssContent;
|
||||
if (fs.existsSync(CWD + '/src' + filePath)) {
|
||||
cssContent = fs.readFileSync(CWD + '/src' + filePath);
|
||||
}
|
||||
else if (fs.existsSync(__dirname + '/../src' + filePath)) {
|
||||
cssContent = fs.readFileSync(__dirname + '/../src' + filePath);
|
||||
}
|
||||
else {
|
||||
res.send('No File Found');
|
||||
return;
|
||||
}
|
||||
cssContent = cssContent.toString().replace(new RegExp('{primaryColor}', 'g'), siteConfig.colors.primaryColor);
|
||||
cssContent = cssContent.replace(new RegExp('{secondaryColor}', 'g'), siteConfig.colors.secondaryColor);
|
||||
cssContent = cssContent.replace(new RegExp('{prismColor}', 'g'), siteConfig.colors.prismColor);
|
||||
|
||||
res.send(cssContent);
|
||||
});
|
||||
|
||||
/* serve static content first from user folder then from docusaurus */
|
||||
app.use(siteConfig.baseUrl, express.static(CWD + '/src'));
|
||||
app.use(siteConfig.baseUrl, express.static(__dirname + '/../src'));
|
||||
|
||||
app.listen(port);
|
||||
console.log('listening on port: ' + port);
|
||||
|
||||
}
|
||||
|
||||
module.exports = execute;
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
const React = require('react');
|
||||
|
||||
class help extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<h1>Help Page</h1>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = help;
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
const React = require('react');
|
||||
|
||||
class index extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<h1>Index Page</h1>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = index;
|
||||
|
After Width: | Height: | Size: 76 KiB |
|
After Width: | Height: | Size: 984 B |
|
After Width: | Height: | Size: 9.4 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
|
@ -0,0 +1,9 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
require('babel-register') ({
|
||||
ignore: false,
|
||||
"presets": ["react"]
|
||||
});
|
||||
|
||||
const server = require('./server/server.js');
|
||||
server();
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
{
|
||||
"scripts": {
|
||||
"start": "./lib/start-server.js",
|
||||
"build": "./lib/build-files.js",
|
||||
"publish": "./lib/publish-gh-pages.js",
|
||||
"examples": "./lib/copy-examples.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"babel-preset-react": "^6.24.1",
|
||||
"babel-register": "^6.24.1",
|
||||
"classnames": "^2.2.5",
|
||||
"express": "^4.15.3",
|
||||
"fs-extra": "^3.0.1",
|
||||
"glob": "^7.1.2",
|
||||
"react": "^15.5.4",
|
||||
"react-dom": "^15.5.4",
|
||||
"shelljs": "^0.7.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-cli": "^6.24.1",
|
||||
"babel-preset-react": "^6.24.1"
|
||||
},
|
||||
"name": "docusaurus",
|
||||
"version": "0.0.8-t",
|
||||
"bin": {
|
||||
"docusaurus-start": "./lib/start-server.js",
|
||||
"docusaurus-build": "./lib/build-files.js",
|
||||
"docusaurus-publish": "./lib/publish-gh-pages.js",
|
||||
"docusaurus-examples": "./lib/copy-examples.js"
|
||||
}
|
||||
}
|
||||