mirror of
https://github.com/facebook/docusaurus.git
synced 2025-06-16 17:52:29 +02:00
feat(v2): docs plugin initial work (#1327)
* feat(v2): pluginify docs * feat(v2): implement docs plugin * fix(v2): fix bugs in docs plugin for translation and versioning
This commit is contained in:
parent
c33e874e1c
commit
a70d9b6720
32 changed files with 576 additions and 371 deletions
|
@ -10,12 +10,13 @@ const path = require('path');
|
||||||
const fs = require('fs-extra');
|
const fs = require('fs-extra');
|
||||||
const {parse, idx, normalizeUrl, generate} = require('@docusaurus/utils');
|
const {parse, idx, normalizeUrl, generate} = require('@docusaurus/utils');
|
||||||
|
|
||||||
|
// TODO: Use a better slugify function that doesn't rely on a specific file extension.
|
||||||
function fileToUrl(fileName) {
|
function fileToUrl(fileName) {
|
||||||
return fileName
|
return fileName
|
||||||
.replace('-', '/')
|
.replace('-', '/')
|
||||||
.replace('-', '/')
|
.replace('-', '/')
|
||||||
.replace('-', '/')
|
.replace('-', '/')
|
||||||
.replace(/\.md$/, '');
|
.replace(/\.mdx?$/, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEFAULT_OPTIONS = {
|
const DEFAULT_OPTIONS = {
|
||||||
|
@ -42,6 +43,10 @@ class DocusaurusPluginContentBlog {
|
||||||
return 'docusaurus-plugin-content-blog';
|
return 'docusaurus-plugin-content-blog';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getPathsToWatch() {
|
||||||
|
return [this.contentPath];
|
||||||
|
}
|
||||||
|
|
||||||
// Fetches blog contents and returns metadata for the contents.
|
// Fetches blog contents and returns metadata for the contents.
|
||||||
async loadContent() {
|
async loadContent() {
|
||||||
const {pageCount, include, routeBasePath} = this.options;
|
const {pageCount, include, routeBasePath} = this.options;
|
||||||
|
@ -150,10 +155,6 @@ class DocusaurusPluginContentBlog {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getPathsToWatch() {
|
|
||||||
return [this.contentPath];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = DocusaurusPluginContentBlog;
|
module.exports = DocusaurusPluginContentBlog;
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@docusaurus/utils": "^1.0.0",
|
"@docusaurus/utils": "^1.0.0",
|
||||||
"classnames": "^2.2.6",
|
|
||||||
"fs-extra": "^7.0.1",
|
"fs-extra": "^7.0.1",
|
||||||
"globby": "^9.1.0"
|
"globby": "^9.1.0"
|
||||||
},
|
},
|
||||||
|
|
|
@ -7,15 +7,26 @@
|
||||||
|
|
||||||
import '@babel/polyfill';
|
import '@babel/polyfill';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import loadDocs from '@lib/load/docs';
|
import loadSetup from '../../docusaurus/test/loadSetup';
|
||||||
import loadSetup from '../../loadSetup';
|
import DocusaurusPluginContentDocs from '../index';
|
||||||
|
|
||||||
describe('loadDocs', () => {
|
describe('loadDocs', () => {
|
||||||
test('simple website', async () => {
|
test.only('simple website', async () => {
|
||||||
const props = await loadSetup('simple');
|
const {env, siteDir, siteConfig} = await loadSetup('simple');
|
||||||
const {siteDir, docsDir, env, siteConfig} = props;
|
const plugin = new DocusaurusPluginContentDocs(
|
||||||
const {docsMetadatas} = await loadDocs({siteDir, docsDir, env, siteConfig});
|
{
|
||||||
expect(docsMetadatas.hello).toEqual({
|
path: '../docs',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
env,
|
||||||
|
siteDir,
|
||||||
|
siteConfig,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
const {docs: docsMetadata} = await plugin.loadContent();
|
||||||
|
const docsDir = plugin.contentPath;
|
||||||
|
|
||||||
|
expect(docsMetadata.hello).toEqual({
|
||||||
category: 'Guides',
|
category: 'Guides',
|
||||||
id: 'hello',
|
id: 'hello',
|
||||||
language: null,
|
language: null,
|
||||||
|
@ -29,7 +40,7 @@ describe('loadDocs', () => {
|
||||||
title: 'Hello, World !',
|
title: 'Hello, World !',
|
||||||
version: null,
|
version: null,
|
||||||
});
|
});
|
||||||
expect(docsMetadatas['foo/bar']).toEqual({
|
expect(docsMetadata['foo/bar']).toEqual({
|
||||||
category: 'Test',
|
category: 'Test',
|
||||||
id: 'foo/bar',
|
id: 'foo/bar',
|
||||||
language: null,
|
language: null,
|
||||||
|
@ -46,10 +57,23 @@ describe('loadDocs', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('versioned website', async () => {
|
test('versioned website', async () => {
|
||||||
const props = await loadSetup('versioned');
|
const {env, siteDir, siteConfig, versionedDir} = await loadSetup(
|
||||||
const {siteDir, docsDir, versionedDir, env, siteConfig} = props;
|
'versioned',
|
||||||
const {docsMetadatas} = await loadDocs({siteDir, docsDir, env, siteConfig});
|
);
|
||||||
expect(docsMetadatas['version-1.0.0-foo/bar']).toEqual({
|
const plugin = new DocusaurusPluginContentDocs(
|
||||||
|
{
|
||||||
|
path: '../docs',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
env,
|
||||||
|
siteDir,
|
||||||
|
siteConfig,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
const {docs: docsMetadata} = await plugin.loadContent();
|
||||||
|
const docsDir = plugin.contentPath;
|
||||||
|
|
||||||
|
expect(docsMetadata['version-1.0.0-foo/bar']).toEqual({
|
||||||
category: 'Test',
|
category: 'Test',
|
||||||
id: 'version-1.0.0-foo/bar',
|
id: 'version-1.0.0-foo/bar',
|
||||||
language: null,
|
language: null,
|
||||||
|
@ -63,7 +87,7 @@ describe('loadDocs', () => {
|
||||||
title: 'Bar',
|
title: 'Bar',
|
||||||
version: '1.0.0',
|
version: '1.0.0',
|
||||||
});
|
});
|
||||||
expect(docsMetadatas['foo/bar']).toEqual({
|
expect(docsMetadata['foo/bar']).toEqual({
|
||||||
category: 'Test',
|
category: 'Test',
|
||||||
id: 'foo/bar',
|
id: 'foo/bar',
|
||||||
language: null,
|
language: null,
|
||||||
|
@ -80,17 +104,27 @@ describe('loadDocs', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('versioned & translated website', async () => {
|
test('versioned & translated website', async () => {
|
||||||
const props = await loadSetup('transversioned');
|
|
||||||
const {
|
const {
|
||||||
siteDir,
|
|
||||||
docsDir,
|
|
||||||
env,
|
env,
|
||||||
|
siteDir,
|
||||||
|
siteConfig,
|
||||||
translatedDir,
|
translatedDir,
|
||||||
versionedDir,
|
versionedDir,
|
||||||
siteConfig,
|
} = await loadSetup('transversioned');
|
||||||
} = props;
|
const plugin = new DocusaurusPluginContentDocs(
|
||||||
const {docsMetadatas} = await loadDocs({siteDir, docsDir, env, siteConfig});
|
{
|
||||||
expect(docsMetadatas['ko-version-1.0.0-foo/bar']).toEqual({
|
path: '../docs',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
env,
|
||||||
|
siteDir,
|
||||||
|
siteConfig,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
const {docs: docsMetadata} = await plugin.loadContent();
|
||||||
|
const docsDir = plugin.contentPath;
|
||||||
|
|
||||||
|
expect(docsMetadata['ko-version-1.0.0-foo/bar']).toEqual({
|
||||||
category: 'Test',
|
category: 'Test',
|
||||||
id: 'ko-version-1.0.0-foo/bar',
|
id: 'ko-version-1.0.0-foo/bar',
|
||||||
language: 'ko',
|
language: 'ko',
|
||||||
|
@ -104,7 +138,7 @@ describe('loadDocs', () => {
|
||||||
title: 'Bar',
|
title: 'Bar',
|
||||||
version: '1.0.0',
|
version: '1.0.0',
|
||||||
});
|
});
|
||||||
expect(docsMetadatas['en-version-1.0.0-foo/baz']).toEqual({
|
expect(docsMetadata['en-version-1.0.0-foo/baz']).toEqual({
|
||||||
category: 'Test',
|
category: 'Test',
|
||||||
id: 'en-version-1.0.0-foo/baz',
|
id: 'en-version-1.0.0-foo/baz',
|
||||||
language: 'en',
|
language: 'en',
|
||||||
|
@ -121,7 +155,7 @@ describe('loadDocs', () => {
|
||||||
title: 'Baz',
|
title: 'Baz',
|
||||||
version: '1.0.0',
|
version: '1.0.0',
|
||||||
});
|
});
|
||||||
expect(docsMetadatas['en-hello']).toEqual({
|
expect(docsMetadata['en-hello']).toEqual({
|
||||||
category: 'Guides',
|
category: 'Guides',
|
||||||
id: 'en-hello',
|
id: 'en-hello',
|
||||||
language: 'en',
|
language: 'en',
|
||||||
|
@ -138,10 +172,23 @@ describe('loadDocs', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('translated website', async () => {
|
test('translated website', async () => {
|
||||||
const props = await loadSetup('translated');
|
const {env, siteDir, siteConfig, translatedDir} = await loadSetup(
|
||||||
const {siteDir, translatedDir, docsDir, env, siteConfig} = props;
|
'translated',
|
||||||
const {docsMetadatas} = await loadDocs({siteDir, docsDir, env, siteConfig});
|
);
|
||||||
expect(docsMetadatas['ko-foo/baz']).toEqual({
|
const plugin = new DocusaurusPluginContentDocs(
|
||||||
|
{
|
||||||
|
path: '../docs',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
env,
|
||||||
|
siteDir,
|
||||||
|
siteConfig,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
const {docs: docsMetadata} = await plugin.loadContent();
|
||||||
|
const docsDir = plugin.contentPath;
|
||||||
|
|
||||||
|
expect(docsMetadata['ko-foo/baz']).toEqual({
|
||||||
category: 'Test',
|
category: 'Test',
|
||||||
id: 'ko-foo/baz',
|
id: 'ko-foo/baz',
|
||||||
language: 'ko',
|
language: 'ko',
|
||||||
|
@ -158,7 +205,7 @@ describe('loadDocs', () => {
|
||||||
title: 'baz',
|
title: 'baz',
|
||||||
version: null,
|
version: null,
|
||||||
});
|
});
|
||||||
expect(docsMetadatas['en-foo/bar']).toEqual({
|
expect(docsMetadata['en-foo/bar']).toEqual({
|
||||||
category: 'Test',
|
category: 'Test',
|
||||||
id: 'en-foo/bar',
|
id: 'en-foo/bar',
|
||||||
language: 'en',
|
language: 'en',
|
||||||
|
@ -175,16 +222,23 @@ describe('loadDocs', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('versioned website with skip next release', async () => {
|
test('versioned website with skip next release', async () => {
|
||||||
const props = await loadSetup('versioned');
|
const {env, siteDir, siteConfig, versionedDir} = await loadSetup(
|
||||||
const {siteDir, docsDir, versionedDir, env, siteConfig} = props;
|
'versioned',
|
||||||
const {docsMetadatas} = await loadDocs({
|
);
|
||||||
siteDir,
|
const plugin = new DocusaurusPluginContentDocs(
|
||||||
docsDir,
|
{
|
||||||
env,
|
path: '../docs',
|
||||||
siteConfig,
|
},
|
||||||
skipNextRelease: true,
|
{
|
||||||
});
|
env,
|
||||||
expect(docsMetadatas['version-1.0.0-foo/bar']).toEqual({
|
siteDir,
|
||||||
|
siteConfig,
|
||||||
|
cliOptions: {skipNextRelease: true},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
const {docs: docsMetadata} = await plugin.loadContent();
|
||||||
|
|
||||||
|
expect(docsMetadata['version-1.0.0-foo/bar']).toEqual({
|
||||||
category: 'Test',
|
category: 'Test',
|
||||||
id: 'version-1.0.0-foo/bar',
|
id: 'version-1.0.0-foo/bar',
|
||||||
language: null,
|
language: null,
|
||||||
|
@ -198,6 +252,6 @@ describe('loadDocs', () => {
|
||||||
title: 'Bar',
|
title: 'Bar',
|
||||||
version: '1.0.0',
|
version: '1.0.0',
|
||||||
});
|
});
|
||||||
expect(docsMetadatas['foo/bar']).toBeUndefined();
|
expect(docsMetadata['foo/bar']).toBeUndefined();
|
||||||
});
|
});
|
||||||
});
|
});
|
|
@ -7,8 +7,8 @@
|
||||||
|
|
||||||
import '@babel/polyfill';
|
import '@babel/polyfill';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import processMetadata from '@lib/load/docs/metadata';
|
import processMetadata from '../src/metadata';
|
||||||
import loadSetup from '../../loadSetup';
|
import loadSetup from '../../docusaurus/test/loadSetup';
|
||||||
|
|
||||||
describe('processMetadata', () => {
|
describe('processMetadata', () => {
|
||||||
test('normal docs', async () => {
|
test('normal docs', async () => {
|
||||||
|
@ -16,8 +16,22 @@ describe('processMetadata', () => {
|
||||||
const {docsDir, env, siteConfig} = props;
|
const {docsDir, env, siteConfig} = props;
|
||||||
const sourceA = path.join('foo', 'bar.md');
|
const sourceA = path.join('foo', 'bar.md');
|
||||||
const sourceB = path.join('hello.md');
|
const sourceB = path.join('hello.md');
|
||||||
const dataA = await processMetadata(sourceA, docsDir, env, {}, siteConfig);
|
const dataA = await processMetadata(
|
||||||
const dataB = await processMetadata(sourceB, docsDir, env, {}, siteConfig);
|
sourceA,
|
||||||
|
docsDir,
|
||||||
|
env,
|
||||||
|
{},
|
||||||
|
siteConfig,
|
||||||
|
'docs',
|
||||||
|
);
|
||||||
|
const dataB = await processMetadata(
|
||||||
|
sourceB,
|
||||||
|
docsDir,
|
||||||
|
env,
|
||||||
|
{},
|
||||||
|
siteConfig,
|
||||||
|
'docs',
|
||||||
|
);
|
||||||
expect(dataA).toEqual({
|
expect(dataA).toEqual({
|
||||||
id: 'foo/bar',
|
id: 'foo/bar',
|
||||||
language: null,
|
language: null,
|
||||||
|
@ -42,7 +56,14 @@ describe('processMetadata', () => {
|
||||||
const props = await loadSetup('simple');
|
const props = await loadSetup('simple');
|
||||||
const {docsDir, env, siteConfig} = props;
|
const {docsDir, env, siteConfig} = props;
|
||||||
const source = path.join('permalink.md');
|
const source = path.join('permalink.md');
|
||||||
const data = await processMetadata(source, docsDir, env, {}, siteConfig);
|
const data = await processMetadata(
|
||||||
|
source,
|
||||||
|
docsDir,
|
||||||
|
env,
|
||||||
|
{},
|
||||||
|
siteConfig,
|
||||||
|
'docs',
|
||||||
|
);
|
||||||
expect(data).toEqual({
|
expect(data).toEqual({
|
||||||
id: 'permalink',
|
id: 'permalink',
|
||||||
language: null,
|
language: null,
|
||||||
|
@ -68,6 +89,7 @@ describe('processMetadata', () => {
|
||||||
env,
|
env,
|
||||||
{},
|
{},
|
||||||
siteConfig,
|
siteConfig,
|
||||||
|
'docs',
|
||||||
);
|
);
|
||||||
const dataB = await processMetadata(
|
const dataB = await processMetadata(
|
||||||
sourceB,
|
sourceB,
|
||||||
|
@ -75,9 +97,24 @@ describe('processMetadata', () => {
|
||||||
env,
|
env,
|
||||||
{},
|
{},
|
||||||
siteConfig,
|
siteConfig,
|
||||||
|
'docs',
|
||||||
|
);
|
||||||
|
const dataC = await processMetadata(
|
||||||
|
sourceC,
|
||||||
|
docsDir,
|
||||||
|
env,
|
||||||
|
{},
|
||||||
|
siteConfig,
|
||||||
|
'docs',
|
||||||
|
);
|
||||||
|
const dataD = await processMetadata(
|
||||||
|
sourceD,
|
||||||
|
docsDir,
|
||||||
|
env,
|
||||||
|
{},
|
||||||
|
siteConfig,
|
||||||
|
'docs',
|
||||||
);
|
);
|
||||||
const dataC = await processMetadata(sourceC, docsDir, env, {}, siteConfig);
|
|
||||||
const dataD = await processMetadata(sourceD, docsDir, env, {}, siteConfig);
|
|
||||||
expect(dataA).toEqual({
|
expect(dataA).toEqual({
|
||||||
id: 'version-1.0.0-foo/bar',
|
id: 'version-1.0.0-foo/bar',
|
||||||
language: null,
|
language: null,
|
||||||
|
@ -133,6 +170,7 @@ describe('processMetadata', () => {
|
||||||
env,
|
env,
|
||||||
{},
|
{},
|
||||||
siteConfig,
|
siteConfig,
|
||||||
|
'docs',
|
||||||
);
|
);
|
||||||
const dataB = await processMetadata(
|
const dataB = await processMetadata(
|
||||||
sourceB,
|
sourceB,
|
||||||
|
@ -140,6 +178,7 @@ describe('processMetadata', () => {
|
||||||
env,
|
env,
|
||||||
{},
|
{},
|
||||||
siteConfig,
|
siteConfig,
|
||||||
|
'docs',
|
||||||
);
|
);
|
||||||
const dataC = await processMetadata(
|
const dataC = await processMetadata(
|
||||||
sourceC,
|
sourceC,
|
||||||
|
@ -147,6 +186,7 @@ describe('processMetadata', () => {
|
||||||
env,
|
env,
|
||||||
{},
|
{},
|
||||||
siteConfig,
|
siteConfig,
|
||||||
|
'docs',
|
||||||
);
|
);
|
||||||
const dataD = await processMetadata(
|
const dataD = await processMetadata(
|
||||||
sourceD,
|
sourceD,
|
||||||
|
@ -154,15 +194,31 @@ describe('processMetadata', () => {
|
||||||
env,
|
env,
|
||||||
{},
|
{},
|
||||||
siteConfig,
|
siteConfig,
|
||||||
|
'docs',
|
||||||
|
);
|
||||||
|
const dataE = await processMetadata(
|
||||||
|
sourceE,
|
||||||
|
docsDir,
|
||||||
|
env,
|
||||||
|
{},
|
||||||
|
siteConfig,
|
||||||
|
'docs',
|
||||||
|
);
|
||||||
|
const dataF = await processMetadata(
|
||||||
|
sourceF,
|
||||||
|
docsDir,
|
||||||
|
env,
|
||||||
|
{},
|
||||||
|
siteConfig,
|
||||||
|
'docs',
|
||||||
);
|
);
|
||||||
const dataE = await processMetadata(sourceE, docsDir, env, {}, siteConfig);
|
|
||||||
const dataF = await processMetadata(sourceF, docsDir, env, {}, siteConfig);
|
|
||||||
const dataG = await processMetadata(
|
const dataG = await processMetadata(
|
||||||
sourceG,
|
sourceG,
|
||||||
versionedDir,
|
versionedDir,
|
||||||
env,
|
env,
|
||||||
{},
|
{},
|
||||||
siteConfig,
|
siteConfig,
|
||||||
|
'docs',
|
||||||
);
|
);
|
||||||
const dataH = await processMetadata(
|
const dataH = await processMetadata(
|
||||||
sourceH,
|
sourceH,
|
||||||
|
@ -170,6 +226,7 @@ describe('processMetadata', () => {
|
||||||
env,
|
env,
|
||||||
{},
|
{},
|
||||||
siteConfig,
|
siteConfig,
|
||||||
|
'docs',
|
||||||
);
|
);
|
||||||
expect(dataA).toEqual({
|
expect(dataA).toEqual({
|
||||||
id: 'ko-version-1.0.0-foo/bar',
|
id: 'ko-version-1.0.0-foo/bar',
|
||||||
|
@ -258,6 +315,7 @@ describe('processMetadata', () => {
|
||||||
env,
|
env,
|
||||||
{},
|
{},
|
||||||
siteConfig,
|
siteConfig,
|
||||||
|
'docs',
|
||||||
);
|
);
|
||||||
const dataB = await processMetadata(
|
const dataB = await processMetadata(
|
||||||
sourceB,
|
sourceB,
|
||||||
|
@ -265,9 +323,24 @@ describe('processMetadata', () => {
|
||||||
env,
|
env,
|
||||||
{},
|
{},
|
||||||
siteConfig,
|
siteConfig,
|
||||||
|
'docs',
|
||||||
|
);
|
||||||
|
const dataC = await processMetadata(
|
||||||
|
sourceC,
|
||||||
|
docsDir,
|
||||||
|
env,
|
||||||
|
{},
|
||||||
|
siteConfig,
|
||||||
|
'docs',
|
||||||
|
);
|
||||||
|
const dataD = await processMetadata(
|
||||||
|
sourceD,
|
||||||
|
docsDir,
|
||||||
|
env,
|
||||||
|
{},
|
||||||
|
siteConfig,
|
||||||
|
'docs',
|
||||||
);
|
);
|
||||||
const dataC = await processMetadata(sourceC, docsDir, env, {}, siteConfig);
|
|
||||||
const dataD = await processMetadata(sourceD, docsDir, env, {}, siteConfig);
|
|
||||||
expect(dataA).toEqual({
|
expect(dataA).toEqual({
|
||||||
id: 'ko-foo/bar',
|
id: 'ko-foo/bar',
|
||||||
language: 'ko',
|
language: 'ko',
|
|
@ -5,7 +5,7 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import createOrder from '@lib/load/docs/order';
|
import createOrder from '../src/order';
|
||||||
|
|
||||||
describe('createOrder', () => {
|
describe('createOrder', () => {
|
||||||
test('multiple sidebars with subcategory', () => {
|
test('multiple sidebars with subcategory', () => {
|
|
@ -6,8 +6,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import loadSidebars from '@lib/load/docs/sidebars';
|
import loadSidebars from '../src/sidebars';
|
||||||
import loadSetup from '../../loadSetup';
|
import loadSetup from '../../docusaurus/test/loadSetup';
|
||||||
|
|
||||||
describe('loadSidebars', () => {
|
describe('loadSidebars', () => {
|
||||||
const fixtures = path.join(__dirname, '..', '__fixtures__');
|
const fixtures = path.join(__dirname, '..', '__fixtures__');
|
210
packages/docusaurus-plugin-content-docs/index.js
Normal file
210
packages/docusaurus-plugin-content-docs/index.js
Normal file
|
@ -0,0 +1,210 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2017-present, Facebook, Inc.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const path = require('path');
|
||||||
|
const globby = require('globby');
|
||||||
|
const {getSubFolder, idx, normalizeUrl} = require('@docusaurus/utils');
|
||||||
|
const createOrder = require('./src/order');
|
||||||
|
const loadSidebars = require('./src/sidebars');
|
||||||
|
const processMetadata = require('./src/metadata');
|
||||||
|
|
||||||
|
const DEFAULT_OPTIONS = {
|
||||||
|
metadataKey: 'docsMetadata',
|
||||||
|
metadataFileName: 'docsMetadata.json',
|
||||||
|
path: 'docs', // Path to data on filesystem, relative to site dir.
|
||||||
|
routeBasePath: 'docs', // URL Route.
|
||||||
|
include: ['**/*.md', '**/*.mdx'], // Extensions to include.
|
||||||
|
// TODO: Read from props rather than hardcoded sidebar.json.
|
||||||
|
sidebar: [], // Sidebar configuration for showing a list of documentation pages.
|
||||||
|
// TODO: Settle themeing.
|
||||||
|
docLayoutComponent: '@theme/Doc',
|
||||||
|
docItemComponent: '@theme/DocBody',
|
||||||
|
};
|
||||||
|
|
||||||
|
class DocusaurusPluginContentDocs {
|
||||||
|
constructor(opts, context) {
|
||||||
|
this.options = {...DEFAULT_OPTIONS, ...opts};
|
||||||
|
this.context = context;
|
||||||
|
this.contentPath = path.resolve(this.context.siteDir, this.options.path);
|
||||||
|
}
|
||||||
|
|
||||||
|
getName() {
|
||||||
|
return 'docusaurus-plugin-content-docs';
|
||||||
|
}
|
||||||
|
|
||||||
|
getPathsToWatch() {
|
||||||
|
return [this.contentPath];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetches blog contents and returns metadata for the contents.
|
||||||
|
async loadContent() {
|
||||||
|
const {include, routeBasePath} = this.options;
|
||||||
|
const {siteDir, env, siteConfig, cliOptions = {}} = this.context;
|
||||||
|
const {skipNextRelease} = cliOptions;
|
||||||
|
const docsDir = this.contentPath;
|
||||||
|
|
||||||
|
// @tested - load all sidebars including versioned sidebars
|
||||||
|
const docsSidebars = loadSidebars({siteDir, env});
|
||||||
|
|
||||||
|
// @tested - build the docs ordering such as next, previous, category and sidebar
|
||||||
|
const order = createOrder(docsSidebars);
|
||||||
|
|
||||||
|
// Settle versions & translations from environment.
|
||||||
|
const translationEnabled = idx(env, ['translation', 'enabled']);
|
||||||
|
const enabledLanguages =
|
||||||
|
translationEnabled && idx(env, ['translation', 'enabledLanguages']);
|
||||||
|
const enabledLangTags =
|
||||||
|
(enabledLanguages && enabledLanguages.map(lang => lang.tag)) || [];
|
||||||
|
const defaultLangTag = idx(env, ['translation', 'defaultLanguage', 'tag']);
|
||||||
|
const versioningEnabled = idx(env, ['versioning', 'enabled']);
|
||||||
|
const versions =
|
||||||
|
(versioningEnabled && idx(env, ['versioning', 'versions'])) || [];
|
||||||
|
|
||||||
|
// Prepare metadata container.
|
||||||
|
const docs = {};
|
||||||
|
|
||||||
|
if (!(versioningEnabled && skipNextRelease)) {
|
||||||
|
// Metadata for default docs files.
|
||||||
|
const docsFiles = await globby(include, {
|
||||||
|
cwd: docsDir,
|
||||||
|
});
|
||||||
|
await Promise.all(
|
||||||
|
docsFiles.map(async source => {
|
||||||
|
// Do not allow reserved version/ translated folder name in 'docs'
|
||||||
|
// e.g: 'docs/version-1.0.0/' should not be allowed as it can cause unwanted bug
|
||||||
|
const subFolder = getSubFolder(
|
||||||
|
path.resolve(docsDir, source),
|
||||||
|
docsDir,
|
||||||
|
);
|
||||||
|
const versionsFolders = versions.map(version => `version-${version}`);
|
||||||
|
if ([...enabledLangTags, ...versionsFolders].includes(subFolder)) {
|
||||||
|
throw new Error(
|
||||||
|
`You cannot have a folder named 'docs/${subFolder}/'`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const metadata = await processMetadata(
|
||||||
|
source,
|
||||||
|
docsDir,
|
||||||
|
env,
|
||||||
|
order,
|
||||||
|
siteConfig,
|
||||||
|
routeBasePath,
|
||||||
|
);
|
||||||
|
docs[metadata.id] = metadata;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Metadata for non-default-language docs.
|
||||||
|
if (translationEnabled) {
|
||||||
|
const translatedDir = path.join(siteDir, 'translated_docs');
|
||||||
|
const translatedFiles = await globby(include, {
|
||||||
|
cwd: translatedDir,
|
||||||
|
});
|
||||||
|
await Promise.all(
|
||||||
|
translatedFiles.map(async source => {
|
||||||
|
/*
|
||||||
|
Do not process disabled & default languages folder in `translated_docs`
|
||||||
|
e.g: 'translated_docs/ja/**' should not be processed if lang 'ja' is disabled
|
||||||
|
*/
|
||||||
|
const translatedFilePath = path.resolve(translatedDir, source);
|
||||||
|
const detectedLangTag = getSubFolder(
|
||||||
|
translatedFilePath,
|
||||||
|
translatedDir,
|
||||||
|
);
|
||||||
|
if (
|
||||||
|
detectedLangTag === defaultLangTag ||
|
||||||
|
!enabledLangTags.includes(detectedLangTag)
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const metadata = await processMetadata(
|
||||||
|
source,
|
||||||
|
translatedDir,
|
||||||
|
env,
|
||||||
|
order,
|
||||||
|
siteConfig,
|
||||||
|
routeBasePath,
|
||||||
|
);
|
||||||
|
docs[metadata.id] = metadata;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Metadata for versioned docs.
|
||||||
|
if (versioningEnabled) {
|
||||||
|
const versionedDir = path.join(siteDir, 'versioned_docs');
|
||||||
|
const versionedFiles = await globby(include, {
|
||||||
|
cwd: versionedDir,
|
||||||
|
});
|
||||||
|
await Promise.all(
|
||||||
|
versionedFiles.map(async source => {
|
||||||
|
const metadata = await processMetadata(
|
||||||
|
source,
|
||||||
|
versionedDir,
|
||||||
|
env,
|
||||||
|
order,
|
||||||
|
siteConfig,
|
||||||
|
routeBasePath,
|
||||||
|
);
|
||||||
|
docs[metadata.id] = metadata;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the titles of the previous and next ids so that we can use them.
|
||||||
|
Object.keys(docs).forEach(currentID => {
|
||||||
|
const previousID = idx(docs, [currentID, 'previous']);
|
||||||
|
if (previousID) {
|
||||||
|
const previousTitle = idx(docs, [previousID, 'title']);
|
||||||
|
docs[currentID].previous_title = previousTitle || 'Previous';
|
||||||
|
}
|
||||||
|
const nextID = idx(docs, [currentID, 'next']);
|
||||||
|
if (nextID) {
|
||||||
|
const nextTitle = idx(docs, [nextID, 'title']);
|
||||||
|
docs[currentID].next_title = nextTitle || 'Next';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create source to metadata mapping.
|
||||||
|
const sourceToMetadata = {};
|
||||||
|
Object.values(docs).forEach(({source, version, permalink, language}) => {
|
||||||
|
sourceToMetadata[source] = {
|
||||||
|
version,
|
||||||
|
permalink,
|
||||||
|
language,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
docs,
|
||||||
|
docsDir,
|
||||||
|
docsSidebars,
|
||||||
|
sourceToMetadata,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async contentLoaded({content, actions}) {
|
||||||
|
const {docLayoutComponent, docItemComponent, routeBasePath} = this.options;
|
||||||
|
const {addRoute} = actions;
|
||||||
|
|
||||||
|
addRoute({
|
||||||
|
path: normalizeUrl([this.context.siteConfig.baseUrl, routeBasePath]),
|
||||||
|
component: docLayoutComponent,
|
||||||
|
routes: Object.values(content.docs).map(metadataItem => ({
|
||||||
|
path: metadataItem.permalink,
|
||||||
|
component: docItemComponent,
|
||||||
|
metadata: metadataItem,
|
||||||
|
modules: [metadataItem.source],
|
||||||
|
})),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = DocusaurusPluginContentDocs;
|
16
packages/docusaurus-plugin-content-docs/package.json
Normal file
16
packages/docusaurus-plugin-content-docs/package.json
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"name": "@docusaurus/plugin-content-docs",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Documentation plugin for Docusaurus",
|
||||||
|
"main": "index.js",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/polyfill": "^7.4.0",
|
||||||
|
"@docusaurus/utils": "^1.0.0",
|
||||||
|
"fs-extra": "^7.0.1",
|
||||||
|
"globby": "^9.1.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@docusaurus/core": "^2.0.0"
|
||||||
|
}
|
||||||
|
}
|
|
@ -56,6 +56,7 @@ module.exports = async function processMetadata(
|
||||||
env,
|
env,
|
||||||
order,
|
order,
|
||||||
siteConfig,
|
siteConfig,
|
||||||
|
docsBasePath,
|
||||||
) {
|
) {
|
||||||
const filepath = path.resolve(refDir, source);
|
const filepath = path.resolve(refDir, source);
|
||||||
const fileString = await fs.readFile(filepath, 'utf-8');
|
const fileString = await fs.readFile(filepath, 'utf-8');
|
||||||
|
@ -122,7 +123,7 @@ module.exports = async function processMetadata(
|
||||||
metadata.source = path.join(refDir, source);
|
metadata.source = path.join(refDir, source);
|
||||||
|
|
||||||
// Build the permalink.
|
// Build the permalink.
|
||||||
const {baseUrl, docsUrl} = siteConfig;
|
const {baseUrl} = siteConfig;
|
||||||
|
|
||||||
// If user has own custom permalink defined in frontmatter
|
// If user has own custom permalink defined in frontmatter
|
||||||
// e.g: :baseUrl:docsUrl/:langPart/:versionPart/endiliey/:id
|
// e.g: :baseUrl:docsUrl/:langPart/:versionPart/endiliey/:id
|
||||||
|
@ -130,7 +131,7 @@ module.exports = async function processMetadata(
|
||||||
metadata.permalink = path.resolve(
|
metadata.permalink = path.resolve(
|
||||||
metadata.permalink
|
metadata.permalink
|
||||||
.replace(/:baseUrl/, baseUrl)
|
.replace(/:baseUrl/, baseUrl)
|
||||||
.replace(/:docsUrl/, docsUrl)
|
.replace(/:docsUrl/, docsBasePath)
|
||||||
.replace(/:langPart/, langPart)
|
.replace(/:langPart/, langPart)
|
||||||
.replace(/:versionPart/, versionPart)
|
.replace(/:versionPart/, versionPart)
|
||||||
.replace(/:id/, metadata.id),
|
.replace(/:id/, metadata.id),
|
||||||
|
@ -138,7 +139,7 @@ module.exports = async function processMetadata(
|
||||||
} else {
|
} else {
|
||||||
metadata.permalink = normalizeUrl([
|
metadata.permalink = normalizeUrl([
|
||||||
baseUrl,
|
baseUrl,
|
||||||
docsUrl,
|
docsBasePath,
|
||||||
langPart,
|
langPart,
|
||||||
versionPart,
|
versionPart,
|
||||||
metadata.id,
|
metadata.id,
|
|
@ -7,14 +7,12 @@
|
||||||
|
|
||||||
const globby = require('globby');
|
const globby = require('globby');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
// TODO: Do not make it relative because plugins can be from node_modules.
|
|
||||||
const {encodePath, fileToPath, idx} = require('@docusaurus/utils');
|
const {encodePath, fileToPath, idx} = require('@docusaurus/utils');
|
||||||
|
|
||||||
const DEFAULT_OPTIONS = {
|
const DEFAULT_OPTIONS = {
|
||||||
metadataKey: 'pagesMetadata',
|
metadataKey: 'pagesMetadata',
|
||||||
metadataFileName: 'pagesMetadata.json',
|
metadataFileName: 'pagesMetadata.json',
|
||||||
path: 'pages', // Path to data on filesystem.
|
path: 'pages', // Path to data on filesystem, relative to site dir.
|
||||||
routeBasePath: '', // URL Route.
|
routeBasePath: '', // URL Route.
|
||||||
include: ['**/*.{js,jsx}'], // Extensions to include.
|
include: ['**/*.{js,jsx}'], // Extensions to include.
|
||||||
component: '@theme/Pages',
|
component: '@theme/Pages',
|
||||||
|
@ -31,6 +29,10 @@ class DocusaurusPluginContentPages {
|
||||||
return 'docusaurus-plugin-content-pages';
|
return 'docusaurus-plugin-content-pages';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getPathsToWatch() {
|
||||||
|
return [this.contentPath];
|
||||||
|
}
|
||||||
|
|
||||||
async loadContent() {
|
async loadContent() {
|
||||||
const {include} = this.options;
|
const {include} = this.options;
|
||||||
const {env, siteConfig} = this.context;
|
const {env, siteConfig} = this.context;
|
||||||
|
@ -102,10 +104,6 @@ class DocusaurusPluginContentPages {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getPathsToWatch() {
|
|
||||||
return [this.contentPath];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = DocusaurusPluginContentPages;
|
module.exports = DocusaurusPluginContentPages;
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
# Breaking Changes
|
# Breaking Changes
|
||||||
|
|
||||||
|
### `siteConfig.js` changes
|
||||||
|
|
||||||
- `siteConfig.js` renamed to `docusaurus.config.js`.
|
- `siteConfig.js` renamed to `docusaurus.config.js`.
|
||||||
|
- Removed the following config options:
|
||||||
|
- `docsUrl`. Use the plugin option on `docusaurus-plugin-content-blog` instead.
|
||||||
|
- `customDocsPath`. Use the plugin option on `docusaurus-plugin-content-blog` instead.
|
||||||
|
|
|
@ -46,7 +46,6 @@ module.exports = async function start(siteDir, cliOptions = {}) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
const {plugins} = props;
|
const {plugins} = props;
|
||||||
const docsRelativeDir = props.siteConfig.customDocsPath;
|
|
||||||
const pluginPaths = _.compact(
|
const pluginPaths = _.compact(
|
||||||
_.flatten(
|
_.flatten(
|
||||||
plugins.map(
|
plugins.map(
|
||||||
|
@ -55,12 +54,7 @@ module.exports = async function start(siteDir, cliOptions = {}) {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
const fsWatcher = chokidar.watch(
|
const fsWatcher = chokidar.watch(
|
||||||
[
|
[...pluginPaths, loadConfig.configFileName, 'sidebars.json'],
|
||||||
...pluginPaths,
|
|
||||||
`../${docsRelativeDir}/**/*.md`,
|
|
||||||
loadConfig.configFileName,
|
|
||||||
'sidebars.json',
|
|
||||||
],
|
|
||||||
{
|
{
|
||||||
cwd: siteDir,
|
cwd: siteDir,
|
||||||
ignoreInitial: true,
|
ignoreInitial: true,
|
||||||
|
|
|
@ -25,11 +25,9 @@ const REQUIRED_FIELDS = [
|
||||||
|
|
||||||
const OPTIONAL_FIELDS = [
|
const OPTIONAL_FIELDS = [
|
||||||
'algolia',
|
'algolia',
|
||||||
'customDocsPath',
|
|
||||||
'customFields',
|
'customFields',
|
||||||
'defaultLanguage',
|
'defaultLanguage',
|
||||||
'disableHeaderTitle',
|
'disableHeaderTitle',
|
||||||
'docsUrl',
|
|
||||||
'githubHost',
|
'githubHost',
|
||||||
'highlight',
|
'highlight',
|
||||||
'markdownPlugins',
|
'markdownPlugins',
|
||||||
|
@ -37,8 +35,7 @@ const OPTIONAL_FIELDS = [
|
||||||
];
|
];
|
||||||
|
|
||||||
const DEFAULT_CONFIG = {
|
const DEFAULT_CONFIG = {
|
||||||
customDocsPath: 'docs',
|
plugins: [],
|
||||||
docsUrl: 'docs',
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function formatFields(fields) {
|
function formatFields(fields) {
|
||||||
|
|
|
@ -1,144 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright (c) 2017-present, Facebook, Inc.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the
|
|
||||||
* LICENSE file in the root directory of this source tree.
|
|
||||||
*/
|
|
||||||
|
|
||||||
const path = require('path');
|
|
||||||
const globby = require('globby');
|
|
||||||
const {getSubFolder, idx} = require('@docusaurus/utils');
|
|
||||||
const createOrder = require('./order');
|
|
||||||
const loadSidebars = require('./sidebars');
|
|
||||||
const processMetadata = require('./metadata');
|
|
||||||
|
|
||||||
async function loadDocs({
|
|
||||||
siteDir,
|
|
||||||
docsDir,
|
|
||||||
env,
|
|
||||||
siteConfig,
|
|
||||||
skipNextRelease = false,
|
|
||||||
}) {
|
|
||||||
// @tested - load all sidebars including versioned sidebars
|
|
||||||
const docsSidebars = loadSidebars({siteDir, env});
|
|
||||||
|
|
||||||
// @tested - build the docs ordering such as next, previous, category and sidebar
|
|
||||||
const order = createOrder(docsSidebars);
|
|
||||||
|
|
||||||
// Settle versions & translations from environment.
|
|
||||||
const translationEnabled = idx(env, ['translation', 'enabled']);
|
|
||||||
const enabledLanguages =
|
|
||||||
translationEnabled && idx(env, ['translation', 'enabledLanguages']);
|
|
||||||
const enabledLangTags =
|
|
||||||
(enabledLanguages && enabledLanguages.map(lang => lang.tag)) || [];
|
|
||||||
const defaultLangTag = idx(env, ['translation', 'defaultLanguage', 'tag']);
|
|
||||||
const versioningEnabled = idx(env, ['versioning', 'enabled']);
|
|
||||||
const versions =
|
|
||||||
(versioningEnabled && idx(env, ['versioning', 'versions'])) || [];
|
|
||||||
|
|
||||||
// Prepare metadata container.
|
|
||||||
const docsMetadatas = {};
|
|
||||||
|
|
||||||
if (!(versioningEnabled && skipNextRelease)) {
|
|
||||||
// Metadata for default docs files.
|
|
||||||
const docsFiles = await globby(['**/*.md'], {
|
|
||||||
cwd: docsDir,
|
|
||||||
});
|
|
||||||
await Promise.all(
|
|
||||||
docsFiles.map(async source => {
|
|
||||||
// Do not allow reserved version/ translated folder name in 'docs'
|
|
||||||
// e.g: 'docs/version-1.0.0/' should not be allowed as it can cause unwanted bug
|
|
||||||
const subFolder = getSubFolder(path.resolve(docsDir, source), docsDir);
|
|
||||||
const versionsFolders = versions.map(version => `version-${version}`);
|
|
||||||
if ([...enabledLangTags, ...versionsFolders].includes(subFolder)) {
|
|
||||||
throw new Error(
|
|
||||||
`You cannot have a folder named 'docs/${subFolder}/'`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const metadata = await processMetadata(
|
|
||||||
source,
|
|
||||||
docsDir,
|
|
||||||
env,
|
|
||||||
order,
|
|
||||||
siteConfig,
|
|
||||||
);
|
|
||||||
docsMetadatas[metadata.id] = metadata;
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Metadata for non-default-language docs.
|
|
||||||
if (translationEnabled) {
|
|
||||||
const translatedDir = path.join(siteDir, 'translated_docs');
|
|
||||||
const translatedFiles = await globby(['**/*.md'], {
|
|
||||||
cwd: translatedDir,
|
|
||||||
});
|
|
||||||
await Promise.all(
|
|
||||||
translatedFiles.map(async source => {
|
|
||||||
/*
|
|
||||||
Do not process disabled & default languages folder in `translated_docs`
|
|
||||||
e.g: 'translated_docs/ja/**' should not be processed if lang 'ja' is disabled
|
|
||||||
*/
|
|
||||||
const translatedFilePath = path.resolve(translatedDir, source);
|
|
||||||
const detectedLangTag = getSubFolder(translatedFilePath, translatedDir);
|
|
||||||
if (
|
|
||||||
detectedLangTag === defaultLangTag ||
|
|
||||||
!enabledLangTags.includes(detectedLangTag)
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const metadata = await processMetadata(
|
|
||||||
source,
|
|
||||||
translatedDir,
|
|
||||||
env,
|
|
||||||
order,
|
|
||||||
siteConfig,
|
|
||||||
);
|
|
||||||
docsMetadatas[metadata.id] = metadata;
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Metadata for versioned docs.
|
|
||||||
if (versioningEnabled) {
|
|
||||||
const versionedDir = path.join(siteDir, 'versioned_docs');
|
|
||||||
const versionedFiles = await globby(['**/*.md'], {
|
|
||||||
cwd: versionedDir,
|
|
||||||
});
|
|
||||||
await Promise.all(
|
|
||||||
versionedFiles.map(async source => {
|
|
||||||
const metadata = await processMetadata(
|
|
||||||
source,
|
|
||||||
versionedDir,
|
|
||||||
env,
|
|
||||||
order,
|
|
||||||
siteConfig,
|
|
||||||
);
|
|
||||||
docsMetadatas[metadata.id] = metadata;
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the titles of the previous and next ids so that we can use them.
|
|
||||||
Object.keys(docsMetadatas).forEach(currentID => {
|
|
||||||
const previousID = idx(docsMetadatas, [currentID, 'previous']);
|
|
||||||
if (previousID) {
|
|
||||||
const previousTitle = idx(docsMetadatas, [previousID, 'title']);
|
|
||||||
docsMetadatas[currentID].previous_title = previousTitle || 'Previous';
|
|
||||||
}
|
|
||||||
const nextID = idx(docsMetadatas, [currentID, 'next']);
|
|
||||||
if (nextID) {
|
|
||||||
const nextTitle = idx(docsMetadatas, [nextID, 'title']);
|
|
||||||
docsMetadatas[currentID].next_title = nextTitle || 'Next';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
docsSidebars,
|
|
||||||
docsMetadatas,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = loadDocs;
|
|
|
@ -7,10 +7,11 @@
|
||||||
|
|
||||||
const ejs = require('ejs');
|
const ejs = require('ejs');
|
||||||
const fs = require('fs-extra');
|
const fs = require('fs-extra');
|
||||||
|
const _ = require('lodash');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
const {generate} = require('@docusaurus/utils');
|
const {generate} = require('@docusaurus/utils');
|
||||||
const loadConfig = require('./config');
|
const loadConfig = require('./config');
|
||||||
const loadDocs = require('./docs');
|
|
||||||
const loadEnv = require('./env');
|
const loadEnv = require('./env');
|
||||||
const loadTheme = require('./theme');
|
const loadTheme = require('./theme');
|
||||||
const loadRoutes = require('./routes');
|
const loadRoutes = require('./routes');
|
||||||
|
@ -40,43 +41,14 @@ module.exports = async function load(siteDir, cliOptions = {}) {
|
||||||
`export default ${JSON.stringify(env, null, 2)};`,
|
`export default ${JSON.stringify(env, null, 2)};`,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Docs
|
|
||||||
const docsDir = path.resolve(siteDir, '..', siteConfig.customDocsPath);
|
|
||||||
const {skipNextRelease} = cliOptions;
|
|
||||||
const {docsMetadatas, docsSidebars} = await loadDocs({
|
|
||||||
siteDir,
|
|
||||||
docsDir,
|
|
||||||
env,
|
|
||||||
siteConfig,
|
|
||||||
skipNextRelease,
|
|
||||||
});
|
|
||||||
await generate(
|
|
||||||
generatedFilesDir,
|
|
||||||
'docsMetadatas.js',
|
|
||||||
`export default ${JSON.stringify(docsMetadatas, null, 2)};`,
|
|
||||||
);
|
|
||||||
await generate(
|
|
||||||
generatedFilesDir,
|
|
||||||
'docsSidebars.js',
|
|
||||||
`export default ${JSON.stringify(docsSidebars, null, 2)};`,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Create source to metadata mapping.
|
|
||||||
const sourceToMetadata = {};
|
|
||||||
Object.values(docsMetadatas).forEach(
|
|
||||||
({source, version, permalink, language}) => {
|
|
||||||
sourceToMetadata[source] = {
|
|
||||||
version,
|
|
||||||
permalink,
|
|
||||||
language,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
// Process plugins.
|
// Process plugins.
|
||||||
const pluginConfigs = siteConfig.plugins || [];
|
const pluginConfigs = siteConfig.plugins || [];
|
||||||
const context = {env, siteDir, generatedFilesDir, siteConfig};
|
const context = {env, siteDir, generatedFilesDir, siteConfig, cliOptions};
|
||||||
const {plugins, pluginRouteConfigs} = await loadPlugins({
|
const {
|
||||||
|
plugins,
|
||||||
|
pluginsRouteConfigs,
|
||||||
|
pluginsLoadedContent,
|
||||||
|
} = await loadPlugins({
|
||||||
pluginConfigs,
|
pluginConfigs,
|
||||||
context,
|
context,
|
||||||
});
|
});
|
||||||
|
@ -91,12 +63,16 @@ module.exports = async function load(siteDir, cliOptions = {}) {
|
||||||
const versionedDir = path.join(siteDir, 'versioned_docs');
|
const versionedDir = path.join(siteDir, 'versioned_docs');
|
||||||
const translatedDir = path.join(siteDir, 'translated_docs');
|
const translatedDir = path.join(siteDir, 'translated_docs');
|
||||||
|
|
||||||
|
// TODO: Make doc dependents use the plugin's content instead
|
||||||
|
// of passing in via props.
|
||||||
|
const {
|
||||||
|
docsDir,
|
||||||
|
docs: docsMetadata,
|
||||||
|
sourceToMetadata,
|
||||||
|
} = pluginsLoadedContent[0].content;
|
||||||
|
|
||||||
// Generate React Router Config.
|
// Generate React Router Config.
|
||||||
const {routesConfig, routesPaths} = await loadRoutes({
|
const {routesConfig, routesPaths} = await loadRoutes(pluginsRouteConfigs);
|
||||||
siteConfig,
|
|
||||||
docsMetadatas,
|
|
||||||
pluginRouteConfigs,
|
|
||||||
});
|
|
||||||
await generate(generatedFilesDir, 'routes.js', routesConfig);
|
await generate(generatedFilesDir, 'routes.js', routesConfig);
|
||||||
|
|
||||||
// Generate contents metadata.
|
// Generate contents metadata.
|
||||||
|
@ -105,21 +81,20 @@ module.exports = async function load(siteDir, cliOptions = {}) {
|
||||||
'../core/templates/metadata.template.ejs',
|
'../core/templates/metadata.template.ejs',
|
||||||
);
|
);
|
||||||
const metadataTemplate = fs.readFileSync(metadataTemplateFile).toString();
|
const metadataTemplate = fs.readFileSync(metadataTemplateFile).toString();
|
||||||
|
const pluginMetadataImports = _.compact(pluginsLoadedContent).map(
|
||||||
|
({metadataKey, contentPath}) => ({
|
||||||
|
name: metadataKey,
|
||||||
|
path: contentPath,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
const metadataFile = ejs.render(metadataTemplate, {
|
const metadataFile = ejs.render(metadataTemplate, {
|
||||||
imports: [
|
imports: [
|
||||||
{
|
...pluginMetadataImports,
|
||||||
name: 'docsMetadatas',
|
|
||||||
path: '@generated/docsMetadatas',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: 'env',
|
name: 'env',
|
||||||
path: '@generated/env',
|
path: '@generated/env',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: 'docsSidebars',
|
|
||||||
path: '@generated/docsSidebars',
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
await generate(generatedFilesDir, 'metadata.js', metadataFile);
|
await generate(generatedFilesDir, 'metadata.js', metadataFile);
|
||||||
|
@ -128,8 +103,7 @@ module.exports = async function load(siteDir, cliOptions = {}) {
|
||||||
siteConfig,
|
siteConfig,
|
||||||
siteDir,
|
siteDir,
|
||||||
docsDir,
|
docsDir,
|
||||||
docsMetadatas,
|
docsMetadata,
|
||||||
docsSidebars,
|
|
||||||
env,
|
env,
|
||||||
outDir,
|
outDir,
|
||||||
themePath,
|
themePath,
|
||||||
|
|
|
@ -6,9 +6,11 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const fs = require('fs-extra');
|
const fs = require('fs-extra');
|
||||||
|
const path = require('path');
|
||||||
|
const {generate} = require('@docusaurus/utils');
|
||||||
|
|
||||||
module.exports = async function loadPlugins({pluginConfigs = [], context}) {
|
module.exports = async function loadPlugins({pluginConfigs = [], context}) {
|
||||||
/* 1. Plugin Lifecycle - Initializiation/Constructor */
|
// 1. Plugin Lifecycle - Initialization/Constructor
|
||||||
const plugins = pluginConfigs.map(({name, path: pluginPath, options}) => {
|
const plugins = pluginConfigs.map(({name, path: pluginPath, options}) => {
|
||||||
let Plugin;
|
let Plugin;
|
||||||
if (pluginPath && fs.existsSync(pluginPath)) {
|
if (pluginPath && fs.existsSync(pluginPath)) {
|
||||||
|
@ -25,46 +27,59 @@ module.exports = async function loadPlugins({pluginConfigs = [], context}) {
|
||||||
return new Plugin(options, context);
|
return new Plugin(options, context);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Do not allow plugin with duplicate name
|
// 2. Plugin lifecycle - loadContent
|
||||||
const pluginNames = new Set();
|
// Currently plugins run lifecycle in parallel and are not order-dependent. We could change
|
||||||
plugins.forEach(plugin => {
|
// this in future if there are plugins which need to run in certain order or depend on
|
||||||
const name = plugin.getName();
|
// others for data.
|
||||||
if (pluginNames.has(name)) {
|
const pluginsLoadedContent = await Promise.all(
|
||||||
throw new Error(`Duplicate plugin with name '${name}' found`);
|
|
||||||
}
|
|
||||||
pluginNames.add(name);
|
|
||||||
});
|
|
||||||
|
|
||||||
/* 2. Plugin lifecycle - LoadContent */
|
|
||||||
const pluginsLoadedContent = {};
|
|
||||||
await Promise.all(
|
|
||||||
plugins.map(async plugin => {
|
plugins.map(async plugin => {
|
||||||
if (!plugin.loadContent) {
|
if (!plugin.loadContent) {
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
const name = plugin.getName();
|
const name = plugin.getName();
|
||||||
pluginsLoadedContent[name] = await plugin.loadContent();
|
const {options} = plugin;
|
||||||
|
const {metadataKey, metadataFileName} = options;
|
||||||
|
const content = await plugin.loadContent();
|
||||||
|
const pluginContentPath = path.join(name, metadataFileName);
|
||||||
|
const pluginContentDir = path.join(context.generatedFilesDir, name);
|
||||||
|
fs.ensureDirSync(pluginContentDir);
|
||||||
|
await generate(
|
||||||
|
pluginContentDir,
|
||||||
|
metadataFileName,
|
||||||
|
JSON.stringify(content, null, 2),
|
||||||
|
);
|
||||||
|
const contentPath = path.join('@generated', pluginContentPath);
|
||||||
|
|
||||||
|
return {
|
||||||
|
metadataKey,
|
||||||
|
contentPath,
|
||||||
|
content,
|
||||||
|
};
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
/* 3. Plugin lifecycle - contentLoaded */
|
// 3. Plugin lifecycle - contentLoaded
|
||||||
const pluginRouteConfigs = [];
|
const pluginsRouteConfigs = [];
|
||||||
const actions = {
|
const actions = {
|
||||||
addRoute: config => pluginRouteConfigs.push(config),
|
addRoute: config => pluginsRouteConfigs.push(config),
|
||||||
};
|
};
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
plugins.map(async plugin => {
|
plugins.map(async (plugin, index) => {
|
||||||
if (!plugin.contentLoaded) {
|
if (!plugin.contentLoaded) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const name = plugin.getName();
|
const loadedContent = pluginsLoadedContent[index];
|
||||||
const content = pluginsLoadedContent[name];
|
await plugin.contentLoaded({
|
||||||
await plugin.contentLoaded({content, actions});
|
content: loadedContent.content,
|
||||||
|
actions,
|
||||||
|
});
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
plugins,
|
plugins,
|
||||||
pluginRouteConfigs,
|
pluginsRouteConfigs,
|
||||||
|
pluginsLoadedContent,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,19 +5,13 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const {normalizeUrl, generateChunkName} = require('@docusaurus/utils');
|
const {generateChunkName} = require('@docusaurus/utils');
|
||||||
|
|
||||||
async function loadRoutes({
|
async function loadRoutes(pluginsRouteConfigs) {
|
||||||
siteConfig = {},
|
|
||||||
docsMetadatas = {},
|
|
||||||
pluginRouteConfigs = [],
|
|
||||||
}) {
|
|
||||||
const imports = [
|
const imports = [
|
||||||
`import React from 'react';`,
|
`import React from 'react';`,
|
||||||
`import Loadable from 'react-loadable';`,
|
`import Loadable from 'react-loadable';`,
|
||||||
`import Loading from '@theme/Loading';`,
|
`import Loading from '@theme/Loading';`,
|
||||||
`import Doc from '@theme/Doc';`,
|
|
||||||
`import DocBody from '@theme/DocBody';`,
|
|
||||||
`import NotFound from '@theme/NotFound';`,
|
`import NotFound from '@theme/NotFound';`,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -28,50 +22,29 @@ async function loadRoutes({
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Docs.
|
|
||||||
const {docsUrl, baseUrl} = siteConfig;
|
|
||||||
function genDocsRoute(metadata) {
|
|
||||||
const {permalink, source} = metadata;
|
|
||||||
addRoutesPath(permalink);
|
|
||||||
return `
|
|
||||||
{
|
|
||||||
path: '${permalink}',
|
|
||||||
exact: true,
|
|
||||||
component: Loadable({
|
|
||||||
loader: () => import(/* webpackChunkName: '${generateChunkName(
|
|
||||||
permalink,
|
|
||||||
)}' */ '${source}'),
|
|
||||||
loading: Loading,
|
|
||||||
render(loaded, props) {
|
|
||||||
let Content = loaded.default;
|
|
||||||
return (
|
|
||||||
<DocBody {...props} metadata={${JSON.stringify(metadata)}}>
|
|
||||||
<Content />
|
|
||||||
</DocBody>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const rootDocsUrl = normalizeUrl([baseUrl, docsUrl]);
|
|
||||||
const docsRoutes = `
|
|
||||||
{
|
|
||||||
path: '${rootDocsUrl}',
|
|
||||||
component: Doc,
|
|
||||||
routes: [${Object.values(docsMetadatas)
|
|
||||||
.map(genDocsRoute)
|
|
||||||
.join(',')}],
|
|
||||||
}`;
|
|
||||||
|
|
||||||
const notFoundRoute = `
|
const notFoundRoute = `
|
||||||
{
|
{
|
||||||
path: '*',
|
path: '*',
|
||||||
component: NotFound,
|
component: NotFound,
|
||||||
}`;
|
}`;
|
||||||
|
|
||||||
const routes = pluginRouteConfigs.map(pluginRouteConfig => {
|
function generateRouteCode(pluginRouteConfig) {
|
||||||
const {path, component, metadata, modules} = pluginRouteConfig;
|
const {path, component, metadata, modules, routes} = pluginRouteConfig;
|
||||||
|
if (routes) {
|
||||||
|
return `
|
||||||
|
{
|
||||||
|
path: '${path}',
|
||||||
|
component: Loadable({
|
||||||
|
loader: () => import(/* webpackChunkName: '${generateChunkName(
|
||||||
|
component,
|
||||||
|
'component',
|
||||||
|
)}' */'${component}'),
|
||||||
|
loading: Loading,
|
||||||
|
}),
|
||||||
|
routes: [${routes.map(generateRouteCode).join(',')}],
|
||||||
|
}`;
|
||||||
|
}
|
||||||
|
|
||||||
addRoutesPath(path);
|
addRoutesPath(path);
|
||||||
return `
|
return `
|
||||||
{
|
{
|
||||||
|
@ -109,14 +82,14 @@ ${modules
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}`;
|
}`;
|
||||||
});
|
}
|
||||||
|
|
||||||
|
const routes = pluginsRouteConfigs.map(generateRouteCode);
|
||||||
|
|
||||||
const routesConfig = `
|
const routesConfig = `
|
||||||
${imports.join('\n')}
|
${imports.join('\n')}
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
// Docs.${docsRoutes},
|
|
||||||
|
|
||||||
// Plugins.${routes.join(',')},
|
// Plugins.${routes.join(',')},
|
||||||
|
|
||||||
// Not Found.${notFoundRoute},
|
// Not Found.${notFoundRoute},
|
||||||
|
|
|
@ -6,9 +6,9 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, {useContext} from 'react';
|
import React, {useContext} from 'react';
|
||||||
|
|
||||||
import Head from '@docusaurus/Head';
|
import Head from '@docusaurus/Head';
|
||||||
import Layout from '@theme/Layout'; // eslint-disable-line
|
import Layout from '@theme/Layout'; // eslint-disable-line
|
||||||
|
|
||||||
import DocusaurusContext from '@docusaurus/context';
|
import DocusaurusContext from '@docusaurus/context';
|
||||||
import Post from '../Post';
|
import Post from '../Post';
|
||||||
import styles from './styles.module.css';
|
import styles from './styles.module.css';
|
||||||
|
|
|
@ -13,17 +13,19 @@ import DocusaurusContext from '@docusaurus/context';
|
||||||
import styles from './styles.module.css';
|
import styles from './styles.module.css';
|
||||||
|
|
||||||
function DocBody(props) {
|
function DocBody(props) {
|
||||||
const {children, metadata} = props;
|
const {metadata, modules} = props;
|
||||||
const context = useContext(DocusaurusContext);
|
const context = useContext(DocusaurusContext);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
context.setContext({metadata});
|
context.setContext({metadata});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const DocContents = modules[0];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className={styles.docContent}>
|
<div className={styles.docContent}>
|
||||||
<h1>{metadata.title}</h1>
|
<h1>{metadata.title}</h1>
|
||||||
{children}
|
<DocContents />
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.paginatorContainer}>
|
<div className={styles.paginatorContainer}>
|
||||||
<DocsPaginator />
|
<DocsPaginator />
|
||||||
|
|
|
@ -14,18 +14,19 @@ import styles from './styles.module.css';
|
||||||
|
|
||||||
function DocsPaginator() {
|
function DocsPaginator() {
|
||||||
const context = useContext(DocusaurusContext);
|
const context = useContext(DocusaurusContext);
|
||||||
const {docsMetadatas, metadata} = context;
|
const {docsMetadata, metadata} = context;
|
||||||
if (!metadata) {
|
if (!metadata || !docsMetadata) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
const {docs} = docsMetadata;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.paginatorContainer}>
|
<div className={styles.paginatorContainer}>
|
||||||
<div>
|
<div>
|
||||||
{metadata.previous && docsMetadatas[metadata.previous] && (
|
{metadata.previous && docs[metadata.previous] && (
|
||||||
<Link
|
<Link
|
||||||
className={styles.paginatorLink}
|
className={styles.paginatorLink}
|
||||||
to={docsMetadatas[metadata.previous].permalink}>
|
to={docs[metadata.previous].permalink}>
|
||||||
<svg className={styles.arrow} viewBox="0 0 24 24">
|
<svg className={styles.arrow} viewBox="0 0 24 24">
|
||||||
<g>
|
<g>
|
||||||
<line x1="19" y1="12" x2="5" y2="12" />
|
<line x1="19" y1="12" x2="5" y2="12" />
|
||||||
|
@ -37,10 +38,10 @@ function DocsPaginator() {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.paginatorRightContainer}>
|
<div className={styles.paginatorRightContainer}>
|
||||||
{metadata.next && docsMetadatas[metadata.next] && (
|
{metadata.next && docs[metadata.next] && (
|
||||||
<Link
|
<Link
|
||||||
className={styles.paginatorLink}
|
className={styles.paginatorLink}
|
||||||
to={docsMetadatas[metadata.next].permalink}>
|
to={docs[metadata.next].permalink}>
|
||||||
<span className={styles.label}>{metadata.next_title}</span>{' '}
|
<span className={styles.label}>{metadata.next_title}</span>{' '}
|
||||||
<svg className={styles.arrow} viewBox="0 0 24 24">
|
<svg className={styles.arrow} viewBox="0 0 24 24">
|
||||||
<g>
|
<g>
|
||||||
|
|
|
@ -14,12 +14,7 @@ import styles from './styles.module.css';
|
||||||
|
|
||||||
function Navbar(props) {
|
function Navbar(props) {
|
||||||
const context = useContext(DocusaurusContext);
|
const context = useContext(DocusaurusContext);
|
||||||
const {
|
const {siteConfig = {}, env = {}, metadata = {}, docsMetadata} = context;
|
||||||
siteConfig = {},
|
|
||||||
env = {},
|
|
||||||
metadata = {},
|
|
||||||
docsMetadatas = {},
|
|
||||||
} = context;
|
|
||||||
const {
|
const {
|
||||||
baseUrl,
|
baseUrl,
|
||||||
headerLinks,
|
headerLinks,
|
||||||
|
@ -28,6 +23,7 @@ function Navbar(props) {
|
||||||
title,
|
title,
|
||||||
disableHeaderTitle,
|
disableHeaderTitle,
|
||||||
} = siteConfig;
|
} = siteConfig;
|
||||||
|
|
||||||
const {language: thisLanguage, version: thisVersion} = metadata;
|
const {language: thisLanguage, version: thisVersion} = metadata;
|
||||||
|
|
||||||
const translationEnabled = env.translation.enabled;
|
const translationEnabled = env.translation.enabled;
|
||||||
|
@ -56,8 +52,9 @@ function Navbar(props) {
|
||||||
? `version-${thisVersion || env.versioning.defaultVersion}-`
|
? `version-${thisVersion || env.versioning.defaultVersion}-`
|
||||||
: '';
|
: '';
|
||||||
const id = langPart + versionPart + link.doc;
|
const id = langPart + versionPart + link.doc;
|
||||||
if (!docsMetadatas[id]) {
|
const {docs} = docsMetadata;
|
||||||
const errorStr = `We could not find the doc wih id: ${id}. Please check your headerLinks correctly\n`;
|
if (!docs[id]) {
|
||||||
|
const errorStr = `We could not find the doc with id: ${id}. Please check your headerLinks correctly\n`;
|
||||||
throw new Error(errorStr);
|
throw new Error(errorStr);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
|
@ -65,7 +62,7 @@ function Navbar(props) {
|
||||||
<Link
|
<Link
|
||||||
activeClassName={styles.navLinkActive}
|
activeClassName={styles.navLinkActive}
|
||||||
className={styles.navLink}
|
className={styles.navLink}
|
||||||
to={docsMetadatas[id].permalink}>
|
to={docs[id].permalink}>
|
||||||
{link.label}
|
{link.label}
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
@ -15,14 +15,14 @@ import styles from './styles.module.css';
|
||||||
|
|
||||||
function Sidebar() {
|
function Sidebar() {
|
||||||
const context = useContext(DocusaurusContext);
|
const context = useContext(DocusaurusContext);
|
||||||
const {metadata = {}, docsSidebars, docsMetadatas} = context;
|
const {metadata = {}, docsMetadata} = context;
|
||||||
const {sidebar, language} = metadata;
|
const {sidebar, language} = metadata;
|
||||||
|
|
||||||
if (!sidebar) {
|
if (!sidebar) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const thisSidebar = docsSidebars[sidebar];
|
const thisSidebar = docsMetadata.docsSidebars[sidebar];
|
||||||
|
|
||||||
if (!thisSidebar) {
|
if (!thisSidebar) {
|
||||||
throw new Error(`Can not find ${sidebar} config`);
|
throw new Error(`Can not find ${sidebar} config`);
|
||||||
|
@ -30,7 +30,7 @@ function Sidebar() {
|
||||||
|
|
||||||
const convertDocLink = item => {
|
const convertDocLink = item => {
|
||||||
const linkID = (language ? `${language}-` : '') + item.id;
|
const linkID = (language ? `${language}-` : '') + item.id;
|
||||||
const linkMetadata = docsMetadatas[linkID];
|
const linkMetadata = docsMetadata.docs[linkID];
|
||||||
|
|
||||||
if (!linkMetadata) {
|
if (!linkMetadata) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
|
|
@ -19,6 +19,12 @@ module.exports = {
|
||||||
headerIcon: 'img/docusaurus.svg',
|
headerIcon: 'img/docusaurus.svg',
|
||||||
favicon: 'img/docusaurus.ico',
|
favicon: 'img/docusaurus.ico',
|
||||||
plugins: [
|
plugins: [
|
||||||
|
{
|
||||||
|
name: '@docusaurus/plugin-content-docs',
|
||||||
|
options: {
|
||||||
|
path: '../docs',
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: '@docusaurus/plugin-content-pages',
|
name: '@docusaurus/plugin-content-pages',
|
||||||
},
|
},
|
||||||
|
|
|
@ -19,6 +19,12 @@ module.exports = {
|
||||||
headerIcon: 'img/docusaurus.svg',
|
headerIcon: 'img/docusaurus.svg',
|
||||||
favicon: 'img/docusaurus.ico',
|
favicon: 'img/docusaurus.ico',
|
||||||
plugins: [
|
plugins: [
|
||||||
|
{
|
||||||
|
name: '@docusaurus/plugin-content-docs',
|
||||||
|
options: {
|
||||||
|
path: '../docs',
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: '@docusaurus/plugin-content-pages',
|
name: '@docusaurus/plugin-content-pages',
|
||||||
},
|
},
|
||||||
|
|
|
@ -20,6 +20,12 @@ module.exports = {
|
||||||
headerIcon: 'img/docusaurus.svg',
|
headerIcon: 'img/docusaurus.svg',
|
||||||
favicon: 'img/docusaurus.ico',
|
favicon: 'img/docusaurus.ico',
|
||||||
plugins: [
|
plugins: [
|
||||||
|
{
|
||||||
|
name: '@docusaurus/plugin-content-docs',
|
||||||
|
options: {
|
||||||
|
path: '../docs',
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: '@docusaurus/plugin-content-pages',
|
name: '@docusaurus/plugin-content-pages',
|
||||||
},
|
},
|
||||||
|
|
|
@ -20,6 +20,12 @@ module.exports = {
|
||||||
headerIcon: 'img/docusaurus.svg',
|
headerIcon: 'img/docusaurus.svg',
|
||||||
favicon: 'img/docusaurus.ico',
|
favicon: 'img/docusaurus.ico',
|
||||||
plugins: [
|
plugins: [
|
||||||
|
{
|
||||||
|
name: '@docusaurus/plugin-content-docs',
|
||||||
|
options: {
|
||||||
|
path: '../docs',
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: '@docusaurus/plugin-content-pages',
|
name: '@docusaurus/plugin-content-pages',
|
||||||
},
|
},
|
||||||
|
|
|
@ -19,6 +19,12 @@ module.exports = {
|
||||||
headerIcon: 'img/docusaurus.svg',
|
headerIcon: 'img/docusaurus.svg',
|
||||||
favicon: 'img/docusaurus.ico',
|
favicon: 'img/docusaurus.ico',
|
||||||
plugins: [
|
plugins: [
|
||||||
|
{
|
||||||
|
name: '@docusaurus/plugin-content-docs',
|
||||||
|
options: {
|
||||||
|
path: '../docs',
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: '@docusaurus/plugin-content-pages',
|
name: '@docusaurus/plugin-content-pages',
|
||||||
},
|
},
|
||||||
|
|
|
@ -16,8 +16,6 @@ describe('loadConfig', () => {
|
||||||
expect(config).toMatchInlineSnapshot(`
|
expect(config).toMatchInlineSnapshot(`
|
||||||
Object {
|
Object {
|
||||||
"baseUrl": "/",
|
"baseUrl": "/",
|
||||||
"customDocsPath": "docs",
|
|
||||||
"docsUrl": "docs",
|
|
||||||
"favicon": "img/docusaurus.ico",
|
"favicon": "img/docusaurus.ico",
|
||||||
"headerIcon": "img/docusaurus.svg",
|
"headerIcon": "img/docusaurus.svg",
|
||||||
"headerLinks": Array [
|
"headerLinks": Array [
|
||||||
|
@ -35,6 +33,12 @@ Object {
|
||||||
],
|
],
|
||||||
"organizationName": "endiliey",
|
"organizationName": "endiliey",
|
||||||
"plugins": Array [
|
"plugins": Array [
|
||||||
|
Object {
|
||||||
|
"name": "@docusaurus/plugin-content-docs",
|
||||||
|
"options": Object {
|
||||||
|
"path": "../docs",
|
||||||
|
},
|
||||||
|
},
|
||||||
Object {
|
Object {
|
||||||
"name": "@docusaurus/plugin-content-pages",
|
"name": "@docusaurus/plugin-content-pages",
|
||||||
},
|
},
|
||||||
|
|
|
@ -11,7 +11,6 @@ module.exports = {
|
||||||
organizationName: 'facebook',
|
organizationName: 'facebook',
|
||||||
projectName: 'docusaurus',
|
projectName: 'docusaurus',
|
||||||
baseUrl: '/',
|
baseUrl: '/',
|
||||||
customDocsPath: './docs',
|
|
||||||
url: 'https://docusaurus.io',
|
url: 'https://docusaurus.io',
|
||||||
headerLinks: [
|
headerLinks: [
|
||||||
{doc: 'installation', label: 'Docs'},
|
{doc: 'installation', label: 'Docs'},
|
||||||
|
@ -30,6 +29,12 @@ module.exports = {
|
||||||
algoliaOptions: {},
|
algoliaOptions: {},
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
|
{
|
||||||
|
name: '@docusaurus/plugin-content-docs',
|
||||||
|
options: {
|
||||||
|
path: '../docs',
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: '@docusaurus/plugin-content-blog',
|
name: '@docusaurus/plugin-content-blog',
|
||||||
options: {
|
options: {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue