diff --git a/packages/docusaurus-plugin-content-blog/index.js b/packages/docusaurus-plugin-content-blog/index.js
index c632f8582a..acbb0e2f6e 100644
--- a/packages/docusaurus-plugin-content-blog/index.js
+++ b/packages/docusaurus-plugin-content-blog/index.js
@@ -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;
diff --git a/packages/docusaurus-plugin-content-blog/package.json b/packages/docusaurus-plugin-content-blog/package.json
index c4747451c1..008273e99b 100644
--- a/packages/docusaurus-plugin-content-blog/package.json
+++ b/packages/docusaurus-plugin-content-blog/package.json
@@ -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"
},
diff --git a/packages/docusaurus/test/load/docs/__snapshots__/sidebars.test.js.snap b/packages/docusaurus-plugin-content-docs/__tests__/__snapshots__/sidebars.test.js.snap
similarity index 100%
rename from packages/docusaurus/test/load/docs/__snapshots__/sidebars.test.js.snap
rename to packages/docusaurus-plugin-content-docs/__tests__/__snapshots__/sidebars.test.js.snap
diff --git a/packages/docusaurus/test/load/docs/index.test.js b/packages/docusaurus-plugin-content-docs/__tests__/index.test.js
similarity index 64%
rename from packages/docusaurus/test/load/docs/index.test.js
rename to packages/docusaurus-plugin-content-docs/__tests__/index.test.js
index 4580aa2c2c..46335a80e9 100644
--- a/packages/docusaurus/test/load/docs/index.test.js
+++ b/packages/docusaurus-plugin-content-docs/__tests__/index.test.js
@@ -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();
});
});
diff --git a/packages/docusaurus/test/load/docs/metadata.test.js b/packages/docusaurus-plugin-content-docs/__tests__/metadata.test.js
similarity index 85%
rename from packages/docusaurus/test/load/docs/metadata.test.js
rename to packages/docusaurus-plugin-content-docs/__tests__/metadata.test.js
index fa20489b0b..8915839126 100644
--- a/packages/docusaurus/test/load/docs/metadata.test.js
+++ b/packages/docusaurus-plugin-content-docs/__tests__/metadata.test.js
@@ -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',
diff --git a/packages/docusaurus/test/load/docs/order.test.js b/packages/docusaurus-plugin-content-docs/__tests__/order.test.js
similarity index 99%
rename from packages/docusaurus/test/load/docs/order.test.js
rename to packages/docusaurus-plugin-content-docs/__tests__/order.test.js
index ca41a15d4f..1b5e967194 100644
--- a/packages/docusaurus/test/load/docs/order.test.js
+++ b/packages/docusaurus-plugin-content-docs/__tests__/order.test.js
@@ -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', () => {
diff --git a/packages/docusaurus/test/load/docs/sidebars.test.js b/packages/docusaurus-plugin-content-docs/__tests__/sidebars.test.js
similarity index 93%
rename from packages/docusaurus/test/load/docs/sidebars.test.js
rename to packages/docusaurus-plugin-content-docs/__tests__/sidebars.test.js
index a34e1c533c..049762a0ad 100644
--- a/packages/docusaurus/test/load/docs/sidebars.test.js
+++ b/packages/docusaurus-plugin-content-docs/__tests__/sidebars.test.js
@@ -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__');
diff --git a/packages/docusaurus-plugin-content-docs/index.js b/packages/docusaurus-plugin-content-docs/index.js
new file mode 100644
index 0000000000..59e50daed6
--- /dev/null
+++ b/packages/docusaurus-plugin-content-docs/index.js
@@ -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;
diff --git a/packages/docusaurus-plugin-content-docs/package.json b/packages/docusaurus-plugin-content-docs/package.json
new file mode 100644
index 0000000000..b0c44989fa
--- /dev/null
+++ b/packages/docusaurus-plugin-content-docs/package.json
@@ -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"
+ }
+}
diff --git a/packages/docusaurus/lib/load/docs/metadata.js b/packages/docusaurus-plugin-content-docs/src/metadata.js
similarity index 97%
rename from packages/docusaurus/lib/load/docs/metadata.js
rename to packages/docusaurus-plugin-content-docs/src/metadata.js
index 3985e67574..32e38c6560 100644
--- a/packages/docusaurus/lib/load/docs/metadata.js
+++ b/packages/docusaurus-plugin-content-docs/src/metadata.js
@@ -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,
diff --git a/packages/docusaurus/lib/load/docs/order.js b/packages/docusaurus-plugin-content-docs/src/order.js
similarity index 100%
rename from packages/docusaurus/lib/load/docs/order.js
rename to packages/docusaurus-plugin-content-docs/src/order.js
diff --git a/packages/docusaurus/lib/load/docs/sidebars.js b/packages/docusaurus-plugin-content-docs/src/sidebars.js
similarity index 100%
rename from packages/docusaurus/lib/load/docs/sidebars.js
rename to packages/docusaurus-plugin-content-docs/src/sidebars.js
diff --git a/packages/docusaurus-plugin-content-pages/index.js b/packages/docusaurus-plugin-content-pages/index.js
index d3baec916a..7e005f1360 100644
--- a/packages/docusaurus-plugin-content-pages/index.js
+++ b/packages/docusaurus-plugin-content-pages/index.js
@@ -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;
diff --git a/packages/docusaurus/CHANGES.md b/packages/docusaurus/CHANGES.md
index f73d47d0f6..5d942e6a37 100644
--- a/packages/docusaurus/CHANGES.md
+++ b/packages/docusaurus/CHANGES.md
@@ -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.
diff --git a/packages/docusaurus/lib/commands/start.js b/packages/docusaurus/lib/commands/start.js
index cef70bbbc1..69f41cfb87 100644
--- a/packages/docusaurus/lib/commands/start.js
+++ b/packages/docusaurus/lib/commands/start.js
@@ -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,
diff --git a/packages/docusaurus/lib/load/config.js b/packages/docusaurus/lib/load/config.js
index 1f75fe0946..f0f41dcda8 100644
--- a/packages/docusaurus/lib/load/config.js
+++ b/packages/docusaurus/lib/load/config.js
@@ -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) {
diff --git a/packages/docusaurus/lib/load/docs/index.js b/packages/docusaurus/lib/load/docs/index.js
deleted file mode 100644
index c19957a796..0000000000
--- a/packages/docusaurus/lib/load/docs/index.js
+++ /dev/null
@@ -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;
diff --git a/packages/docusaurus/lib/load/index.js b/packages/docusaurus/lib/load/index.js
index 045c0dab2f..612302e181 100644
--- a/packages/docusaurus/lib/load/index.js
+++ b/packages/docusaurus/lib/load/index.js
@@ -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,
diff --git a/packages/docusaurus/lib/load/plugins.js b/packages/docusaurus/lib/load/plugins.js
index fa26260361..cfb223ce38 100644
--- a/packages/docusaurus/lib/load/plugins.js
+++ b/packages/docusaurus/lib/load/plugins.js
@@ -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,
};
};
diff --git a/packages/docusaurus/lib/load/routes.js b/packages/docusaurus/lib/load/routes.js
index c2ced2180a..000e06ac57 100644
--- a/packages/docusaurus/lib/load/routes.js
+++ b/packages/docusaurus/lib/load/routes.js
@@ -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 (
-