chore: removal of Docusaurus v1 code from master (it is now available on docusaurus-v1 branch) (#4902)
|
@ -6,20 +6,14 @@ build
|
|||
coverage
|
||||
jest.config.js
|
||||
jest.transform.js
|
||||
website-1.x/
|
||||
website/
|
||||
scripts
|
||||
|
||||
packages/docusaurus/lib/
|
||||
packages/docusaurus-*/lib/*
|
||||
!packages/docusaurus-1.x/lib/*
|
||||
packages/docusaurus-1.x/lib/core/__tests__/split-tab.test.js
|
||||
packages/docusaurus-1.x/lib/core/metadata.js
|
||||
packages/docusaurus-1.x/lib/core/MetadataBlog.js
|
||||
packages/docusaurus-*/lib-next/
|
||||
packages/docusaurus-plugin-ideal-image/copyUntypedFiles.js
|
||||
|
||||
packages/docusaurus-1.x/.eslintrc.js
|
||||
packages/docusaurus-init/templates/facebook/.eslintrc.js
|
||||
|
||||
examples/bootstrap/babel.config.js
|
||||
|
|
33
.github/workflows/migration-cli.yml
vendored
|
@ -1,33 +0,0 @@
|
|||
name: Migration CLI
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- packages/docusaurus-migration/**
|
||||
- website-1.x/**
|
||||
|
||||
jobs:
|
||||
build:
|
||||
timeout-minutes: 30
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node: ['12', '14']
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js ${{ matrix.node }}
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
- name: Installation
|
||||
uses: bahmutov/npm-install@v1
|
||||
with:
|
||||
install-command: yarn
|
||||
- name: Migrate D1 website
|
||||
run: yarn test:v1Migration:migrate
|
||||
- name: Build D1 migrated website
|
||||
run: yarn test:v1Migration:build
|
||||
env:
|
||||
CI: true
|
29
.github/workflows/v1-tests-windows.yml
vendored
|
@ -1,29 +0,0 @@
|
|||
name: V1 Tests Windows
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- 'website-1.x/**'
|
||||
- 'packages/docusaurus-1.x/**'
|
||||
- 'packages/docusaurus-init-1.x/**'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
timeout-minutes: 30
|
||||
runs-on: windows-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node: ['12', '14']
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js ${{ matrix.node }}
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
- name: Installation
|
||||
run: yarn || yarn || yarn # 3 attempts to avoid timeout errors...
|
||||
- name: Docusaurus 1 Tests
|
||||
run: yarn test:v1
|
||||
# Not useful to build v1 site because tests already build v1 (packages/docusaurus-1.x/lib/__tests__/build-files.test.js)
|
31
.github/workflows/v1-tests.yml
vendored
|
@ -1,31 +0,0 @@
|
|||
name: V1 Tests
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- 'website-1.x/**'
|
||||
- 'packages/docusaurus-1.x/**'
|
||||
- 'packages/docusaurus-init-1.x/**'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
timeout-minutes: 30
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node: ['12', '14']
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js ${{ matrix.node }}
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
- name: Installation
|
||||
uses: bahmutov/npm-install@v1
|
||||
with:
|
||||
install-command: yarn
|
||||
- name: Test
|
||||
run: yarn test:v1
|
||||
# Not useful to build v1 site because tests already build v1 (packages/docusaurus-1.x/lib/__tests__/build-files.test.js)
|
5
.gitignore
vendored
|
@ -20,17 +20,12 @@ test-website
|
|||
|
||||
packages/docusaurus/lib/
|
||||
packages/docusaurus-*/lib/*
|
||||
!packages/docusaurus-1.x/lib/*
|
||||
packages/docusaurus-1.x/lib/core/metadata.js
|
||||
packages/docusaurus-1.x/lib/core/MetadataBlog.js
|
||||
packages/docusaurus-*/lib-next/
|
||||
|
||||
website/netlifyDeployPreview/*
|
||||
!website/netlifyDeployPreview/index.html
|
||||
!website/netlifyDeployPreview/_redirects
|
||||
|
||||
website-1.x-migrated
|
||||
|
||||
website/i18n/**/*
|
||||
#!website/i18n/fr
|
||||
#!website/i18n/fr/**/*
|
||||
|
|
|
@ -6,13 +6,9 @@ coverage
|
|||
.docusaurus
|
||||
packages/docusaurus/lib/
|
||||
packages/docusaurus-*/lib/*
|
||||
!packages/docusaurus-1.x/lib/*
|
||||
packages/docusaurus-1.x/lib/core/metadata.js
|
||||
packages/docusaurus-1.x/lib/core/MetadataBlog.js
|
||||
packages/docusaurus-*/lib-next/
|
||||
__fixtures__
|
||||
|
||||
website-1.x/translated_docs
|
||||
website/i18n
|
||||
website/versions.json
|
||||
|
||||
|
|
1355
CHANGELOG-1.x.md
|
@ -22,7 +22,7 @@
|
|||
|
||||
> **We are working hard on Docusaurus v2. If you are new to Docusaurus, try using the new version instead of v1. See the [Docusaurus v2 website](https://docusaurus.io/) for more details.**
|
||||
|
||||
> Docusaurus v1 doc is available at [v1.docusaurus.io](https://v1.docusaurus.io)
|
||||
> Docusaurus v1 doc is available at [v1.docusaurus.io](https://v1.docusaurus.io) and code is available on branch [docusaurus-v1](https://github.com/facebook/docusaurus/tree/docusaurus-v1)
|
||||
|
||||
## Introduction
|
||||
|
||||
|
|
|
@ -176,8 +176,6 @@ yarn build:packages
|
|||
yarn lerna publish 2.0.0-alpha.68 --exact
|
||||
```
|
||||
|
||||
~~**Note**: The v1 packages will also be modified because it's part of the monorepo. It is not ideal but we will live with it for now.~~
|
||||
|
||||
This command does a few things:
|
||||
|
||||
- Modifies the versions of all the `package.json` in the repository to be `2.0.0-alpha.41` and creates a commit
|
||||
|
@ -234,98 +232,3 @@ NOTE: most likely this last item will be relevant for each new release, so do no
|
|||
|
||||
https://github.com/facebook/docusaurus/releases/tag/%VER%
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Docusaurus 1
|
||||
|
||||
### IMPORTANT: v1 packages are now private:
|
||||
|
||||
**TLDR**: you need to mark them as public, publish, and mark them back as private
|
||||
|
||||
v1 packages have been marked as `private: true` on purpose. This is because lerna will publish ALL (v1+v2) packages with the lerna-publish command.
|
||||
|
||||
Unfortunately it seems there is no way to tell it to ignore v1 packages while publishing v2.
|
||||
|
||||
During a long time, we published all these packages using the `@next` dist tag: `yarn lerna publish 2.0.0-alpha.41 --dist-tag next --exact`. It caused problems because v2 packages will then all need @next during npm/yarn installs, confusing some users (https://github.com/facebook/docusaurus/issues/3755).
|
||||
|
||||
We made the v1 packages private so that lerna publish won't publish them, so that we can publish v2 packages under latest dist tag, without creating v1 upgrades that people will be notified abut.
|
||||
|
||||
### Updated v1 release process
|
||||
|
||||
Process reworked by @slorber at `1.14.6`, it may not be perfect yet:
|
||||
|
||||
Suppose we are at `v1.14.5`, and want to release `v1.14.6`:
|
||||
|
||||
- Assign appropriate `tag: xyz` labels to merged PRs
|
||||
- Be on master (up-to-date): `git co master && git pull`
|
||||
- Create a new branch: `git co -b slorber/release-1.14.6`
|
||||
- Get the changelog from last release: `git fetch --tags && GITHUB_AUTH=<myToken> yarn changelog --from=v1.14.5`
|
||||
- Update [CHANGELOG-1.x.md](https://github.com/facebook/docusaurus/blob/master/CHANGELOG-1.x.md), but remove the v2-related items manually.
|
||||
- Run `yarn install`
|
||||
- Version the docs: `yarn workspace docusaurus-1-website docusaurus-version 1.14.6`
|
||||
- Test the v1 website locally: `yarn start:v1` + `yarn build:v1`
|
||||
- Make the two v1 packages private: false
|
||||
- Publish: `yarn workspace docusaurus-init publish --no-git-tag-version --new-version 1.14.6`
|
||||
- Publish: `yarn workspace docusaurus publish --no-git-tag-version --new-version 1.14.6`
|
||||
- Make the v1 package private: true
|
||||
|
||||
The release is now published. It's worth to test it by initializing a new v1 site:
|
||||
|
||||
```sh
|
||||
mkdir my-v1-website
|
||||
cd my-v1-website
|
||||
npx docusaurus-init
|
||||
cd website
|
||||
yarn start
|
||||
```
|
||||
|
||||
Finish the release:
|
||||
|
||||
- Commit: `git commit -am "chore(v1): release v1.14.6"`
|
||||
- Push: `git push origin slorber/release-1.14.6`
|
||||
- Run `git tag v1.14.6` (important: the tag is prefixed by **`v`**)
|
||||
- Run `git push origin v1.14.6`
|
||||
- Ensure you can run `yarn install` (it may fail and need to use v2 versions on the v1 packages...)
|
||||
- Open a PR, and merge it
|
||||
- Create the [new Github release](https://github.com/facebook/docusaurus/releases/new), paste the changelog
|
||||
- The End
|
||||
|
||||
### Historical v1 release process
|
||||
|
||||
1. Bump version number in [`package.json`](https://github.com/facebook/docusaurus/blob/master/packages/docusaurus-1.x/package.json).
|
||||
1. Update the [CHANGELOG-1.x.md](https://github.com/facebook/docusaurus/blob/master/CHANGELOG-1.x.md), including at the reference links at the bottom.
|
||||
1. Do this always, but particularly important if there were any `package.json` changes in this release:
|
||||
1. If there is no `node_modules` directory in you local Docusaurus version, run `yarn install` and `npm install`.
|
||||
1. Run `yarn upgrade` to update `yarn.lock` and `npm update` to update `package-lock.json`.
|
||||
1. From the `website-1.x` directory, run `npm run docusaurus-version x.x.x`, where x.x.x is the same version number you updated to in `package.json`.
|
||||
1. Test your PR locally on a project that was created via [these instructions](https://github.com/facebook/docusaurus/blob/master/admin/local-third-party-project-testing.md).
|
||||
1. Submit your PR
|
||||
1. When your PR is merged, rebase to get the PR commit locally
|
||||
1. Run `npm publish`
|
||||
1. Tag the commit with the new version prefixed with a `v` (e.g. `v1.19.0`) and push the tag to `master`
|
||||
1. Go to https://github.com/facebook/docusaurus/releases/new
|
||||
1. Under the "Tag version" field, look for the newly-created tag
|
||||
1. Paste the CHANGELOG changes in the textarea below
|
||||
1. Hit the green "Publish release" button
|
||||
1. Profit! 💰
|
||||
|
||||
### What version should you use?
|
||||
|
||||
The version number should generally increase by some factor than the current one. You can check current version by looking in `package.json`.
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "docusaurus",
|
||||
"version": "1.0.0-alpha.41",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/facebook/docusaurus.git"
|
||||
}
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
For the above example, you may want to bump the version to `1.0.0-alpha.42` or `1.0.0-beta.1` or `1.0.1`.
|
||||
|
||||
You can also see the full list of all published versions with `npm show docusaurus versions --json`.
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
project_id: '290988'
|
||||
api_token_env: 'CROWDIN_PERSONAL_TOKEN'
|
||||
base_path: "."
|
||||
preserve_hierarchy: true
|
||||
|
||||
languages_mapping: &languages_mapping
|
||||
locale:
|
||||
'es-ES': 'es-ES'
|
||||
'fr': 'fr'
|
||||
'it': 'it'
|
||||
'ja': 'ja'
|
||||
'ko': 'ko'
|
||||
'nl': 'nl'
|
||||
'pt-BR': 'pt-BR'
|
||||
'ro': 'ro'
|
||||
'ru': 'ru'
|
||||
'sk': 'sk-SK'
|
||||
'sr': 'sr'
|
||||
'sv-SE': 'sv-SE'
|
||||
'tr': 'tr'
|
||||
'uk': 'uk'
|
||||
'vi': 'vi'
|
||||
'zh-CN': 'zh-CN'
|
||||
'zh-TW': 'zh-TW'
|
||||
|
||||
files:
|
||||
-
|
||||
source: '/website-1.x/docs/**/*.md'
|
||||
translation: '/website-1.x/translated_docs/%locale%/**/%original_file_name%'
|
||||
languages_mapping: *languages_mapping
|
||||
-
|
||||
source: '/website-1.x/versioned_docs/**/*.md'
|
||||
translation: '/website-1.x/translated_docs/%locale%/**/%original_file_name%'
|
||||
languages_mapping: *languages_mapping
|
||||
-
|
||||
source: '/website-1.x/i18n/en.json'
|
||||
translation: '/website-1.x/i18n/%locale%.json'
|
||||
languages_mapping: *languages_mapping
|
|
@ -34,7 +34,7 @@ files:
|
|||
translation: '/website/i18n/%two_letters_code%/docusaurus-plugin-content-docs/**/%original_file_name%',
|
||||
},
|
||||
{
|
||||
source: '/website-1.x/blog/**/*',
|
||||
source: '/website/blog/**/*',
|
||||
translation: '/website/i18n/%two_letters_code%/docusaurus-plugin-content-blog/**/%original_file_name%',
|
||||
},
|
||||
{
|
||||
|
|
|
@ -7,19 +7,9 @@
|
|||
|
||||
const path = require('path');
|
||||
|
||||
const isWin = process.platform === 'win32';
|
||||
|
||||
const windowsSpecificIgnorePatterns = [
|
||||
// v1 is legacy, not really worth it to invest in making its tests work on Windows
|
||||
'/packages/docusaurus-1.x',
|
||||
'/packages/docusaurus-init-1.x',
|
||||
];
|
||||
|
||||
const ignorePatterns = [
|
||||
'/node_modules/',
|
||||
'__fixtures__',
|
||||
'/packages/docusaurus-1.x',
|
||||
'/packages/docusaurus-init-1.x',
|
||||
'/packages/docusaurus/lib',
|
||||
'/packages/docusaurus-utils/lib',
|
||||
'/packages/docusaurus-utils-validation/lib',
|
||||
|
@ -29,7 +19,7 @@ const ignorePatterns = [
|
|||
'/packages/docusaurus-theme-classic/lib',
|
||||
'/packages/docusaurus-theme-classic/lib-next',
|
||||
'/packages/docusaurus-migrate/lib',
|
||||
].concat(isWin ? windowsSpecificIgnorePatterns : []);
|
||||
];
|
||||
|
||||
module.exports = {
|
||||
rootDir: path.resolve(__dirname),
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
const path = require('path');
|
||||
|
||||
const ignorePatterns = ['/node_modules/', '__fixtures__'];
|
||||
|
||||
module.exports = {
|
||||
rootDir: path.join(path.resolve(__dirname), 'packages', 'docusaurus-1.x'),
|
||||
verbose: true,
|
||||
testURL: 'http://localhost/',
|
||||
testEnvironment: 'node',
|
||||
testPathIgnorePatterns: ignorePatterns,
|
||||
coveragePathIgnorePatterns: ignorePatterns,
|
||||
transform: {
|
||||
'^.+\\.[jt]sx?$': 'babel-jest',
|
||||
},
|
||||
setupFiles: ['../../jest/stylelint-rule-test.js', '../../jest/polyfills.js'],
|
||||
moduleNameMapper: {
|
||||
'@docusaurus/router': 'react-router-dom',
|
||||
},
|
||||
};
|
|
@ -3,13 +3,10 @@
|
|||
"workspaces": [
|
||||
"packages/*",
|
||||
"website",
|
||||
"website-1.x",
|
||||
"website-1.x-migrated",
|
||||
"packages/docusaurus-init/templates/*"
|
||||
],
|
||||
"scripts": {
|
||||
"start": "yarn build:packages && yarn start:v2",
|
||||
"start:v1": "yarn workspace docusaurus-1-website start",
|
||||
"start:v2": "yarn workspace docusaurus-2-website start",
|
||||
"start:v2:watch": "nodemon --watch \"./packages/*/lib/**/*.*\" --exec \"yarn start:v2\"",
|
||||
"start:v2:baseUrl": "yarn workspace docusaurus-2-website start:baseUrl",
|
||||
|
@ -19,14 +16,12 @@
|
|||
"examples:generate": "node generateExamples",
|
||||
"build": "yarn build:packages && yarn build:v2",
|
||||
"build:packages": "lerna run build --no-private",
|
||||
"build:v1": "yarn workspace docusaurus-1-website build",
|
||||
"build:v2": "yarn workspace docusaurus-2-website build",
|
||||
"build:v2:baseUrl": "yarn workspace docusaurus-2-website build:baseUrl",
|
||||
"build:v2:blogOnly": "yarn workspace docusaurus-2-website build:blogOnly",
|
||||
"build:v2:deployPreview": "cross-env NETLIFY=true CONTEXT='deploy-preview' yarn workspace docusaurus-2-website build",
|
||||
"build:v2:en": "yarn workspace docusaurus-2-website build --locale en",
|
||||
"clear:v2": "yarn workspace docusaurus-2-website clear",
|
||||
"serve:v1": "serve website-1.x/build/docusaurus",
|
||||
"serve:v2": "yarn workspace docusaurus-2-website serve",
|
||||
"serve:v2:baseUrl": "serve website",
|
||||
"serve:v2:ssl": "yarn serve:v2:ssl:gencert && yarn serve:v2:ssl:message && yarn serve:v2:ssl:serve",
|
||||
|
@ -52,13 +47,9 @@
|
|||
"lint:style": "stylelint \"**/*.css\"",
|
||||
"lerna": "lerna",
|
||||
"test": "cross-env TZ=UTC jest",
|
||||
"test:v1": "cross-env TZ=UTC V1_I18N=false jest --config jest.config.v1.js",
|
||||
"test:build:v2": "./admin/scripts/test-release.sh",
|
||||
"watch": "yarn lerna run --parallel --no-private watch",
|
||||
"clear": "yarn workspace docusaurus-2-website clear && yarn lerna exec --ignore docusaurus yarn rimraf lib",
|
||||
"test:v1Migration:migrate": "rimraf website-1.x-migrated && docusaurus-migrate migrate ./website-1.x ./website-1.x-migrated && sed -i -- 's/docusaurus-1-website/docusaurus-1-website-migrated/g;' website-1.x-migrated/package.json",
|
||||
"test:v1Migration:start": "yarn workspace docusaurus-1-website-migrated start",
|
||||
"test:v1Migration:build": "yarn workspace docusaurus-1-website-migrated build",
|
||||
"test:baseUrl": "yarn build:v2:baseUrl && yarn serve:v2:baseUrl",
|
||||
"lock:update": "npx yarn-deduplicate"
|
||||
},
|
||||
|
|
|
@ -1,58 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
const OFF = 0;
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const WARNING = 1;
|
||||
const ERROR = 2;
|
||||
|
||||
module.exports = {
|
||||
env: {
|
||||
browser: true,
|
||||
commonjs: true,
|
||||
jest: true,
|
||||
node: true,
|
||||
},
|
||||
parser: 'babel-eslint',
|
||||
extends: ['airbnb', 'prettier'],
|
||||
rules: {
|
||||
'no-console': OFF, // We have console.error, console.warn, etc.
|
||||
radix: ERROR,
|
||||
'class-methods-use-this': OFF,
|
||||
'func-names': OFF,
|
||||
'no-empty': [ERROR, {allowEmptyCatch: true}],
|
||||
'no-param-reassign': OFF,
|
||||
'no-plusplus': OFF,
|
||||
'prefer-template': OFF,
|
||||
'import/no-extraneous-dependencies': OFF,
|
||||
'react/jsx-fragments': OFF, // Babylon v6 does not support <> fragments
|
||||
'react/jsx-closing-bracket-location': OFF, // Formatting is left to Prettier.
|
||||
'react/jsx-filename-extension': OFF, // Enable in future when migrating.
|
||||
'react/jsx-one-expression-per-line': OFF, // Formatting is left to Prettier.
|
||||
'react/no-array-index-key': OFF, // It's ok if you use it for static content.
|
||||
'react/no-danger': OFF, // Need this to inject scripts.
|
||||
'react/no-multi-comp': OFF, // One component per file creates too many files.
|
||||
'react/no-unescaped-entities': [ERROR, {forbid: ['>', '}']}],
|
||||
|
||||
// Existing ESLint errors sorted by frequency, silencing first.
|
||||
'react/button-has-type': OFF, // 1
|
||||
'react/forbid-prop-types': OFF, // 1
|
||||
'react/require-default-props': OFF, // 1
|
||||
'jsx-a11y/control-has-associated-label': OFF, // 1
|
||||
'react/jsx-no-target-blank': OFF, // 1
|
||||
'react/jsx-props-no-spreading': OFF, // 2
|
||||
'max-classes-per-file': OFF, // 2
|
||||
'prefer-object-spread': OFF, // 5
|
||||
'jsx-a11y/anchor-is-valid': OFF, // 9
|
||||
'import/no-unresolved': OFF, // 15
|
||||
'react/prefer-stateless-function': OFF, // 22
|
||||
'import/no-dynamic-require': OFF, // 46
|
||||
'prefer-destructuring': OFF, // 69
|
||||
'global-require': OFF, // 85
|
||||
'react/prop-types': OFF, // 197
|
||||
'react/destructuring-assignment': OFF, // 342
|
||||
},
|
||||
};
|
|
@ -1,6 +0,0 @@
|
|||
lib/core/metadata.js
|
||||
lib/core/MetadataBlog.js
|
||||
yarn-error.log
|
||||
yarn.lock
|
||||
website
|
||||
docusaurus-init
|
|
@ -1,78 +0,0 @@
|
|||
<h1 align="center">
|
||||
<p align="center">Docusaurus</p>
|
||||
<a href="https://docusaurus.io"><img src="https://docusaurus.io/img/slash-introducing.svg" alt="Docusaurus"></a>
|
||||
</h1>
|
||||
|
||||
<p align="center">
|
||||
<a href="#backers" alt="sponsors on Open Collective"><img src="https://opencollective.com/Docusaurus/backers/badge.svg" /></a>
|
||||
<a href="#sponsors" alt="Sponsors on Open Collective"><img src="https://opencollective.com/Docusaurus/sponsors/badge.svg" /></a>
|
||||
<a href="https://www.npmjs.com/package/docusaurus"><img src="https://img.shields.io/npm/v/docusaurus.svg?style=flat" alt="npm version"></a>
|
||||
<a href="https://circleci.com/gh/facebook/docusaurus"><img src="https://circleci.com/gh/facebook/docusaurus.svg?style=shield" alt="CircleCI Status"></a>
|
||||
<a href="CONTRIBUTING.md#pull-requests"><img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg" alt="PRs Welcome"></a>
|
||||
<a href="https://discord.gg/docusaurus"><img src="https://img.shields.io/badge/chat-on%20discord-7289da.svg" alt="Chat"></a>
|
||||
<a href="https://github.com/prettier/prettier"><img alt="code style: prettier" src="https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square"></a>
|
||||
<a href="https://github.com/facebook/jest"><img src="https://img.shields.io/badge/tested_with-jest-99424f.svg" alt="Tested with Jest"></a>
|
||||
<a href="https://app.netlify.com/sites/docusaurus-preview/deploys"><img src="https://api.netlify.com/api/v1/badges/57ebb454-c937-4c1d-a228-d9dccb494f49/deploy-status" alt="Netlify Status"></a>
|
||||
</p>
|
||||
|
||||
## Introduction
|
||||
|
||||
Docusaurus is a project for easily building, deploying, and maintaining open source project websites.
|
||||
|
||||
- **Simple to Start** Docusaurus is built to be easy to [get up and running](https://docusaurus.io/docs/en/installation.html) in as little time possible. We've built Docusaurus to handle the website build process so you can focus on your project.
|
||||
- **Localizable** Docusaurus ships with [localization support](https://docusaurus.io/docs/en/translation.html) via CrowdIn. Empower and grow your international community by translating your documentation.
|
||||
- **Customizable** While Docusaurus ships with the key pages and sections you need to get started, including a home page, a docs section, a [blog](https://docusaurus.io/docs/en/blog.html), and additional support pages, it is also [customizable](https://docusaurus.io/docs/en/custom-pages.html) as well to ensure you have a site that is [uniquely yours](https://docusaurus.io/docs/en/api-pages.html).
|
||||
|
||||
## Installation
|
||||
|
||||
Docusaurus is available as the [`docusaurus` package](https://www.npmjs.com/package/docusaurus) on [npm](https://www.npmjs.com).
|
||||
|
||||
We have also released the [`docusaurus-init` package](https://www.npmjs.com/package/docusaurus-init) to make [getting started](https://docusaurus.io/docs/en/installation.html) with Docusaurus even easier.
|
||||
|
||||
## Contributing
|
||||
|
||||
We've released Docusaurus because it helps us better scale and support the many OSS projects at Facebook. We hope that other organizations can benefit from the project. We are thankful for any contributions from the community.
|
||||
|
||||
### [Code of Conduct](https://code.fb.com/codeofconduct)
|
||||
|
||||
Facebook has adopted a Code of Conduct that we expect project participants to adhere to. Please read [the full text](https://code.fb.com/codeofconduct) so that you can understand what actions will and will not be tolerated.
|
||||
|
||||
### Contributing Guide
|
||||
|
||||
Read our [contributing guide](https://github.com/facebook/docusaurus/blob/master/CONTRIBUTING.md) to learn about our development process, how to propose bugfixes and improvements, and how to build and test your changes to Docusaurus.
|
||||
|
||||
### Beginner Friendly Bugs
|
||||
|
||||
To help you get your feet wet and get you familiar with our contribution process, we have a list of [beginner friendly bugs](https://github.com/facebook/docusaurus/labels/good%20first%20issue) that might contain smaller issues to tackle first. This is a great place to get started.
|
||||
|
||||
## Contact
|
||||
|
||||
We have a few channels for contact:
|
||||
|
||||
- [Discord](https://discord.gg/docusaurus) with two text channels:
|
||||
- `#docusaurus-users` for those using Docusaurus.
|
||||
- `#docusaurus-dev` for those wanting to contribute to the Docusaurus core.
|
||||
- [@docusaurus](https://twitter.com/docusaurus) on Twitter
|
||||
- [GitHub Issues](https://github.com/facebook/docusaurus/issues)
|
||||
|
||||
## Contributors
|
||||
|
||||
This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)]. <a href="https://github.com/facebook/docusaurus/graphs/contributors"><img src="https://opencollective.com/Docusaurus/contributors.svg?width=890&button=false" /></a>
|
||||
|
||||
## Backers
|
||||
|
||||
Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/Docusaurus#backer)]
|
||||
|
||||
<a href="https://opencollective.com/Docusaurus#backers" target="_blank"><img src="https://opencollective.com/Docusaurus/backers.svg?width=890"></a>
|
||||
|
||||
## Sponsors
|
||||
|
||||
Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/Docusaurus#sponsor)]
|
||||
|
||||
<a href="https://opencollective.com/Docusaurus/sponsor/0/website" target="_blank"><img src="https://opencollective.com/Docusaurus/sponsor/0/avatar.svg"></a> <a href="https://opencollective.com/Docusaurus/sponsor/1/website" target="_blank"><img src="https://opencollective.com/Docusaurus/sponsor/1/avatar.svg"></a>
|
||||
|
||||
## License
|
||||
|
||||
Docusaurus is [MIT licensed](./LICENSE).
|
||||
|
||||
The Docusaurus documentation (e.g., `.md` files in the `/docs` folder) is [Creative Commons licensed](./LICENSE-docs).
|
|
@ -1,10 +0,0 @@
|
|||
FROM node:lts
|
||||
|
||||
WORKDIR /app/website
|
||||
|
||||
EXPOSE 3000 35729
|
||||
COPY ./docs /app/docs
|
||||
COPY ./website /app/website
|
||||
RUN yarn install
|
||||
|
||||
CMD ["yarn", "start"]
|
|
@ -1,198 +0,0 @@
|
|||
This website was created with [Docusaurus](https://docusaurus.io/).
|
||||
|
||||
# What's In This Document
|
||||
|
||||
- [Get Started in 5 Minutes](#get-started-in-5-minutes)
|
||||
- [Directory Structure](#directory-structure)
|
||||
- [Editing Content](#editing-content)
|
||||
- [Adding Content](#adding-content)
|
||||
- [Full Documentation](#full-documentation)
|
||||
|
||||
# Get Started in 5 Minutes
|
||||
|
||||
1. Make sure all the dependencies for the website are installed:
|
||||
|
||||
```sh
|
||||
# Install dependencies
|
||||
$ yarn
|
||||
```
|
||||
|
||||
2. Run your dev server:
|
||||
|
||||
```sh
|
||||
# Start the site
|
||||
$ yarn start
|
||||
```
|
||||
|
||||
## Directory Structure
|
||||
|
||||
Your project file structure should look something like this
|
||||
|
||||
```
|
||||
my-docusaurus/
|
||||
docs/
|
||||
doc-1.md
|
||||
doc-2.md
|
||||
doc-3.md
|
||||
website/
|
||||
blog/
|
||||
2016-3-11-oldest-post.md
|
||||
2017-10-24-newest-post.md
|
||||
core/
|
||||
node_modules/
|
||||
pages/
|
||||
static/
|
||||
css/
|
||||
img/
|
||||
package.json
|
||||
sidebars.json
|
||||
siteConfig.js
|
||||
```
|
||||
|
||||
# Editing Content
|
||||
|
||||
## Editing an existing docs page
|
||||
|
||||
Edit docs by navigating to `docs/` and editing the corresponding document:
|
||||
|
||||
`docs/doc-to-be-edited.md`
|
||||
|
||||
```markdown
|
||||
---
|
||||
id: page-needs-edit
|
||||
title: This Doc Needs To Be Edited
|
||||
---
|
||||
|
||||
Edit me...
|
||||
```
|
||||
|
||||
For more information about docs, click [here](https://docusaurus.io/docs/en/navigation)
|
||||
|
||||
## Editing an existing blog post
|
||||
|
||||
Edit blog posts by navigating to `website/blog` and editing the corresponding post:
|
||||
|
||||
`website/blog/post-to-be-edited.md`
|
||||
|
||||
```markdown
|
||||
---
|
||||
id: post-needs-edit
|
||||
title: This Blog Post Needs To Be Edited
|
||||
---
|
||||
|
||||
Edit me...
|
||||
```
|
||||
|
||||
For more information about blog posts, click [here](https://docusaurus.io/docs/en/adding-blog)
|
||||
|
||||
# Adding Content
|
||||
|
||||
## Adding a new docs page to an existing sidebar
|
||||
|
||||
1. Create the doc as a new markdown file in `/docs`, example `docs/newly-created-doc.md`:
|
||||
|
||||
```md
|
||||
---
|
||||
id: newly-created-doc
|
||||
title: This Doc Needs To Be Edited
|
||||
---
|
||||
|
||||
My new content here..
|
||||
```
|
||||
|
||||
1. Refer to that doc's ID in an existing sidebar in `website/sidebars.json`:
|
||||
|
||||
```javascript
|
||||
// Add newly-created-doc to the Getting Started category of docs
|
||||
{
|
||||
"docs": {
|
||||
"Getting Started": [
|
||||
"quick-start",
|
||||
"newly-created-doc" // new doc here
|
||||
],
|
||||
...
|
||||
},
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
For more information about adding new docs, click [here](https://docusaurus.io/docs/en/navigation)
|
||||
|
||||
## Adding a new blog post
|
||||
|
||||
1. Make sure there is a header link to your blog in `website/siteConfig.js`:
|
||||
|
||||
`website/siteConfig.js`
|
||||
|
||||
```javascript
|
||||
headerLinks: [
|
||||
...
|
||||
{ blog: true, label: 'Blog' },
|
||||
...
|
||||
]
|
||||
```
|
||||
|
||||
2. Create the blog post with the format `YYYY-MM-DD-My-Blog-Post-Title.md` in `website/blog`:
|
||||
|
||||
`website/blog/2018-05-21-New-Blog-Post.md`
|
||||
|
||||
```markdown
|
||||
---
|
||||
author: Frank Li
|
||||
authorURL: https://twitter.com/foobarbaz
|
||||
authorFBID: 503283835
|
||||
title: New Blog Post
|
||||
---
|
||||
|
||||
Lorem Ipsum...
|
||||
```
|
||||
|
||||
For more information about blog posts, click [here](https://docusaurus.io/docs/en/adding-blog)
|
||||
|
||||
## Adding items to your site's top navigation bar
|
||||
|
||||
1. Add links to docs, custom pages or external links by editing the headerLinks field of `website/siteConfig.js`:
|
||||
|
||||
`website/siteConfig.js`
|
||||
|
||||
```javascript
|
||||
{
|
||||
headerLinks: [
|
||||
...
|
||||
/* you can add docs */
|
||||
{ doc: 'my-examples', label: 'Examples' },
|
||||
/* you can add custom pages */
|
||||
{ page: 'help', label: 'Help' },
|
||||
/* you can add external links */
|
||||
{ href: 'https://github.com/facebook/docusaurus', label: 'GitHub' },
|
||||
...
|
||||
],
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
For more information about the navigation bar, click [here](https://docusaurus.io/docs/en/navigation)
|
||||
|
||||
## Adding custom pages
|
||||
|
||||
1. Docusaurus uses React components to build pages. The components are saved as .js files in `website/pages/en`:
|
||||
1. If you want your page to show up in your navigation header, you will need to update `website/siteConfig.js` to add to the `headerLinks` element:
|
||||
|
||||
`website/siteConfig.js`
|
||||
|
||||
```javascript
|
||||
{
|
||||
headerLinks: [
|
||||
...
|
||||
{ page: 'my-new-custom-page', label: 'My New Custom Page' },
|
||||
...
|
||||
],
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
For more information about custom pages, click [here](https://docusaurus.io/docs/en/custom-pages).
|
||||
|
||||
# Full Documentation
|
||||
|
||||
Full documentation can be found on the [website](https://docusaurus.io/).
|
|
@ -1,18 +0,0 @@
|
|||
---
|
||||
title: Blog Title
|
||||
author: Blog Author
|
||||
authorURL: http://twitter.com/
|
||||
authorFBID: 100002976521003
|
||||
---
|
||||
|
||||
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.
|
|
@ -1,18 +0,0 @@
|
|||
---
|
||||
title: New Blog Post
|
||||
author: Blog Author
|
||||
authorURL: http://twitter.com/
|
||||
authorFBID: 100002976521003
|
||||
---
|
||||
|
||||
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.
|
|
@ -1,14 +0,0 @@
|
|||
---
|
||||
title: Adding RSS Support - RSS Truncation Test
|
||||
author: Eric Nakagawa
|
||||
authorURL: http://twitter.com/ericnakagawa
|
||||
authorFBID: 661277173
|
||||
---
|
||||
|
||||
1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
|
||||
|
||||
This should be truncated.
|
||||
|
||||
<!--truncate-->
|
||||
|
||||
This line should never render in XML.
|
|
@ -1,10 +0,0 @@
|
|||
---
|
||||
title: Adding RSS Support
|
||||
author: Eric Nakagawa
|
||||
authorURL: http://twitter.com/ericnakagawa
|
||||
authorFBID: 661277173
|
||||
---
|
||||
|
||||
This is a test post.
|
||||
|
||||
A whole bunch of other information.
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
title: New Version 1.0.0
|
||||
author: Eric Nakagawa
|
||||
authorURL: http://twitter.com/ericnakagawa
|
||||
authorFBID: 661277173
|
||||
---
|
||||
|
||||
This blog post will test file name parsing issues when periods are present.
|
|
@ -1,116 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
const React = require('react');
|
||||
|
||||
class Footer extends React.Component {
|
||||
docUrl(doc) {
|
||||
const baseUrl = this.props.config.baseUrl;
|
||||
const docsUrl = this.props.config.docsUrl;
|
||||
const docsPart = `${docsUrl ? `${docsUrl}/` : ''}`;
|
||||
return `${baseUrl}${docsPart}${doc}`;
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<footer className="nav-footer" id="footer">
|
||||
<section className="sitemap">
|
||||
<a href={this.props.config.baseUrl} className="nav-home">
|
||||
{this.props.config.footerIcon && (
|
||||
<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.docUrl('doc1.html')}>
|
||||
Getting Started (or other categories)
|
||||
</a>
|
||||
<a href={this.docUrl('doc2.html')}>Guides (or other categories)</a>
|
||||
<a href={this.docUrl('doc3.html')}>
|
||||
API Reference (or other categories)
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<h5>Community</h5>
|
||||
<a href={`${this.props.config.baseUrl}users`}>User Showcase</a>
|
||||
<a
|
||||
href="https://stackoverflow.com/questions/tagged/"
|
||||
target="_blank"
|
||||
rel="noreferrer noopener">
|
||||
Stack Overflow
|
||||
</a>
|
||||
<a href="https://discordapp.com/">Project Chat</a>
|
||||
<a
|
||||
href="https://twitter.com/"
|
||||
target="_blank"
|
||||
rel="noreferrer noopener">
|
||||
Twitter
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<h5>More</h5>
|
||||
<a href={`${this.props.config.baseUrl}blog`}>Blog</a>
|
||||
<a href="https://github.com/">GitHub</a>
|
||||
<a
|
||||
className="github-button"
|
||||
href={this.props.config.repoUrl}
|
||||
data-icon="octicon-star"
|
||||
data-count-href="/facebook/docusaurus/stargazers"
|
||||
data-show-count="true"
|
||||
data-count-aria-label="# stargazers on GitHub"
|
||||
aria-label="Star this project on GitHub">
|
||||
Star
|
||||
</a>
|
||||
{this.props.config.twitterUsername && (
|
||||
<div className="social">
|
||||
<a
|
||||
href={`https://twitter.com/${this.props.config.twitterUsername}`}
|
||||
className="twitter-follow-button">
|
||||
Follow @{this.props.config.twitterUsername}
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
{this.props.config.facebookAppId && (
|
||||
<div className="social">
|
||||
<div
|
||||
className="fb-like"
|
||||
data-href={this.props.config.url}
|
||||
data-colorscheme="dark"
|
||||
data-layout="standard"
|
||||
data-share="true"
|
||||
data-width="225"
|
||||
data-show-faces="false"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<a
|
||||
href="https://opensource.facebook.com/"
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
className="fbOpenSource">
|
||||
<img
|
||||
src={`${this.props.config.baseUrl}img/oss_logo.png`}
|
||||
alt="Facebook Open Source"
|
||||
width="170"
|
||||
height="45"
|
||||
/>
|
||||
</a>
|
||||
<section className="copyright">{this.props.config.copyright}</section>
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Footer;
|
|
@ -1,18 +0,0 @@
|
|||
version: "3"
|
||||
|
||||
services:
|
||||
docusaurus:
|
||||
build: .
|
||||
ports:
|
||||
- 3000:3000
|
||||
- 35729:35729
|
||||
volumes:
|
||||
- ./docs:/app/docs
|
||||
- ./website/blog:/app/website/blog
|
||||
- ./website/core:/app/website/core
|
||||
- ./website/i18n:/app/website/i18n
|
||||
- ./website/pages:/app/website/pages
|
||||
- ./website/static:/app/website/static
|
||||
- ./website/sidebars.json:/app/website/sidebars.json
|
||||
- ./website/siteConfig.js:/app/website/siteConfig.js
|
||||
working_dir: /app/website
|
|
@ -1,2 +0,0 @@
|
|||
*/node_modules
|
||||
*.log
|
|
@ -1,29 +0,0 @@
|
|||
---
|
||||
id: doc1
|
||||
title: Latin-ish
|
||||
sidebar_label: Example Page
|
||||
---
|
||||
|
||||
Check the [documentation](https://docusaurus.io) for how to use Docusaurus.
|
||||
|
||||
## Lorem
|
||||
|
||||
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.
|
||||
|
||||
## Mauris In Code
|
||||
|
||||
```
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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.
|
|
@ -1,7 +0,0 @@
|
|||
---
|
||||
id: doc2
|
||||
title: document number 2
|
||||
---
|
||||
|
||||
This is a link to [another document.](doc3.md)
|
||||
This is a link to an [external page.](http://www.example.com)
|
|
@ -1,14 +0,0 @@
|
|||
---
|
||||
id: doc3
|
||||
title: This is document number 3
|
||||
---
|
||||
|
||||
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.
|
|
@ -1,6 +0,0 @@
|
|||
---
|
||||
id: doc4
|
||||
title: Other Document
|
||||
---
|
||||
|
||||
this is another document
|
|
@ -1,6 +0,0 @@
|
|||
---
|
||||
id: doc5
|
||||
title: Fifth Document
|
||||
---
|
||||
|
||||
Another one
|
|
@ -1,12 +0,0 @@
|
|||
.DS_Store
|
||||
|
||||
node_modules
|
||||
|
||||
lib/core/metadata.js
|
||||
lib/core/MetadataBlog.js
|
||||
|
||||
website/translated_docs
|
||||
website/build/
|
||||
website/yarn.lock
|
||||
website/node_modules
|
||||
website/i18n/*
|
|
@ -1,54 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
const React = require('react');
|
||||
|
||||
const CompLibrary = require('../../core/CompLibrary.js');
|
||||
|
||||
const Container = CompLibrary.Container;
|
||||
const GridBlock = CompLibrary.GridBlock;
|
||||
|
||||
function Help(props) {
|
||||
const {config: siteConfig, language = ''} = props;
|
||||
const {baseUrl, docsUrl} = siteConfig;
|
||||
const docsPart = `${docsUrl ? `${docsUrl}/` : ''}`;
|
||||
const langPart = `${language ? `${language}/` : ''}`;
|
||||
const docUrl = (doc) => `${baseUrl}${docsPart}${langPart}${doc}`;
|
||||
|
||||
const supportLinks = [
|
||||
{
|
||||
content: `Learn more using the [documentation on this site.](${docUrl(
|
||||
'doc1.html',
|
||||
)})`,
|
||||
title: 'Browse Docs',
|
||||
},
|
||||
{
|
||||
content: 'Ask questions about the documentation and project',
|
||||
title: 'Join the community',
|
||||
},
|
||||
{
|
||||
content: "Find out what's new with this project",
|
||||
title: 'Stay up to date',
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="docMainWrapper wrapper">
|
||||
<Container className="mainContainer documentContainer postContainer">
|
||||
<div className="post">
|
||||
<header className="postHeader">
|
||||
<h1>Need help?</h1>
|
||||
</header>
|
||||
<p>This project is maintained by a dedicated group of people.</p>
|
||||
<GridBlock contents={supportLinks} layout="threeColumn" />
|
||||
</div>
|
||||
</Container>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = Help;
|
|
@ -1,213 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
const React = require('react');
|
||||
|
||||
const CompLibrary = require('../../core/CompLibrary.js');
|
||||
|
||||
const MarkdownBlock = CompLibrary.MarkdownBlock; /* Used to read markdown */
|
||||
const Container = CompLibrary.Container;
|
||||
const GridBlock = CompLibrary.GridBlock;
|
||||
|
||||
class HomeSplash extends React.Component {
|
||||
render() {
|
||||
const {siteConfig, language = ''} = this.props;
|
||||
const {baseUrl, docsUrl} = siteConfig;
|
||||
const docsPart = `${docsUrl ? `${docsUrl}/` : ''}`;
|
||||
const langPart = `${language ? `${language}/` : ''}`;
|
||||
const docUrl = (doc) => `${baseUrl}${docsPart}${langPart}${doc}`;
|
||||
|
||||
const SplashContainer = (props) => (
|
||||
<div className="homeContainer">
|
||||
<div className="homeSplashFade">
|
||||
<div className="wrapper homeWrapper">{props.children}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const Logo = (props) => (
|
||||
<div className="projectLogo">
|
||||
<img src={props.img_src} alt="Project Logo" />
|
||||
</div>
|
||||
);
|
||||
|
||||
const ProjectTitle = (props) => (
|
||||
<h2 className="projectTitle">
|
||||
{props.title}
|
||||
<small>{props.tagline}</small>
|
||||
</h2>
|
||||
);
|
||||
|
||||
const PromoSection = (props) => (
|
||||
<div className="section promoSection">
|
||||
<div className="promoRow">
|
||||
<div className="pluginRowBlock">{props.children}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const Button = (props) => (
|
||||
<div className="pluginWrapper buttonWrapper">
|
||||
<a className="button" href={props.href} target={props.target}>
|
||||
{props.children}
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<SplashContainer>
|
||||
<Logo img_src={`${baseUrl}img/undraw_monitor.svg`} />
|
||||
<div className="inner">
|
||||
<ProjectTitle tagline={siteConfig.tagline} title={siteConfig.title} />
|
||||
<PromoSection>
|
||||
<Button href="#try">Try It Out</Button>
|
||||
<Button href={docUrl('doc1.html')}>Example Link</Button>
|
||||
<Button href={docUrl('doc2.html')}>Example Link 2</Button>
|
||||
</PromoSection>
|
||||
</div>
|
||||
</SplashContainer>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class Index extends React.Component {
|
||||
render() {
|
||||
const {config: siteConfig, language = ''} = this.props;
|
||||
const {baseUrl} = siteConfig;
|
||||
|
||||
const Block = (props) => (
|
||||
<Container
|
||||
padding={['bottom', 'top']}
|
||||
id={props.id}
|
||||
background={props.background}>
|
||||
<GridBlock
|
||||
align="center"
|
||||
contents={props.children}
|
||||
layout={props.layout}
|
||||
/>
|
||||
</Container>
|
||||
);
|
||||
|
||||
const FeatureCallout = () => (
|
||||
<div
|
||||
className="productShowcaseSection paddingBottom"
|
||||
style={{textAlign: 'center'}}>
|
||||
<h2>Feature Callout</h2>
|
||||
<MarkdownBlock>These are features of this project</MarkdownBlock>
|
||||
</div>
|
||||
);
|
||||
|
||||
const TryOut = () => (
|
||||
<Block id="try">
|
||||
{[
|
||||
{
|
||||
content:
|
||||
'To make your landing page more attractive, use illustrations! Check out ' +
|
||||
'[**unDraw**](https://undraw.co/) which provides you with customizable illustrations which are free to use. ' +
|
||||
'The illustrations you see on this page are from unDraw.',
|
||||
image: `${baseUrl}img/undraw_code_review.svg`,
|
||||
imageAlign: 'left',
|
||||
title: 'Wonderful SVG Illustrations',
|
||||
},
|
||||
]}
|
||||
</Block>
|
||||
);
|
||||
|
||||
const Description = () => (
|
||||
<Block background="dark">
|
||||
{[
|
||||
{
|
||||
content:
|
||||
'This is another description of how this project is useful',
|
||||
image: `${baseUrl}img/undraw_note_list.svg`,
|
||||
imageAlign: 'right',
|
||||
title: 'Description',
|
||||
},
|
||||
]}
|
||||
</Block>
|
||||
);
|
||||
|
||||
const LearnHow = () => (
|
||||
<Block background="light">
|
||||
{[
|
||||
{
|
||||
content:
|
||||
'Each new Docusaurus project has **randomly-generated** theme colors.',
|
||||
image: `${baseUrl}img/undraw_youtube_tutorial.svg`,
|
||||
imageAlign: 'right',
|
||||
title: 'Randomly Generated Theme Colors',
|
||||
},
|
||||
]}
|
||||
</Block>
|
||||
);
|
||||
|
||||
const Features = () => (
|
||||
<Block layout="fourColumn">
|
||||
{[
|
||||
{
|
||||
content: 'This is the content of my feature',
|
||||
image: `${baseUrl}img/undraw_react.svg`,
|
||||
imageAlign: 'top',
|
||||
title: 'Feature One',
|
||||
},
|
||||
{
|
||||
content: 'The content of my second feature',
|
||||
image: `${baseUrl}img/undraw_operating_system.svg`,
|
||||
imageAlign: 'top',
|
||||
title: 'Feature Two',
|
||||
},
|
||||
]}
|
||||
</Block>
|
||||
);
|
||||
|
||||
const Showcase = () => {
|
||||
if ((siteConfig.users || []).length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const showcase = siteConfig.users
|
||||
.filter((user) => user.pinned)
|
||||
.map((user) => (
|
||||
<a href={user.infoLink} key={user.infoLink}>
|
||||
<img src={user.image} alt={user.caption} title={user.caption} />
|
||||
</a>
|
||||
));
|
||||
|
||||
const pageUrl = (page) =>
|
||||
baseUrl + (language ? `${language}/` : '') + page;
|
||||
|
||||
return (
|
||||
<div className="productShowcaseSection paddingBottom">
|
||||
<h2>Who is Using This?</h2>
|
||||
<p>This project is used by all these people</p>
|
||||
<div className="logos">{showcase}</div>
|
||||
<div className="more-users">
|
||||
<a className="button" href={pageUrl('users.html')}>
|
||||
More {siteConfig.title} Users
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<HomeSplash siteConfig={siteConfig} language={language} />
|
||||
<div className="mainContainer">
|
||||
<Features />
|
||||
<FeatureCallout />
|
||||
<LearnHow />
|
||||
<TryOut />
|
||||
<Description />
|
||||
<Showcase />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Index;
|
|
@ -1,53 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
const React = require('react');
|
||||
|
||||
const CompLibrary = require('../../core/CompLibrary.js');
|
||||
|
||||
const Container = CompLibrary.Container;
|
||||
|
||||
class Users extends React.Component {
|
||||
render() {
|
||||
const {config: siteConfig} = this.props;
|
||||
if ((siteConfig.users || []).length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const showcase = siteConfig.users.map((user) => (
|
||||
<a href={user.infoLink} key={user.infoLink}>
|
||||
<img src={user.image} alt={user.caption} title={user.caption} />
|
||||
</a>
|
||||
));
|
||||
|
||||
return (
|
||||
<div className="mainContainer">
|
||||
<Container padding={['bottom', 'top']}>
|
||||
<div className="showcaseSection">
|
||||
<div className="prose">
|
||||
<h1>Who is Using This?</h1>
|
||||
<p>This project is used by many folks</p>
|
||||
</div>
|
||||
<div className="logos">{showcase}</div>
|
||||
{siteConfig.repoUrl && (
|
||||
<React.Fragment>
|
||||
<p>Are you using this project?</p>
|
||||
<a
|
||||
href={`${siteConfig.repoUrl}/edit/master/website/siteConfig.js`}
|
||||
className="button">
|
||||
Add your company
|
||||
</a>
|
||||
</React.Fragment>
|
||||
)}
|
||||
</div>
|
||||
</Container>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Users;
|
|
@ -1,10 +0,0 @@
|
|||
{
|
||||
"docs": {
|
||||
"Docusaurus": ["doc1"],
|
||||
"First Category": ["doc2"],
|
||||
"Second Category": ["doc3"]
|
||||
},
|
||||
"docs-other": {
|
||||
"First Category": ["doc4", "doc5"]
|
||||
}
|
||||
}
|
|
@ -1,110 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
// See https://docusaurus.io/docs/site-config for all the possible
|
||||
// site configuration options.
|
||||
|
||||
// List of projects/orgs using your project for the users page.
|
||||
const users = [
|
||||
{
|
||||
caption: 'User1',
|
||||
// You will need to prepend the image path with your baseUrl
|
||||
// if it is not '/', like: '/test-site/img/image.jpg'.
|
||||
image: '/img/undraw_open_source.svg',
|
||||
infoLink: 'https://www.facebook.com',
|
||||
pinned: true,
|
||||
},
|
||||
];
|
||||
|
||||
const siteConfig = {
|
||||
title: 'Test Site', // Title for your website.
|
||||
tagline: 'A website for testing',
|
||||
url: 'https://your-docusaurus-test-site.com', // Your website URL
|
||||
baseUrl: '/', // Base URL for your project */
|
||||
// For github.io type URLs, you would set the url and baseUrl like:
|
||||
// url: 'https://facebook.github.io',
|
||||
// baseUrl: '/test-site/',
|
||||
|
||||
// Used for publishing and more
|
||||
projectName: 'test-site',
|
||||
organizationName: 'facebook',
|
||||
// For top-level user or org sites, the organization is still the same.
|
||||
// e.g., for the https://JoelMarcey.github.io site, it would be set like...
|
||||
// organizationName: 'JoelMarcey'
|
||||
|
||||
// For no header links in the top nav bar -> headerLinks: [],
|
||||
headerLinks: [
|
||||
{doc: 'doc1', label: 'Docs'},
|
||||
{doc: 'doc4', label: 'API'},
|
||||
{page: 'help', label: 'Help'},
|
||||
{blog: true, label: 'Blog'},
|
||||
],
|
||||
|
||||
// If you have users set above, you add it here:
|
||||
users,
|
||||
|
||||
/* path to images for header/footer */
|
||||
headerIcon: 'img/favicon.ico',
|
||||
footerIcon: 'img/favicon.ico',
|
||||
favicon: 'img/favicon.ico',
|
||||
|
||||
/* Colors for website */
|
||||
colors: {
|
||||
primaryColor: '{{primaryColor}}',
|
||||
secondaryColor: '{{secondaryColor}}',
|
||||
},
|
||||
|
||||
/* Custom fonts for website */
|
||||
/*
|
||||
fonts: {
|
||||
myFont: [
|
||||
"Times New Roman",
|
||||
"Serif"
|
||||
],
|
||||
myOtherFont: [
|
||||
"-apple-system",
|
||||
"system-ui"
|
||||
]
|
||||
},
|
||||
*/
|
||||
|
||||
// This copyright info is used in /core/Footer.js and blog RSS/Atom feeds.
|
||||
copyright: `Copyright © ${new Date().getFullYear()} Your Name or Your Company Name`,
|
||||
|
||||
highlight: {
|
||||
// Highlight.js theme to use for syntax highlighting in code blocks.
|
||||
theme: 'default',
|
||||
},
|
||||
|
||||
// Add custom scripts here that would be placed in <script> tags.
|
||||
scripts: ['https://buttons.github.io/buttons.js'],
|
||||
|
||||
// On page navigation for the current documentation page.
|
||||
onPageNav: 'separate',
|
||||
// No .html extensions for paths.
|
||||
cleanUrl: true,
|
||||
|
||||
// Open Graph and Twitter card images.
|
||||
ogImage: 'img/undraw_online.svg',
|
||||
twitterImage: 'img/undraw_tweetstorm.svg',
|
||||
|
||||
// For sites with a sizable amount of content, set collapsible to true.
|
||||
// Expand/collapse the links and subcategories under categories.
|
||||
// docsSideNavCollapsible: true,
|
||||
|
||||
// Show documentation's last contributor's name.
|
||||
// enableUpdateBy: true,
|
||||
|
||||
// Show documentation's last update time.
|
||||
// enableUpdateTime: true,
|
||||
|
||||
// You may provide arbitrary config keys to be used as needed by your
|
||||
// template. For example, if you need your repo's URL...
|
||||
// repoUrl: 'https://github.com/facebook/test-site',
|
||||
};
|
||||
|
||||
module.exports = siteConfig;
|
|
@ -1,23 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/* your custom css */
|
||||
|
||||
@media only screen and (min-device-width: 360px) and (max-device-width: 736px) {
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 1024px) {
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1023px) {
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 1400px) {
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 1500px) {
|
||||
}
|
Before Width: | Height: | Size: 766 B |
Before Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 9.2 KiB |
Before Width: | Height: | Size: 29 KiB |
|
@ -1,49 +0,0 @@
|
|||
project_identifier_env: CROWDIN_DOCUSAURUS_PROJECT_ID
|
||||
api_key_env: CROWDIN_DOCUSAURUS_API_KEY
|
||||
base_path: "./"
|
||||
preserve_hierarchy: true
|
||||
|
||||
files:
|
||||
-
|
||||
source: '/docs/*.md'
|
||||
translation: '/website/translated_docs/%locale%/%original_file_name%'
|
||||
languages_mapping: &anchor
|
||||
locale:
|
||||
'af': 'af'
|
||||
'ar': 'ar'
|
||||
'bs-BA': 'bs-BA'
|
||||
'ca': 'ca'
|
||||
'cs': 'cs'
|
||||
'da': 'da'
|
||||
'de': 'de'
|
||||
'el': 'el'
|
||||
'es-ES': 'es-ES'
|
||||
'fa': 'fa-IR'
|
||||
'fi': 'fi'
|
||||
'fr': 'fr'
|
||||
'he': 'he'
|
||||
'hu': 'hu'
|
||||
'id': 'id-ID'
|
||||
'it': 'it'
|
||||
'ja': 'ja'
|
||||
'ko': 'ko'
|
||||
'mr': 'mr-IN'
|
||||
'nl': 'nl'
|
||||
'no': 'no-NO'
|
||||
'pl': 'pl'
|
||||
'pt-BR': 'pt-BR'
|
||||
'pt-PT': 'pt-PT'
|
||||
'ro': 'ro'
|
||||
'ru': 'ru'
|
||||
'sk': 'sk-SK'
|
||||
'sr': 'sr'
|
||||
'sv-SE': 'sv-SE'
|
||||
'tr': 'tr'
|
||||
'uk': 'uk'
|
||||
'vi': 'vi'
|
||||
'zh-CN': 'zh-CN'
|
||||
'zh-TW': 'zh-TW'
|
||||
-
|
||||
source: '/website/i18n/en.json'
|
||||
translation: '/website/i18n/%locale%.json'
|
||||
languages_mapping: *anchor
|
|
@ -1,181 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
const languages = [
|
||||
{
|
||||
enabled: true,
|
||||
name: 'English',
|
||||
tag: 'en',
|
||||
},
|
||||
{
|
||||
enabled: false,
|
||||
name: '日本語',
|
||||
tag: 'ja',
|
||||
},
|
||||
{
|
||||
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-CN',
|
||||
},
|
||||
{enabled: false, name: '繁體中文', tag: 'zh-TW'},
|
||||
];
|
||||
module.exports = languages;
|
|
@ -1,69 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
const React = require('react');
|
||||
|
||||
const CompLibrary = require('../../core/CompLibrary.js');
|
||||
|
||||
const Container = CompLibrary.Container;
|
||||
const GridBlock = CompLibrary.GridBlock;
|
||||
|
||||
const translate = require('../../server/translate.js').translate;
|
||||
|
||||
class Help extends React.Component {
|
||||
render() {
|
||||
const supportLinks = [
|
||||
{
|
||||
content: (
|
||||
<translate>
|
||||
Learn more using the [documentation on this
|
||||
site.](/test-site/docs/en/doc1.html)
|
||||
</translate>
|
||||
),
|
||||
title: <translate>Browse Docs</translate>,
|
||||
},
|
||||
{
|
||||
content: (
|
||||
<translate>
|
||||
Ask questions about the documentation and project
|
||||
</translate>
|
||||
),
|
||||
title: <translate>Join the community</translate>,
|
||||
},
|
||||
{
|
||||
content: <translate>Find out what's new with this project</translate>,
|
||||
title: <translate>Stay up to date</translate>,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="docMainWrapper wrapper">
|
||||
<Container className="mainContainer documentContainer postContainer">
|
||||
<div className="post">
|
||||
<header className="postHeader">
|
||||
<h2>
|
||||
<translate>Need help?</translate>
|
||||
</h2>
|
||||
</header>
|
||||
<p>
|
||||
<translate desc="statement made to reader">
|
||||
This project is maintained by a dedicated group of people.
|
||||
</translate>
|
||||
</p>
|
||||
<GridBlock contents={supportLinks} layout="threeColumn" />
|
||||
</div>
|
||||
</Container>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Help.defaultProps = {
|
||||
language: 'en',
|
||||
};
|
||||
|
||||
module.exports = Help;
|
|
@ -1,116 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
const React = require('react');
|
||||
|
||||
const CompLibrary = require('../../core/CompLibrary');
|
||||
|
||||
const Container = CompLibrary.Container;
|
||||
|
||||
const CWD = process.cwd();
|
||||
|
||||
const versions = require(`${CWD}/versions.json`);
|
||||
|
||||
function Versions(props) {
|
||||
const {config: siteConfig} = props;
|
||||
const latestVersion = versions[0];
|
||||
const repoUrl = `https://github.com/${siteConfig.organizationName}/${siteConfig.projectName}`;
|
||||
return (
|
||||
<div className="docMainWrapper wrapper">
|
||||
<Container className="mainContainer versionsContainer">
|
||||
<div className="post">
|
||||
<header className="postHeader">
|
||||
<h1>{siteConfig.title} Versions</h1>
|
||||
</header>
|
||||
<p>New versions of this project are released every so often.</p>
|
||||
<h3 id="latest">Current version (Stable)</h3>
|
||||
<table className="versions">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>{latestVersion}</th>
|
||||
<td>
|
||||
{/* You are supposed to change this href where appropriate
|
||||
Example: href="<baseUrl>/docs(/:language)/:id" */}
|
||||
<a
|
||||
href={`${siteConfig.baseUrl}${siteConfig.docsUrl}/${
|
||||
props.language ? props.language + '/' : ''
|
||||
}doc1`}>
|
||||
Documentation
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="">Release Notes</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>
|
||||
This is the version that is configured automatically when you first
|
||||
install this project.
|
||||
</p>
|
||||
<h3 id="rc">Pre-release versions</h3>
|
||||
<table className="versions">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>master</th>
|
||||
<td>
|
||||
{/* You are supposed to change this href where appropriate
|
||||
Example: href="<baseUrl>/docs(/:language)/next/:id" */}
|
||||
<a
|
||||
href={`${siteConfig.baseUrl}${siteConfig.docsUrl}/${
|
||||
props.language ? props.language + '/' : ''
|
||||
}next/doc1`}>
|
||||
Documentation
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href={repoUrl}>Source Code</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>Other text describing this section.</p>
|
||||
<h3 id="archive">Past Versions</h3>
|
||||
<p>Here you can find previous versions of the documentation.</p>
|
||||
<table className="versions">
|
||||
<tbody>
|
||||
{versions.map(
|
||||
(version) =>
|
||||
version !== latestVersion && (
|
||||
<tr key={version}>
|
||||
<th>{version}</th>
|
||||
<td>
|
||||
{/* You are supposed to change this href where appropriate
|
||||
Example: href="<baseUrl>/docs(/:language)/:version/:id" */}
|
||||
<a
|
||||
href={`${siteConfig.baseUrl}${siteConfig.docsUrl}/${
|
||||
props.language ? props.language + '/' : ''
|
||||
}${version}/doc1`}>
|
||||
Documentation
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href={`${repoUrl}/releases/tag/v${version}`}>
|
||||
Release Notes
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
),
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
<p>
|
||||
You can find past versions of this project on{' '}
|
||||
<a href={repoUrl}>GitHub</a>.
|
||||
</p>
|
||||
</div>
|
||||
</Container>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = Versions;
|
|
@ -1,112 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
require('@babel/polyfill');
|
||||
const filepath = require('filepath');
|
||||
const fm = require('front-matter');
|
||||
const fs = require('fs-extra');
|
||||
const glob = require('glob-promise');
|
||||
const rimraf = require('rimraf');
|
||||
const shell = require('shelljs');
|
||||
|
||||
const CWD = process.cwd();
|
||||
|
||||
const loadConfig = require('../server/config');
|
||||
|
||||
const website1Dir = 'website-1.x';
|
||||
const siteConfig = loadConfig(`${CWD}/${website1Dir}/siteConfig.js`);
|
||||
const buildDir = `${CWD}/${website1Dir}/build`;
|
||||
const docsDir = `${CWD}/docs`;
|
||||
|
||||
let inputMarkdownFiles = [];
|
||||
let inputAssetsFiles = [];
|
||||
let outputHTMLFiles = [];
|
||||
let outputAssetsFiles = [];
|
||||
|
||||
function clearBuildFolder() {
|
||||
return rimraf(buildDir);
|
||||
}
|
||||
|
||||
describe('Build files', () => {
|
||||
beforeAll(() => {
|
||||
shell.exec(`yarn workspace docusaurus-1-website build`, {
|
||||
// silent: true
|
||||
});
|
||||
|
||||
return Promise.all([
|
||||
glob(`${docsDir}/**/*.md`),
|
||||
glob(`${buildDir}/${siteConfig.projectName}/docs/**/*.html`),
|
||||
glob(`${docsDir}/assets/*`),
|
||||
glob(`${buildDir}/${siteConfig.projectName}/img/*`),
|
||||
]).then((results) => {
|
||||
[
|
||||
inputMarkdownFiles,
|
||||
outputHTMLFiles,
|
||||
inputAssetsFiles,
|
||||
outputAssetsFiles,
|
||||
] = results;
|
||||
});
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
clearBuildFolder();
|
||||
});
|
||||
|
||||
test('Build folder exists', () =>
|
||||
fs.stat(buildDir).then((status) => {
|
||||
expect(status.isDirectory()).toBeTruthy();
|
||||
}));
|
||||
|
||||
test('Generated HTML for each Markdown resource', () => {
|
||||
const metadata = outputHTMLFiles.map((file) =>
|
||||
filepath.create(file).basename(),
|
||||
);
|
||||
inputMarkdownFiles.forEach((file) => {
|
||||
const data = fs.readFileSync(file, 'utf8');
|
||||
const frontmatter = fm(data);
|
||||
expect(metadata).toContain(`${frontmatter.attributes.id}.html`);
|
||||
});
|
||||
});
|
||||
|
||||
test('Generated table of contents', () => {
|
||||
outputHTMLFiles.forEach((file) => {
|
||||
const fileContents = fs.readFileSync(file, 'utf8');
|
||||
expect(fileContents).not.toContain('<AUTOGENERATED_TABLE_OF_CONTENTS>');
|
||||
});
|
||||
});
|
||||
|
||||
test('Copied assets from /docs/assets', () => {
|
||||
const metadata = outputAssetsFiles.map((file) =>
|
||||
filepath.create(file).basename(),
|
||||
);
|
||||
inputAssetsFiles.forEach((file) => {
|
||||
const path = filepath.create(file);
|
||||
expect(metadata).toContain(path.basename());
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Build files but skip next release', () => {
|
||||
beforeAll(() => {
|
||||
shell.exec(
|
||||
`yarn workspace docusaurus-1-website build --skip-next-release`,
|
||||
{
|
||||
// silent: true,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
clearBuildFolder();
|
||||
});
|
||||
|
||||
test('Did not generate HTML files from markdown files for next release', () => {
|
||||
expect(
|
||||
glob.sync(`${buildDir}/${siteConfig.projectName}/docs/**/next`).length,
|
||||
).toBe(0);
|
||||
});
|
||||
});
|
|
@ -1,48 +0,0 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
require('@babel/polyfill');
|
||||
require('@babel/register')({
|
||||
babelrc: false,
|
||||
only: [__dirname, `${process.cwd()}/core`],
|
||||
plugins: [
|
||||
require('./server/translate-plugin.js'),
|
||||
require('@babel/plugin-proposal-class-properties').default,
|
||||
require('@babel/plugin-proposal-object-rest-spread').default,
|
||||
],
|
||||
presets: [
|
||||
require('@babel/preset-react').default,
|
||||
require('@babel/preset-env').default,
|
||||
],
|
||||
});
|
||||
|
||||
// initial check that required files are present
|
||||
const chalk = require('chalk');
|
||||
const fs = require('fs');
|
||||
|
||||
const CWD = process.cwd();
|
||||
|
||||
if (!fs.existsSync(`${CWD}/siteConfig.js`)) {
|
||||
console.error(
|
||||
chalk.red('Error: No siteConfig.js file found in website folder!'),
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// generate all static html files
|
||||
const generate = require('./server/generate.js');
|
||||
|
||||
generate()
|
||||
.then(() => {
|
||||
console.log("Site built successfully. Generated files in 'build' folder.");
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
|
@ -1,270 +0,0 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
const chalk = require('chalk');
|
||||
const commander = require('commander');
|
||||
const fs = require('fs-extra');
|
||||
const glob = require('glob');
|
||||
const path = require('path');
|
||||
|
||||
const CWD = process.cwd();
|
||||
|
||||
const toHex = (color) => {
|
||||
const hex = color.toString(16);
|
||||
return hex.length === 1 ? `0${hex}` : hex;
|
||||
};
|
||||
|
||||
const colorScheme = () => {
|
||||
let primaryColor = '#';
|
||||
let secondaryColor = '#';
|
||||
for (let i = 0; i < 3; i++) {
|
||||
// 175 is our ceiling to prevent the color from being too bright
|
||||
const color = Math.floor(Math.random() * 176);
|
||||
const darkColor = Math.floor(color * 0.7);
|
||||
primaryColor += toHex(color);
|
||||
secondaryColor += toHex(darkColor);
|
||||
}
|
||||
return {primaryColor, secondaryColor};
|
||||
};
|
||||
|
||||
let feature;
|
||||
|
||||
commander
|
||||
.arguments('[feature]')
|
||||
.action((feat) => {
|
||||
feature = feat;
|
||||
})
|
||||
.parse(process.argv);
|
||||
|
||||
// add scripts to package.json file
|
||||
if (fs.existsSync(`${CWD}/package.json`)) {
|
||||
const packageContent = JSON.parse(
|
||||
fs.readFileSync(`${CWD}/package.json`, 'utf8'),
|
||||
);
|
||||
if (!packageContent.scripts) {
|
||||
packageContent.scripts = {};
|
||||
}
|
||||
packageContent.scripts.start = 'docusaurus-start';
|
||||
packageContent.scripts.build = 'docusaurus-build';
|
||||
packageContent.scripts['publish-gh-pages'] = 'docusaurus-publish';
|
||||
packageContent.scripts.examples = 'docusaurus-examples';
|
||||
packageContent.scripts['write-translations'] =
|
||||
'docusaurus-write-translations';
|
||||
packageContent.scripts.version = 'docusaurus-version';
|
||||
packageContent.scripts['rename-version'] = 'docusaurus-rename-version';
|
||||
fs.writeFileSync(
|
||||
`${CWD}/package.json`,
|
||||
`${JSON.stringify(packageContent, null, 2)}\n`,
|
||||
);
|
||||
console.log(
|
||||
`${chalk.green('Wrote docusaurus scripts to package.json file.')}\n`,
|
||||
);
|
||||
}
|
||||
|
||||
const outerFolder = path.basename(path.dirname(CWD));
|
||||
|
||||
let docsCreatedInIntendedDirectory = true;
|
||||
|
||||
// handles cases where feature is "translations", "versions" or neither/not present
|
||||
if (feature === 'translations') {
|
||||
// copy files for translations
|
||||
const folder = path.join(__dirname, '..', 'examples', 'translations');
|
||||
if (fs.existsSync(`${CWD}/../crowdin.yaml`)) {
|
||||
console.log(
|
||||
`${chalk.yellow('crowdin.yaml already exists')} in ${chalk.yellow(
|
||||
`${outerFolder}/`,
|
||||
)}. Rename or remove the file to regenerate an example version.\n`,
|
||||
);
|
||||
} else {
|
||||
fs.copySync(`${folder}/crowdin.yaml`, `${CWD}/../crowdin.yaml`);
|
||||
}
|
||||
const files = glob.sync(`${folder}/**/*`);
|
||||
files.forEach((file) => {
|
||||
if (fs.lstatSync(file).isDirectory()) {
|
||||
return;
|
||||
}
|
||||
if (path.basename(file) === 'crowdin.yaml') {
|
||||
return;
|
||||
}
|
||||
const filePath = path.resolve(file).split(path.resolve(folder))[1];
|
||||
try {
|
||||
fs.copySync(file, CWD + filePath, {
|
||||
overwrite: false,
|
||||
errorOnExist: true,
|
||||
});
|
||||
} catch (e) {
|
||||
console.log(
|
||||
`${chalk.yellow(
|
||||
`${path.basename(filePath)} already exists`,
|
||||
)} in ${chalk.yellow(
|
||||
`website${filePath.split(path.basename(filePath))[0]}`,
|
||||
)}. Rename or remove the file to regenerate an example version.\n`,
|
||||
);
|
||||
}
|
||||
});
|
||||
} else if (feature === 'versions') {
|
||||
// copy files for versions
|
||||
const folder = path.join(__dirname, '..', 'examples', 'versions');
|
||||
const files = glob.sync(`${folder}/**/*`);
|
||||
files.forEach((file) => {
|
||||
if (fs.lstatSync(file).isDirectory()) {
|
||||
return;
|
||||
}
|
||||
const filePath = path.resolve(file).split(path.resolve(folder))[1];
|
||||
try {
|
||||
fs.copySync(file, CWD + filePath, {
|
||||
overwrite: false,
|
||||
errorOnExist: true,
|
||||
});
|
||||
} catch (e) {
|
||||
console.log(
|
||||
`${chalk.yellow(
|
||||
`${path.basename(filePath)} already exists`,
|
||||
)} in ${chalk.yellow(
|
||||
`website${filePath.split(path.basename(filePath))[0]}`,
|
||||
)}. Rename or remove the file to regenerate an example version.\n`,
|
||||
);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
const folder = path.join(__dirname, '..', 'examples', 'basics');
|
||||
// copy docs examples
|
||||
let targetDocsDir = `${CWD}/../docs`;
|
||||
if (fs.existsSync(targetDocsDir)) {
|
||||
console.log(
|
||||
`- ${chalk.green('docs')} already exists in ${chalk.blue(
|
||||
outerFolder,
|
||||
)}. Copying into ${CWD}/../docs-examples-from-docusaurus instead.`,
|
||||
);
|
||||
targetDocsDir = `${CWD}/../docs-examples-from-docusaurus`;
|
||||
docsCreatedInIntendedDirectory = false;
|
||||
}
|
||||
|
||||
fs.copySync(`${folder}/docs`, targetDocsDir);
|
||||
|
||||
// copy blog examples
|
||||
if (fs.existsSync(`${CWD}/blog`)) {
|
||||
console.log(
|
||||
`- ${chalk.green('blog')} already exists in ${chalk.blue(
|
||||
`${outerFolder}/website`,
|
||||
)}.`,
|
||||
);
|
||||
} else {
|
||||
fs.copySync(path.join(folder, 'blog'), path.join(CWD, 'blog'));
|
||||
}
|
||||
|
||||
const copyFileToProjectFolder = (fileNameFrom, fileNameTo) => {
|
||||
const copiedFileName = fileNameTo || fileNameFrom;
|
||||
const src = path.join(folder, fileNameFrom);
|
||||
const dest = path.join(CWD, '..', copiedFileName);
|
||||
if (fs.existsSync(dest)) {
|
||||
console.log(
|
||||
`- ${chalk.green(copiedFileName)} already exists in ${chalk.blue(
|
||||
outerFolder,
|
||||
)}.`,
|
||||
);
|
||||
} else {
|
||||
fs.copySync(src, dest);
|
||||
}
|
||||
};
|
||||
|
||||
// copy .gitignore file
|
||||
copyFileToProjectFolder('gitignore', '.gitignore');
|
||||
|
||||
// copy Dockerfile file
|
||||
copyFileToProjectFolder('Dockerfile');
|
||||
|
||||
// copy docker-compose.yml file
|
||||
copyFileToProjectFolder('docker-compose.yml');
|
||||
|
||||
// copy .dockerignore file
|
||||
copyFileToProjectFolder('dockerignore', '.dockerignore');
|
||||
|
||||
// copy other files
|
||||
const files = glob.sync(`${folder}/**/*`);
|
||||
const {primaryColor, secondaryColor} = colorScheme();
|
||||
files.forEach((file) => {
|
||||
if (fs.lstatSync(file).isDirectory()) {
|
||||
return;
|
||||
}
|
||||
const containingFolder = path.basename(path.dirname(file));
|
||||
if (
|
||||
path.basename(file) === 'gitignore' ||
|
||||
path.basename(file) === 'Dockerfile' ||
|
||||
path.basename(file) === 'docker-compose.yml' ||
|
||||
path.basename(file) === 'dockerignore' ||
|
||||
containingFolder === 'blog' ||
|
||||
containingFolder === 'docs'
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const filePath = path.resolve(file).split(path.resolve(folder))[1];
|
||||
if (
|
||||
path.basename(file) === 'siteConfig.js' &&
|
||||
!fs.existsSync(CWD + filePath)
|
||||
) {
|
||||
const siteConfig = fs
|
||||
.readFileSync(file, 'utf8')
|
||||
.replace('{{primaryColor}}', primaryColor)
|
||||
.replace('{{secondaryColor}}', secondaryColor);
|
||||
fs.writeFileSync(CWD + filePath, siteConfig);
|
||||
} else {
|
||||
try {
|
||||
fs.copySync(file, CWD + filePath, {
|
||||
overwrite: false,
|
||||
errorOnExist: true,
|
||||
});
|
||||
} catch (e) {
|
||||
console.log(
|
||||
`- ${chalk.green(
|
||||
`${path.basename(filePath)}`,
|
||||
)} already exists in ${chalk.blue(
|
||||
`${outerFolder}/website${
|
||||
filePath.split(path.basename(filePath))[0]
|
||||
}`,
|
||||
)}.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const svgs = glob.sync(`${CWD}/static/img/**/*.svg`);
|
||||
svgs.forEach((file) => {
|
||||
// Replace primary colors of SVGs.
|
||||
const newImage = fs
|
||||
.readFileSync(file, 'utf8')
|
||||
.replace(/{{primaryColor}}/g, primaryColor);
|
||||
fs.writeFileSync(file, newImage);
|
||||
});
|
||||
|
||||
try {
|
||||
const tree = require('tree-node-cli');
|
||||
const dirString = tree(path.join(CWD, '..'), {
|
||||
exclude: [
|
||||
/node_modules/, // npm
|
||||
/vendor/, // composer
|
||||
],
|
||||
});
|
||||
console.log(dirString);
|
||||
} catch (error) {
|
||||
console.warn(`Error printing directory: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (!docsCreatedInIntendedDirectory) {
|
||||
console.log(
|
||||
`The ${chalk.yellow(
|
||||
`${outerFolder}/docs`,
|
||||
)} directory was not created because it already exists. ` +
|
||||
`Please manually convert the contents into a Docusaurus-compatible format ` +
|
||||
`by referring to the examples from ${chalk.yellow(
|
||||
`${outerFolder}/docs-examples-from-docusaurus`,
|
||||
)}.\n`,
|
||||
);
|
||||
}
|
|
@ -1,83 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
const React = require('react');
|
||||
const BlogPost = require('./BlogPost.js');
|
||||
const BlogSidebar = require('./BlogSidebar.js');
|
||||
const Container = require('./Container.js');
|
||||
const MetadataBlog = require('./MetadataBlog.js');
|
||||
|
||||
const MetadataPublicBlog =
|
||||
process.env.NODE_ENV === 'development'
|
||||
? MetadataBlog
|
||||
: MetadataBlog.filter((item) => !item.unlisted);
|
||||
const Site = require('./Site.js');
|
||||
const utils = require('./utils.js');
|
||||
|
||||
// used to generate entire blog pages, i.e. collection of truncated blog posts
|
||||
class BlogPageLayout extends React.Component {
|
||||
getPageURL(page) {
|
||||
let url = `${this.props.config.baseUrl}blog/`;
|
||||
if (page > 0) {
|
||||
url += `page${page + 1}/`;
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
render() {
|
||||
const perPage = this.props.metadata.perPage;
|
||||
const page = this.props.metadata.page;
|
||||
return (
|
||||
<Site
|
||||
title="Blog"
|
||||
language="en"
|
||||
config={this.props.config}
|
||||
className="blog"
|
||||
metadata={{blog: true, blogListing: true}}>
|
||||
<div className="docMainWrapper wrapper">
|
||||
<BlogSidebar
|
||||
language={this.props.language}
|
||||
config={this.props.config}
|
||||
/>
|
||||
<Container className="mainContainer postContainer blogContainer">
|
||||
<div className="posts">
|
||||
{MetadataPublicBlog.slice(
|
||||
page * perPage,
|
||||
(page + 1) * perPage,
|
||||
).map((post) => (
|
||||
<BlogPost
|
||||
post={post}
|
||||
content={post.content}
|
||||
truncate
|
||||
key={
|
||||
utils.getPath(post.path, this.props.config.cleanUrl) +
|
||||
post.title
|
||||
}
|
||||
config={this.props.config}
|
||||
/>
|
||||
))}
|
||||
<div className="docs-prevnext">
|
||||
{page > 0 && (
|
||||
<a className="docs-prev" href={this.getPageURL(page - 1)}>
|
||||
← Prev
|
||||
</a>
|
||||
)}
|
||||
{MetadataPublicBlog.length > (page + 1) * perPage && (
|
||||
<a className="docs-next" href={this.getPageURL(page + 1)}>
|
||||
Next →
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</div>
|
||||
</Site>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = BlogPageLayout;
|
|
@ -1,130 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
const React = require('react');
|
||||
|
||||
const MarkdownBlock = require('./MarkdownBlock.js');
|
||||
const utils = require('./utils.js');
|
||||
|
||||
// inner blog component for the article itself, without sidebar/header/footer
|
||||
class BlogPost extends React.Component {
|
||||
renderContent() {
|
||||
if (this.props.truncate) {
|
||||
return (
|
||||
<article className="post-content">
|
||||
<MarkdownBlock>
|
||||
{utils.extractBlogPostBeforeTruncate(this.props.content)}
|
||||
</MarkdownBlock>
|
||||
{utils.blogPostHasTruncateMarker(this.props.content) && (
|
||||
<div className="read-more">
|
||||
<a
|
||||
className="button"
|
||||
href={`${this.props.config.baseUrl}blog/${utils.getPath(
|
||||
this.props.post.path,
|
||||
this.props.config.cleanUrl,
|
||||
)}`}>
|
||||
Read More
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
</article>
|
||||
);
|
||||
}
|
||||
return <MarkdownBlock>{this.props.content}</MarkdownBlock>;
|
||||
}
|
||||
|
||||
renderAuthorPhoto() {
|
||||
const post = this.props.post;
|
||||
const className = `authorPhoto${
|
||||
post.author && post.authorTitle ? ' authorPhotoBig' : ''
|
||||
}`;
|
||||
if (post.authorFBID || post.authorImageURL) {
|
||||
const authorImageURL = post.authorFBID
|
||||
? `https://graph.facebook.com/${post.authorFBID}/picture/?height=200&width=200`
|
||||
: post.authorImageURL;
|
||||
return (
|
||||
<div className={className}>
|
||||
<a href={post.authorURL} target="_blank" rel="noreferrer noopener">
|
||||
<img src={authorImageURL} alt={post.author} />
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
renderTitle() {
|
||||
const post = this.props.post;
|
||||
return (
|
||||
<h1 className="postHeaderTitle">
|
||||
<a
|
||||
href={`${this.props.config.baseUrl}blog/${utils.getPath(
|
||||
post.path,
|
||||
this.props.config.cleanUrl,
|
||||
)}`}>
|
||||
{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.renderTitle()}
|
||||
<p className="post-meta">
|
||||
{month} {day}, {year}
|
||||
</p>
|
||||
<div className="authorBlock">
|
||||
{post.author ? (
|
||||
<p className="post-authorName">
|
||||
<a
|
||||
href={post.authorURL}
|
||||
target="_blank"
|
||||
rel="noreferrer noopener">
|
||||
{post.author}
|
||||
</a>
|
||||
{post.authorTitle}
|
||||
</p>
|
||||
) : null}
|
||||
{this.renderAuthorPhoto()}
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="post">
|
||||
{this.renderPostHeader()}
|
||||
{this.renderContent()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = BlogPost;
|
|
@ -1,142 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
const classNames = require('classnames');
|
||||
const React = require('react');
|
||||
|
||||
const BlogPost = require('./BlogPost.js');
|
||||
const BlogSidebar = require('./BlogSidebar.js');
|
||||
const Container = require('./Container.js');
|
||||
const Site = require('./Site.js');
|
||||
const OnPageNav = require('./nav/OnPageNav.js');
|
||||
const utils = require('./utils.js');
|
||||
|
||||
// used for entire blog posts, i.e., each written blog article with sidebar with site header/footer
|
||||
class BlogPostLayout extends React.Component {
|
||||
getDescription() {
|
||||
const descLines = this.props.children.trim().split('\n');
|
||||
for (let i = 0; i < descLines.length; i++) {
|
||||
// Don't want blank lines or descriptions that are raw image rendering strings.
|
||||
if (descLines[i] && !descLines[i].startsWith('![')) {
|
||||
return descLines[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
renderSocialButtons() {
|
||||
const post = this.props.metadata;
|
||||
post.path = utils.getPath(post.path, this.props.config.cleanUrl);
|
||||
|
||||
const fbComment = this.props.config.facebookAppId &&
|
||||
this.props.config.facebookComments && (
|
||||
<div className="blogSocialSectionItem">
|
||||
{/* Facebook SDK require 'fb-comments' class */}
|
||||
<div
|
||||
className="fb-comments"
|
||||
data-href={`${
|
||||
this.props.config.url + this.props.config.baseUrl
|
||||
}blog/${post.path}`}
|
||||
data-width="100%"
|
||||
data-numposts="5"
|
||||
data-order-by="time"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
const fbLike = this.props.config.facebookAppId && (
|
||||
<div className="blogSocialSectionItem">
|
||||
{/* Facebook SDK require 'fb-like' class */}
|
||||
<div
|
||||
className="fb-like"
|
||||
data-href={`${
|
||||
this.props.config.url + this.props.config.baseUrl
|
||||
}blog/${post.path}`}
|
||||
data-layout="standard"
|
||||
data-share="true"
|
||||
data-width="225"
|
||||
data-show-faces="false"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
const twitterShare = this.props.config.twitter && (
|
||||
<div className="blogSocialSectionItem">
|
||||
<a
|
||||
href="https://twitter.com/share"
|
||||
className="twitter-share-button"
|
||||
data-text={post.title}
|
||||
data-url={`${this.props.config.url + this.props.config.baseUrl}blog/${
|
||||
post.path
|
||||
}`}
|
||||
data-related={this.props.config.twitter}
|
||||
data-via={post.authorTwitter}
|
||||
data-show-count="false">
|
||||
Tweet
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="blogSocialSection">
|
||||
{twitterShare}
|
||||
{fbLike}
|
||||
{fbComment}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const hasOnPageNav = this.props.config.onPageNav === 'separate';
|
||||
const post = this.props.metadata;
|
||||
post.path = utils.getPath(post.path, this.props.config.cleanUrl);
|
||||
const blogSidebarTitleConfig = this.props.config.blogSidebarTitle || {};
|
||||
return (
|
||||
<Site
|
||||
className={classNames('sideNavVisible', {
|
||||
separateOnPageNav: hasOnPageNav,
|
||||
})}
|
||||
url={`blog/${post.path}`}
|
||||
title={this.props.metadata.title}
|
||||
language="en"
|
||||
description={this.getDescription()}
|
||||
config={this.props.config}
|
||||
metadata={{blog: true}}>
|
||||
<div className="docMainWrapper wrapper">
|
||||
<BlogSidebar
|
||||
language="en"
|
||||
current={post}
|
||||
config={this.props.config}
|
||||
/>
|
||||
<Container className="mainContainer postContainer blogContainer">
|
||||
<div className="lonePost">
|
||||
<BlogPost
|
||||
post={post}
|
||||
content={this.props.children}
|
||||
language="en"
|
||||
config={this.props.config}
|
||||
/>
|
||||
{this.renderSocialButtons()}
|
||||
</div>
|
||||
<div className="blog-recent">
|
||||
<a className="button" href={`${this.props.config.baseUrl}blog/`}>
|
||||
{blogSidebarTitleConfig.default || 'Recent Posts'}
|
||||
</a>
|
||||
</div>
|
||||
</Container>
|
||||
{hasOnPageNav && (
|
||||
<nav className="onPageNav">
|
||||
<OnPageNav rawContent={this.props.children} />
|
||||
</nav>
|
||||
)}
|
||||
</div>
|
||||
</Site>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = BlogPostLayout;
|
|
@ -1,62 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
const React = require('react');
|
||||
const SideNav = require('./nav/SideNav.js');
|
||||
|
||||
const MetadataBlog = require('./MetadataBlog.js');
|
||||
|
||||
const MetadataPublicBlog =
|
||||
process.env.NODE_ENV === 'development'
|
||||
? MetadataBlog
|
||||
: MetadataBlog.filter((item) => !item.unlisted);
|
||||
|
||||
class BlogSidebar extends React.Component {
|
||||
render() {
|
||||
let blogSidebarCount = 5;
|
||||
const blogSidebarTitleConfig = this.props.config.blogSidebarTitle || {};
|
||||
let blogSidebarTitle = blogSidebarTitleConfig.default || 'Recent Posts';
|
||||
if (this.props.config.blogSidebarCount) {
|
||||
if (this.props.config.blogSidebarCount === 'ALL') {
|
||||
blogSidebarCount = MetadataPublicBlog.length;
|
||||
blogSidebarTitle = blogSidebarTitleConfig.all || 'All Blog Posts';
|
||||
} else {
|
||||
blogSidebarCount = this.props.config.blogSidebarCount;
|
||||
}
|
||||
}
|
||||
|
||||
const contents = [
|
||||
{
|
||||
type: 'CATEGORY',
|
||||
title: blogSidebarTitle,
|
||||
children: MetadataPublicBlog.slice(0, blogSidebarCount).map((item) => ({
|
||||
type: 'LINK',
|
||||
item,
|
||||
})),
|
||||
},
|
||||
];
|
||||
const title = this.props.current && this.props.current.title;
|
||||
|
||||
const current = {
|
||||
id: title || '',
|
||||
category: blogSidebarTitle,
|
||||
};
|
||||
return (
|
||||
<div className="docsNavContainer" id="docsNav">
|
||||
<SideNav
|
||||
language={this.props.language}
|
||||
root={`${this.props.config.baseUrl}blog/`}
|
||||
title="Blog"
|
||||
contents={contents}
|
||||
current={current}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = BlogSidebar;
|
|
@ -1,66 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import _ from 'lodash';
|
||||
|
||||
const React = require('react');
|
||||
const Remarkable = require('./Remarkable');
|
||||
|
||||
/**
|
||||
* The MarkdownBlock component is used to parse markdown and render to HTML.
|
||||
*/
|
||||
class MarkdownBlock extends React.Component {
|
||||
render() {
|
||||
const groupId = _.uniqueId();
|
||||
|
||||
const tabs = this.props.children.map(({title, content}) => ({
|
||||
id: _.uniqueId(),
|
||||
groupId,
|
||||
label: title,
|
||||
lang: title,
|
||||
panelContent: <Remarkable source={content} />,
|
||||
}));
|
||||
|
||||
return (
|
||||
<div className="tabs">
|
||||
<div className="nav-tabs">
|
||||
{tabs.map((t, i) => {
|
||||
const tabId = `tab-group-${groupId}-tab-${t.id}`;
|
||||
const contentId = `tab-group-${groupId}-content-${t.id}`;
|
||||
return (
|
||||
<div
|
||||
id={tabId}
|
||||
key={tabId}
|
||||
className={`nav-link${i === 0 ? ' active' : ''}`}
|
||||
data-group={`group_${t.groupId}`}
|
||||
data-tab={contentId}>
|
||||
{t.label}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div className="tab-content">
|
||||
{tabs.map((t, i) => {
|
||||
const id = `tab-group-${groupId}-content-${t.id}`;
|
||||
return (
|
||||
<div
|
||||
id={id}
|
||||
key={id}
|
||||
className={`tab-pane${i === 0 ? ' active' : ''}`}
|
||||
data-group={`group_${t.groupId}`}
|
||||
tabIndex="-1">
|
||||
{t.panelContent}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = MarkdownBlock;
|
|
@ -1,17 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
const MarkdownBlock = require('./MarkdownBlock.js');
|
||||
const Container = require('./Container.js');
|
||||
const GridBlock = require('./GridBlock.js');
|
||||
|
||||
// A collection of components to provide to users
|
||||
module.exports = {
|
||||
MarkdownBlock,
|
||||
Container,
|
||||
GridBlock,
|
||||
};
|
|
@ -1,44 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
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: null,
|
||||
padding: [],
|
||||
wrapper: true,
|
||||
};
|
||||
|
||||
module.exports = Container;
|
|
@ -1,254 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
const React = require('react');
|
||||
const {renderToStaticMarkup} = require('react-dom/server');
|
||||
const MarkdownBlock = require('./MarkdownBlock.js');
|
||||
const CodeTabsMarkdownBlock = require('./CodeTabsMarkdownBlock.js');
|
||||
|
||||
const translate = require('../server/translate.js').translate;
|
||||
|
||||
const editThisDoc = translate(
|
||||
'Edit this Doc|recruitment message asking to edit the doc source',
|
||||
);
|
||||
const translateThisDoc = translate(
|
||||
'Translate this Doc|recruitment message asking to translate the docs',
|
||||
);
|
||||
|
||||
const splitTabsToTitleAndContent = (lines, indents) => {
|
||||
let first = false;
|
||||
let inBlock = false;
|
||||
let whitespace = false;
|
||||
const tc = [];
|
||||
let current = {
|
||||
content: [],
|
||||
};
|
||||
lines.forEach((line) => {
|
||||
if (indents) {
|
||||
line = line.replace(new RegExp(`^((\\t|\\s{4}){${indents}})`, 'g'), '');
|
||||
}
|
||||
let pos = 0;
|
||||
const end = line.length;
|
||||
const isToken = (cline, cpos, ...chars) => {
|
||||
for (let i = 0; i < chars.length; i++) {
|
||||
if (cline.charCodeAt(cpos) !== chars[i]) {
|
||||
return false;
|
||||
}
|
||||
cpos++;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
while (pos + 1 < end) {
|
||||
// Skip all the whitespace when we first start the scan.
|
||||
for (let max = end; pos < max; pos++) {
|
||||
if (line.charCodeAt(pos) !== 0x20 && line.charCodeAt(pos) !== 0x0a) {
|
||||
break;
|
||||
}
|
||||
whitespace = true;
|
||||
}
|
||||
// Check for the start of a comment: <!--
|
||||
// If we're in a code block we skip it.
|
||||
if (
|
||||
isToken(
|
||||
line,
|
||||
pos,
|
||||
0x3c /* < */,
|
||||
0x21 /* ! */,
|
||||
0x2d /* - */,
|
||||
0x2d /* - */,
|
||||
) &&
|
||||
!inBlock
|
||||
) {
|
||||
if (current !== null && current.title !== undefined) {
|
||||
tc.push({
|
||||
title: current.title,
|
||||
content: current.content.join('\n'),
|
||||
});
|
||||
current = {
|
||||
content: [],
|
||||
};
|
||||
}
|
||||
first = true;
|
||||
pos += 4;
|
||||
let b0;
|
||||
let b1;
|
||||
const buf = [];
|
||||
// Add all characters to the title buffer until
|
||||
// we reach the end marker: -->
|
||||
for (let max = end; pos < max; pos++) {
|
||||
const b = line.charCodeAt(pos);
|
||||
if (b0 === 0x2d /* - */ && b1 === 0x2d /* - */) {
|
||||
if (b !== 0x3e /* > */) {
|
||||
throw new Error(`Invalid comment sequence "--"`);
|
||||
}
|
||||
break;
|
||||
}
|
||||
buf.push(b);
|
||||
b0 = b1;
|
||||
b1 = b;
|
||||
}
|
||||
// Clear the line out before we add it to content.
|
||||
// This also means tabs can only be defined on a line by itself.
|
||||
line = '\n';
|
||||
// Trim the last 2 characters: --
|
||||
current.title = String.fromCharCode(...buf)
|
||||
.substring(0, buf.length - 2)
|
||||
.trim();
|
||||
}
|
||||
// If the first thing in a code tab is not a title it's invalid.
|
||||
if (!first) {
|
||||
throw new Error(`Invalid code tab markdown`);
|
||||
}
|
||||
// Check for code block: ```
|
||||
// If the line begins with whitespace we don't consider it a code block.
|
||||
if (
|
||||
isToken(line, pos, 0x60 /* ` */, 0x60 /* ` */, 0x60 /* ` */) &&
|
||||
!whitespace
|
||||
) {
|
||||
pos += 3;
|
||||
inBlock = !inBlock;
|
||||
}
|
||||
pos++;
|
||||
whitespace = false;
|
||||
}
|
||||
current.content.push(line);
|
||||
});
|
||||
if (current !== null && current.title !== undefined) {
|
||||
tc.push({
|
||||
title: current.title,
|
||||
content: current.content.join('\n'),
|
||||
});
|
||||
}
|
||||
return tc;
|
||||
};
|
||||
|
||||
const cleanTheCodeTag = (content, indents) => {
|
||||
const prepend = (line, indent) => {
|
||||
if (indent) {
|
||||
return ' '.repeat(indent) + line;
|
||||
}
|
||||
return line;
|
||||
};
|
||||
const contents = content.split(/(<pre>)(.*?)(<\/pre>)/gms);
|
||||
let inCodeBlock = false;
|
||||
const cleanContents = contents.map((c) => {
|
||||
if (c === '<pre>') {
|
||||
inCodeBlock = true;
|
||||
return c;
|
||||
}
|
||||
if (c === '</pre>') {
|
||||
inCodeBlock = false;
|
||||
return c;
|
||||
}
|
||||
if (inCodeBlock) {
|
||||
return c.replace(/\n/g, '<br />');
|
||||
}
|
||||
return prepend(c, indents);
|
||||
});
|
||||
return cleanContents.join('');
|
||||
};
|
||||
|
||||
// inner doc component for article itself
|
||||
class Doc extends React.Component {
|
||||
renderContent() {
|
||||
const {content} = this.props;
|
||||
let indents = 0;
|
||||
return content.replace(
|
||||
/(\t|\s{4})*?(<!--DOCUSAURUS_CODE_TABS-->\n)(.*?)((\n|\t|\s{4})<!--END_DOCUSAURUS_CODE_TABS-->)/gms,
|
||||
(m) => {
|
||||
const contents = m.split('\n').filter((c) => {
|
||||
if (!indents) {
|
||||
indents = (
|
||||
c.match(/((\t|\s{4})+)<!--DOCUSAURUS_CODE_TABS-->/) || []
|
||||
).length;
|
||||
}
|
||||
if (c.match(/(\t|\s{4})+<!--DOCUSAURUS_CODE_TABS-->/)) {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
c.match(
|
||||
/<!--END_DOCUSAURUS_CODE_TABS-->|<!--DOCUSAURUS_CODE_TABS-->/,
|
||||
) ||
|
||||
(indents > 0 &&
|
||||
c.match(
|
||||
/(\t|\s{4})+(<!--END_DOCUSAURUS_CODE_TABS-->|<!--DOCUSAURUS_CODE_TABS-->)/,
|
||||
))
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
if (indents) {
|
||||
indents -= 1;
|
||||
}
|
||||
const codeTabsMarkdownBlock = renderToStaticMarkup(
|
||||
<CodeTabsMarkdownBlock>
|
||||
{splitTabsToTitleAndContent(contents, indents)}
|
||||
</CodeTabsMarkdownBlock>,
|
||||
);
|
||||
return cleanTheCodeTag(codeTabsMarkdownBlock, indents);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
let docSource = this.props.source;
|
||||
|
||||
if (this.props.version && this.props.version !== 'next') {
|
||||
// If versioning is enabled and the current version is not next, we need to trim out "version-*" from the source if we want a valid edit link.
|
||||
docSource = docSource.match(new RegExp(/version-.*?\/(.*\.md)/, 'i'))[1];
|
||||
}
|
||||
|
||||
const editUrl =
|
||||
this.props.metadata.custom_edit_url ||
|
||||
(this.props.config.editUrl && this.props.config.editUrl + docSource);
|
||||
let editLink = editUrl && (
|
||||
<a
|
||||
className="edit-page-link button"
|
||||
href={editUrl}
|
||||
target="_blank"
|
||||
rel="noreferrer noopener">
|
||||
{editThisDoc}
|
||||
</a>
|
||||
);
|
||||
|
||||
// If internationalization is enabled, show Recruiting link instead of Edit Link.
|
||||
if (
|
||||
this.props.language &&
|
||||
this.props.language !== 'en' &&
|
||||
this.props.config.translationRecruitingLink
|
||||
) {
|
||||
editLink = (
|
||||
<a
|
||||
className="edit-page-link button"
|
||||
href={`${this.props.config.translationRecruitingLink}/${this.props.language}`}
|
||||
target="_blank"
|
||||
rel="noreferrer noopener">
|
||||
{translateThisDoc}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="post">
|
||||
<header className="postHeader">
|
||||
{editLink}
|
||||
{!this.props.hideTitle && (
|
||||
<h1 id="__docusaurus" className="postHeaderTitle">
|
||||
{this.props.title}
|
||||
</h1>
|
||||
)}
|
||||
</header>
|
||||
<article>
|
||||
<MarkdownBlock>{this.renderContent()}</MarkdownBlock>
|
||||
</article>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Doc;
|
|
@ -1,167 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
const classNames = require('classnames');
|
||||
const path = require('path');
|
||||
const React = require('react');
|
||||
const url = require('url');
|
||||
|
||||
const Container = require('./Container.js');
|
||||
const Doc = require('./Doc.js');
|
||||
const DocsSidebar = require('./DocsSidebar.js');
|
||||
const OnPageNav = require('./nav/OnPageNav.js');
|
||||
const Site = require('./Site.js');
|
||||
const translation = require('../server/translation.js');
|
||||
const docs = require('../server/docs.js');
|
||||
const {idx, getGitLastUpdatedTime, getGitLastUpdatedBy} = require('./utils.js');
|
||||
|
||||
// component used to generate whole webpage for docs, including sidebar/header/footer
|
||||
class DocsLayout extends React.Component {
|
||||
getRelativeURL = (from, to) => {
|
||||
const extension = this.props.config.cleanUrl ? '' : '.html';
|
||||
const relativeHref = path
|
||||
.relative(`${from}.html`, `${to}.html`)
|
||||
.replace('\\', '/')
|
||||
.replace(/^\.\.\//, '')
|
||||
.replace(/\.html$/, extension);
|
||||
return url.resolve(
|
||||
`${this.props.config.baseUrl}${this.props.metadata.permalink}`,
|
||||
relativeHref,
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const metadata = this.props.metadata;
|
||||
const content = this.props.children;
|
||||
const i18n = translation[metadata.language];
|
||||
const id = metadata.localized_id;
|
||||
const defaultTitle = metadata.title;
|
||||
let DocComponent = Doc;
|
||||
|
||||
if (this.props.Doc) {
|
||||
DocComponent = this.props.Doc;
|
||||
}
|
||||
const filepath = docs.getFilePath(metadata);
|
||||
|
||||
const updateTime = this.props.config.enableUpdateTime
|
||||
? getGitLastUpdatedTime(filepath)
|
||||
: null;
|
||||
const updateAuthor = this.props.config.enableUpdateBy
|
||||
? getGitLastUpdatedBy(filepath)
|
||||
: null;
|
||||
|
||||
const title =
|
||||
idx(i18n, ['localized-strings', 'docs', id, 'title']) || defaultTitle;
|
||||
const hasOnPageNav = this.props.config.onPageNav === 'separate';
|
||||
|
||||
const previousTitle =
|
||||
idx(i18n, [
|
||||
'localized-strings',
|
||||
'docs',
|
||||
metadata.previous_id,
|
||||
'sidebar_label',
|
||||
]) ||
|
||||
idx(i18n, ['localized-strings', 'docs', metadata.previous_id, 'title']) ||
|
||||
idx(i18n, ['localized-strings', 'previous']) ||
|
||||
metadata.previous_title ||
|
||||
'Previous';
|
||||
const nextTitle =
|
||||
idx(i18n, [
|
||||
'localized-strings',
|
||||
'docs',
|
||||
metadata.next_id,
|
||||
'sidebar_label',
|
||||
]) ||
|
||||
idx(i18n, ['localized-strings', 'docs', metadata.next_id, 'title']) ||
|
||||
idx(i18n, ['localized-strings', 'next']) ||
|
||||
metadata.next_title ||
|
||||
'Next';
|
||||
|
||||
return (
|
||||
<Site
|
||||
config={this.props.config}
|
||||
className={classNames('sideNavVisible', {
|
||||
separateOnPageNav: hasOnPageNav,
|
||||
})}
|
||||
title={title}
|
||||
description={metadata.description || content.trim().split('\n')[0]}
|
||||
language={metadata.language}
|
||||
version={metadata.version}
|
||||
metadata={metadata}>
|
||||
<div className="docMainWrapper wrapper">
|
||||
<DocsSidebar
|
||||
collapsible={this.props.config.docsSideNavCollapsible}
|
||||
metadata={metadata}
|
||||
/>
|
||||
<Container className="mainContainer docsContainer">
|
||||
<DocComponent
|
||||
metadata={metadata}
|
||||
content={content}
|
||||
config={this.props.config}
|
||||
source={metadata.source}
|
||||
hideTitle={metadata.hide_title}
|
||||
title={title}
|
||||
version={metadata.version}
|
||||
language={metadata.language}
|
||||
/>
|
||||
{(updateTime || updateAuthor) && (
|
||||
<div className="docLastUpdate">
|
||||
<em>
|
||||
Last updated
|
||||
{updateTime && ` on ${updateTime}`}
|
||||
{updateAuthor && ` by ${updateAuthor}`}
|
||||
</em>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="docs-prevnext">
|
||||
{metadata.previous_id && (
|
||||
<a
|
||||
className="docs-prev button"
|
||||
href={this.getRelativeURL(
|
||||
metadata.localized_id,
|
||||
metadata.previous_id,
|
||||
)}>
|
||||
<span className="arrow-prev">← </span>
|
||||
<span
|
||||
className={
|
||||
previousTitle.match(/[a-z][A-Z]/) &&
|
||||
'function-name-prevnext'
|
||||
}>
|
||||
{previousTitle}
|
||||
</span>
|
||||
</a>
|
||||
)}
|
||||
{metadata.next_id && (
|
||||
<a
|
||||
className="docs-next button"
|
||||
href={this.getRelativeURL(
|
||||
metadata.localized_id,
|
||||
metadata.next_id,
|
||||
)}>
|
||||
<span
|
||||
className={
|
||||
nextTitle.match(/[a-z][A-Z]/) && 'function-name-prevnext'
|
||||
}>
|
||||
{nextTitle}
|
||||
</span>
|
||||
<span className="arrow-next"> →</span>
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
</Container>
|
||||
{hasOnPageNav && (
|
||||
<nav className="onPageNav">
|
||||
<OnPageNav rawContent={content} />
|
||||
</nav>
|
||||
)}
|
||||
</div>
|
||||
</Site>
|
||||
);
|
||||
}
|
||||
}
|
||||
module.exports = DocsLayout;
|
|
@ -1,54 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
const React = require('react');
|
||||
const fs = require('fs');
|
||||
const SideNav = require('./nav/SideNav.js');
|
||||
const Metadata = require('./metadata.js');
|
||||
|
||||
const readCategories = require('../server/readCategories.js');
|
||||
|
||||
let languages;
|
||||
|
||||
const CWD = process.cwd();
|
||||
if (fs.existsSync(`${CWD}/languages.js`)) {
|
||||
languages = require(`${CWD}/languages.js`);
|
||||
} else {
|
||||
languages = [
|
||||
{
|
||||
enabled: true,
|
||||
name: 'English',
|
||||
tag: 'en',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
class DocsSidebar extends React.Component {
|
||||
render() {
|
||||
const {category, sidebar} = this.props.metadata;
|
||||
const docsCategories = readCategories(sidebar, Metadata, languages);
|
||||
|
||||
if (!category) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="docsNavContainer" id="docsNav">
|
||||
<SideNav
|
||||
collapsible={this.props.collapsible}
|
||||
language={this.props.metadata.language}
|
||||
root={this.props.root}
|
||||
title={this.props.title}
|
||||
contents={docsCategories[this.props.metadata.language]}
|
||||
current={this.props.metadata}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = DocsSidebar;
|
|
@ -1,104 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
const React = require('react');
|
||||
const classNames = require('classnames');
|
||||
|
||||
const MarkdownBlock = require('./MarkdownBlock.js');
|
||||
|
||||
class GridBlock extends React.Component {
|
||||
renderBlock(origBlock) {
|
||||
const blockDefaults = {
|
||||
imageAlign: 'left',
|
||||
};
|
||||
|
||||
const block = {
|
||||
...blockDefaults,
|
||||
...origBlock,
|
||||
};
|
||||
|
||||
const blockClasses = classNames('blockElement', this.props.className, {
|
||||
alignCenter: this.props.align === 'center',
|
||||
alignRight: this.props.align === 'right',
|
||||
fourByGridBlock: this.props.layout === 'fourColumn',
|
||||
imageAlignSide:
|
||||
block.image &&
|
||||
(block.imageAlign === 'left' || block.imageAlign === 'right'),
|
||||
imageAlignTop: block.image && block.imageAlign === 'top',
|
||||
imageAlignRight: block.image && block.imageAlign === 'right',
|
||||
imageAlignBottom: block.image && block.imageAlign === 'bottom',
|
||||
imageAlignLeft: block.image && block.imageAlign === 'left',
|
||||
threeByGridBlock: this.props.layout === 'threeColumn',
|
||||
twoByGridBlock: this.props.layout === 'twoColumn',
|
||||
});
|
||||
|
||||
const topLeftImage =
|
||||
(block.imageAlign === 'top' || block.imageAlign === 'left') &&
|
||||
this.renderBlockImage(block.image, block.imageLink, block.imageAlt);
|
||||
|
||||
const bottomRightImage =
|
||||
(block.imageAlign === 'bottom' || block.imageAlign === 'right') &&
|
||||
this.renderBlockImage(block.image, block.imageLink, block.imageAlt);
|
||||
|
||||
return (
|
||||
<div className={blockClasses} key={block.title}>
|
||||
{topLeftImage}
|
||||
<div className="blockContent">
|
||||
{this.renderBlockTitle(block.title)}
|
||||
<MarkdownBlock>{block.content}</MarkdownBlock>
|
||||
</div>
|
||||
{bottomRightImage}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderBlockImage(image, imageLink, imageAlt) {
|
||||
if (!image) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="blockImage">
|
||||
{imageLink ? (
|
||||
<a href={imageLink}>
|
||||
<img src={image} alt={imageAlt} />
|
||||
</a>
|
||||
) : (
|
||||
<img src={image} alt={imageAlt} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderBlockTitle(title) {
|
||||
if (!title) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<h2>
|
||||
<MarkdownBlock>{title}</MarkdownBlock>
|
||||
</h2>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="gridBlock">
|
||||
{this.props.contents.map(this.renderBlock, this)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
GridBlock.defaultProps = {
|
||||
align: 'left',
|
||||
contents: [],
|
||||
layout: 'twoColumn',
|
||||
};
|
||||
|
||||
module.exports = GridBlock;
|
|
@ -1,189 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
const React = require('react');
|
||||
|
||||
// html head for each page
|
||||
class Head extends React.Component {
|
||||
render() {
|
||||
const links = this.props.config.headerLinks;
|
||||
const hasBlog = links.some((link) => link.blog);
|
||||
|
||||
const highlight = {
|
||||
version: '9.12.0',
|
||||
theme: 'default',
|
||||
...this.props.config.highlight,
|
||||
};
|
||||
|
||||
// Use user-provided themeUrl if it exists, else construct one from version and theme.
|
||||
const highlightThemeURL = highlight.themeUrl
|
||||
? highlight.themeUrl
|
||||
: `//cdnjs.cloudflare.com/ajax/libs/highlight.js/${highlight.version}/styles/${highlight.theme}.min.css`;
|
||||
|
||||
// ensure the siteUrl variable ends with a single slash
|
||||
const siteUrl = `${(
|
||||
this.props.config.url + this.props.config.baseUrl
|
||||
).replace(/\/+$/, '')}/`;
|
||||
|
||||
return (
|
||||
<head>
|
||||
<meta charSet="utf-8" />
|
||||
<meta httpEquiv="X-UA-Compatible" content="IE=edge" />
|
||||
<title>{this.props.title}</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta name="generator" content="Docusaurus" />
|
||||
<meta name="description" content={this.props.description} />
|
||||
{this.props.version && (
|
||||
<meta name="docsearch:version" content={this.props.version} />
|
||||
)}
|
||||
{this.props.language && (
|
||||
<meta name="docsearch:language" content={this.props.language} />
|
||||
)}
|
||||
<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:description" content={this.props.description} />
|
||||
{this.props.config.ogImage && (
|
||||
<meta
|
||||
property="og:image"
|
||||
content={siteUrl + this.props.config.ogImage}
|
||||
/>
|
||||
)}
|
||||
<meta name="twitter:card" content="summary" />
|
||||
{this.props.config.twitterImage && (
|
||||
<meta
|
||||
name="twitter:image"
|
||||
content={siteUrl + this.props.config.twitterImage}
|
||||
/>
|
||||
)}
|
||||
{this.props.config.noIndex && <meta name="robots" content="noindex" />}
|
||||
{this.props.redirect && (
|
||||
<meta httpEquiv="refresh" content={`0; URL=${this.props.redirect}`} />
|
||||
)}
|
||||
{this.props.config.manifest && (
|
||||
<link rel="manifest" href={siteUrl + this.props.config.manifest} />
|
||||
)}
|
||||
<link
|
||||
rel="shortcut icon"
|
||||
href={this.props.config.baseUrl + this.props.config.favicon}
|
||||
/>
|
||||
{this.props.config.algolia && (
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/docsearch.js/1/docsearch.min.css"
|
||||
/>
|
||||
)}
|
||||
<link rel="stylesheet" href={highlightThemeURL} />
|
||||
{hasBlog && (
|
||||
<link
|
||||
rel="alternate"
|
||||
type="application/atom+xml"
|
||||
href={`${siteUrl}blog/atom.xml`}
|
||||
title={`${this.props.config.title} Blog ATOM Feed`}
|
||||
/>
|
||||
)}
|
||||
{hasBlog && (
|
||||
<link
|
||||
rel="alternate"
|
||||
type="application/rss+xml"
|
||||
href={`${siteUrl}blog/feed.xml`}
|
||||
title={`${this.props.config.title} Blog RSS Feed`}
|
||||
/>
|
||||
)}
|
||||
{this.props.config.gaTrackingId && this.props.config.gaGtag && (
|
||||
<script
|
||||
async
|
||||
src={`https://www.googletagmanager.com/gtag/js?id=${this.props.config.gaTrackingId}`}
|
||||
/>
|
||||
)}
|
||||
{this.props.config.gaTrackingId && this.props.config.gaGtag && (
|
||||
<script
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments); }
|
||||
gtag('js', new Date());
|
||||
gtag('config', '${this.props.config.gaTrackingId}');
|
||||
`,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{this.props.config.gaTrackingId && !this.props.config.gaGtag && (
|
||||
<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', '${this.props.config.gaTrackingId}', 'auto');
|
||||
ga('send', 'pageview');
|
||||
`,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* External resources */}
|
||||
{this.props.config.stylesheets &&
|
||||
this.props.config.stylesheets.map((source) =>
|
||||
source.href ? (
|
||||
<link rel="stylesheet" key={source.href} {...source} />
|
||||
) : (
|
||||
<link rel="stylesheet" key={source} href={source} />
|
||||
),
|
||||
)}
|
||||
{this.props.config.scripts &&
|
||||
this.props.config.scripts.map((source) =>
|
||||
source.src ? (
|
||||
<script type="text/javascript" key={source.src} {...source} />
|
||||
) : (
|
||||
<script type="text/javascript" src={source} key={source} />
|
||||
),
|
||||
)}
|
||||
|
||||
{this.props.config.scrollToTop && (
|
||||
<script src="https://unpkg.com/vanilla-back-to-top@7.1.14/dist/vanilla-back-to-top.min.js" />
|
||||
)}
|
||||
{this.props.config.scrollToTop && (
|
||||
<script
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
addBackToTop(
|
||||
${JSON.stringify(
|
||||
Object.assign(
|
||||
{},
|
||||
{zIndex: 100},
|
||||
this.props.config.scrollToTopOptions,
|
||||
),
|
||||
)}
|
||||
)
|
||||
});
|
||||
`,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<script src={`${this.props.config.baseUrl}js/scrollSpy.js`} />
|
||||
{this.props.config.usePrism && (
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href={`${this.props.config.baseUrl}css/prism.css`}
|
||||
/>
|
||||
)}
|
||||
{/* Site defined code. Keep these at the end to avoid overriding. */}
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href={`${this.props.config.baseUrl}css/main.css`}
|
||||
/>
|
||||
<script src={`${this.props.config.baseUrl}js/codetabs.js`} />
|
||||
</head>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Head;
|
|
@ -1,20 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
const React = require('react');
|
||||
const Remarkable = require('./Remarkable');
|
||||
|
||||
/**
|
||||
* The MarkdownBlock component is used to parse markdown and render to HTML.
|
||||
*/
|
||||
class MarkdownBlock extends React.Component {
|
||||
render() {
|
||||
return <Remarkable source={this.props.children} />;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = MarkdownBlock;
|
|
@ -1,56 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
const React = require('react');
|
||||
const Head = require('./Head.js');
|
||||
const translation = require('../server/translation.js');
|
||||
const {idx} = require('./utils.js');
|
||||
|
||||
// Component used to provide same head, header, footer, other scripts to all pages
|
||||
class Redirect extends React.Component {
|
||||
render() {
|
||||
const tagline =
|
||||
idx(translation, [this.props.language, 'localized-strings', 'tagline']) ||
|
||||
this.props.config.tagline;
|
||||
const title = this.props.title
|
||||
? `${this.props.title} · ${this.props.config.title}`
|
||||
: (!this.props.config.disableTitleTagline &&
|
||||
`${this.props.config.title} · ${tagline}`) ||
|
||||
this.props.config.title;
|
||||
const description = this.props.description || tagline;
|
||||
const url =
|
||||
this.props.config.url +
|
||||
this.props.config.baseUrl +
|
||||
(this.props.url || 'index.html');
|
||||
|
||||
const redirect = this.props.redirect || false;
|
||||
|
||||
return (
|
||||
<html lang="en">
|
||||
<Head
|
||||
config={this.props.config}
|
||||
description={description}
|
||||
title={title}
|
||||
url={url}
|
||||
redirect={redirect}
|
||||
/>
|
||||
<body className={this.props.className}>
|
||||
<script
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
<!--
|
||||
window.location.href = "${this.props.redirect}";
|
||||
// -->
|
||||
`,
|
||||
}}
|
||||
/>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
}
|
||||
module.exports = Redirect;
|
|
@ -1,44 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
const React = require('react');
|
||||
const renderMarkdown = require('./renderMarkdown.js');
|
||||
|
||||
class Remarkable extends React.Component {
|
||||
content() {
|
||||
if (this.props.source) {
|
||||
return (
|
||||
<span
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: renderMarkdown(this.props.source),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return React.Children.map(this.props.children, (child) => {
|
||||
if (typeof child === 'string') {
|
||||
return (
|
||||
<span dangerouslySetInnerHTML={{__html: renderMarkdown(child)}} />
|
||||
);
|
||||
}
|
||||
|
||||
return child;
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const Container = this.props.container;
|
||||
return <Container>{this.content()}</Container>;
|
||||
}
|
||||
}
|
||||
|
||||
Remarkable.defaultProps = {
|
||||
container: 'div',
|
||||
};
|
||||
|
||||
module.exports = Remarkable;
|
|
@ -1,194 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
const React = require('react');
|
||||
const fs = require('fs');
|
||||
const classNames = require('classnames');
|
||||
|
||||
const HeaderNav = require('./nav/HeaderNav.js');
|
||||
const Head = require('./Head.js');
|
||||
|
||||
const Footer = require(`${process.cwd()}/core/Footer.js`);
|
||||
const translation = require('../server/translation.js');
|
||||
const env = require('../server/env.js');
|
||||
const liveReloadServer = require('../server/liveReloadServer.js');
|
||||
const {idx, getPath} = require('./utils.js');
|
||||
|
||||
const CWD = process.cwd();
|
||||
|
||||
// Component used to provide same head, header, footer, other scripts to all pages
|
||||
class Site extends React.Component {
|
||||
mobileNavHasOneRow(headerLinks) {
|
||||
const hasLanguageDropdown =
|
||||
env.translation.enabled && env.translation.enabledLanguages().length > 1;
|
||||
const hasOrdinaryHeaderLinks = headerLinks.some(
|
||||
(link) => !(link.languages || link.search),
|
||||
);
|
||||
return !(hasLanguageDropdown || hasOrdinaryHeaderLinks);
|
||||
}
|
||||
|
||||
render() {
|
||||
const tagline =
|
||||
idx(translation, [this.props.language, 'localized-strings', 'tagline']) ||
|
||||
this.props.config.tagline;
|
||||
const title = this.props.title
|
||||
? `${this.props.title} · ${this.props.config.title}`
|
||||
: (!this.props.config.disableTitleTagline &&
|
||||
`${this.props.config.title} · ${tagline}`) ||
|
||||
this.props.config.title;
|
||||
const description = this.props.description || tagline;
|
||||
const path = getPath(
|
||||
this.props.config.baseUrl + (this.props.url || 'index.html'),
|
||||
this.props.config.cleanUrl,
|
||||
);
|
||||
const url = this.props.config.url + path;
|
||||
let docsVersion = this.props.version;
|
||||
|
||||
const liveReloadScriptUrl = liveReloadServer.getReloadScriptUrl();
|
||||
|
||||
if (!docsVersion && fs.existsSync(`${CWD}/versions.json`)) {
|
||||
const latestVersion = require(`${CWD}/versions.json`)[0];
|
||||
docsVersion = latestVersion;
|
||||
}
|
||||
|
||||
const navPusherClasses = classNames('navPusher', {
|
||||
singleRowMobileNav: this.mobileNavHasOneRow(
|
||||
this.props.config.headerLinks,
|
||||
),
|
||||
});
|
||||
|
||||
return (
|
||||
<html lang={this.props.language}>
|
||||
<Head
|
||||
config={this.props.config}
|
||||
description={description}
|
||||
title={title}
|
||||
url={url}
|
||||
language={this.props.language}
|
||||
version={this.props.version}
|
||||
/>
|
||||
<body className={this.props.className}>
|
||||
<HeaderNav
|
||||
config={this.props.config}
|
||||
baseUrl={this.props.config.baseUrl}
|
||||
title={this.props.config.title}
|
||||
language={this.props.language}
|
||||
version={this.props.version}
|
||||
current={this.props.metadata}
|
||||
/>
|
||||
<div className={navPusherClasses}>
|
||||
{this.props.children}
|
||||
<Footer config={this.props.config} language={this.props.language} />
|
||||
</div>
|
||||
{this.props.config.algolia && (
|
||||
<script
|
||||
type="text/javascript"
|
||||
src="https://cdn.jsdelivr.net/docsearch.js/1/docsearch.min.js"
|
||||
/>
|
||||
)}
|
||||
{this.props.config.facebookAppId && (
|
||||
<script
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `window.fbAsyncInit = function() {FB.init({appId:'${this.props.config.facebookAppId}',xfbml:true,version:'v2.7'});};(function(d, s, id){var js, fjs = d.getElementsByTagName(s)[0];if (d.getElementById(id)) {return;}js = d.createElement(s); js.id = id;js.src = '//connect.facebook.net/en_US/sdk.js';fjs.parentNode.insertBefore(js, fjs);}(document, 'script','facebook-jssdk'));
|
||||
`,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{this.props.config.facebookPixelId && (
|
||||
<script
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
!function(f,b,e,v,n,t,s)
|
||||
{if(f.fbq)return;n=f.fbq=function(){n.callMethod?
|
||||
n.callMethod.apply(n,arguments):n.queue.push(arguments)};
|
||||
if(!f._fbq)f._fbq=n;n.push=n;n.loaded=!0;n.version='2.0';
|
||||
n.queue=[];t=b.createElement(e);t.async=!0;
|
||||
t.src=v;s=b.getElementsByTagName(e)[0];
|
||||
s.parentNode.insertBefore(t,s)}(window, document,'script',
|
||||
'https://connect.facebook.net/en_US/fbevents.js');
|
||||
fbq('init', '${this.props.config.facebookPixelId}');
|
||||
fbq('track', 'PageView');
|
||||
`,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{(this.props.config.twitter || this.props.config.twitterUsername) && (
|
||||
<script
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `window.twttr=(function(d,s, id){var js,fjs=d.getElementsByTagName(s)[0],t=window.twttr||{};if(d.getElementById(id))return t;js=d.createElement(s);js.id=id;js.src='https://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js, fjs);t._e = [];t.ready = function(f) {t._e.push(f);};return t;}(document, 'script', 'twitter-wjs'));`,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{this.props.config.algolia && (
|
||||
<script
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
document.addEventListener('keyup', function(e) {
|
||||
if (e.target !== document.body) {
|
||||
return;
|
||||
}
|
||||
// keyCode for '/' (slash)
|
||||
if (e.keyCode === 191) {
|
||||
const search = document.getElementById('search_input_react');
|
||||
search && search.focus();
|
||||
}
|
||||
});
|
||||
`,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{this.props.config.algolia &&
|
||||
(this.props.config.algolia.algoliaOptions ? (
|
||||
<script
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
var search = docsearch({
|
||||
${
|
||||
this.props.config.algolia.appId
|
||||
? `appId: '${this.props.config.algolia.appId}',`
|
||||
: ''
|
||||
}
|
||||
apiKey: '${this.props.config.algolia.apiKey}',
|
||||
indexName: '${this.props.config.algolia.indexName}',
|
||||
inputSelector: '#search_input_react',
|
||||
algoliaOptions: ${JSON.stringify(
|
||||
this.props.config.algolia.algoliaOptions,
|
||||
)
|
||||
.replace('VERSION', docsVersion)
|
||||
.replace('LANGUAGE', this.props.language)}
|
||||
});
|
||||
`,
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<script
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
var search = docsearch({
|
||||
${
|
||||
this.props.config.algolia.appId
|
||||
? `appId: '${this.props.config.algolia.appId}',`
|
||||
: ''
|
||||
}
|
||||
apiKey: '${this.props.config.algolia.apiKey}',
|
||||
indexName: '${this.props.config.algolia.indexName}',
|
||||
inputSelector: '#search_input_react'
|
||||
});
|
||||
`,
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
|
||||
{process.env.NODE_ENV === 'development' && liveReloadScriptUrl && (
|
||||
<script src={liveReloadScriptUrl} />
|
||||
)}
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
}
|
||||
module.exports = Site;
|
|
@ -1,6 +0,0 @@
|
|||
---
|
||||
title: Draft Example
|
||||
unlisted: true
|
||||
---
|
||||
|
||||
This blog post should not appear in the sidebar or the blog feed because it is unlisted.
|
|
@ -1,15 +0,0 @@
|
|||
---
|
||||
title: Truncation Example
|
||||
---
|
||||
|
||||
All this will be part of the blog post summary.
|
||||
|
||||
Even this.
|
||||
|
||||
<!--truncate-->
|
||||
|
||||
But anything from here on down will not be.
|
||||
|
||||
Not this.
|
||||
|
||||
Or this.
|
|
@ -1,13 +0,0 @@
|
|||
---
|
||||
title: Non-truncation Example
|
||||
---
|
||||
|
||||
All this will be part of the blog post summary.
|
||||
|
||||
Even this.
|
||||
|
||||
And anything from here on down will still be.
|
||||
|
||||
And this.
|
||||
|
||||
And this too.
|
|
@ -1,35 +0,0 @@
|
|||
## foo
|
||||
|
||||
### foo
|
||||
|
||||
### foo 1
|
||||
|
||||
## foo 1
|
||||
|
||||
## foo 2
|
||||
|
||||
### foo
|
||||
|
||||
#### 4th level headings
|
||||
|
||||
All 4th level headings should not be shown by default
|
||||
|
||||
## bar
|
||||
|
||||
### bar
|
||||
|
||||
#### bar
|
||||
|
||||
4th level heading should be ignored by default, but is should be always taken into account, when generating slugs
|
||||
|
||||
### `bar`
|
||||
|
||||
#### `bar`
|
||||
|
||||
## bar
|
||||
|
||||
### bar
|
||||
|
||||
#### bar
|
||||
|
||||
## bar
|
|
@ -1,28 +0,0 @@
|
|||
---
|
||||
id: pokemon-commands
|
||||
title: Pokemon Commands
|
||||
---
|
||||
|
||||
## Commands
|
||||
|
||||
<AUTOGENERATED_TABLE_OF_CONTENTS>
|
||||
|
||||
---
|
||||
|
||||
## Reference
|
||||
|
||||
### `pokemon-run`
|
||||
|
||||
Alias: `run`.
|
||||
|
||||
### `pokemon-fight`
|
||||
|
||||
Alias: `fight`
|
||||
|
||||
### `pokemon-bag`
|
||||
|
||||
Alias: `bag`
|
||||
|
||||
### `pokemon-rename`
|
||||
|
||||
Alias: `rename`
|
|
@ -1,34 +0,0 @@
|
|||
<!--DOCUSAURUS_CODE_TABS-->
|
||||
<!--JavaScript-->
|
||||
|
||||
```js
|
||||
console.log('Hello, world!');
|
||||
```
|
||||
|
||||
<!--Python-->
|
||||
|
||||
```py
|
||||
print('Hello, world!')
|
||||
```
|
||||
|
||||
<!--C-->
|
||||
|
||||
```C
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
printf("Hello World!");
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
<!--Pascal-->
|
||||
|
||||
```Pascal
|
||||
program HelloWorld;
|
||||
begin
|
||||
WriteLn('Hello, world!');
|
||||
end.
|
||||
```
|
||||
|
||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
|
@ -1,32 +0,0 @@
|
|||
1. Doc
|
||||
* Hello
|
||||
<!--DOCUSAURUS_CODE_TABS-->
|
||||
<!--JavaScript-->
|
||||
```js
|
||||
console.log('Hello, world!');
|
||||
```
|
||||
<!--Python-->
|
||||
```py
|
||||
print('Hello, world!')
|
||||
```
|
||||
|
||||
<!--C-->
|
||||
```C
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
printf("Hello World!");
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
<!--Pascal-->
|
||||
```Pascal
|
||||
program HelloWorld;
|
||||
begin
|
||||
WriteLn('Hello, world!');
|
||||
end.
|
||||
```
|
||||
|
||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||
1. Do that
|
|
@ -1,5 +0,0 @@
|
|||
---
|
||||
title: Don't edit this
|
||||
---
|
||||
|
||||
Do not edit this file
|
|
@ -1,10 +0,0 @@
|
|||
{
|
||||
"_comment": "This file is auto-generated by write-translations.js",
|
||||
"localized-strings": {
|
||||
},
|
||||
"pages-strings": {
|
||||
"Help Translate|recruit community translators for your project": "Help Us Translate",
|
||||
"Edit this Doc|recruitment message asking to edit the doc source": "Edit",
|
||||
"Translate this Doc|recruitment message asking to translate the docs": "Translate"
|
||||
}
|
||||
}
|
|
@ -1,64 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/* List of projects/orgs using your project for the users page */
|
||||
|
||||
const siteConfig = {
|
||||
title: 'Docusaurus',
|
||||
tagline: 'Easy to Maintain Open Source Documentation Websites',
|
||||
url: 'https://docusaurus.io',
|
||||
baseUrl: '/',
|
||||
organizationName: 'facebook',
|
||||
projectName: 'Docusaurus',
|
||||
cname: 'docusaurus.io',
|
||||
noIndex: false,
|
||||
editUrl: 'https://github.com/facebook/docusaurus/edit/master/docs/',
|
||||
headerLinks: [],
|
||||
headerIcon: 'img/docusaurus.svg',
|
||||
footerIcon: 'img/docusaurus_monochrome.svg',
|
||||
favicon: 'img/docusaurus.ico',
|
||||
algolia: {
|
||||
apiKey: '3eb9507824b8be89e7a199ecaa1a9d2c',
|
||||
indexName: 'docusaurus',
|
||||
algoliaOptions: {
|
||||
facetFilters: ['language:LANGUAGE', 'version:VERSION'],
|
||||
},
|
||||
},
|
||||
colors: {
|
||||
primaryColor: '#2E8555',
|
||||
secondaryColor: '#205C3B',
|
||||
},
|
||||
translationRecruitingLink: 'https://crowdin.com/project/docusaurus',
|
||||
copyright: `Copyright © ${new Date().getFullYear()} Facebook Inc.`,
|
||||
usePrism: ['jsx'],
|
||||
highlight: {
|
||||
theme: 'atom-one-dark',
|
||||
},
|
||||
scripts: [
|
||||
'https://buttons.github.io/buttons.js',
|
||||
'https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/2.0.0/clipboard.min.js',
|
||||
'/js/code-blocks-buttons.js',
|
||||
],
|
||||
gaTrackingId: 'UA-44373548-31',
|
||||
facebookAppId: '199138890728411',
|
||||
facebookComments: true,
|
||||
twitter: 'true',
|
||||
twitterUsername: 'docusaurus',
|
||||
ogImage: 'img/docusaurus.png',
|
||||
twitterImage: 'img/docusaurus.png',
|
||||
onPageNav: 'separate',
|
||||
cleanUrl: true,
|
||||
scrollToTop: true,
|
||||
scrollToTopOptions: {
|
||||
zIndex: 100,
|
||||
},
|
||||
enableUpdateTime: true,
|
||||
enableUpdateBy: true,
|
||||
docsSideNavCollapsible: true,
|
||||
};
|
||||
|
||||
module.exports = siteConfig;
|
|
@ -1,257 +0,0 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`getTOC with custom heading levels 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"children": Array [],
|
||||
"content": "foo",
|
||||
"hashLink": "foo-1",
|
||||
"rawContent": "foo",
|
||||
},
|
||||
Object {
|
||||
"children": Array [],
|
||||
"content": "foo 1",
|
||||
"hashLink": "foo-1-1",
|
||||
"rawContent": "foo 1",
|
||||
},
|
||||
],
|
||||
"content": "foo",
|
||||
"hashLink": "foo",
|
||||
"rawContent": "foo",
|
||||
},
|
||||
Object {
|
||||
"children": Array [],
|
||||
"content": "foo 1",
|
||||
"hashLink": "foo-1-2",
|
||||
"rawContent": "foo 1",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"children": Array [],
|
||||
"content": "foo",
|
||||
"hashLink": "foo-3",
|
||||
"rawContent": "foo",
|
||||
},
|
||||
Object {
|
||||
"children": Array [],
|
||||
"content": "4th level headings",
|
||||
"hashLink": "4th-level-headings",
|
||||
"rawContent": "4th level headings",
|
||||
},
|
||||
],
|
||||
"content": "foo 2",
|
||||
"hashLink": "foo-2",
|
||||
"rawContent": "foo 2",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"children": Array [],
|
||||
"content": "bar",
|
||||
"hashLink": "bar-1",
|
||||
"rawContent": "bar",
|
||||
},
|
||||
Object {
|
||||
"children": Array [],
|
||||
"content": "bar",
|
||||
"hashLink": "bar-2",
|
||||
"rawContent": "bar",
|
||||
},
|
||||
Object {
|
||||
"children": Array [],
|
||||
"content": "<code>bar</code>",
|
||||
"hashLink": "bar-3",
|
||||
"rawContent": "\`bar\`",
|
||||
},
|
||||
Object {
|
||||
"children": Array [],
|
||||
"content": "<code>bar</code>",
|
||||
"hashLink": "bar-4",
|
||||
"rawContent": "\`bar\`",
|
||||
},
|
||||
],
|
||||
"content": "bar",
|
||||
"hashLink": "bar",
|
||||
"rawContent": "bar",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"children": Array [],
|
||||
"content": "bar",
|
||||
"hashLink": "bar-6",
|
||||
"rawContent": "bar",
|
||||
},
|
||||
Object {
|
||||
"children": Array [],
|
||||
"content": "bar",
|
||||
"hashLink": "bar-7",
|
||||
"rawContent": "bar",
|
||||
},
|
||||
],
|
||||
"content": "bar",
|
||||
"hashLink": "bar-5",
|
||||
"rawContent": "bar",
|
||||
},
|
||||
Object {
|
||||
"children": Array [],
|
||||
"content": "bar",
|
||||
"hashLink": "bar-8",
|
||||
"rawContent": "bar",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`getTOC with defaults 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"children": Array [],
|
||||
"content": "foo",
|
||||
"hashLink": "foo-1",
|
||||
"rawContent": "foo",
|
||||
},
|
||||
Object {
|
||||
"children": Array [],
|
||||
"content": "foo 1",
|
||||
"hashLink": "foo-1-1",
|
||||
"rawContent": "foo 1",
|
||||
},
|
||||
],
|
||||
"content": "foo",
|
||||
"hashLink": "foo",
|
||||
"rawContent": "foo",
|
||||
},
|
||||
Object {
|
||||
"children": Array [],
|
||||
"content": "foo 1",
|
||||
"hashLink": "foo-1-2",
|
||||
"rawContent": "foo 1",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"children": Array [],
|
||||
"content": "foo",
|
||||
"hashLink": "foo-3",
|
||||
"rawContent": "foo",
|
||||
},
|
||||
],
|
||||
"content": "foo 2",
|
||||
"hashLink": "foo-2",
|
||||
"rawContent": "foo 2",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"children": Array [],
|
||||
"content": "bar",
|
||||
"hashLink": "bar-1",
|
||||
"rawContent": "bar",
|
||||
},
|
||||
Object {
|
||||
"children": Array [],
|
||||
"content": "<code>bar</code>",
|
||||
"hashLink": "bar-3",
|
||||
"rawContent": "\`bar\`",
|
||||
},
|
||||
],
|
||||
"content": "bar",
|
||||
"hashLink": "bar",
|
||||
"rawContent": "bar",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"children": Array [],
|
||||
"content": "bar",
|
||||
"hashLink": "bar-6",
|
||||
"rawContent": "bar",
|
||||
},
|
||||
],
|
||||
"content": "bar",
|
||||
"hashLink": "bar-5",
|
||||
"rawContent": "bar",
|
||||
},
|
||||
Object {
|
||||
"children": Array [],
|
||||
"content": "bar",
|
||||
"hashLink": "bar-8",
|
||||
"rawContent": "bar",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`insertTOC AUTOGENERATED_TABLE_OF_CONTENTS does not exist 1`] = `
|
||||
"## foo
|
||||
|
||||
### foo
|
||||
|
||||
### foo 1
|
||||
|
||||
## foo 1
|
||||
|
||||
## foo 2
|
||||
|
||||
### foo
|
||||
|
||||
#### 4th level headings
|
||||
|
||||
All 4th level headings should not be shown by default
|
||||
|
||||
## bar
|
||||
|
||||
### bar
|
||||
|
||||
#### bar
|
||||
|
||||
4th level heading should be ignored by default, but is should be always taken into account, when generating slugs
|
||||
|
||||
### \`bar\`
|
||||
|
||||
#### \`bar\`
|
||||
|
||||
## bar
|
||||
|
||||
### bar
|
||||
|
||||
#### bar
|
||||
|
||||
## bar
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`insertTOC AUTOGENERATED_TABLE_OF_CONTENTS exists 1`] = `
|
||||
"
|
||||
## Commands
|
||||
|
||||
- [\`pokemon-run\`](#pokemon-run)
|
||||
- [\`pokemon-fight\`](#pokemon-fight)
|
||||
- [\`pokemon-bag\`](#pokemon-bag)
|
||||
- [\`pokemon-rename\`](#pokemon-rename)
|
||||
|
||||
---
|
||||
|
||||
## Reference
|
||||
|
||||
### \`pokemon-run\`
|
||||
|
||||
Alias: \`run\`.
|
||||
|
||||
### \`pokemon-fight\`
|
||||
|
||||
Alias: \`fight\`
|
||||
|
||||
### \`pokemon-bag\`
|
||||
|
||||
Alias: \`bag\`
|
||||
|
||||
### \`pokemon-rename\`
|
||||
|
||||
Alias: \`rename\`
|
||||
"
|
||||
`;
|
|
@ -1,30 +0,0 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`utils extractBlogPostBeforeTruncate 1`] = `
|
||||
"---
|
||||
title: Truncation Example
|
||||
---
|
||||
|
||||
All this will be part of the blog post summary.
|
||||
|
||||
Even this.
|
||||
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`utils extractBlogPostBeforeTruncate 2`] = `
|
||||
"---
|
||||
title: Non-truncation Example
|
||||
---
|
||||
|
||||
All this will be part of the blog post summary.
|
||||
|
||||
Even this.
|
||||
|
||||
And anything from here on down will still be.
|
||||
|
||||
And this.
|
||||
|
||||
And this too.
|
||||
"
|
||||
`;
|
|
@ -1,124 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @jest-environment jsdom
|
||||
*/
|
||||
process.cwd = () => `${__dirname}/__fixtures__/website`;
|
||||
|
||||
const React = require('react');
|
||||
const {configure, mount} = require('enzyme');
|
||||
const Adapter = require('enzyme-adapter-react-16');
|
||||
const fs = require('fs');
|
||||
const _ = require('lodash');
|
||||
const Doc = require('../Doc.js');
|
||||
|
||||
configure({adapter: new Adapter()});
|
||||
|
||||
describe('when code tabs are used correctly', () => {
|
||||
// clear unique id counter
|
||||
_.uniqueId = _.runInContext().uniqueId;
|
||||
const props = {
|
||||
content: fs.readFileSync(
|
||||
`${__dirname}/__fixtures__/split-tab_doc1.md`,
|
||||
'utf-8',
|
||||
),
|
||||
metadata: {},
|
||||
config: {},
|
||||
};
|
||||
let mountedDoc;
|
||||
const docPage = () => {
|
||||
if (!mountedDoc) {
|
||||
mountedDoc = mount(<Doc {...props} />);
|
||||
}
|
||||
return mountedDoc;
|
||||
};
|
||||
const page = docPage();
|
||||
it('renders tabs correctly', () => {
|
||||
const node = page.getDOMNode();
|
||||
const firstTab = node.querySelector('[data-tab$="-content-2"]').textContent;
|
||||
expect(firstTab).toEqual('JavaScript');
|
||||
const secondTab = node.querySelector('[data-tab$="-content-3"]')
|
||||
.textContent;
|
||||
expect(secondTab).toEqual('Python');
|
||||
const thirdTab = node.querySelector('[data-tab$="-content-4"]').textContent;
|
||||
expect(thirdTab).toEqual('C');
|
||||
const fourthTab = node.querySelector('[data-tab$="-content-5"]')
|
||||
.textContent;
|
||||
expect(fourthTab).toEqual('Pascal');
|
||||
});
|
||||
it('renders content correctly', () => {
|
||||
const node = page.getDOMNode();
|
||||
const firstContent = node.querySelector('[id$="-content-2"] code')
|
||||
.textContent;
|
||||
expect(firstContent).toEqual("console.log('Hello, world!');");
|
||||
const secondContent = node.querySelector('[id$="-content-3"] code')
|
||||
.textContent;
|
||||
expect(secondContent).toEqual("print('Hello, world!')");
|
||||
const thirdContent = node.querySelector('[id$="-content-4"] code')
|
||||
.textContent;
|
||||
expect(thirdContent).toEqual(
|
||||
'#include <stdio.h>int main() { printf("Hello World!"); return 0;}',
|
||||
);
|
||||
const fourthContent = node.querySelector('[id$="-content-5"] code')
|
||||
.textContent;
|
||||
expect(fourthContent).toEqual(
|
||||
"program HelloWorld;begin WriteLn('Hello, world!');end.",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when code tab is used in a list', () => {
|
||||
// clear unique id counter
|
||||
_.uniqueId = _.runInContext().uniqueId;
|
||||
const props = {
|
||||
content: fs.readFileSync(
|
||||
`${__dirname}/__fixtures__/split-tab_doc2.md`,
|
||||
'utf-8',
|
||||
),
|
||||
metadata: {},
|
||||
config: {},
|
||||
};
|
||||
let mountedDoc;
|
||||
const docPage = () => {
|
||||
if (!mountedDoc) {
|
||||
mountedDoc = mount(<Doc {...props} />);
|
||||
}
|
||||
return mountedDoc;
|
||||
};
|
||||
const page = docPage();
|
||||
it('renders tabs correctly', () => {
|
||||
const node = page.getDOMNode();
|
||||
const firstTab = node.querySelector('[data-tab$="-content-2"]').textContent;
|
||||
expect(firstTab).toEqual('JavaScript');
|
||||
const secondTab = node.querySelector('[data-tab$="-content-3"]')
|
||||
.textContent;
|
||||
expect(secondTab).toEqual('Python');
|
||||
const thirdTab = node.querySelector('[data-tab$="-content-4"]').textContent;
|
||||
expect(thirdTab).toEqual('C');
|
||||
const fourthTab = node.querySelector('[data-tab$="-content-5"]')
|
||||
.textContent;
|
||||
expect(fourthTab).toEqual('Pascal');
|
||||
});
|
||||
it('renders content correctly', () => {
|
||||
const node = page.getDOMNode();
|
||||
const firstContent = node.querySelector('[id$="-content-2"] code')
|
||||
.textContent;
|
||||
expect(firstContent).toEqual("console.log('Hello, world!');");
|
||||
const secondContent = node.querySelector('[id$="-content-3"] code')
|
||||
.textContent;
|
||||
expect(secondContent).toEqual("print('Hello, world!')");
|
||||
const thirdContent = node.querySelector('[id$="-content-4"] code')
|
||||
.textContent;
|
||||
expect(thirdContent).toEqual(
|
||||
'#include <stdio.h>int main() { printf("Hello World!"); return 0;}',
|
||||
);
|
||||
const fourthContent = node.querySelector('[id$="-content-5"] code')
|
||||
.textContent;
|
||||
expect(fourthContent).toEqual(
|
||||
"program HelloWorld;begin WriteLn('Hello, world!');end.",
|
||||
);
|
||||
});
|
||||
});
|
|
@ -1,43 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
const GitHubSlugger = require('github-slugger');
|
||||
const toSlug = require('../toSlug');
|
||||
|
||||
[
|
||||
['Hello world! ', 'hello-world'],
|
||||
['React 16', 'react-16'],
|
||||
['Hello. // (world?)! ', 'hello----world'],
|
||||
['Привет мир! ', 'привет-мир'],
|
||||
['Über Café.', 'über-café'],
|
||||
['Someting long ...', 'someting-long-'],
|
||||
['foo_bar', 'foo_bar'],
|
||||
['some _ heading', 'some-_-heading'],
|
||||
["I'm good", 'im-good'],
|
||||
['This is awes’ome', 'this-is-awesome'],
|
||||
].forEach(([input, output]) => {
|
||||
test(`toSlug('${input}') -> '${output}'`, () => {
|
||||
expect(toSlug(input)).toBe(output);
|
||||
});
|
||||
});
|
||||
|
||||
test('unique slugs if `slug` argument passed', () => {
|
||||
[
|
||||
['foo', 'foo'],
|
||||
['foo', 'foo-1'],
|
||||
['foo 1', 'foo-1-1'],
|
||||
['foo 1', 'foo-1-2'],
|
||||
['foo 2', 'foo-2'],
|
||||
['foo', 'foo-3'],
|
||||
['foo_bar', 'foo_bar'],
|
||||
['some _ heading', 'some-_-heading'],
|
||||
].reduce((slugger, [input, output]) => {
|
||||
expect(toSlug(input, slugger)).toBe(output);
|
||||
|
||||
return slugger;
|
||||
}, new GitHubSlugger());
|
||||
});
|
|
@ -1,101 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
const path = require('path');
|
||||
const readFileSync = require('fs').readFileSync;
|
||||
const {getTOC, insertTOC} = require('../toc');
|
||||
const {extractMetadata} = require('../../server/metadataUtils');
|
||||
|
||||
const getTOCmd = readFileSync(
|
||||
path.join(__dirname, '__fixtures__', 'getTOC.md'),
|
||||
'utf8',
|
||||
);
|
||||
|
||||
const insertTOCmd = readFileSync(
|
||||
path.join(__dirname, '__fixtures__', 'insertTOC.md'),
|
||||
'utf8',
|
||||
);
|
||||
|
||||
describe('getTOC', () => {
|
||||
test('with defaults', () => {
|
||||
const headings = getTOC(getTOCmd);
|
||||
const headingsJson = JSON.stringify(headings);
|
||||
|
||||
expect(headings).toMatchSnapshot();
|
||||
expect(headingsJson).toContain('bar-8'); // maximum unique bar index is 8
|
||||
expect(headingsJson).not.toContain('4th level headings');
|
||||
});
|
||||
|
||||
test('with custom heading levels', () => {
|
||||
const headings = getTOC(getTOCmd, 'h2', ['h3', 'h4']);
|
||||
const headingsJson = JSON.stringify(headings);
|
||||
|
||||
expect(headings).toMatchSnapshot();
|
||||
expect(headingsJson).toContain('bar-8'); // maximum unique bar index is 8
|
||||
expect(headingsJson).toContain('4th level headings');
|
||||
});
|
||||
|
||||
test('html tag in source', () => {
|
||||
const headings = getTOC(`## <a name="foo"></a> Foo`, 'h2', []);
|
||||
|
||||
expect(headings[0].hashLink).toEqual('a-namefooa-foo');
|
||||
expect(headings[0].rawContent).toEqual(`<a name="foo"></a> Foo`);
|
||||
expect(headings[0].content).toEqual(`<a name="foo"></a> Foo`);
|
||||
});
|
||||
|
||||
test('transform markdown syntax to html syntax', () => {
|
||||
const headings = getTOC(`## <a name="foo"></a> _Foo_`, 'h2', []);
|
||||
|
||||
expect(headings[0].hashLink).toEqual('a-namefooa-_foo_');
|
||||
expect(headings[0].rawContent).toEqual(`<a name="foo"></a> _Foo_`);
|
||||
expect(headings[0].content).toEqual(`<a name="foo"></a> <em>Foo</em>`);
|
||||
|
||||
const headings2 = getTOC(`## **Foo**`, 'h2', []);
|
||||
|
||||
expect(headings2[0].hashLink).toEqual('foo');
|
||||
expect(headings2[0].rawContent).toEqual(`**Foo**`);
|
||||
expect(headings2[0].content).toEqual(`<strong>Foo</strong>`);
|
||||
});
|
||||
|
||||
test('does not strip tags randomly', () => {
|
||||
// eslint-disable-next-line no-useless-escape
|
||||
const headings = getTOC(`## function1 [array\<string>]`, 'h2', []);
|
||||
|
||||
expect(headings[0].hashLink).toEqual('function1-arraystring');
|
||||
expect(headings[0].rawContent).toEqual(`function1 [array<string>]`);
|
||||
expect(headings[0].content).toEqual(`function1 [array<string>]`);
|
||||
});
|
||||
|
||||
test('test slugPreprocessor', () => {
|
||||
const headings = getTOC(`## <a name="foo"></a> Foo`, 'h2', [], (s) =>
|
||||
s.replace(/foo/gi, 'bar'),
|
||||
);
|
||||
|
||||
expect(headings[0].hashLink).toEqual('a-namebara-bar');
|
||||
expect(headings[0].rawContent).toEqual(`<a name="foo"></a> Foo`);
|
||||
expect(headings[0].content).toEqual(`<a name="foo"></a> Foo`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('insertTOC', () => {
|
||||
test('null or undefined content', () => {
|
||||
expect(insertTOC(null)).toBeNull();
|
||||
expect(insertTOC(undefined)).toBeUndefined();
|
||||
});
|
||||
|
||||
test('AUTOGENERATED_TABLE_OF_CONTENTS does not exist', () => {
|
||||
const rawContent = extractMetadata(getTOCmd).rawContent;
|
||||
expect(insertTOC(rawContent)).toMatchSnapshot();
|
||||
expect(insertTOC(rawContent)).toEqual(rawContent);
|
||||
});
|
||||
|
||||
test('AUTOGENERATED_TABLE_OF_CONTENTS exists', () => {
|
||||
const rawContent = extractMetadata(insertTOCmd).rawContent;
|
||||
expect(insertTOC(rawContent)).toMatchSnapshot();
|
||||
expect(insertTOC(rawContent)).not.toEqual(rawContent);
|
||||
});
|
||||
});
|
|
@ -1,176 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const shell = require('shelljs');
|
||||
const utils = require('../utils');
|
||||
|
||||
const blogPostWithTruncateContents = fs.readFileSync(
|
||||
path.join(__dirname, '__fixtures__', 'blog-post-with-truncate.md'),
|
||||
'utf8',
|
||||
);
|
||||
|
||||
const blogPostWithoutTruncateContents = fs.readFileSync(
|
||||
path.join(__dirname, '__fixtures__', 'blog-post-without-truncate.md'),
|
||||
'utf8',
|
||||
);
|
||||
|
||||
describe('utils', () => {
|
||||
test('blogPostHasTruncateMarker', () => {
|
||||
expect(utils.blogPostHasTruncateMarker(blogPostWithTruncateContents)).toBe(
|
||||
true,
|
||||
);
|
||||
expect(
|
||||
utils.blogPostHasTruncateMarker(blogPostWithoutTruncateContents),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
test('extractBlogPostBeforeTruncate', () => {
|
||||
expect(
|
||||
utils.extractBlogPostBeforeTruncate(blogPostWithTruncateContents),
|
||||
).toMatchSnapshot();
|
||||
expect(
|
||||
utils.extractBlogPostBeforeTruncate(blogPostWithoutTruncateContents),
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('getPath', () => {
|
||||
// does not change/transform path
|
||||
expect(utils.getPath('/en/users.html', false)).toBe('/en/users.html');
|
||||
expect(utils.getPath('/docs/en/versioning.html', false)).toBe(
|
||||
'/docs/en/versioning.html',
|
||||
);
|
||||
expect(utils.getPath(undefined, false)).toBeUndefined();
|
||||
expect(utils.getPath(null, false)).toBeNull();
|
||||
|
||||
// transform to pretty/clean path
|
||||
const cleanPath = (pathStr) => utils.getPath(pathStr, true);
|
||||
expect(cleanPath('/en/users')).toBe('/en/users');
|
||||
expect(cleanPath('/docs/versioning.html')).toBe('/docs/versioning');
|
||||
expect(cleanPath('/en/users.html')).toBe('/en/users');
|
||||
expect(cleanPath('/docs/en/asd/index.html')).toBe('/docs/en/asd/');
|
||||
expect(cleanPath('/en/help/index.html')).toBe('/en/help/');
|
||||
expect(cleanPath('/index.html')).toBe('/');
|
||||
expect(cleanPath('/react/index.html')).toBe('/react/');
|
||||
expect(cleanPath('/en/help.a.b.c.d.e.html')).toBe('/en/help.a.b.c.d.e');
|
||||
expect(cleanPath('/en/help.js')).toBe('/en/help.js');
|
||||
expect(cleanPath('/test.md')).toBe('/test.md');
|
||||
expect(cleanPath('/blog/7.0.0')).toBe('/blog/7.0.0');
|
||||
expect(cleanPath('/test/5.html.2')).toBe('/test/5.html.2');
|
||||
expect(cleanPath('/docs/en/5.2')).toBe('/docs/en/5.2');
|
||||
});
|
||||
|
||||
test('removeExtension', () => {
|
||||
expect(utils.removeExtension('/endiliey.html')).toBe('/endiliey');
|
||||
expect(utils.removeExtension('/a.b/')).toBe('/a.b/');
|
||||
expect(utils.removeExtension('/a.b/c.png')).toBe('/a.b/c');
|
||||
expect(utils.removeExtension('/a.b/c.d.e')).toBe('/a.b/c.d');
|
||||
expect(utils.removeExtension('/docs/test')).toBe('/docs/test');
|
||||
expect(utils.removeExtension('pages.js')).toBe('pages');
|
||||
});
|
||||
|
||||
test('getGitLastUpdatedTime', () => {
|
||||
// existing test file in repository with git timestamp
|
||||
const existingFilePath = path.join(__dirname, '__fixtures__', 'test.md');
|
||||
const gitLastUpdatedTime = utils.getGitLastUpdatedTime(existingFilePath);
|
||||
expect(typeof gitLastUpdatedTime).toBe('string');
|
||||
expect(Date.parse(gitLastUpdatedTime)).not.toBeNaN();
|
||||
expect(gitLastUpdatedTime).not.toBeNull();
|
||||
|
||||
// non existing file
|
||||
const nonExistingFilePath = path.join(
|
||||
__dirname,
|
||||
'__fixtures__',
|
||||
'.nonExisting',
|
||||
);
|
||||
expect(utils.getGitLastUpdatedTime(null)).toBeNull();
|
||||
expect(utils.getGitLastUpdatedTime(undefined)).toBeNull();
|
||||
expect(utils.getGitLastUpdatedTime(nonExistingFilePath)).toBeNull();
|
||||
|
||||
// temporary created file that has no git timestamp
|
||||
const tempFilePath = path.join(__dirname, '__fixtures__', '.temp');
|
||||
fs.writeFileSync(tempFilePath, 'Lorem ipsum :)');
|
||||
expect(utils.getGitLastUpdatedTime(tempFilePath)).toBeNull();
|
||||
fs.unlinkSync(tempFilePath);
|
||||
|
||||
// test renaming and moving file
|
||||
|
||||
const tempFilePath2 = path.join(__dirname, '__fixtures__', '.temp2');
|
||||
const tempFilePath3 = path.join(
|
||||
__dirname,
|
||||
'__fixtures__',
|
||||
'test',
|
||||
'.temp3',
|
||||
);
|
||||
|
||||
// create new file
|
||||
shell.exec = jest.fn(() => ({
|
||||
stdout:
|
||||
'1539502055, Yangshun Tay\n' +
|
||||
'\n' +
|
||||
' create mode 100644 v1/lib/core/__tests__/__fixtures__/.temp2\n',
|
||||
}));
|
||||
const createTime = utils.getGitLastUpdatedTime(tempFilePath2);
|
||||
expect(typeof createTime).toBe('string');
|
||||
|
||||
// rename / move the file
|
||||
shell.exec = jest.fn(() => ({
|
||||
stdout:
|
||||
'1539502056, Joel Marcey\n' +
|
||||
'\n' +
|
||||
' rename v1/lib/core/__tests__/__fixtures__/{.temp2 => test/.temp3} (100%)\n' +
|
||||
'1539502055, Yangshun Tay\n' +
|
||||
'\n' +
|
||||
' create mode 100644 v1/lib/core/__tests__/__fixtures__/.temp2\n',
|
||||
}));
|
||||
const lastUpdateTime = utils.getGitLastUpdatedTime(tempFilePath3);
|
||||
// should only consider file content change
|
||||
expect(lastUpdateTime).toEqual(createTime);
|
||||
});
|
||||
|
||||
test('idx', () => {
|
||||
const a = {};
|
||||
const b = {hello: 'world'};
|
||||
const env = {
|
||||
translation: {
|
||||
enabled: true,
|
||||
enabledLanguages: [
|
||||
{
|
||||
enabled: true,
|
||||
name: 'English',
|
||||
tag: 'en',
|
||||
},
|
||||
{
|
||||
enabled: true,
|
||||
name: '日本語',
|
||||
tag: 'ja',
|
||||
},
|
||||
],
|
||||
},
|
||||
versioning: {
|
||||
enabled: false,
|
||||
versions: [],
|
||||
},
|
||||
};
|
||||
const variable = 'enabledLanguages';
|
||||
expect(utils.idx(a, [('b', 'c')])).toBeUndefined();
|
||||
expect(utils.idx(b, ['hello'])).toEqual('world');
|
||||
expect(utils.idx(b, 'hello')).toEqual('world');
|
||||
expect(utils.idx(env, 'typo')).toBeUndefined();
|
||||
expect(utils.idx(env, 'versioning')).toEqual({
|
||||
enabled: false,
|
||||
versions: [],
|
||||
});
|
||||
expect(utils.idx(env, ['translation', 'enabled'])).toEqual(true);
|
||||
expect(
|
||||
utils.idx(env, ['translation', variable]).map((lang) => lang.tag),
|
||||
).toEqual(['en', 'ja']);
|
||||
expect(utils.idx(undefined)).toBeUndefined();
|
||||
expect(utils.idx(null)).toBeNull();
|
||||
});
|
||||
});
|
|
@ -1,37 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
const GithubSlugger = require('github-slugger');
|
||||
const toSlug = require('./toSlug');
|
||||
|
||||
/**
|
||||
* The anchors plugin adds GFM-style anchors to headings.
|
||||
*/
|
||||
function anchors(md, slugPreprocessor) {
|
||||
const originalRender = md.renderer.rules.heading_open;
|
||||
|
||||
md.renderer.rules.heading_open = function (tokens, idx, options, env) {
|
||||
if (!env.slugger) {
|
||||
env.slugger = new GithubSlugger();
|
||||
}
|
||||
const slugger = env.slugger;
|
||||
const textToken = tokens[idx + 1];
|
||||
|
||||
if (textToken.content) {
|
||||
const slugBase =
|
||||
slugPreprocessor && typeof slugPreprocessor === 'function'
|
||||
? slugPreprocessor(textToken.content)
|
||||
: textToken.content;
|
||||
const anchor = toSlug(slugBase, slugger);
|
||||
return `<h${tokens[idx].hLevel}><a class="anchor" aria-hidden="true" id="${anchor}"></a><a href="#${anchor}" aria-hidden="true" class="hash-link"><svg class="hash-link-icon" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>`;
|
||||
}
|
||||
|
||||
return originalRender(tokens, idx, options, env);
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = anchors;
|
|
@ -1,337 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
const CWD = process.cwd();
|
||||
|
||||
const React = require('react');
|
||||
const fs = require('fs');
|
||||
const classNames = require('classnames');
|
||||
|
||||
const loadConfig = require('../../server/config');
|
||||
|
||||
const siteConfig = loadConfig(`${CWD}/siteConfig.js`);
|
||||
const translation = require('../../server/translation.js');
|
||||
const env = require('../../server/env.js');
|
||||
|
||||
const translate = require('../../server/translate.js').translate;
|
||||
const setLanguage = require('../../server/translate.js').setLanguage;
|
||||
|
||||
const readMetadata = require('../../server/readMetadata.js');
|
||||
|
||||
readMetadata.generateMetadataDocs();
|
||||
const Metadata = require('../metadata.js');
|
||||
const {idx, getPath} = require('../utils.js');
|
||||
|
||||
const extension = siteConfig.cleanUrl ? '' : '.html';
|
||||
|
||||
// language dropdown nav item for when translations are enabled
|
||||
class LanguageDropDown extends React.Component {
|
||||
render() {
|
||||
setLanguage(this.props.language || 'en');
|
||||
const helpTranslateString = translate(
|
||||
'Help Translate|recruit community translators for your project',
|
||||
);
|
||||
const docsPart = `${siteConfig.docsUrl ? `${siteConfig.docsUrl}/` : ''}`;
|
||||
// add all enabled languages to dropdown
|
||||
const enabledLanguages = env.translation
|
||||
.enabledLanguages()
|
||||
.filter((lang) => lang.tag !== this.props.language)
|
||||
.map((lang) => {
|
||||
// build the href so that we try to stay in current url but change the language.
|
||||
let href = siteConfig.baseUrl + lang.tag;
|
||||
if (
|
||||
this.props.current &&
|
||||
this.props.current.permalink &&
|
||||
this.props.language
|
||||
) {
|
||||
href =
|
||||
siteConfig.baseUrl +
|
||||
this.props.current.permalink.replace(
|
||||
new RegExp(`^${docsPart}${this.props.language}/`),
|
||||
`${docsPart}${lang.tag}/`,
|
||||
);
|
||||
} else if (this.props.current.id && this.props.current.id !== 'index') {
|
||||
href = `${siteConfig.baseUrl + lang.tag}/${this.props.current.id}`;
|
||||
}
|
||||
return (
|
||||
<li key={lang.tag}>
|
||||
<a href={getPath(href, this.props.cleanUrl)}>{lang.name}</a>
|
||||
</li>
|
||||
);
|
||||
});
|
||||
// if no languages are enabled besides English, return null
|
||||
if (enabledLanguages.length < 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get the current language full name for display in the header nav
|
||||
const currentLanguage = env.translation
|
||||
.enabledLanguages()
|
||||
.filter((lang) => lang.tag === this.props.language)
|
||||
.map((lang) => lang.name);
|
||||
|
||||
// add Crowdin project recruiting link
|
||||
if (siteConfig.translationRecruitingLink) {
|
||||
enabledLanguages.push(
|
||||
<li key="recruiting">
|
||||
<a
|
||||
href={siteConfig.translationRecruitingLink}
|
||||
target="_blank"
|
||||
rel="noreferrer noopener">
|
||||
{helpTranslateString}
|
||||
</a>
|
||||
</li>,
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<span>
|
||||
<li key="languages">
|
||||
<a id="languages-menu" href="#">
|
||||
<img
|
||||
className="languages-icon"
|
||||
src={`${this.props.baseUrl}img/language.svg`}
|
||||
alt="Languages icon"
|
||||
/>
|
||||
{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(event) {
|
||||
event.preventDefault();
|
||||
|
||||
if (languagesDropDown.className == "hide") {
|
||||
languagesDropDown.className = "visible";
|
||||
} else {
|
||||
languagesDropDown.className = "hide";
|
||||
}
|
||||
});
|
||||
`,
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// header navbar used by all pages generated with docusaurus
|
||||
class HeaderNav extends React.Component {
|
||||
// function to generate each header link, used with each object in siteConfig.headerLinks
|
||||
makeLinks(link) {
|
||||
let href;
|
||||
let docItemActive = false;
|
||||
let docGroupActive = false;
|
||||
if (link.search && this.props.config.algolia) {
|
||||
// return algolia search bar
|
||||
const placeholder = this.props.config.algolia.placeholder || 'Search';
|
||||
return (
|
||||
<li className="navSearchWrapper reactNavSearchWrapper" key="search">
|
||||
<input
|
||||
id="search_input_react"
|
||||
type="text"
|
||||
placeholder={placeholder}
|
||||
title={placeholder}
|
||||
/>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
if (link.languages) {
|
||||
if (
|
||||
env.translation.enabled &&
|
||||
env.translation.enabledLanguages().length > 1
|
||||
) {
|
||||
return (
|
||||
<LanguageDropDown
|
||||
baseUrl={this.props.baseUrl}
|
||||
language={this.props.language}
|
||||
current={this.props.current}
|
||||
cleanUrl={this.props.config.cleanUrl}
|
||||
key="languagedropdown"
|
||||
/>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
if (link.doc) {
|
||||
// set link to document with current page's language/version
|
||||
const langPart = env.translation.enabled
|
||||
? `${this.props.language || 'en'}-`
|
||||
: '';
|
||||
const versionPart =
|
||||
env.versioning.enabled && this.props.version !== 'next'
|
||||
? `version-${this.props.version || env.versioning.defaultVersion}-`
|
||||
: '';
|
||||
const id = langPart + versionPart + link.doc;
|
||||
if (!Metadata[id]) {
|
||||
let errorStr = `Processing the following \`doc\` field in \`headerLinks\` within \`siteConfig.js\`: '${link.doc}'`;
|
||||
if (id === link.doc) {
|
||||
errorStr +=
|
||||
' It looks like there is no document with that id that exists in your docs directory. Please double check the spelling of your `doc` field and the `id` fields of your docs.';
|
||||
} else {
|
||||
errorStr += `${
|
||||
'. Check the spelling of your `doc` field. If that seems sane, and a document in your docs folder exists with that `id` value, \nthen this is likely a bug in Docusaurus.' +
|
||||
' Docusaurus thinks one or both of translations (currently set to: '
|
||||
}${env.translation.enabled}) or versioning (currently set to: ${
|
||||
env.versioning.enabled
|
||||
}) is enabled when maybe they should not be. \nThus my internal id for this doc is: '${id}'. Please file an issue for this possible bug on GitHub.`;
|
||||
}
|
||||
throw new Error(errorStr);
|
||||
}
|
||||
href =
|
||||
this.props.config.baseUrl +
|
||||
getPath(Metadata[id].permalink, this.props.config.cleanUrl);
|
||||
|
||||
const {id: currentID, sidebar} = this.props.current;
|
||||
docItemActive = currentID && currentID === id;
|
||||
docGroupActive = sidebar && sidebar === Metadata[id].sidebar;
|
||||
} else if (link.page) {
|
||||
// set link to page with current page's language if appropriate
|
||||
const language = this.props.language || '';
|
||||
if (fs.existsSync(`${CWD}/pages/en/${link.page}.js`)) {
|
||||
href =
|
||||
siteConfig.baseUrl +
|
||||
(env.translation.enabled ? `${language}/` : '') +
|
||||
link.page +
|
||||
extension;
|
||||
} else {
|
||||
href = siteConfig.baseUrl + link.page + extension;
|
||||
}
|
||||
} else if (link.href) {
|
||||
// set link to specified href
|
||||
href = link.href;
|
||||
} else if (link.blog) {
|
||||
// set link to blog url
|
||||
href = `${this.props.baseUrl}blog/`;
|
||||
}
|
||||
const itemClasses = classNames({
|
||||
siteNavGroupActive:
|
||||
(link.doc && docGroupActive) || (link.blog && this.props.current.blog),
|
||||
siteNavItemActive:
|
||||
docItemActive ||
|
||||
(link.blog && this.props.current.blogListing) ||
|
||||
(link.page && link.page === this.props.current.id),
|
||||
});
|
||||
const i18n = translation[this.props.language];
|
||||
return (
|
||||
<li key={`${link.label}page`} className={itemClasses}>
|
||||
<a href={href} target={link.external ? '_blank' : '_self'}>
|
||||
{idx(i18n, ['localized-strings', 'links', link.label]) || link.label}
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
renderResponsiveNav() {
|
||||
const headerLinks = this.props.config.headerLinks;
|
||||
// add language drop down to end if location not specified
|
||||
let languages = false;
|
||||
headerLinks.forEach((link) => {
|
||||
if (link.languages) {
|
||||
languages = true;
|
||||
}
|
||||
});
|
||||
if (!languages) {
|
||||
headerLinks.push({languages: true});
|
||||
}
|
||||
let search = false;
|
||||
headerLinks.forEach((link) => {
|
||||
if (
|
||||
link.doc &&
|
||||
!fs.existsSync(`${CWD}/../${readMetadata.getDocsPath()}/`)
|
||||
) {
|
||||
throw new Error(
|
||||
`You have 'doc' in your headerLinks, but no '${readMetadata.getDocsPath()}' folder exists one level up from ` +
|
||||
`'website' folder. Did you run \`docusaurus-init\` or \`npm run examples\`? If so, ` +
|
||||
`make sure you rename 'docs-examples-from-docusaurus' to 'docs'.`,
|
||||
);
|
||||
}
|
||||
if (link.blog && !fs.existsSync(`${CWD}/blog/`)) {
|
||||
throw new Error(
|
||||
"You have 'blog' in your headerLinks, but no 'blog' folder exists in your " +
|
||||
"'website' folder. Did you run `docusaurus-init` or `npm run examples`? If so, " +
|
||||
"make sure you rename 'blog-examples-from-docusaurus' to 'blog'.",
|
||||
);
|
||||
}
|
||||
if (link.page && !fs.existsSync(`${CWD}/pages/`)) {
|
||||
throw new Error(
|
||||
"You have 'page' in your headerLinks, but no 'pages' folder exists in your " +
|
||||
"'website' folder.",
|
||||
);
|
||||
}
|
||||
// We will add search bar to end if location not specified
|
||||
if (link.search) {
|
||||
search = true;
|
||||
}
|
||||
});
|
||||
if (!search && this.props.config.algolia) {
|
||||
headerLinks.push({search: true});
|
||||
}
|
||||
return (
|
||||
<div className="navigationWrapper navigationSlider">
|
||||
<nav className="slidingNav">
|
||||
<ul className="nav-site nav-site-internal">
|
||||
{headerLinks.map(this.makeLinks, this)}
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const headerClass = siteConfig.headerIcon
|
||||
? 'headerTitleWithLogo'
|
||||
: 'headerTitle';
|
||||
const versionsLink =
|
||||
this.props.baseUrl +
|
||||
(env.translation.enabled
|
||||
? `${this.props.language}/versions${extension}`
|
||||
: `versions${extension}`);
|
||||
return (
|
||||
<div className="fixedHeaderContainer">
|
||||
<div className="headerWrapper wrapper">
|
||||
<header>
|
||||
<a
|
||||
href={
|
||||
this.props.baseUrl +
|
||||
(env.translation.enabled ? this.props.language : '')
|
||||
}>
|
||||
{siteConfig.headerIcon && (
|
||||
<img
|
||||
className="logo"
|
||||
src={this.props.baseUrl + siteConfig.headerIcon}
|
||||
alt={siteConfig.title}
|
||||
/>
|
||||
)}
|
||||
{!this.props.config.disableHeaderTitle && (
|
||||
<h2 className={headerClass}>{this.props.title}</h2>
|
||||
)}
|
||||
</a>
|
||||
{env.versioning.enabled && (
|
||||
<a href={versionsLink}>
|
||||
<h3>{this.props.version || env.versioning.defaultVersion}</h3>
|
||||
</a>
|
||||
)}
|
||||
{this.renderResponsiveNav()}
|
||||
</header>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
HeaderNav.defaultProps = {
|
||||
current: {},
|
||||
};
|
||||
|
||||
module.exports = HeaderNav;
|
|
@ -1,57 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
const React = require('react');
|
||||
|
||||
const siteConfig = require(`${process.cwd()}/siteConfig.js`);
|
||||
const {getTOC} = require('../toc');
|
||||
|
||||
const Link = ({hashLink, content}) => (
|
||||
<a
|
||||
href={`#${hashLink}`}
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: content,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
const Headings = ({headings}) => {
|
||||
if (!headings.length) return null;
|
||||
return (
|
||||
<ul className="toc-headings">
|
||||
{headings.map((heading) => (
|
||||
<li key={heading.hashLink}>
|
||||
<Link hashLink={heading.hashLink} content={heading.content} />
|
||||
<Headings headings={heading.children} />
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
};
|
||||
|
||||
class OnPageNav extends React.Component {
|
||||
render() {
|
||||
const customTags = siteConfig.onPageNavHeadings;
|
||||
const headings = customTags
|
||||
? getTOC(
|
||||
this.props.rawContent,
|
||||
customTags.topLevel,
|
||||
customTags.sub,
|
||||
siteConfig.slugPreprocessor,
|
||||
)
|
||||
: getTOC(
|
||||
this.props.rawContent,
|
||||
undefined,
|
||||
undefined,
|
||||
siteConfig.slugPreprocessor,
|
||||
);
|
||||
|
||||
return <Headings headings={headings} />;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = OnPageNav;
|
|
@ -1,234 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
const React = require('react');
|
||||
const classNames = require('classnames');
|
||||
|
||||
const siteConfig = require(`${process.cwd()}/siteConfig.js`);
|
||||
const translation = require('../../server/translation.js');
|
||||
const {getPath, idx} = require('../utils.js');
|
||||
|
||||
class SideNav extends React.Component {
|
||||
// return appropriately translated category string
|
||||
getLocalizedCategoryString(category) {
|
||||
const categoryString =
|
||||
idx(translation, [
|
||||
this.props.language,
|
||||
'localized-strings',
|
||||
'categories',
|
||||
category,
|
||||
]) || category;
|
||||
return categoryString;
|
||||
}
|
||||
|
||||
// return appropriately translated label to use for doc/blog in sidebar
|
||||
getLocalizedString(metadata) {
|
||||
let localizedString;
|
||||
const i18n = translation[this.props.language];
|
||||
const id = metadata.localized_id;
|
||||
const sbTitle = metadata.sidebar_label;
|
||||
|
||||
if (sbTitle) {
|
||||
localizedString =
|
||||
idx(i18n, ['localized-strings', 'docs', id, 'sidebar_label']) ||
|
||||
sbTitle;
|
||||
} else {
|
||||
localizedString =
|
||||
idx(i18n, ['localized-strings', 'docs', id, 'title']) || metadata.title;
|
||||
}
|
||||
return localizedString;
|
||||
}
|
||||
|
||||
// return link to doc in sidebar
|
||||
getLink(metadata) {
|
||||
if (metadata.permalink) {
|
||||
const targetLink = getPath(metadata.permalink, siteConfig.cleanUrl);
|
||||
if (targetLink.match(/^https?:/)) {
|
||||
return targetLink;
|
||||
}
|
||||
return siteConfig.baseUrl + targetLink;
|
||||
}
|
||||
if (metadata.path) {
|
||||
return `${siteConfig.baseUrl}blog/${getPath(
|
||||
metadata.path,
|
||||
siteConfig.cleanUrl,
|
||||
)}`;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
renderCategory = (categoryItem) => {
|
||||
let ulClassName = '';
|
||||
let categoryClassName = 'navGroupCategoryTitle';
|
||||
let arrow;
|
||||
|
||||
if (this.props.collapsible) {
|
||||
categoryClassName += ' collapsible';
|
||||
ulClassName = 'hide';
|
||||
arrow = (
|
||||
<span className="arrow">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24">
|
||||
<path
|
||||
fill="#565656"
|
||||
d="M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z"
|
||||
/>
|
||||
<path d="M0 0h24v24H0z" fill="none" />
|
||||
</svg>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="navGroup" key={categoryItem.title}>
|
||||
<h3 className={categoryClassName}>
|
||||
{this.getLocalizedCategoryString(categoryItem.title)}
|
||||
{arrow}
|
||||
</h3>
|
||||
<ul className={ulClassName}>
|
||||
{categoryItem.children.map((item) => {
|
||||
switch (item.type) {
|
||||
case 'LINK':
|
||||
return this.renderItemLink(item);
|
||||
case 'SUBCATEGORY':
|
||||
return this.renderSubcategory(item);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
renderSubcategory = (subcategoryItem) => (
|
||||
<div className="navGroup subNavGroup" key={subcategoryItem.title}>
|
||||
<h4 className="navGroupSubcategoryTitle">
|
||||
{this.getLocalizedCategoryString(subcategoryItem.title)}
|
||||
</h4>
|
||||
<ul>{subcategoryItem.children.map(this.renderItemLink)}</ul>
|
||||
</div>
|
||||
);
|
||||
|
||||
renderItemLink = (linkItem) => {
|
||||
const linkMetadata = linkItem.item;
|
||||
const itemClasses = classNames('navListItem', {
|
||||
navListItemActive: linkMetadata.id === this.props.current.id,
|
||||
});
|
||||
return (
|
||||
<li className={itemClasses} key={linkMetadata.id}>
|
||||
<a className="navItem" href={this.getLink(linkMetadata)}>
|
||||
{this.getLocalizedString(linkMetadata)}
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<nav className="toc">
|
||||
<div className="toggleNav">
|
||||
<section className="navWrapper wrapper">
|
||||
<div className="navBreadcrumb wrapper">
|
||||
<div className="navToggle" id="navToggler">
|
||||
<div className="hamburger-menu">
|
||||
<div className="line1" />
|
||||
<div className="line2" />
|
||||
<div className="line3" />
|
||||
</div>
|
||||
</div>
|
||||
<h2>
|
||||
<i>›</i>
|
||||
<span>
|
||||
{this.getLocalizedCategoryString(
|
||||
this.props.current.subcategory ||
|
||||
this.props.current.category,
|
||||
)}
|
||||
</span>
|
||||
</h2>
|
||||
{siteConfig.onPageNav === 'separate' && (
|
||||
<div className="tocToggler" id="tocToggler">
|
||||
<i className="icon-toc" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="navGroups">
|
||||
{this.props.contents.map(this.renderCategory)}
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<script
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
var coll = document.getElementsByClassName('collapsible');
|
||||
var checkActiveCategory = true;
|
||||
for (var i = 0; i < coll.length; i++) {
|
||||
var links = coll[i].nextElementSibling.getElementsByTagName('*');
|
||||
if (checkActiveCategory){
|
||||
for (var j = 0; j < links.length; j++) {
|
||||
if (links[j].classList.contains('navListItemActive')){
|
||||
coll[i].nextElementSibling.classList.toggle('hide');
|
||||
coll[i].childNodes[1].classList.toggle('rotate');
|
||||
checkActiveCategory = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
coll[i].addEventListener('click', function() {
|
||||
var arrow = this.childNodes[1];
|
||||
arrow.classList.toggle('rotate');
|
||||
var content = this.nextElementSibling;
|
||||
content.classList.toggle('hide');
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
createToggler('#navToggler', '#docsNav', 'docsSliderActive');
|
||||
createToggler('#tocToggler', 'body', 'tocActive');
|
||||
|
||||
var headings = document.querySelector('.toc-headings');
|
||||
headings && headings.addEventListener('click', function(event) {
|
||||
var el = event.target;
|
||||
while(el !== headings){
|
||||
if (el.tagName === 'A') {
|
||||
document.body.classList.remove('tocActive');
|
||||
break;
|
||||
} else{
|
||||
el = el.parentNode;
|
||||
}
|
||||
}
|
||||
}, false);
|
||||
|
||||
function createToggler(togglerSelector, targetSelector, className) {
|
||||
var toggler = document.querySelector(togglerSelector);
|
||||
var target = document.querySelector(targetSelector);
|
||||
|
||||
if (!toggler) {
|
||||
return;
|
||||
}
|
||||
|
||||
toggler.onclick = function(event) {
|
||||
event.preventDefault();
|
||||
|
||||
target.classList.toggle(className);
|
||||
};
|
||||
}
|
||||
});
|
||||
`,
|
||||
}}
|
||||
/>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
SideNav.defaultProps = {
|
||||
collapsible: false,
|
||||
contents: [],
|
||||
};
|
||||
|
||||
module.exports = SideNav;
|
|
@ -1,131 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
const _ = require('lodash');
|
||||
const hljs = require('highlight.js');
|
||||
const {Remarkable: Markdown} = require('remarkable');
|
||||
const {linkify} = require('remarkable/linkify');
|
||||
const prismjs = require('prismjs');
|
||||
const loadLanguages = require('prismjs/components/index');
|
||||
const chalk = require('chalk');
|
||||
const anchors = require('./anchors.js');
|
||||
|
||||
const CWD = process.cwd();
|
||||
|
||||
const alias = {
|
||||
js: 'jsx',
|
||||
html: 'markup',
|
||||
sh: 'bash',
|
||||
md: 'markdown',
|
||||
};
|
||||
|
||||
class MarkdownRenderer {
|
||||
constructor() {
|
||||
const siteConfig = require(`${CWD}/siteConfig.js`);
|
||||
let markdownOptions = {
|
||||
// Highlight.js expects hljs css classes on the code element.
|
||||
// This results in <pre><code class="hljs css languages-jsx">
|
||||
langPrefix: 'hljs css language-',
|
||||
highlight(str, lang) {
|
||||
// User's own custom highlighting function
|
||||
if (siteConfig.highlight && siteConfig.highlight.hljs) {
|
||||
siteConfig.highlight.hljs(hljs);
|
||||
}
|
||||
// Fallback to default language
|
||||
lang =
|
||||
lang || (siteConfig.highlight && siteConfig.highlight.defaultLang);
|
||||
if (lang === 'text') {
|
||||
return str;
|
||||
}
|
||||
if (lang) {
|
||||
try {
|
||||
if (
|
||||
siteConfig.usePrism === true ||
|
||||
(siteConfig.usePrism &&
|
||||
siteConfig.usePrism.length > 0 &&
|
||||
siteConfig.usePrism.indexOf(lang) !== -1)
|
||||
) {
|
||||
const language = alias[lang] || lang;
|
||||
try {
|
||||
// Currently people using prismjs on Node have to individually require()
|
||||
// every single language (https://github.com/PrismJS/prism/issues/593)
|
||||
loadLanguages([language]);
|
||||
return prismjs.highlight(
|
||||
str,
|
||||
prismjs.languages[language],
|
||||
language,
|
||||
);
|
||||
} catch (err) {
|
||||
if (err.code === 'MODULE_NOT_FOUND') {
|
||||
const unsupportedLanguageError = chalk.yellow(
|
||||
`Warning: ${chalk.red(
|
||||
language,
|
||||
)} is not supported by prismjs.` +
|
||||
'\nPlease refer to https://prismjs.com/#languages-list for the list of supported languages.',
|
||||
);
|
||||
console.log(unsupportedLanguageError);
|
||||
} else console.error(err);
|
||||
}
|
||||
}
|
||||
if (hljs.getLanguage(lang)) {
|
||||
return hljs.highlight(lang, str).value;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
return hljs.highlightAuto(str).value;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
|
||||
return '';
|
||||
},
|
||||
html: true,
|
||||
};
|
||||
|
||||
// Allow overriding default options
|
||||
if (siteConfig.markdownOptions) {
|
||||
markdownOptions = _.merge(
|
||||
{},
|
||||
markdownOptions,
|
||||
siteConfig.markdownOptions,
|
||||
);
|
||||
}
|
||||
|
||||
const md = new Markdown(markdownOptions);
|
||||
|
||||
// Register anchors plugin
|
||||
md.use(anchors, siteConfig.slugPreprocessor);
|
||||
|
||||
// Linkify
|
||||
md.use(linkify);
|
||||
|
||||
// Allow client sites to register their own plugins
|
||||
if (siteConfig.markdownPlugins) {
|
||||
siteConfig.markdownPlugins.forEach((plugin) => {
|
||||
md.use(plugin);
|
||||
});
|
||||
}
|
||||
|
||||
this.md = md;
|
||||
}
|
||||
|
||||
toHtml(source) {
|
||||
const html = this.md.render(source);
|
||||
|
||||
// Ensure fenced code blocks use Highlight.js hljs class
|
||||
// https://github.com/jonschlinkert/remarkable/issues/224
|
||||
return html.replace(/<pre><code>/g, '<pre><code class="hljs">');
|
||||
}
|
||||
}
|
||||
|
||||
const renderMarkdown = new MarkdownRenderer();
|
||||
|
||||
module.exports = (source) => renderMarkdown.toHtml(source);
|
|
@ -1,21 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
const GitHubSlugger = require('github-slugger');
|
||||
|
||||
/**
|
||||
* Converts a string to a slug, that can be used in heading anchors
|
||||
*
|
||||
* @param {string} string
|
||||
* @param {() => string} [slugger] - reused slugger to track used slugs and
|
||||
* ensure that new slug will be unique
|
||||
*
|
||||
* @return {string}
|
||||
*/
|
||||
module.exports = (string, slugger = new GitHubSlugger()) => {
|
||||
return slugger.slug(string);
|
||||
};
|
|
@ -1,88 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
const {Remarkable} = require('remarkable');
|
||||
const mdToc = require('markdown-toc');
|
||||
const GithubSlugger = require('github-slugger');
|
||||
const toSlug = require('./toSlug');
|
||||
|
||||
const tocRegex = new RegExp('<AUTOGENERATED_TABLE_OF_CONTENTS>', 'i');
|
||||
|
||||
/**
|
||||
* Returns a table of content from the headings
|
||||
*
|
||||
* @return array
|
||||
* Array of heading objects with `hashLink`, `content` and `children` fields
|
||||
*
|
||||
*/
|
||||
function getTOC(
|
||||
content,
|
||||
headingTags = 'h2',
|
||||
subHeadingTags = 'h3',
|
||||
slugPreprocessor = undefined,
|
||||
) {
|
||||
const tagToLevel = (tag) => Number(tag.slice(1));
|
||||
const headingLevels = [].concat(headingTags).map(tagToLevel);
|
||||
const subHeadingLevels = subHeadingTags
|
||||
? [].concat(subHeadingTags).map(tagToLevel)
|
||||
: [];
|
||||
const allowedHeadingLevels = headingLevels.concat(subHeadingLevels);
|
||||
const md = new Remarkable({
|
||||
// Enable HTML tags in source (same as './renderMarkdown.js')
|
||||
html: true,
|
||||
});
|
||||
const headings = mdToc(content).json;
|
||||
const toc = [];
|
||||
const slugger = new GithubSlugger();
|
||||
let current;
|
||||
|
||||
headings.forEach((heading) => {
|
||||
const rawContent = heading.content;
|
||||
const rendered = md.renderInline(rawContent);
|
||||
const slugBase =
|
||||
slugPreprocessor && typeof slugPreprocessor === 'function'
|
||||
? slugPreprocessor(rawContent)
|
||||
: rawContent;
|
||||
const hashLink = toSlug(slugBase, slugger);
|
||||
if (!allowedHeadingLevels.includes(heading.lvl)) {
|
||||
return;
|
||||
}
|
||||
const entry = {
|
||||
hashLink,
|
||||
rawContent,
|
||||
content: rendered,
|
||||
children: [],
|
||||
};
|
||||
if (headingLevels.includes(heading.lvl)) {
|
||||
toc.push(entry);
|
||||
current = entry;
|
||||
} else if (current) {
|
||||
current.children.push(entry);
|
||||
}
|
||||
});
|
||||
return toc;
|
||||
}
|
||||
|
||||
// takes the content of a doc article and returns the content with a table of
|
||||
// contents inserted
|
||||
function insertTOC(rawContent, slugPreprocessor = undefined) {
|
||||
if (!rawContent || !tocRegex.test(rawContent)) {
|
||||
return rawContent;
|
||||
}
|
||||
const filterRe = /^`[^`]*`/;
|
||||
const headers = getTOC(rawContent, 'h3', null, slugPreprocessor);
|
||||
const tableOfContents = headers
|
||||
.filter((header) => filterRe.test(header.rawContent))
|
||||
.map((header) => ` - [${header.rawContent}](#${header.hashLink})`)
|
||||
.join('\n');
|
||||
return rawContent.replace(tocRegex, tableOfContents);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getTOC,
|
||||
insertTOC,
|
||||
};
|
|
@ -1,25 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
// 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;
|
|
@ -1,122 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
const shell = require('shelljs');
|
||||
|
||||
const TRUNCATE_MARKER = /<!--\s*truncate\s*-->/;
|
||||
|
||||
function blogPostHasTruncateMarker(content) {
|
||||
return TRUNCATE_MARKER.test(content);
|
||||
}
|
||||
|
||||
function extractBlogPostBeforeTruncate(content) {
|
||||
return content.split(TRUNCATE_MARKER)[0];
|
||||
}
|
||||
|
||||
function removeExtension(pathStr) {
|
||||
return pathStr.replace(/\.[^/.]+$/, '');
|
||||
}
|
||||
|
||||
function getPath(pathStr, cleanUrl = false) {
|
||||
if (!pathStr || !cleanUrl || !pathStr.endsWith('.html')) {
|
||||
return pathStr;
|
||||
}
|
||||
return pathStr.endsWith('/index.html')
|
||||
? pathStr.replace(/index\.html$/, '')
|
||||
: removeExtension(pathStr);
|
||||
}
|
||||
|
||||
function idx(target, keyPaths) {
|
||||
return (
|
||||
target &&
|
||||
(Array.isArray(keyPaths)
|
||||
? keyPaths.reduce((obj, key) => obj && obj[key], target)
|
||||
: target[keyPaths])
|
||||
);
|
||||
}
|
||||
|
||||
function getGitLastUpdated(filepath) {
|
||||
const timestampAndAuthorRegex = /^(\d+), (.+)$/;
|
||||
|
||||
function isTimestampAndAuthor(str) {
|
||||
return timestampAndAuthorRegex.test(str);
|
||||
}
|
||||
|
||||
function getTimestampAndAuthor(str) {
|
||||
if (!str) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const temp = str.match(timestampAndAuthorRegex);
|
||||
return !temp || temp.length < 3
|
||||
? null
|
||||
: {timestamp: temp[1], author: temp[2]};
|
||||
}
|
||||
|
||||
// Wrap in try/catch in case the shell commands fail (e.g. project doesn't use Git, etc).
|
||||
try {
|
||||
// To differentiate between content change and file renaming / moving, use --summary
|
||||
// To follow the file history until before it is moved (when we create new version), use
|
||||
// --follow.
|
||||
const silentState = shell.config.silent; // Save old silent state.
|
||||
shell.config.silent = true;
|
||||
const result = shell
|
||||
.exec(`git log --follow --summary --format="%ct, %an" "${filepath}"`)
|
||||
.stdout.trim();
|
||||
shell.config.silent = silentState;
|
||||
|
||||
// Format the log results to be
|
||||
// ['1234567890, Yangshun Tay', 'rename ...', '1234567880,
|
||||
// 'Joel Marcey', 'move ...', '1234567870', '1234567860']
|
||||
const records = result
|
||||
.toString('utf-8')
|
||||
.replace(/\n\s*\n/g, '\n')
|
||||
.split('\n')
|
||||
.filter(String);
|
||||
const lastContentModifierCommit = records.find((item, index, arr) => {
|
||||
const currentItemIsTimestampAndAuthor = isTimestampAndAuthor(item);
|
||||
const isLastTwoItem = index + 2 >= arr.length;
|
||||
const nextItemIsTimestampAndAuthor = isTimestampAndAuthor(arr[index + 1]);
|
||||
return (
|
||||
currentItemIsTimestampAndAuthor &&
|
||||
(isLastTwoItem || nextItemIsTimestampAndAuthor)
|
||||
);
|
||||
});
|
||||
|
||||
return lastContentModifierCommit
|
||||
? getTimestampAndAuthor(lastContentModifierCommit)
|
||||
: null;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function getGitLastUpdatedTime(filepath) {
|
||||
const commit = getGitLastUpdated(filepath);
|
||||
|
||||
if (commit && commit.timestamp) {
|
||||
const date = new Date(parseInt(commit.timestamp, 10) * 1000);
|
||||
return date.toLocaleDateString();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function getGitLastUpdatedBy(filepath) {
|
||||
const commit = getGitLastUpdated(filepath);
|
||||
return commit ? commit.author : null;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
blogPostHasTruncateMarker,
|
||||
extractBlogPostBeforeTruncate,
|
||||
getGitLastUpdatedTime,
|
||||
getGitLastUpdatedBy,
|
||||
getPath,
|
||||
removeExtension,
|
||||
idx,
|
||||
};
|
|
@ -1,199 +0,0 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
const shell = require('shelljs');
|
||||
|
||||
if (!shell.which('git')) {
|
||||
shell.echo('Sorry, this script requires git');
|
||||
shell.exit(1);
|
||||
}
|
||||
|
||||
const siteConfig = require(`${process.cwd()}/siteConfig.js`);
|
||||
const GIT_USER = process.env.GIT_USER;
|
||||
const CURRENT_BRANCH =
|
||||
process.env.CIRCLE_BRANCH ||
|
||||
shell.exec('git rev-parse --abbrev-ref HEAD').stdout.trim();
|
||||
const ORGANIZATION_NAME =
|
||||
process.env.ORGANIZATION_NAME ||
|
||||
process.env.CIRCLE_PROJECT_USERNAME ||
|
||||
siteConfig.organizationName;
|
||||
const PROJECT_NAME =
|
||||
process.env.PROJECT_NAME ||
|
||||
process.env.CIRCLE_PROJECT_REPONAME ||
|
||||
siteConfig.projectName;
|
||||
const IS_PULL_REQUEST =
|
||||
process.env.CI_PULL_REQUEST || process.env.CIRCLE_PULL_REQUEST;
|
||||
const USE_SSH = process.env.USE_SSH;
|
||||
// github.io indicates organization repos that deploy via master. All others use gh-pages.
|
||||
const DEPLOYMENT_BRANCH =
|
||||
PROJECT_NAME.indexOf('.github.io') !== -1 ? 'master' : 'gh-pages';
|
||||
const GITHUB_DOMAIN = 'github.com';
|
||||
// For GitHub enterprise, allow specifying a different host.
|
||||
const GITHUB_HOST =
|
||||
process.env.GITHUB_HOST || siteConfig.githubHost || GITHUB_DOMAIN;
|
||||
const CUSTOM_COMMIT_MESSAGE = process.env.CUSTOM_COMMIT_MESSAGE;
|
||||
|
||||
// Mostly added to unlock our own Docusaurus v1 site deploy...
|
||||
// See https://github.com/facebook/docusaurus/issues/4394
|
||||
const FORCE_DEPLOY = process.env.FORCE_DEPLOY;
|
||||
|
||||
if (!ORGANIZATION_NAME) {
|
||||
shell.echo(
|
||||
"Missing project organization name. Did you forget to define 'organizationName' in siteConfig.js? You may also export it via the ORGANIZATION_NAME environment variable.",
|
||||
);
|
||||
shell.exit(0);
|
||||
}
|
||||
|
||||
if (!PROJECT_NAME) {
|
||||
shell.echo(
|
||||
"Missing project name. Did you forget to define 'projectName' in siteConfig.js? You may also export it via the PROJECT_NAME environment variable.",
|
||||
);
|
||||
shell.exit(0);
|
||||
}
|
||||
|
||||
if (USE_SSH !== 'true' && !GIT_USER) {
|
||||
shell.echo(
|
||||
"Missing git user. Did you forget to export the 'GIT_USER' environment variable?",
|
||||
);
|
||||
shell.exit(0);
|
||||
}
|
||||
|
||||
let remoteBranch;
|
||||
if (USE_SSH === 'true') {
|
||||
remoteBranch = `git@${GITHUB_HOST}:${ORGANIZATION_NAME}/${PROJECT_NAME}.git`;
|
||||
} else {
|
||||
remoteBranch = `https://${GIT_USER}@${GITHUB_HOST}/${ORGANIZATION_NAME}/${PROJECT_NAME}.git`;
|
||||
}
|
||||
|
||||
if (FORCE_DEPLOY === 'true') {
|
||||
shell.echo('Force deploy used!');
|
||||
} else if (IS_PULL_REQUEST) {
|
||||
shell.echo('Skipping deploy on a pull request');
|
||||
shell.exit(0);
|
||||
}
|
||||
|
||||
// When we want to do a cross repo publish (#717), we can allow publishing to the same branch.
|
||||
const currentRepoUrl = shell
|
||||
.exec('git config --get remote.origin.url')
|
||||
.stdout.trim();
|
||||
const crossRepoPublish = !currentRepoUrl.endsWith(
|
||||
`${ORGANIZATION_NAME}/${PROJECT_NAME}.git`,
|
||||
);
|
||||
|
||||
// build static html files, then push to DEPLOYMENT_BRANCH branch of specified repo
|
||||
|
||||
if (CURRENT_BRANCH === DEPLOYMENT_BRANCH && !crossRepoPublish) {
|
||||
shell.echo(`Cannot deploy from a ${DEPLOYMENT_BRANCH} branch. Only to it`);
|
||||
shell.exit(1);
|
||||
}
|
||||
|
||||
if (
|
||||
shell.exec(
|
||||
`node ${path.join(__dirname, 'build-files.js')} ${process.argv
|
||||
.slice(2)
|
||||
.join(' ')}`,
|
||||
).code
|
||||
) {
|
||||
shell.echo('Error: generating html failed');
|
||||
shell.exit(1);
|
||||
}
|
||||
|
||||
// Save the commit hash that triggers publish-gh-pages before checking out to deployment branch
|
||||
const currentCommit = shell.exec('git rev-parse HEAD').stdout.trim();
|
||||
|
||||
shell.cd(process.cwd());
|
||||
shell.cd('build');
|
||||
|
||||
if (
|
||||
shell.exec(`git clone ${remoteBranch} ${PROJECT_NAME}-${DEPLOYMENT_BRANCH}`)
|
||||
.code !== 0
|
||||
) {
|
||||
shell.echo('Error: git clone failed');
|
||||
shell.exit(1);
|
||||
}
|
||||
|
||||
shell.cd(`${PROJECT_NAME}-${DEPLOYMENT_BRANCH}`);
|
||||
|
||||
// If the default branch is the one we're deploying to, then we'll fail to create it.
|
||||
// This is the case of a cross-repo publish, where we clone a github.io repo with a default master branch.
|
||||
const defaultBranch = shell
|
||||
.exec('git rev-parse --abbrev-ref HEAD')
|
||||
.stdout.trim();
|
||||
if (defaultBranch !== DEPLOYMENT_BRANCH) {
|
||||
if (shell.exec(`git checkout origin/${DEPLOYMENT_BRANCH}`).code !== 0) {
|
||||
if (shell.exec(`git checkout --orphan ${DEPLOYMENT_BRANCH}`).code !== 0) {
|
||||
shell.echo(`Error: Git checkout ${DEPLOYMENT_BRANCH} failed`);
|
||||
shell.exit(1);
|
||||
}
|
||||
} else if (
|
||||
shell.exec(`git checkout -b ${DEPLOYMENT_BRANCH}`).code +
|
||||
shell.exec(`git branch --set-upstream-to=origin/${DEPLOYMENT_BRANCH}`)
|
||||
.code !==
|
||||
0
|
||||
) {
|
||||
shell.echo(`Error: Git checkout ${DEPLOYMENT_BRANCH} failed`);
|
||||
shell.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
shell.exec('git rm -rf .');
|
||||
|
||||
shell.cd('../..');
|
||||
|
||||
const fromPath = path.join('build', `${PROJECT_NAME}`);
|
||||
const toPath = path.join('build', `${PROJECT_NAME}-${DEPLOYMENT_BRANCH}`);
|
||||
// In github.io case, project is deployed to root. Need to not recursively
|
||||
// copy the deployment-branch to be.
|
||||
const excludePath = `${PROJECT_NAME}-${DEPLOYMENT_BRANCH}`;
|
||||
|
||||
// cannot use shell.cp because it doesn't support copying dotfiles and we
|
||||
// need to copy directories like .circleci, for example
|
||||
// https://github.com/shelljs/shelljs/issues/79
|
||||
fs.copy(
|
||||
fromPath,
|
||||
toPath,
|
||||
(src) => {
|
||||
if (src.indexOf('.DS_Store') !== -1) {
|
||||
return false;
|
||||
}
|
||||
if (src.indexOf(excludePath) !== -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
(error) => {
|
||||
if (error) {
|
||||
shell.echo(`Error: Copying build assets failed with error '${error}'`);
|
||||
shell.exit(1);
|
||||
}
|
||||
|
||||
shell.cd(path.join('build', `${PROJECT_NAME}-${DEPLOYMENT_BRANCH}`));
|
||||
shell.exec('git add --all');
|
||||
|
||||
const commitMessage = CUSTOM_COMMIT_MESSAGE || 'Deploy website';
|
||||
const commitResults = shell.exec(
|
||||
`git commit -m "${commitMessage}" -m "Deploy website version based on ${currentCommit}"`,
|
||||
);
|
||||
if (shell.exec(`git push origin ${DEPLOYMENT_BRANCH}`).code !== 0) {
|
||||
shell.echo('Error: Git push failed');
|
||||
shell.exit(1);
|
||||
} else if (commitResults.code === 0) {
|
||||
// The commit might return a non-zero value when site is up to date.
|
||||
const websiteURL =
|
||||
GITHUB_HOST === GITHUB_DOMAIN
|
||||
? `https://${ORGANIZATION_NAME}.github.io/${PROJECT_NAME}` // gh-pages hosted repo
|
||||
: `https://${GITHUB_HOST}/pages/${ORGANIZATION_NAME}/${PROJECT_NAME}`; // GitHub enterprise hosting.
|
||||
shell.echo(`Website is live at: ${websiteURL}`);
|
||||
shell.exit(0);
|
||||
}
|
||||
},
|
||||
);
|
|
@ -1,131 +0,0 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
const chalk = require('chalk');
|
||||
const program = require('commander');
|
||||
const escapeStringRegexp = require('escape-string-regexp');
|
||||
const fs = require('fs');
|
||||
const glob = require('glob');
|
||||
const path = require('path');
|
||||
|
||||
const metadataUtils = require('./server/metadataUtils.js');
|
||||
|
||||
const CWD = process.cwd();
|
||||
|
||||
// generate a doc header from metadata
|
||||
function makeHeader(metadata) {
|
||||
let header = '---\n';
|
||||
Object.keys(metadata).forEach((key) => {
|
||||
header += `${key}: ${metadata[key]}\n`;
|
||||
});
|
||||
header += '---\n';
|
||||
return header;
|
||||
}
|
||||
|
||||
let currentVersion;
|
||||
let newVersion;
|
||||
|
||||
program
|
||||
.arguments('<version_name> <new_version_name>')
|
||||
.action((ver1, ver2) => {
|
||||
currentVersion = ver1;
|
||||
newVersion = ver2;
|
||||
})
|
||||
.parse(process.argv);
|
||||
|
||||
// require user to input two command line arguments, current version to be
|
||||
// renamed, and new version name
|
||||
if (
|
||||
typeof currentVersion === 'undefined' ||
|
||||
typeof newVersion === 'undefined'
|
||||
) {
|
||||
console.error(
|
||||
`${chalk.yellow(
|
||||
'Version numbers are not properly specified!',
|
||||
)}\nSpecify as command line arguments: the current version you wish to rename, then the version number you want to rename it to. `,
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// error if no versions currently exist
|
||||
if (!fs.existsSync(`${CWD}/versions.json`)) {
|
||||
console.error(
|
||||
`${chalk.yellow(
|
||||
'No versions found!',
|
||||
)}\nNo versions.json file currently exists. Use the \`versions\` script if you wish to create new versions.`,
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const versions = JSON.parse(fs.readFileSync(`${CWD}/versions.json`, 'utf8'));
|
||||
|
||||
const versionIndex = versions.indexOf(currentVersion);
|
||||
// error if current specified version does not exist
|
||||
if (versionIndex < 0) {
|
||||
console.error(
|
||||
`${chalk.yellow(
|
||||
`Version ${currentVersion} does not currently exist!`,
|
||||
)}\n Version ${currentVersion} is not in the versions.json file. You can only rename existing versions.`,
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
// replace old version with new version in versions.json file
|
||||
versions[versionIndex] = newVersion;
|
||||
fs.writeFileSync(
|
||||
`${CWD}/versions.json`,
|
||||
`${JSON.stringify(versions, null, 2)}\n`,
|
||||
);
|
||||
|
||||
// if folder of docs for this version exists, rename folder and rewrite doc
|
||||
// headers to use new version
|
||||
if (fs.existsSync(`${CWD}/versioned_docs/version-${currentVersion}`)) {
|
||||
fs.renameSync(
|
||||
`${CWD}/versioned_docs/version-${currentVersion}`,
|
||||
`${CWD}/versioned_docs/version-${newVersion}`,
|
||||
);
|
||||
|
||||
const files = glob.sync(`${CWD}/versioned_docs/version-${newVersion}/*`);
|
||||
files.forEach((file) => {
|
||||
const extension = path.extname(file);
|
||||
if (extension !== '.md' && extension !== '.markdown') {
|
||||
return;
|
||||
}
|
||||
const res = metadataUtils.extractMetadata(fs.readFileSync(file, 'utf8'));
|
||||
const metadata = res.metadata;
|
||||
const rawContent = res.rawContent;
|
||||
if (!metadata.id) {
|
||||
return;
|
||||
}
|
||||
metadata.id = metadata.id.replace(
|
||||
`version-${currentVersion}-`,
|
||||
`version-${newVersion}-`,
|
||||
);
|
||||
fs.writeFileSync(file, makeHeader(metadata) + rawContent);
|
||||
});
|
||||
}
|
||||
|
||||
// if sidebar file exists for this version, rename sidebar file and rewrite
|
||||
// doc ids in the file
|
||||
const currentSidebarFile = `${CWD}/versioned_sidebars/version-${currentVersion}-sidebars.json`;
|
||||
const newSidebarFile = `${CWD}/versioned_sidebars/version-${newVersion}-sidebars.json`;
|
||||
if (fs.existsSync(currentSidebarFile)) {
|
||||
fs.renameSync(currentSidebarFile, newSidebarFile);
|
||||
let sidebarContent = fs.readFileSync(newSidebarFile, 'utf8');
|
||||
sidebarContent = sidebarContent.replace(
|
||||
new RegExp(`version-${escapeStringRegexp(currentVersion)}-`, 'g'),
|
||||
`version-${newVersion}-`,
|
||||
);
|
||||
fs.writeFileSync(newSidebarFile, sidebarContent);
|
||||
}
|
||||
|
||||
console.log(
|
||||
`${chalk.green('Successfully renamed version ')}${chalk.yellow(
|
||||
currentVersion,
|
||||
)}${chalk.green(' to version ')}${chalk.yellow(newVersion)}\n`,
|
||||
);
|