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:
Yangshun Tay 2019-03-31 11:37:35 -07:00 committed by GitHub
parent c33e874e1c
commit a70d9b6720
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
32 changed files with 576 additions and 371 deletions

View file

@ -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;

View file

@ -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"
}, },

View file

@ -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();
}); });
}); });

View file

@ -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',

View file

@ -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', () => {

View file

@ -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__');

View 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;

View 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"
}
}

View file

@ -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,

View file

@ -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;

View file

@ -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.

View file

@ -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,

View file

@ -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) {

View file

@ -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;

View file

@ -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,

View file

@ -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,
}; };
}; };

View file

@ -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},

View file

@ -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';

View file

@ -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 />

View file

@ -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>

View file

@ -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>

View file

@ -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(

View file

@ -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',
}, },

View file

@ -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',
}, },

View file

@ -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',
}, },

View file

@ -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',
}, },

View file

@ -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',
}, },

View file

@ -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",
}, },

View file

@ -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: {