mirror of
https://github.com/facebook/docusaurus.git
synced 2025-04-30 10:48:05 +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 {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) {
|
||||
return fileName
|
||||
.replace('-', '/')
|
||||
.replace('-', '/')
|
||||
.replace('-', '/')
|
||||
.replace(/\.md$/, '');
|
||||
.replace(/\.mdx?$/, '');
|
||||
}
|
||||
|
||||
const DEFAULT_OPTIONS = {
|
||||
|
@ -42,6 +43,10 @@ class DocusaurusPluginContentBlog {
|
|||
return 'docusaurus-plugin-content-blog';
|
||||
}
|
||||
|
||||
getPathsToWatch() {
|
||||
return [this.contentPath];
|
||||
}
|
||||
|
||||
// Fetches blog contents and returns metadata for the contents.
|
||||
async loadContent() {
|
||||
const {pageCount, include, routeBasePath} = this.options;
|
||||
|
@ -150,10 +155,6 @@ class DocusaurusPluginContentBlog {
|
|||
});
|
||||
});
|
||||
}
|
||||
|
||||
getPathsToWatch() {
|
||||
return [this.contentPath];
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = DocusaurusPluginContentBlog;
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@docusaurus/utils": "^1.0.0",
|
||||
"classnames": "^2.2.6",
|
||||
"fs-extra": "^7.0.1",
|
||||
"globby": "^9.1.0"
|
||||
},
|
||||
|
|
|
@ -7,15 +7,26 @@
|
|||
|
||||
import '@babel/polyfill';
|
||||
import path from 'path';
|
||||
import loadDocs from '@lib/load/docs';
|
||||
import loadSetup from '../../loadSetup';
|
||||
import loadSetup from '../../docusaurus/test/loadSetup';
|
||||
import DocusaurusPluginContentDocs from '../index';
|
||||
|
||||
describe('loadDocs', () => {
|
||||
test('simple website', async () => {
|
||||
const props = await loadSetup('simple');
|
||||
const {siteDir, docsDir, env, siteConfig} = props;
|
||||
const {docsMetadatas} = await loadDocs({siteDir, docsDir, env, siteConfig});
|
||||
expect(docsMetadatas.hello).toEqual({
|
||||
test.only('simple website', async () => {
|
||||
const {env, siteDir, siteConfig} = await loadSetup('simple');
|
||||
const plugin = new DocusaurusPluginContentDocs(
|
||||
{
|
||||
path: '../docs',
|
||||
},
|
||||
{
|
||||
env,
|
||||
siteDir,
|
||||
siteConfig,
|
||||
},
|
||||
);
|
||||
const {docs: docsMetadata} = await plugin.loadContent();
|
||||
const docsDir = plugin.contentPath;
|
||||
|
||||
expect(docsMetadata.hello).toEqual({
|
||||
category: 'Guides',
|
||||
id: 'hello',
|
||||
language: null,
|
||||
|
@ -29,7 +40,7 @@ describe('loadDocs', () => {
|
|||
title: 'Hello, World !',
|
||||
version: null,
|
||||
});
|
||||
expect(docsMetadatas['foo/bar']).toEqual({
|
||||
expect(docsMetadata['foo/bar']).toEqual({
|
||||
category: 'Test',
|
||||
id: 'foo/bar',
|
||||
language: null,
|
||||
|
@ -46,10 +57,23 @@ describe('loadDocs', () => {
|
|||
});
|
||||
|
||||
test('versioned website', async () => {
|
||||
const props = await loadSetup('versioned');
|
||||
const {siteDir, docsDir, versionedDir, env, siteConfig} = props;
|
||||
const {docsMetadatas} = await loadDocs({siteDir, docsDir, env, siteConfig});
|
||||
expect(docsMetadatas['version-1.0.0-foo/bar']).toEqual({
|
||||
const {env, siteDir, siteConfig, versionedDir} = await loadSetup(
|
||||
'versioned',
|
||||
);
|
||||
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',
|
||||
id: 'version-1.0.0-foo/bar',
|
||||
language: null,
|
||||
|
@ -63,7 +87,7 @@ describe('loadDocs', () => {
|
|||
title: 'Bar',
|
||||
version: '1.0.0',
|
||||
});
|
||||
expect(docsMetadatas['foo/bar']).toEqual({
|
||||
expect(docsMetadata['foo/bar']).toEqual({
|
||||
category: 'Test',
|
||||
id: 'foo/bar',
|
||||
language: null,
|
||||
|
@ -80,17 +104,27 @@ describe('loadDocs', () => {
|
|||
});
|
||||
|
||||
test('versioned & translated website', async () => {
|
||||
const props = await loadSetup('transversioned');
|
||||
const {
|
||||
siteDir,
|
||||
docsDir,
|
||||
env,
|
||||
siteDir,
|
||||
siteConfig,
|
||||
translatedDir,
|
||||
versionedDir,
|
||||
siteConfig,
|
||||
} = props;
|
||||
const {docsMetadatas} = await loadDocs({siteDir, docsDir, env, siteConfig});
|
||||
expect(docsMetadatas['ko-version-1.0.0-foo/bar']).toEqual({
|
||||
} = await loadSetup('transversioned');
|
||||
const plugin = new DocusaurusPluginContentDocs(
|
||||
{
|
||||
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',
|
||||
id: 'ko-version-1.0.0-foo/bar',
|
||||
language: 'ko',
|
||||
|
@ -104,7 +138,7 @@ describe('loadDocs', () => {
|
|||
title: 'Bar',
|
||||
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',
|
||||
id: 'en-version-1.0.0-foo/baz',
|
||||
language: 'en',
|
||||
|
@ -121,7 +155,7 @@ describe('loadDocs', () => {
|
|||
title: 'Baz',
|
||||
version: '1.0.0',
|
||||
});
|
||||
expect(docsMetadatas['en-hello']).toEqual({
|
||||
expect(docsMetadata['en-hello']).toEqual({
|
||||
category: 'Guides',
|
||||
id: 'en-hello',
|
||||
language: 'en',
|
||||
|
@ -138,10 +172,23 @@ describe('loadDocs', () => {
|
|||
});
|
||||
|
||||
test('translated website', async () => {
|
||||
const props = await loadSetup('translated');
|
||||
const {siteDir, translatedDir, docsDir, env, siteConfig} = props;
|
||||
const {docsMetadatas} = await loadDocs({siteDir, docsDir, env, siteConfig});
|
||||
expect(docsMetadatas['ko-foo/baz']).toEqual({
|
||||
const {env, siteDir, siteConfig, translatedDir} = await loadSetup(
|
||||
'translated',
|
||||
);
|
||||
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',
|
||||
id: 'ko-foo/baz',
|
||||
language: 'ko',
|
||||
|
@ -158,7 +205,7 @@ describe('loadDocs', () => {
|
|||
title: 'baz',
|
||||
version: null,
|
||||
});
|
||||
expect(docsMetadatas['en-foo/bar']).toEqual({
|
||||
expect(docsMetadata['en-foo/bar']).toEqual({
|
||||
category: 'Test',
|
||||
id: 'en-foo/bar',
|
||||
language: 'en',
|
||||
|
@ -175,16 +222,23 @@ describe('loadDocs', () => {
|
|||
});
|
||||
|
||||
test('versioned website with skip next release', async () => {
|
||||
const props = await loadSetup('versioned');
|
||||
const {siteDir, docsDir, versionedDir, env, siteConfig} = props;
|
||||
const {docsMetadatas} = await loadDocs({
|
||||
siteDir,
|
||||
docsDir,
|
||||
env,
|
||||
siteConfig,
|
||||
skipNextRelease: true,
|
||||
});
|
||||
expect(docsMetadatas['version-1.0.0-foo/bar']).toEqual({
|
||||
const {env, siteDir, siteConfig, versionedDir} = await loadSetup(
|
||||
'versioned',
|
||||
);
|
||||
const plugin = new DocusaurusPluginContentDocs(
|
||||
{
|
||||
path: '../docs',
|
||||
},
|
||||
{
|
||||
env,
|
||||
siteDir,
|
||||
siteConfig,
|
||||
cliOptions: {skipNextRelease: true},
|
||||
},
|
||||
);
|
||||
const {docs: docsMetadata} = await plugin.loadContent();
|
||||
|
||||
expect(docsMetadata['version-1.0.0-foo/bar']).toEqual({
|
||||
category: 'Test',
|
||||
id: 'version-1.0.0-foo/bar',
|
||||
language: null,
|
||||
|
@ -198,6 +252,6 @@ describe('loadDocs', () => {
|
|||
title: 'Bar',
|
||||
version: '1.0.0',
|
||||
});
|
||||
expect(docsMetadatas['foo/bar']).toBeUndefined();
|
||||
expect(docsMetadata['foo/bar']).toBeUndefined();
|
||||
});
|
||||
});
|
|
@ -7,8 +7,8 @@
|
|||
|
||||
import '@babel/polyfill';
|
||||
import path from 'path';
|
||||
import processMetadata from '@lib/load/docs/metadata';
|
||||
import loadSetup from '../../loadSetup';
|
||||
import processMetadata from '../src/metadata';
|
||||
import loadSetup from '../../docusaurus/test/loadSetup';
|
||||
|
||||
describe('processMetadata', () => {
|
||||
test('normal docs', async () => {
|
||||
|
@ -16,8 +16,22 @@ describe('processMetadata', () => {
|
|||
const {docsDir, env, siteConfig} = props;
|
||||
const sourceA = path.join('foo', 'bar.md');
|
||||
const sourceB = path.join('hello.md');
|
||||
const dataA = await processMetadata(sourceA, docsDir, env, {}, siteConfig);
|
||||
const dataB = await processMetadata(sourceB, docsDir, env, {}, siteConfig);
|
||||
const dataA = await processMetadata(
|
||||
sourceA,
|
||||
docsDir,
|
||||
env,
|
||||
{},
|
||||
siteConfig,
|
||||
'docs',
|
||||
);
|
||||
const dataB = await processMetadata(
|
||||
sourceB,
|
||||
docsDir,
|
||||
env,
|
||||
{},
|
||||
siteConfig,
|
||||
'docs',
|
||||
);
|
||||
expect(dataA).toEqual({
|
||||
id: 'foo/bar',
|
||||
language: null,
|
||||
|
@ -42,7 +56,14 @@ describe('processMetadata', () => {
|
|||
const props = await loadSetup('simple');
|
||||
const {docsDir, env, siteConfig} = props;
|
||||
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({
|
||||
id: 'permalink',
|
||||
language: null,
|
||||
|
@ -68,6 +89,7 @@ describe('processMetadata', () => {
|
|||
env,
|
||||
{},
|
||||
siteConfig,
|
||||
'docs',
|
||||
);
|
||||
const dataB = await processMetadata(
|
||||
sourceB,
|
||||
|
@ -75,9 +97,24 @@ describe('processMetadata', () => {
|
|||
env,
|
||||
{},
|
||||
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({
|
||||
id: 'version-1.0.0-foo/bar',
|
||||
language: null,
|
||||
|
@ -133,6 +170,7 @@ describe('processMetadata', () => {
|
|||
env,
|
||||
{},
|
||||
siteConfig,
|
||||
'docs',
|
||||
);
|
||||
const dataB = await processMetadata(
|
||||
sourceB,
|
||||
|
@ -140,6 +178,7 @@ describe('processMetadata', () => {
|
|||
env,
|
||||
{},
|
||||
siteConfig,
|
||||
'docs',
|
||||
);
|
||||
const dataC = await processMetadata(
|
||||
sourceC,
|
||||
|
@ -147,6 +186,7 @@ describe('processMetadata', () => {
|
|||
env,
|
||||
{},
|
||||
siteConfig,
|
||||
'docs',
|
||||
);
|
||||
const dataD = await processMetadata(
|
||||
sourceD,
|
||||
|
@ -154,15 +194,31 @@ describe('processMetadata', () => {
|
|||
env,
|
||||
{},
|
||||
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(
|
||||
sourceG,
|
||||
versionedDir,
|
||||
env,
|
||||
{},
|
||||
siteConfig,
|
||||
'docs',
|
||||
);
|
||||
const dataH = await processMetadata(
|
||||
sourceH,
|
||||
|
@ -170,6 +226,7 @@ describe('processMetadata', () => {
|
|||
env,
|
||||
{},
|
||||
siteConfig,
|
||||
'docs',
|
||||
);
|
||||
expect(dataA).toEqual({
|
||||
id: 'ko-version-1.0.0-foo/bar',
|
||||
|
@ -258,6 +315,7 @@ describe('processMetadata', () => {
|
|||
env,
|
||||
{},
|
||||
siteConfig,
|
||||
'docs',
|
||||
);
|
||||
const dataB = await processMetadata(
|
||||
sourceB,
|
||||
|
@ -265,9 +323,24 @@ describe('processMetadata', () => {
|
|||
env,
|
||||
{},
|
||||
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({
|
||||
id: 'ko-foo/bar',
|
||||
language: 'ko',
|
|
@ -5,7 +5,7 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import createOrder from '@lib/load/docs/order';
|
||||
import createOrder from '../src/order';
|
||||
|
||||
describe('createOrder', () => {
|
||||
test('multiple sidebars with subcategory', () => {
|
|
@ -6,8 +6,8 @@
|
|||
*/
|
||||
|
||||
import path from 'path';
|
||||
import loadSidebars from '@lib/load/docs/sidebars';
|
||||
import loadSetup from '../../loadSetup';
|
||||
import loadSidebars from '../src/sidebars';
|
||||
import loadSetup from '../../docusaurus/test/loadSetup';
|
||||
|
||||
describe('loadSidebars', () => {
|
||||
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,
|
||||
order,
|
||||
siteConfig,
|
||||
docsBasePath,
|
||||
) {
|
||||
const filepath = path.resolve(refDir, source);
|
||||
const fileString = await fs.readFile(filepath, 'utf-8');
|
||||
|
@ -122,7 +123,7 @@ module.exports = async function processMetadata(
|
|||
metadata.source = path.join(refDir, source);
|
||||
|
||||
// Build the permalink.
|
||||
const {baseUrl, docsUrl} = siteConfig;
|
||||
const {baseUrl} = siteConfig;
|
||||
|
||||
// If user has own custom permalink defined in frontmatter
|
||||
// e.g: :baseUrl:docsUrl/:langPart/:versionPart/endiliey/:id
|
||||
|
@ -130,7 +131,7 @@ module.exports = async function processMetadata(
|
|||
metadata.permalink = path.resolve(
|
||||
metadata.permalink
|
||||
.replace(/:baseUrl/, baseUrl)
|
||||
.replace(/:docsUrl/, docsUrl)
|
||||
.replace(/:docsUrl/, docsBasePath)
|
||||
.replace(/:langPart/, langPart)
|
||||
.replace(/:versionPart/, versionPart)
|
||||
.replace(/:id/, metadata.id),
|
||||
|
@ -138,7 +139,7 @@ module.exports = async function processMetadata(
|
|||
} else {
|
||||
metadata.permalink = normalizeUrl([
|
||||
baseUrl,
|
||||
docsUrl,
|
||||
docsBasePath,
|
||||
langPart,
|
||||
versionPart,
|
||||
metadata.id,
|
|
@ -7,14 +7,12 @@
|
|||
|
||||
const globby = require('globby');
|
||||
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 DEFAULT_OPTIONS = {
|
||||
metadataKey: 'pagesMetadata',
|
||||
metadataFileName: 'pagesMetadata.json',
|
||||
path: 'pages', // Path to data on filesystem.
|
||||
path: 'pages', // Path to data on filesystem, relative to site dir.
|
||||
routeBasePath: '', // URL Route.
|
||||
include: ['**/*.{js,jsx}'], // Extensions to include.
|
||||
component: '@theme/Pages',
|
||||
|
@ -31,6 +29,10 @@ class DocusaurusPluginContentPages {
|
|||
return 'docusaurus-plugin-content-pages';
|
||||
}
|
||||
|
||||
getPathsToWatch() {
|
||||
return [this.contentPath];
|
||||
}
|
||||
|
||||
async loadContent() {
|
||||
const {include} = this.options;
|
||||
const {env, siteConfig} = this.context;
|
||||
|
@ -102,10 +104,6 @@ class DocusaurusPluginContentPages {
|
|||
});
|
||||
});
|
||||
}
|
||||
|
||||
getPathsToWatch() {
|
||||
return [this.contentPath];
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = DocusaurusPluginContentPages;
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
# Breaking Changes
|
||||
|
||||
### `siteConfig.js` changes
|
||||
|
||||
- `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 docsRelativeDir = props.siteConfig.customDocsPath;
|
||||
const pluginPaths = _.compact(
|
||||
_.flatten(
|
||||
plugins.map(
|
||||
|
@ -55,12 +54,7 @@ module.exports = async function start(siteDir, cliOptions = {}) {
|
|||
),
|
||||
);
|
||||
const fsWatcher = chokidar.watch(
|
||||
[
|
||||
...pluginPaths,
|
||||
`../${docsRelativeDir}/**/*.md`,
|
||||
loadConfig.configFileName,
|
||||
'sidebars.json',
|
||||
],
|
||||
[...pluginPaths, loadConfig.configFileName, 'sidebars.json'],
|
||||
{
|
||||
cwd: siteDir,
|
||||
ignoreInitial: true,
|
||||
|
|
|
@ -25,11 +25,9 @@ const REQUIRED_FIELDS = [
|
|||
|
||||
const OPTIONAL_FIELDS = [
|
||||
'algolia',
|
||||
'customDocsPath',
|
||||
'customFields',
|
||||
'defaultLanguage',
|
||||
'disableHeaderTitle',
|
||||
'docsUrl',
|
||||
'githubHost',
|
||||
'highlight',
|
||||
'markdownPlugins',
|
||||
|
@ -37,8 +35,7 @@ const OPTIONAL_FIELDS = [
|
|||
];
|
||||
|
||||
const DEFAULT_CONFIG = {
|
||||
customDocsPath: 'docs',
|
||||
docsUrl: 'docs',
|
||||
plugins: [],
|
||||
};
|
||||
|
||||
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 fs = require('fs-extra');
|
||||
const _ = require('lodash');
|
||||
const path = require('path');
|
||||
|
||||
const {generate} = require('@docusaurus/utils');
|
||||
const loadConfig = require('./config');
|
||||
const loadDocs = require('./docs');
|
||||
const loadEnv = require('./env');
|
||||
const loadTheme = require('./theme');
|
||||
const loadRoutes = require('./routes');
|
||||
|
@ -40,43 +41,14 @@ module.exports = async function load(siteDir, cliOptions = {}) {
|
|||
`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.
|
||||
const pluginConfigs = siteConfig.plugins || [];
|
||||
const context = {env, siteDir, generatedFilesDir, siteConfig};
|
||||
const {plugins, pluginRouteConfigs} = await loadPlugins({
|
||||
const context = {env, siteDir, generatedFilesDir, siteConfig, cliOptions};
|
||||
const {
|
||||
plugins,
|
||||
pluginsRouteConfigs,
|
||||
pluginsLoadedContent,
|
||||
} = await loadPlugins({
|
||||
pluginConfigs,
|
||||
context,
|
||||
});
|
||||
|
@ -91,12 +63,16 @@ module.exports = async function load(siteDir, cliOptions = {}) {
|
|||
const versionedDir = path.join(siteDir, 'versioned_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.
|
||||
const {routesConfig, routesPaths} = await loadRoutes({
|
||||
siteConfig,
|
||||
docsMetadatas,
|
||||
pluginRouteConfigs,
|
||||
});
|
||||
const {routesConfig, routesPaths} = await loadRoutes(pluginsRouteConfigs);
|
||||
await generate(generatedFilesDir, 'routes.js', routesConfig);
|
||||
|
||||
// Generate contents metadata.
|
||||
|
@ -105,21 +81,20 @@ module.exports = async function load(siteDir, cliOptions = {}) {
|
|||
'../core/templates/metadata.template.ejs',
|
||||
);
|
||||
const metadataTemplate = fs.readFileSync(metadataTemplateFile).toString();
|
||||
const pluginMetadataImports = _.compact(pluginsLoadedContent).map(
|
||||
({metadataKey, contentPath}) => ({
|
||||
name: metadataKey,
|
||||
path: contentPath,
|
||||
}),
|
||||
);
|
||||
|
||||
const metadataFile = ejs.render(metadataTemplate, {
|
||||
imports: [
|
||||
{
|
||||
name: 'docsMetadatas',
|
||||
path: '@generated/docsMetadatas',
|
||||
},
|
||||
...pluginMetadataImports,
|
||||
{
|
||||
name: 'env',
|
||||
path: '@generated/env',
|
||||
},
|
||||
{
|
||||
name: 'docsSidebars',
|
||||
path: '@generated/docsSidebars',
|
||||
},
|
||||
],
|
||||
});
|
||||
await generate(generatedFilesDir, 'metadata.js', metadataFile);
|
||||
|
@ -128,8 +103,7 @@ module.exports = async function load(siteDir, cliOptions = {}) {
|
|||
siteConfig,
|
||||
siteDir,
|
||||
docsDir,
|
||||
docsMetadatas,
|
||||
docsSidebars,
|
||||
docsMetadata,
|
||||
env,
|
||||
outDir,
|
||||
themePath,
|
||||
|
|
|
@ -6,9 +6,11 @@
|
|||
*/
|
||||
|
||||
const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
const {generate} = require('@docusaurus/utils');
|
||||
|
||||
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}) => {
|
||||
let Plugin;
|
||||
if (pluginPath && fs.existsSync(pluginPath)) {
|
||||
|
@ -25,46 +27,59 @@ module.exports = async function loadPlugins({pluginConfigs = [], context}) {
|
|||
return new Plugin(options, context);
|
||||
});
|
||||
|
||||
// Do not allow plugin with duplicate name
|
||||
const pluginNames = new Set();
|
||||
plugins.forEach(plugin => {
|
||||
const name = plugin.getName();
|
||||
if (pluginNames.has(name)) {
|
||||
throw new Error(`Duplicate plugin with name '${name}' found`);
|
||||
}
|
||||
pluginNames.add(name);
|
||||
});
|
||||
|
||||
/* 2. Plugin lifecycle - LoadContent */
|
||||
const pluginsLoadedContent = {};
|
||||
await Promise.all(
|
||||
// 2. Plugin lifecycle - loadContent
|
||||
// Currently plugins run lifecycle in parallel and are not order-dependent. We could change
|
||||
// this in future if there are plugins which need to run in certain order or depend on
|
||||
// others for data.
|
||||
const pluginsLoadedContent = await Promise.all(
|
||||
plugins.map(async plugin => {
|
||||
if (!plugin.loadContent) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
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 */
|
||||
const pluginRouteConfigs = [];
|
||||
// 3. Plugin lifecycle - contentLoaded
|
||||
const pluginsRouteConfigs = [];
|
||||
const actions = {
|
||||
addRoute: config => pluginRouteConfigs.push(config),
|
||||
addRoute: config => pluginsRouteConfigs.push(config),
|
||||
};
|
||||
|
||||
await Promise.all(
|
||||
plugins.map(async plugin => {
|
||||
plugins.map(async (plugin, index) => {
|
||||
if (!plugin.contentLoaded) {
|
||||
return;
|
||||
}
|
||||
const name = plugin.getName();
|
||||
const content = pluginsLoadedContent[name];
|
||||
await plugin.contentLoaded({content, actions});
|
||||
const loadedContent = pluginsLoadedContent[index];
|
||||
await plugin.contentLoaded({
|
||||
content: loadedContent.content,
|
||||
actions,
|
||||
});
|
||||
}),
|
||||
);
|
||||
|
||||
return {
|
||||
plugins,
|
||||
pluginRouteConfigs,
|
||||
pluginsRouteConfigs,
|
||||
pluginsLoadedContent,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -5,19 +5,13 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
const {normalizeUrl, generateChunkName} = require('@docusaurus/utils');
|
||||
const {generateChunkName} = require('@docusaurus/utils');
|
||||
|
||||
async function loadRoutes({
|
||||
siteConfig = {},
|
||||
docsMetadatas = {},
|
||||
pluginRouteConfigs = [],
|
||||
}) {
|
||||
async function loadRoutes(pluginsRouteConfigs) {
|
||||
const imports = [
|
||||
`import React from 'react';`,
|
||||
`import Loadable from 'react-loadable';`,
|
||||
`import Loading from '@theme/Loading';`,
|
||||
`import Doc from '@theme/Doc';`,
|
||||
`import DocBody from '@theme/DocBody';`,
|
||||
`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 = `
|
||||
{
|
||||
path: '*',
|
||||
component: NotFound,
|
||||
}`;
|
||||
|
||||
const routes = pluginRouteConfigs.map(pluginRouteConfig => {
|
||||
const {path, component, metadata, modules} = pluginRouteConfig;
|
||||
function generateRouteCode(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);
|
||||
return `
|
||||
{
|
||||
|
@ -109,14 +82,14 @@ ${modules
|
|||
}
|
||||
})
|
||||
}`;
|
||||
});
|
||||
}
|
||||
|
||||
const routes = pluginsRouteConfigs.map(generateRouteCode);
|
||||
|
||||
const routesConfig = `
|
||||
${imports.join('\n')}
|
||||
|
||||
const routes = [
|
||||
// Docs.${docsRoutes},
|
||||
|
||||
// Plugins.${routes.join(',')},
|
||||
|
||||
// Not Found.${notFoundRoute},
|
||||
|
|
|
@ -6,9 +6,9 @@
|
|||
*/
|
||||
|
||||
import React, {useContext} from 'react';
|
||||
|
||||
import Head from '@docusaurus/Head';
|
||||
import Layout from '@theme/Layout'; // eslint-disable-line
|
||||
|
||||
import DocusaurusContext from '@docusaurus/context';
|
||||
import Post from '../Post';
|
||||
import styles from './styles.module.css';
|
||||
|
|
|
@ -13,17 +13,19 @@ import DocusaurusContext from '@docusaurus/context';
|
|||
import styles from './styles.module.css';
|
||||
|
||||
function DocBody(props) {
|
||||
const {children, metadata} = props;
|
||||
const {metadata, modules} = props;
|
||||
const context = useContext(DocusaurusContext);
|
||||
useEffect(() => {
|
||||
context.setContext({metadata});
|
||||
}, []);
|
||||
|
||||
const DocContents = modules[0];
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={styles.docContent}>
|
||||
<h1>{metadata.title}</h1>
|
||||
{children}
|
||||
<DocContents />
|
||||
</div>
|
||||
<div className={styles.paginatorContainer}>
|
||||
<DocsPaginator />
|
||||
|
|
|
@ -14,18 +14,19 @@ import styles from './styles.module.css';
|
|||
|
||||
function DocsPaginator() {
|
||||
const context = useContext(DocusaurusContext);
|
||||
const {docsMetadatas, metadata} = context;
|
||||
if (!metadata) {
|
||||
const {docsMetadata, metadata} = context;
|
||||
if (!metadata || !docsMetadata) {
|
||||
return null;
|
||||
}
|
||||
const {docs} = docsMetadata;
|
||||
|
||||
return (
|
||||
<div className={styles.paginatorContainer}>
|
||||
<div>
|
||||
{metadata.previous && docsMetadatas[metadata.previous] && (
|
||||
{metadata.previous && docs[metadata.previous] && (
|
||||
<Link
|
||||
className={styles.paginatorLink}
|
||||
to={docsMetadatas[metadata.previous].permalink}>
|
||||
to={docs[metadata.previous].permalink}>
|
||||
<svg className={styles.arrow} viewBox="0 0 24 24">
|
||||
<g>
|
||||
<line x1="19" y1="12" x2="5" y2="12" />
|
||||
|
@ -37,10 +38,10 @@ function DocsPaginator() {
|
|||
)}
|
||||
</div>
|
||||
<div className={styles.paginatorRightContainer}>
|
||||
{metadata.next && docsMetadatas[metadata.next] && (
|
||||
{metadata.next && docs[metadata.next] && (
|
||||
<Link
|
||||
className={styles.paginatorLink}
|
||||
to={docsMetadatas[metadata.next].permalink}>
|
||||
to={docs[metadata.next].permalink}>
|
||||
<span className={styles.label}>{metadata.next_title}</span>{' '}
|
||||
<svg className={styles.arrow} viewBox="0 0 24 24">
|
||||
<g>
|
||||
|
|
|
@ -14,12 +14,7 @@ import styles from './styles.module.css';
|
|||
|
||||
function Navbar(props) {
|
||||
const context = useContext(DocusaurusContext);
|
||||
const {
|
||||
siteConfig = {},
|
||||
env = {},
|
||||
metadata = {},
|
||||
docsMetadatas = {},
|
||||
} = context;
|
||||
const {siteConfig = {}, env = {}, metadata = {}, docsMetadata} = context;
|
||||
const {
|
||||
baseUrl,
|
||||
headerLinks,
|
||||
|
@ -28,6 +23,7 @@ function Navbar(props) {
|
|||
title,
|
||||
disableHeaderTitle,
|
||||
} = siteConfig;
|
||||
|
||||
const {language: thisLanguage, version: thisVersion} = metadata;
|
||||
|
||||
const translationEnabled = env.translation.enabled;
|
||||
|
@ -56,8 +52,9 @@ function Navbar(props) {
|
|||
? `version-${thisVersion || env.versioning.defaultVersion}-`
|
||||
: '';
|
||||
const id = langPart + versionPart + link.doc;
|
||||
if (!docsMetadatas[id]) {
|
||||
const errorStr = `We could not find the doc wih id: ${id}. Please check your headerLinks correctly\n`;
|
||||
const {docs} = docsMetadata;
|
||||
if (!docs[id]) {
|
||||
const errorStr = `We could not find the doc with id: ${id}. Please check your headerLinks correctly\n`;
|
||||
throw new Error(errorStr);
|
||||
}
|
||||
return (
|
||||
|
@ -65,7 +62,7 @@ function Navbar(props) {
|
|||
<Link
|
||||
activeClassName={styles.navLinkActive}
|
||||
className={styles.navLink}
|
||||
to={docsMetadatas[id].permalink}>
|
||||
to={docs[id].permalink}>
|
||||
{link.label}
|
||||
</Link>
|
||||
</li>
|
||||
|
|
|
@ -15,14 +15,14 @@ import styles from './styles.module.css';
|
|||
|
||||
function Sidebar() {
|
||||
const context = useContext(DocusaurusContext);
|
||||
const {metadata = {}, docsSidebars, docsMetadatas} = context;
|
||||
const {metadata = {}, docsMetadata} = context;
|
||||
const {sidebar, language} = metadata;
|
||||
|
||||
if (!sidebar) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const thisSidebar = docsSidebars[sidebar];
|
||||
const thisSidebar = docsMetadata.docsSidebars[sidebar];
|
||||
|
||||
if (!thisSidebar) {
|
||||
throw new Error(`Can not find ${sidebar} config`);
|
||||
|
@ -30,7 +30,7 @@ function Sidebar() {
|
|||
|
||||
const convertDocLink = item => {
|
||||
const linkID = (language ? `${language}-` : '') + item.id;
|
||||
const linkMetadata = docsMetadatas[linkID];
|
||||
const linkMetadata = docsMetadata.docs[linkID];
|
||||
|
||||
if (!linkMetadata) {
|
||||
throw new Error(
|
||||
|
|
|
@ -19,6 +19,12 @@ module.exports = {
|
|||
headerIcon: 'img/docusaurus.svg',
|
||||
favicon: 'img/docusaurus.ico',
|
||||
plugins: [
|
||||
{
|
||||
name: '@docusaurus/plugin-content-docs',
|
||||
options: {
|
||||
path: '../docs',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: '@docusaurus/plugin-content-pages',
|
||||
},
|
||||
|
|
|
@ -19,6 +19,12 @@ module.exports = {
|
|||
headerIcon: 'img/docusaurus.svg',
|
||||
favicon: 'img/docusaurus.ico',
|
||||
plugins: [
|
||||
{
|
||||
name: '@docusaurus/plugin-content-docs',
|
||||
options: {
|
||||
path: '../docs',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: '@docusaurus/plugin-content-pages',
|
||||
},
|
||||
|
|
|
@ -20,6 +20,12 @@ module.exports = {
|
|||
headerIcon: 'img/docusaurus.svg',
|
||||
favicon: 'img/docusaurus.ico',
|
||||
plugins: [
|
||||
{
|
||||
name: '@docusaurus/plugin-content-docs',
|
||||
options: {
|
||||
path: '../docs',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: '@docusaurus/plugin-content-pages',
|
||||
},
|
||||
|
|
|
@ -20,6 +20,12 @@ module.exports = {
|
|||
headerIcon: 'img/docusaurus.svg',
|
||||
favicon: 'img/docusaurus.ico',
|
||||
plugins: [
|
||||
{
|
||||
name: '@docusaurus/plugin-content-docs',
|
||||
options: {
|
||||
path: '../docs',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: '@docusaurus/plugin-content-pages',
|
||||
},
|
||||
|
|
|
@ -19,6 +19,12 @@ module.exports = {
|
|||
headerIcon: 'img/docusaurus.svg',
|
||||
favicon: 'img/docusaurus.ico',
|
||||
plugins: [
|
||||
{
|
||||
name: '@docusaurus/plugin-content-docs',
|
||||
options: {
|
||||
path: '../docs',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: '@docusaurus/plugin-content-pages',
|
||||
},
|
||||
|
|
|
@ -16,8 +16,6 @@ describe('loadConfig', () => {
|
|||
expect(config).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"baseUrl": "/",
|
||||
"customDocsPath": "docs",
|
||||
"docsUrl": "docs",
|
||||
"favicon": "img/docusaurus.ico",
|
||||
"headerIcon": "img/docusaurus.svg",
|
||||
"headerLinks": Array [
|
||||
|
@ -35,6 +33,12 @@ Object {
|
|||
],
|
||||
"organizationName": "endiliey",
|
||||
"plugins": Array [
|
||||
Object {
|
||||
"name": "@docusaurus/plugin-content-docs",
|
||||
"options": Object {
|
||||
"path": "../docs",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"name": "@docusaurus/plugin-content-pages",
|
||||
},
|
||||
|
|
|
@ -11,7 +11,6 @@ module.exports = {
|
|||
organizationName: 'facebook',
|
||||
projectName: 'docusaurus',
|
||||
baseUrl: '/',
|
||||
customDocsPath: './docs',
|
||||
url: 'https://docusaurus.io',
|
||||
headerLinks: [
|
||||
{doc: 'installation', label: 'Docs'},
|
||||
|
@ -30,6 +29,12 @@ module.exports = {
|
|||
algoliaOptions: {},
|
||||
},
|
||||
plugins: [
|
||||
{
|
||||
name: '@docusaurus/plugin-content-docs',
|
||||
options: {
|
||||
path: '../docs',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: '@docusaurus/plugin-content-blog',
|
||||
options: {
|
||||
|
|
Loading…
Add table
Reference in a new issue