From 2a5402c8ca4db4ed874b4b7fe0374cab16482349 Mon Sep 17 00:00:00 2001 From: slorber Date: Sat, 8 Aug 2020 18:14:39 +0200 Subject: [PATCH 01/30] safe refactorings --- .../src/__tests__/index.test.ts | 16 +-- .../docusaurus-plugin-content-docs/src/env.ts | 1 - .../src/index.ts | 105 ++++++++++-------- .../src/order.ts | 56 +++++----- 4 files changed, 96 insertions(+), 82 deletions(-) diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/index.test.ts b/packages/docusaurus-plugin-content-docs/src/__tests__/index.test.ts index ffa0d92cf3d9..0b7c7e20dc27 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/index.test.ts +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/index.test.ts @@ -113,14 +113,16 @@ describe('empty/no docs website', () => { }); test('docs folder does not exist', async () => { - const plugin = pluginContentDocs( - context, - normalizePluginOptions(PluginOptionSchema, { - path: '/path/does/not/exist/', - }), + expect(() => + pluginContentDocs( + context, + normalizePluginOptions(PluginOptionSchema, { + path: '/path/does/not/exist/', + }), + ), + ).toThrowErrorMatchingInlineSnapshot( + `"No docs directory found for the docs plugin at: /path/does/not/exist"`, ); - const content = await plugin.loadContent(); - expect(content).toBeNull(); }); }); diff --git a/packages/docusaurus-plugin-content-docs/src/env.ts b/packages/docusaurus-plugin-content-docs/src/env.ts index a5062ae0f471..508a708c17b0 100644 --- a/packages/docusaurus-plugin-content-docs/src/env.ts +++ b/packages/docusaurus-plugin-content-docs/src/env.ts @@ -72,7 +72,6 @@ export default function ( fs.readFileSync(versionsJSONFile, 'utf8'), ); if (parsedVersions && parsedVersions.length > 0) { - // eslint-disable-next-line prefer-destructuring versioning.latestVersion = parsedVersions[0]; versioning.enabled = true; versioning.versions = parsedVersions; diff --git a/packages/docusaurus-plugin-content-docs/src/index.ts b/packages/docusaurus-plugin-content-docs/src/index.ts index f0dfbcfda381..978b31b4f7a4 100644 --- a/packages/docusaurus-plugin-content-docs/src/index.ts +++ b/packages/docusaurus-plugin-content-docs/src/index.ts @@ -67,18 +67,30 @@ import {VERSIONS_JSON_FILE} from './constants'; import {PluginOptionSchema} from './pluginOptionSchema'; import {ValidationError} from '@hapi/joi'; +// TODO remove homePageId before end of 2020 +// "slug: /" is better because the home doc can be different across versions +function logHomePageIdDeprecated(homePageId: string) { + console.log( + chalk.red( + `The docs plugin option homePageId=${homePageId} is deprecated. To make a doc the "home", prefer frontmatter: "slug: /"`, + ), + ); +} + +function ensureDocsDirExist(docsDir: string) { + if (!fs.existsSync(docsDir)) { + throw new Error( + `No docs directory found for the docs plugin at: ${docsDir}`, + ); + } +} + export default function pluginContentDocs( context: LoadContext, options: PluginOptions, ): Plugin { - // TODO remove homePageId before end of 2020 - // "slug: /" is better because the home doc can be different across versions if (options.homePageId) { - console.log( - chalk.red( - `The docs plugin option homePageId=${options.homePageId} is deprecated. To make a doc the "home", prefer frontmatter: "slug: /"`, - ), - ); + logHomePageIdDeprecated(options.homePageId); } if (options.admonitions) { @@ -89,6 +101,8 @@ export default function pluginContentDocs( const {siteDir, generatedFilesDir, baseUrl} = context; const docsDir = path.resolve(siteDir, options.path); + ensureDocsDirExist(docsDir); + const sourceToPermalink: SourceToPermalink = {}; const pluginId = options.id ?? DEFAULT_PLUGIN_ID; const isDefaultPluginId = pluginId === DEFAULT_PLUGIN_ID; @@ -113,6 +127,22 @@ export default function pluginContentDocs( } = versioning; const versionsNames = versions.map((version) => `version-${version}`); + async function processDocsMetadata({ + source, + refDir, + }: { + source: string; + refDir: string; + }) { + return processMetadata({ + source, + refDir, + context, + options, + env, + }); + } + return { name: 'docusaurus-plugin-content-docs', @@ -144,6 +174,14 @@ export default function pluginContentDocs( }); }, + getClientModules() { + const modules = []; + if (options.admonitions) { + modules.push(require.resolve('remark-admonitions/styles/infima.css')); + } + return modules; + }, + getPathsToWatch() { const {include} = options; let globPattern = include.map((pattern) => `${docsDir}/${pattern}`); @@ -164,29 +202,9 @@ export default function pluginContentDocs( return [...globPattern, options.sidebarPath]; }, - getClientModules() { - const modules = []; - - if (options.admonitions) { - modules.push(require.resolve('remark-admonitions/styles/infima.css')); - } - - return modules; - }, - - // Fetches blog contents and returns metadata for the contents. async loadContent() { const {include, sidebarPath} = options; - if (!fs.existsSync(docsDir)) { - console.error( - chalk.red( - `No docs directory found for the docs plugin at: ${docsDir}`, - ), - ); - return null; - } - // Prepare metadata container. const docsMetadataRaw: DocsMetadataRaw = {}; const docsPromises = []; @@ -202,12 +220,9 @@ export default function pluginContentDocs( docsPromises.push( Promise.all( docsFiles.map(async (source) => { - const metadata: MetadataRaw = await processMetadata({ + const metadata: MetadataRaw = await processDocsMetadata({ source, refDir: docsDir, - context, - options, - env, }); docsMetadataRaw[metadata.id] = metadata; }), @@ -228,12 +243,9 @@ export default function pluginContentDocs( docsPromises.push( Promise.all( versionedFiles.map(async (source) => { - const metadata = await processMetadata({ + const metadata = await processDocsMetadata({ source, refDir: versionedDir, - context, - options, - env, }); docsMetadataRaw[metadata.id] = metadata; }), @@ -259,21 +271,22 @@ export default function pluginContentDocs( const docsMetadata: DocsMetadata = {}; const permalinkToSidebar: PermalinkToSidebar = {}; const versionToSidebars: VersionToSidebars = {}; + + function toDocNavLink(doc: MetadataRaw) { + return { + title: doc.title, + permalink: doc.permalink, + }; + } + Object.keys(docsMetadataRaw).forEach((currentID) => { const {next: nextID, previous: previousID, sidebar} = - order[currentID] || {}; + order[currentID] ?? {}; const previous = previousID - ? { - title: docsMetadataRaw[previousID]?.title ?? 'Previous', - permalink: docsMetadataRaw[previousID]?.permalink, - } - : undefined; - const next = nextID - ? { - title: docsMetadataRaw[nextID]?.title ?? 'Next', - permalink: docsMetadataRaw[nextID]?.permalink, - } + ? toDocNavLink(docsMetadataRaw[previousID]!) : undefined; + const next = nextID ? toDocNavLink(docsMetadataRaw[nextID]) : undefined; + docsMetadata[currentID] = { ...docsMetadataRaw[currentID], sidebar, diff --git a/packages/docusaurus-plugin-content-docs/src/order.ts b/packages/docusaurus-plugin-content-docs/src/order.ts index 1073335f1144..8cd545ec41c0 100644 --- a/packages/docusaurus-plugin-content-docs/src/order.ts +++ b/packages/docusaurus-plugin-content-docs/src/order.ts @@ -5,7 +5,26 @@ * LICENSE file in the root directory of this source tree. */ -import {Sidebar, SidebarItem, Order} from './types'; +import {Sidebar, SidebarItem, Order, SidebarItemDoc} from './types'; +import {flatten} from 'lodash'; + +function getOrderedDocItems(items: SidebarItem[]): SidebarItemDoc[] { + function getDocItemsRecursion(item: SidebarItem): SidebarItemDoc[] { + if (item.type === 'doc') { + return [item]; + } + if (item.type === 'category') { + return getOrderedDocItems(item.items); + } + // Refs and links should not be shown in navigation. + if (item.type === 'ref' || item.type === 'link') { + return []; + } + throw new Error(`unknown sidebar item type = ${item.type}`); + } + + return flatten(items.map(getDocItemsRecursion)); +} // Build the docs meta such as next, previous, category and sidebar. export default function createOrder(allSidebars: Sidebar = {}): Order { @@ -14,41 +33,22 @@ export default function createOrder(allSidebars: Sidebar = {}): Order { Object.keys(allSidebars).forEach((sidebarId) => { const sidebar = allSidebars[sidebarId]; - const ids: string[] = []; - const indexItems = ({items}: {items: SidebarItem[]}) => { - items.forEach((item) => { - switch (item.type) { - case 'category': - indexItems({ - items: item.items, - }); - break; - case 'ref': - case 'link': - // Refs and links should not be shown in navigation. - break; - case 'doc': - ids.push(item.id); - break; - default: - } - }); - }; - - indexItems({items: sidebar}); + const docIds: string[] = getOrderedDocItems(sidebar).map( + (docItem) => docItem.id, + ); // eslint-disable-next-line - for (let i = 0; i < ids.length; i++) { - const id = ids[i]; + for (let i = 0; i < docIds.length; i++) { + const id = docIds[i]; let previous; let next; if (i > 0) { - previous = ids[i - 1]; + previous = docIds[i - 1]; } - if (i < ids.length - 1) { - next = ids[i + 1]; + if (i < docIds.length - 1) { + next = docIds[i + 1]; } order[id] = { From 01fa24069a8797657b04cffd4c206f405df2b16f Mon Sep 17 00:00:00 2001 From: slorber Date: Sat, 8 Aug 2020 18:21:09 +0200 Subject: [PATCH 02/30] safe refactors --- .../src/index.ts | 41 +++++++++---------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/packages/docusaurus-plugin-content-docs/src/index.ts b/packages/docusaurus-plugin-content-docs/src/index.ts index 978b31b4f7a4..f030f4ce938c 100644 --- a/packages/docusaurus-plugin-content-docs/src/index.ts +++ b/packages/docusaurus-plugin-content-docs/src/index.ts @@ -67,16 +67,6 @@ import {VERSIONS_JSON_FILE} from './constants'; import {PluginOptionSchema} from './pluginOptionSchema'; import {ValidationError} from '@hapi/joi'; -// TODO remove homePageId before end of 2020 -// "slug: /" is better because the home doc can be different across versions -function logHomePageIdDeprecated(homePageId: string) { - console.log( - chalk.red( - `The docs plugin option homePageId=${homePageId} is deprecated. To make a doc the "home", prefer frontmatter: "slug: /"`, - ), - ); -} - function ensureDocsDirExist(docsDir: string) { if (!fs.existsSync(docsDir)) { throw new Error( @@ -89,16 +79,6 @@ export default function pluginContentDocs( context: LoadContext, options: PluginOptions, ): Plugin { - if (options.homePageId) { - logHomePageIdDeprecated(options.homePageId); - } - - if (options.admonitions) { - options.remarkPlugins = options.remarkPlugins.concat([ - [admonitions, options.admonitions], - ]); - } - const {siteDir, generatedFilesDir, baseUrl} = context; const docsDir = path.resolve(siteDir, options.path); ensureDocsDirExist(docsDir); @@ -594,6 +574,25 @@ export function validateOptions({ PluginOptions, ValidationError > { - const validatedOptions = validate(PluginOptionSchema, options); + // @ts-expect-error: TODO bad OptionValidationContext, need refactor + const validatedOptions: PluginOptions = validate(PluginOptionSchema, options); + + if (validatedOptions.homePageId) { + // TODO remove homePageId before end of 2020 + // "slug: /" is better because the home doc can be different across versions + console.log( + chalk.red( + `The docs plugin option homePageId=${validatedOptions.homePageId} is deprecated. To make a doc the "home", prefer frontmatter: "slug: /"`, + ), + ); + } + + if (options.admonitions) { + validatedOptions.remarkPlugins = validatedOptions.remarkPlugins.concat([ + [admonitions, options.admonitions], + ]); + } + + // @ts-expect-error: TODO bad OptionValidationContext, need refactor return validatedOptions; } From d54dcd3ec2e486f7a24f8cf7f2792f0a10b852ce Mon Sep 17 00:00:00 2001 From: slorber Date: Sat, 8 Aug 2020 19:51:13 +0200 Subject: [PATCH 03/30] add code to read versions more generically --- .../version-withSlugs-sidebars.json | 10 ++ .../src/__tests__/index.test.ts | 2 +- .../src/__tests__/pluginOptionSchema.test.ts | 1 + .../src/__tests__/version.test.ts | 24 ++-- .../src/constants.ts | 3 + .../docusaurus-plugin-content-docs/src/env.ts | 122 ++++++++++++++++-- .../src/index.ts | 55 ++++---- .../src/pluginOptionSchema.ts | 4 + .../src/types.ts | 5 +- .../src/version.ts | 12 +- 10 files changed, 183 insertions(+), 55 deletions(-) create mode 100644 packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/versioned-site/versioned_sidebars/version-withSlugs-sidebars.json diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/versioned-site/versioned_sidebars/version-withSlugs-sidebars.json b/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/versioned-site/versioned_sidebars/version-withSlugs-sidebars.json new file mode 100644 index 000000000000..98a2d8a4f74b --- /dev/null +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/versioned-site/versioned_sidebars/version-withSlugs-sidebars.json @@ -0,0 +1,10 @@ +{ + "version-1.0.1/docs": { + "Test": [ + "version-1.0.1/foo/bar" + ], + "Guides": [ + "version-1.0.1/hello" + ] + } +} diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/index.test.ts b/packages/docusaurus-plugin-content-docs/src/__tests__/index.test.ts index 0b7c7e20dc27..1be94fec64e2 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/index.test.ts +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/index.test.ts @@ -121,7 +121,7 @@ describe('empty/no docs website', () => { }), ), ).toThrowErrorMatchingInlineSnapshot( - `"No docs directory found for the docs plugin at: /path/does/not/exist"`, + `"The docs folder does not exist for version [current]. A docs folder is expected to be found at /path/does/not/exist"`, ); }); }); diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/pluginOptionSchema.test.ts b/packages/docusaurus-plugin-content-docs/src/__tests__/pluginOptionSchema.test.ts index 08ddaf7f1f14..2ef791c8681e 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/pluginOptionSchema.test.ts +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/pluginOptionSchema.test.ts @@ -32,6 +32,7 @@ describe('normalizeDocsPluginOptions', () => { showLastUpdateAuthor: true, admonitions: {}, excludeNextVersionDocs: true, + includeCurrentVersion: false, disableVersioning: true, }; const {value} = await PluginOptionSchema.validate(userOptions); diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/version.test.ts b/packages/docusaurus-plugin-content-docs/src/__tests__/version.test.ts index a7a70fc1c6fb..bd5cd1f0e3ca 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/version.test.ts +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/version.test.ts @@ -10,9 +10,9 @@ import {docsVersion} from '../version'; import {PathOptions} from '../types'; import fs from 'fs-extra'; import { - getVersionedDocsDir, - getVersionsJSONFile, - getVersionedSidebarsDir, + getVersionedDocsDirPath, + getVersionsFilePath, + getVersionedSidebarsDirPath, } from '../env'; import {DEFAULT_PLUGIN_ID} from '@docusaurus/core/lib/constants'; @@ -163,19 +163,19 @@ describe('docsVersion', () => { expect(copyMock).toHaveBeenCalledWith( path.join(simpleSiteDir, options.path), path.join( - getVersionedDocsDir(simpleSiteDir, DEFAULT_PLUGIN_ID), + getVersionedDocsDirPath(simpleSiteDir, DEFAULT_PLUGIN_ID), 'version-1.0.0', ), ); expect(versionedSidebar).toMatchSnapshot(); expect(versionedSidebarPath).toEqual( path.join( - getVersionedSidebarsDir(simpleSiteDir, DEFAULT_PLUGIN_ID), + getVersionedSidebarsDirPath(simpleSiteDir, DEFAULT_PLUGIN_ID), 'version-1.0.0-sidebars.json', ), ); expect(versionsPath).toEqual( - getVersionsJSONFile(simpleSiteDir, DEFAULT_PLUGIN_ID), + getVersionsFilePath(simpleSiteDir, DEFAULT_PLUGIN_ID), ); expect(versions).toEqual(['1.0.0']); expect(consoleMock).toHaveBeenCalledWith('[docs] Version 1.0.0 created!'); @@ -211,19 +211,19 @@ describe('docsVersion', () => { expect(copyMock).toHaveBeenCalledWith( path.join(versionedSiteDir, options.path), path.join( - getVersionedDocsDir(versionedSiteDir, DEFAULT_PLUGIN_ID), + getVersionedDocsDirPath(versionedSiteDir, DEFAULT_PLUGIN_ID), 'version-2.0.0', ), ); expect(versionedSidebar).toMatchSnapshot(); expect(versionedSidebarPath).toEqual( path.join( - getVersionedSidebarsDir(versionedSiteDir, DEFAULT_PLUGIN_ID), + getVersionedSidebarsDirPath(versionedSiteDir, DEFAULT_PLUGIN_ID), 'version-2.0.0-sidebars.json', ), ); expect(versionsPath).toEqual( - getVersionsJSONFile(versionedSiteDir, DEFAULT_PLUGIN_ID), + getVersionsFilePath(versionedSiteDir, DEFAULT_PLUGIN_ID), ); expect(versions).toEqual(['2.0.0', '1.0.1', '1.0.0', 'withSlugs']); expect(consoleMock).toHaveBeenCalledWith('[docs] Version 2.0.0 created!'); @@ -261,19 +261,19 @@ describe('docsVersion', () => { expect(copyMock).toHaveBeenCalledWith( path.join(versionedSiteDir, options.path), path.join( - getVersionedDocsDir(versionedSiteDir, pluginId), + getVersionedDocsDirPath(versionedSiteDir, pluginId), 'version-2.0.0', ), ); expect(versionedSidebar).toMatchSnapshot(); expect(versionedSidebarPath).toEqual( path.join( - getVersionedSidebarsDir(versionedSiteDir, pluginId), + getVersionedSidebarsDirPath(versionedSiteDir, pluginId), 'version-2.0.0-sidebars.json', ), ); expect(versionsPath).toEqual( - getVersionsJSONFile(versionedSiteDir, pluginId), + getVersionsFilePath(versionedSiteDir, pluginId), ); expect(versions).toEqual(['2.0.0', '1.0.0']); expect(consoleMock).toHaveBeenCalledWith( diff --git a/packages/docusaurus-plugin-content-docs/src/constants.ts b/packages/docusaurus-plugin-content-docs/src/constants.ts index ca7d99f27cf1..822b8e600e88 100644 --- a/packages/docusaurus-plugin-content-docs/src/constants.ts +++ b/packages/docusaurus-plugin-content-docs/src/constants.ts @@ -5,6 +5,9 @@ * LICENSE file in the root directory of this source tree. */ +// The name of the version at the root of your site (website/docs) +export const CURRENT_VERSION_NAME = 'current'; + export const VERSIONED_DOCS_DIR = 'versioned_docs'; export const VERSIONED_SIDEBARS_DIR = 'versioned_sidebars'; export const VERSIONS_JSON_FILE = 'versions.json'; diff --git a/packages/docusaurus-plugin-content-docs/src/env.ts b/packages/docusaurus-plugin-content-docs/src/env.ts index 508a708c17b0..9e39c20f26e6 100644 --- a/packages/docusaurus-plugin-content-docs/src/env.ts +++ b/packages/docusaurus-plugin-content-docs/src/env.ts @@ -7,11 +7,12 @@ import path from 'path'; import fs from 'fs-extra'; -import {VersioningEnv, Env} from './types'; +import {VersioningEnv, Env, PluginOptions} from './types'; import { VERSIONS_JSON_FILE, VERSIONED_DOCS_DIR, VERSIONED_SIDEBARS_DIR, + CURRENT_VERSION_NAME, } from './constants'; import {DEFAULT_PLUGIN_ID} from '@docusaurus/core/lib/constants'; @@ -25,11 +26,14 @@ function addPluginIdPrefix(fileOrDir: string, pluginId: string): string { } } -export function getVersionedDocsDir(siteDir: string, pluginId: string): string { +export function getVersionedDocsDirPath( + siteDir: string, + pluginId: string, +): string { return path.join(siteDir, addPluginIdPrefix(VERSIONED_DOCS_DIR, pluginId)); } -export function getVersionedSidebarsDir( +export function getVersionedSidebarsDirPath( siteDir: string, pluginId: string, ): string { @@ -39,13 +43,113 @@ export function getVersionedSidebarsDir( ); } -export function getVersionsJSONFile(siteDir: string, pluginId: string): string { +export function getVersionsFilePath(siteDir: string, pluginId: string): string { return path.join(siteDir, addPluginIdPrefix(VERSIONS_JSON_FILE, pluginId)); } -type EnvOptions = Partial<{disableVersioning: boolean}>; +function readVersionsFile(siteDir: string, pluginId: string): string[] | null { + const versionsFilePath = getVersionsFilePath(siteDir, pluginId); + if (fs.existsSync(versionsFilePath)) { + const content = JSON.parse(fs.readFileSync(versionsFilePath, 'utf8')); + if ( + content instanceof Array && + content.every((version) => typeof version === 'string') + ) { + return content; + } else { + throw new Error( + `The versions file should contain an array of versions! ${versionsFilePath}`, + ); + } + } else { + return null; + } +} + +function readVersionNames( + siteDir: string, + {id: pluginId, disableVersioning, includeCurrentVersion}: PluginOptions, +): string[] { + const versions = disableVersioning + ? [] + : readVersionsFile(siteDir, pluginId) ?? []; + + // We add the current version at the beginning, unless + // - user don't want to + // - it's been explicitly added to versions.json + if (includeCurrentVersion && !versions.includes(CURRENT_VERSION_NAME)) { + versions.unshift(CURRENT_VERSION_NAME); + } + return versions; +} + +type VersionMetadata = { + versionName: string; + docsPath: string; + sidebarPath: string; +}; + +function createVersionMetadata({ + versionName, + siteDir, + options, +}: { + versionName: string; + siteDir: string; + options: PluginOptions; +}): VersionMetadata { + if (versionName === CURRENT_VERSION_NAME) { + const docsPath = path.resolve(siteDir, options.path); + const sidebarPath = path.resolve(siteDir, options.sidebarPath); + return {versionName, docsPath, sidebarPath}; + } else { + const docsPath = path.join( + getVersionedDocsDirPath(siteDir, options.id), + `version-${versionName}`, + ); + const sidebarPath = path.join( + getVersionedSidebarsDirPath(siteDir, options.id), + `version-${versionName}-sidebars.json`, + ); + return {versionName, docsPath, sidebarPath}; + } +} + +function checkVersionMetadataPaths({ + versionName, + docsPath, + sidebarPath, +}: VersionMetadata) { + if (!fs.existsSync(docsPath)) { + throw new Error( + `The docs folder does not exist for version [${versionName}]. A docs folder is expected to be found at ${docsPath}`, + ); + } + if (!fs.existsSync(sidebarPath)) { + throw new Error( + `The sidebar file does not exist for version [${versionName}]. A sidebar file is expected to be found at ${sidebarPath}`, + ); + } +} + +export function readVersionsMetadata( + siteDir: string, + options: PluginOptions, +): VersionMetadata[] { + const versionNames = readVersionNames(siteDir, options); + const versionsMetadata = versionNames.map((versionName) => + createVersionMetadata({versionName, siteDir, options}), + ); + versionsMetadata.forEach(checkVersionMetadataPaths); + return versionsMetadata; +} -export default function ( +// TODO remove soon +type EnvOptions = Partial<{ + disableVersioning: boolean; + includeCurrentVersion: boolean; +}>; +export default function loadEnv( siteDir: string, pluginId: string, options: EnvOptions = {disableVersioning: false}, @@ -65,7 +169,7 @@ export default function ( sidebarsDir: '', }; - const versionsJSONFile = getVersionsJSONFile(siteDir, pluginId); + const versionsJSONFile = getVersionsFilePath(siteDir, pluginId); if (fs.existsSync(versionsJSONFile)) { if (!options.disableVersioning) { const parsedVersions = JSON.parse( @@ -75,8 +179,8 @@ export default function ( versioning.latestVersion = parsedVersions[0]; versioning.enabled = true; versioning.versions = parsedVersions; - versioning.docsDir = getVersionedDocsDir(siteDir, pluginId); - versioning.sidebarsDir = getVersionedSidebarsDir(siteDir, pluginId); + versioning.docsDir = getVersionedDocsDirPath(siteDir, pluginId); + versioning.sidebarsDir = getVersionedSidebarsDirPath(siteDir, pluginId); } } } diff --git a/packages/docusaurus-plugin-content-docs/src/index.ts b/packages/docusaurus-plugin-content-docs/src/index.ts index f030f4ce938c..4a904ba72294 100644 --- a/packages/docusaurus-plugin-content-docs/src/index.ts +++ b/packages/docusaurus-plugin-content-docs/src/index.ts @@ -10,7 +10,6 @@ import pick from 'lodash.pick'; import pickBy from 'lodash.pickby'; import sortBy from 'lodash.sortby'; import globby from 'globby'; -import fs from 'fs-extra'; import path from 'path'; import chalk from 'chalk'; @@ -36,7 +35,7 @@ import { import createOrder from './order'; import loadSidebars from './sidebars'; import processMetadata from './metadata'; -import loadEnv from './env'; +import loadEnv, {readVersionsMetadata} from './env'; import { PluginOptions, @@ -67,25 +66,19 @@ import {VERSIONS_JSON_FILE} from './constants'; import {PluginOptionSchema} from './pluginOptionSchema'; import {ValidationError} from '@hapi/joi'; -function ensureDocsDirExist(docsDir: string) { - if (!fs.existsSync(docsDir)) { - throw new Error( - `No docs directory found for the docs plugin at: ${docsDir}`, - ); - } -} - export default function pluginContentDocs( context: LoadContext, options: PluginOptions, ): Plugin { const {siteDir, generatedFilesDir, baseUrl} = context; + + const versionsMetadata = readVersionsMetadata(siteDir, options); + console.log(versionsMetadata); + const docsDir = path.resolve(siteDir, options.path); - ensureDocsDirExist(docsDir); const sourceToPermalink: SourceToPermalink = {}; const pluginId = options.id ?? DEFAULT_PLUGIN_ID; - const isDefaultPluginId = pluginId === DEFAULT_PLUGIN_ID; const pluginDataDirRoot = path.join( generatedFilesDir, @@ -135,6 +128,10 @@ export default function pluginContentDocs( }, extendCli(cli) { + const isDefaultPluginId = pluginId === DEFAULT_PLUGIN_ID; + + // Need to create one distinct command per plugin instance + // otherwise 2 instances would try to execute the command! const command = isDefaultPluginId ? 'docs:version' : `docs:version:${pluginId}`; @@ -183,17 +180,14 @@ export default function pluginContentDocs( }, async loadContent() { - const {include, sidebarPath} = options; + const {includeCurrentVersion, include, sidebarPath} = options; // Prepare metadata container. const docsMetadataRaw: DocsMetadataRaw = {}; const docsPromises = []; - const includeDefaultDocs = !( - options.excludeNextVersionDocs && process.argv[2] === 'build' - ); - // Metadata for default/master docs files. - if (includeDefaultDocs) { + // Metadata for current docs files. + if (includeCurrentVersion) { const docsFiles = await globby(include, { cwd: docsDir, }); @@ -238,7 +232,7 @@ export default function pluginContentDocs( (versionName) => `${versionedSidebarsDir}/${versionName}-sidebars.json`, ); - if (includeDefaultDocs) { + if (includeCurrentVersion) { sidebarPaths.unshift(sidebarPath); } @@ -574,19 +568,30 @@ export function validateOptions({ PluginOptions, ValidationError > { - // @ts-expect-error: TODO bad OptionValidationContext, need refactor - const validatedOptions: PluginOptions = validate(PluginOptionSchema, options); + // TODO remove homePageId before end of 2020 + // "slug: /" is better because the home doc can be different across versions + if (options.homePageId) { + console.log( + chalk.red( + `The docs plugin option homePageId=${options.homePageId} is deprecated. To make a doc the "home", prefer frontmatter: "slug: /"`, + ), + ); + } - if (validatedOptions.homePageId) { - // TODO remove homePageId before end of 2020 - // "slug: /" is better because the home doc can be different across versions + if (typeof options.excludeNextVersionDocs !== 'undefined') { console.log( chalk.red( - `The docs plugin option homePageId=${validatedOptions.homePageId} is deprecated. To make a doc the "home", prefer frontmatter: "slug: /"`, + `The docs plugin option excludeNextVersionDocs=${ + options.excludeNextVersionDocs + } is deprecated. Use the includeCurrentVersion=${!options.excludeNextVersionDocs} option instead!"`, ), ); + options.includeCurrentVersion = !options.excludeNextVersionDocs; } + // @ts-expect-error: TODO bad OptionValidationContext, need refactor + const validatedOptions: PluginOptions = validate(PluginOptionSchema, options); + if (options.admonitions) { validatedOptions.remarkPlugins = validatedOptions.remarkPlugins.concat([ [admonitions, options.admonitions], diff --git a/packages/docusaurus-plugin-content-docs/src/pluginOptionSchema.ts b/packages/docusaurus-plugin-content-docs/src/pluginOptionSchema.ts index 7e67024a99f4..228b3327fce5 100644 --- a/packages/docusaurus-plugin-content-docs/src/pluginOptionSchema.ts +++ b/packages/docusaurus-plugin-content-docs/src/pluginOptionSchema.ts @@ -27,6 +27,7 @@ export const DEFAULT_OPTIONS: PluginOptions = { showLastUpdateAuthor: false, admonitions: {}, excludeNextVersionDocs: false, + includeCurrentVersion: true, disableVersioning: false, }; @@ -49,5 +50,8 @@ export const PluginOptionSchema = Joi.object({ excludeNextVersionDocs: Joi.bool().default( DEFAULT_OPTIONS.excludeNextVersionDocs, ), + includeCurrentVersion: Joi.bool().default( + DEFAULT_OPTIONS.includeCurrentVersion, + ), disableVersioning: Joi.bool().default(DEFAULT_OPTIONS.disableVersioning), }); diff --git a/packages/docusaurus-plugin-content-docs/src/types.ts b/packages/docusaurus-plugin-content-docs/src/types.ts index 7dc2f1636770..b9424a6e6ae8 100644 --- a/packages/docusaurus-plugin-content-docs/src/types.ts +++ b/packages/docusaurus-plugin-content-docs/src/types.ts @@ -24,7 +24,7 @@ export interface PathOptions { } export interface PluginOptions extends MetadataOptions, PathOptions { - id?: string; + id: string; include: string[]; docLayoutComponent: string; docItemComponent: string; @@ -32,7 +32,8 @@ export interface PluginOptions extends MetadataOptions, PathOptions { rehypePlugins: string[]; admonitions: any; disableVersioning: boolean; - excludeNextVersionDocs: boolean; + excludeNextVersionDocs?: boolean; + includeCurrentVersion: boolean; } export type SidebarItemDoc = { diff --git a/packages/docusaurus-plugin-content-docs/src/version.ts b/packages/docusaurus-plugin-content-docs/src/version.ts index 860beaffb53b..989d2fd0a729 100644 --- a/packages/docusaurus-plugin-content-docs/src/version.ts +++ b/packages/docusaurus-plugin-content-docs/src/version.ts @@ -6,9 +6,9 @@ */ import { - getVersionsJSONFile, - getVersionedDocsDir, - getVersionedSidebarsDir, + getVersionsFilePath, + getVersionedDocsDirPath, + getVersionedSidebarsDirPath, } from './env'; import fs from 'fs-extra'; import path from 'path'; @@ -63,7 +63,7 @@ export function docsVersion( // Load existing versions. let versions = []; - const versionsJSONFile = getVersionsJSONFile(siteDir, pluginId); + const versionsJSONFile = getVersionsFilePath(siteDir, pluginId); if (fs.existsSync(versionsJSONFile)) { versions = JSON.parse(fs.readFileSync(versionsJSONFile, 'utf8')); } @@ -80,7 +80,7 @@ export function docsVersion( // Copy docs files. const docsDir = path.join(siteDir, docsPath); if (fs.existsSync(docsDir) && fs.readdirSync(docsDir).length > 0) { - const versionedDir = getVersionedDocsDir(siteDir, pluginId); + const versionedDir = getVersionedDocsDirPath(siteDir, pluginId); const newVersionDir = path.join(versionedDir, `version-${version}`); fs.copySync(docsDir, newVersionDir); } else { @@ -116,7 +116,7 @@ export function docsVersion( {}, ); - const versionedSidebarsDir = getVersionedSidebarsDir(siteDir, pluginId); + const versionedSidebarsDir = getVersionedSidebarsDirPath(siteDir, pluginId); const newSidebarFile = path.join( versionedSidebarsDir, `version-${version}-sidebars.json`, From 97d18de29681c065b9585f70c788e4ae27d63aad Mon Sep 17 00:00:00 2001 From: slorber Date: Sat, 8 Aug 2020 21:43:48 +0200 Subject: [PATCH 04/30] refactor docs plugin --- .../__snapshots__/index.test.ts.snap | 1112 ----------------- .../src/__tests__/index.test.ts | 10 +- .../src/__tests__/metadata.test.ts | 8 +- .../src/__tests__/pluginOptionSchema.test.ts | 1 + .../docusaurus-plugin-content-docs/src/env.ts | 8 +- .../src/index.ts | 155 ++- .../src/metadata.ts | 112 +- .../src/pluginOptionSchema.ts | 2 +- .../src/types.ts | 6 + 9 files changed, 144 insertions(+), 1270 deletions(-) diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/__snapshots__/index.test.ts.snap b/packages/docusaurus-plugin-content-docs/src/__tests__/__snapshots__/index.test.ts.snap index 93ef7be61ea0..87bb00a0d64a 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/__snapshots__/index.test.ts.snap +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/__snapshots__/index.test.ts.snap @@ -548,1115 +548,3 @@ Available document ids= - slugs/resolvedSlug - slugs/tryToEscapeSlug" `; - -exports[`versioned website (community) content: all sidebars 1`] = ` -Object { - "community": Array [ - Object { - "href": "/community/next/team", - "label": "team", - "type": "link", - }, - ], - "version-1.0.0/community": Array [ - Object { - "href": "/community/team", - "label": "team", - "type": "link", - }, - ], -} -`; - -exports[`versioned website (community) content: base metadata for latest version 1`] = ` -Object { - "docsSidebars": Object { - "version-1.0.0/community": Array [ - Object { - "href": "/community/team", - "label": "team", - "type": "link", - }, - ], - }, - "permalinkToSidebar": Object { - "/community/team": "version-1.0.0/community", - }, - "version": "1.0.0", -} -`; - -exports[`versioned website (community) content: base metadata for next version 1`] = ` -Object { - "docsSidebars": Object { - "community": Array [ - Object { - "href": "/community/next/team", - "label": "team", - "type": "link", - }, - ], - }, - "permalinkToSidebar": Object { - "/community/next/team": "community", - }, - "version": "next", -} -`; - -exports[`versioned website (community) content: data 1`] = ` -Object { - "community-next-route-f11.json": "{ - \\"docsSidebars\\": { - \\"community\\": [ - { - \\"type\\": \\"link\\", - \\"label\\": \\"team\\", - \\"href\\": \\"/community/next/team\\" - } - ] - }, - \\"permalinkToSidebar\\": { - \\"/community/next/team\\": \\"community\\" - }, - \\"version\\": \\"next\\" -}", - "community-route-aa2.json": "{ - \\"docsSidebars\\": { - \\"version-1.0.0/community\\": [ - { - \\"type\\": \\"link\\", - \\"label\\": \\"team\\", - \\"href\\": \\"/community/team\\" - } - ] - }, - \\"permalinkToSidebar\\": { - \\"/community/team\\": \\"version-1.0.0/community\\" - }, - \\"version\\": \\"1.0.0\\" -}", - "site-community-team-md-9d8.json": "{ - \\"unversionedId\\": \\"team\\", - \\"id\\": \\"team\\", - \\"isDocsHomePage\\": false, - \\"title\\": \\"team\\", - \\"description\\": \\"Team current version\\", - \\"source\\": \\"@site/community/team.md\\", - \\"slug\\": \\"/team\\", - \\"permalink\\": \\"/community/next/team\\", - \\"version\\": \\"next\\", - \\"sidebar\\": \\"community\\" -}", - "site-community-versioned-docs-version-1-0-0-team-md-359.json": "{ - \\"unversionedId\\": \\"team\\", - \\"id\\": \\"version-1.0.0/team\\", - \\"isDocsHomePage\\": false, - \\"title\\": \\"team\\", - \\"description\\": \\"Team 1.0.0\\", - \\"source\\": \\"@site/community_versioned_docs/version-1.0.0/team.md\\", - \\"slug\\": \\"/team\\", - \\"permalink\\": \\"/community/team\\", - \\"version\\": \\"1.0.0\\", - \\"sidebar\\": \\"version-1.0.0/community\\" -}", -} -`; - -exports[`versioned website (community) content: global data 1`] = ` -Object { - "pluginName": Object { - "pluginId": Object { - "latestVersionName": "1.0.0", - "path": "/community", - "versions": Array [ - Object { - "docs": Array [ - Object { - "id": "team", - "path": "/community/next/team", - }, - ], - "mainDocId": "team", - "name": "next", - "path": "/community/next", - }, - Object { - "docs": Array [ - Object { - "id": "team", - "path": "/community/team", - }, - ], - "mainDocId": "team", - "name": "1.0.0", - "path": "/community", - }, - ], - }, - }, -} -`; - -exports[`versioned website (community) content: route config 1`] = ` -Array [ - Object { - "component": "@theme/DocPage", - "exact": false, - "modules": Object { - "docsMetadata": "~docs/community-next-route-f11.json", - }, - "path": "/community/next", - "priority": undefined, - "routes": Array [ - Object { - "component": "@theme/DocItem", - "exact": true, - "modules": Object { - "content": "@site/community/team.md", - }, - "path": "/community/next/team", - }, - ], - }, - Object { - "component": "@theme/DocPage", - "exact": false, - "modules": Object { - "docsMetadata": "~docs/community-route-aa2.json", - }, - "path": "/community", - "priority": -1, - "routes": Array [ - Object { - "component": "@theme/DocItem", - "exact": true, - "modules": Object { - "content": "@site/community_versioned_docs/version-1.0.0/team.md", - }, - "path": "/community/team", - }, - ], - }, -] -`; - -exports[`versioned website (community) content: sidebars needed for each version 1`] = ` -Object { - "1.0.0": Set { - "version-1.0.0/community", - }, - "next": Set { - "community", - }, -} -`; - -exports[`versioned website content: all sidebars 1`] = ` -Object { - "docs": Array [ - Object { - "collapsed": true, - "items": Array [ - Object { - "href": "/docs/next/foo/barSlug", - "label": "bar", - "type": "link", - }, - ], - "label": "Test", - "type": "category", - }, - Object { - "collapsed": true, - "items": Array [ - Object { - "href": "/docs/next/", - "label": "hello", - "type": "link", - }, - ], - "label": "Guides", - "type": "category", - }, - ], - "version-1.0.0/docs": Array [ - Object { - "collapsed": true, - "items": Array [ - Object { - "href": "/docs/1.0.0/foo/barSlug", - "label": "bar", - "type": "link", - }, - Object { - "href": "/docs/1.0.0/foo/baz", - "label": "baz", - "type": "link", - }, - ], - "label": "Test", - "type": "category", - }, - Object { - "collapsed": true, - "items": Array [ - Object { - "href": "/docs/1.0.0/", - "label": "hello", - "type": "link", - }, - ], - "label": "Guides", - "type": "category", - }, - ], - "version-1.0.1/docs": Array [ - Object { - "collapsed": true, - "items": Array [ - Object { - "href": "/docs/foo/bar", - "label": "bar", - "type": "link", - }, - ], - "label": "Test", - "type": "category", - }, - Object { - "collapsed": true, - "items": Array [ - Object { - "href": "/docs/", - "label": "hello", - "type": "link", - }, - ], - "label": "Guides", - "type": "category", - }, - ], -} -`; - -exports[`versioned website content: base metadata for first version 1`] = ` -Object { - "docsSidebars": Object { - "version-1.0.0/docs": Array [ - Object { - "collapsed": true, - "items": Array [ - Object { - "href": "/docs/1.0.0/foo/barSlug", - "label": "bar", - "type": "link", - }, - Object { - "href": "/docs/1.0.0/foo/baz", - "label": "baz", - "type": "link", - }, - ], - "label": "Test", - "type": "category", - }, - Object { - "collapsed": true, - "items": Array [ - Object { - "href": "/docs/1.0.0/", - "label": "hello", - "type": "link", - }, - ], - "label": "Guides", - "type": "category", - }, - ], - }, - "permalinkToSidebar": Object { - "/docs/1.0.0/": "version-1.0.0/docs", - "/docs/1.0.0/foo/barSlug": "version-1.0.0/docs", - "/docs/1.0.0/foo/baz": "version-1.0.0/docs", - }, - "version": "1.0.0", -} -`; - -exports[`versioned website content: base metadata for latest version 1`] = ` -Object { - "docsSidebars": Object { - "version-1.0.1/docs": Array [ - Object { - "collapsed": true, - "items": Array [ - Object { - "href": "/docs/foo/bar", - "label": "bar", - "type": "link", - }, - ], - "label": "Test", - "type": "category", - }, - Object { - "collapsed": true, - "items": Array [ - Object { - "href": "/docs/", - "label": "hello", - "type": "link", - }, - ], - "label": "Guides", - "type": "category", - }, - ], - }, - "permalinkToSidebar": Object { - "/docs/": "version-1.0.1/docs", - "/docs/foo/bar": "version-1.0.1/docs", - }, - "version": "1.0.1", -} -`; - -exports[`versioned website content: base metadata for next version 1`] = ` -Object { - "docsSidebars": Object { - "docs": Array [ - Object { - "collapsed": true, - "items": Array [ - Object { - "href": "/docs/next/foo/barSlug", - "label": "bar", - "type": "link", - }, - ], - "label": "Test", - "type": "category", - }, - Object { - "collapsed": true, - "items": Array [ - Object { - "href": "/docs/next/", - "label": "hello", - "type": "link", - }, - ], - "label": "Guides", - "type": "category", - }, - ], - }, - "permalinkToSidebar": Object { - "/docs/next/": "docs", - "/docs/next/foo/barSlug": "docs", - }, - "version": "next", -} -`; - -exports[`versioned website content: data 1`] = ` -Object { - "docs-1-0-0-route-660.json": "{ - \\"docsSidebars\\": { - \\"version-1.0.0/docs\\": [ - { - \\"collapsed\\": true, - \\"type\\": \\"category\\", - \\"label\\": \\"Test\\", - \\"items\\": [ - { - \\"type\\": \\"link\\", - \\"label\\": \\"bar\\", - \\"href\\": \\"/docs/1.0.0/foo/barSlug\\" - }, - { - \\"type\\": \\"link\\", - \\"label\\": \\"baz\\", - \\"href\\": \\"/docs/1.0.0/foo/baz\\" - } - ] - }, - { - \\"collapsed\\": true, - \\"type\\": \\"category\\", - \\"label\\": \\"Guides\\", - \\"items\\": [ - { - \\"type\\": \\"link\\", - \\"label\\": \\"hello\\", - \\"href\\": \\"/docs/1.0.0/\\" - } - ] - } - ] - }, - \\"permalinkToSidebar\\": { - \\"/docs/1.0.0/\\": \\"version-1.0.0/docs\\", - \\"/docs/1.0.0/foo/barSlug\\": \\"version-1.0.0/docs\\", - \\"/docs/1.0.0/foo/baz\\": \\"version-1.0.0/docs\\" - }, - \\"version\\": \\"1.0.0\\" -}", - "docs-next-route-1c8.json": "{ - \\"docsSidebars\\": { - \\"docs\\": [ - { - \\"collapsed\\": true, - \\"type\\": \\"category\\", - \\"label\\": \\"Test\\", - \\"items\\": [ - { - \\"type\\": \\"link\\", - \\"label\\": \\"bar\\", - \\"href\\": \\"/docs/next/foo/barSlug\\" - } - ] - }, - { - \\"collapsed\\": true, - \\"type\\": \\"category\\", - \\"label\\": \\"Guides\\", - \\"items\\": [ - { - \\"type\\": \\"link\\", - \\"label\\": \\"hello\\", - \\"href\\": \\"/docs/next/\\" - } - ] - } - ] - }, - \\"permalinkToSidebar\\": { - \\"/docs/next/\\": \\"docs\\", - \\"/docs/next/foo/barSlug\\": \\"docs\\" - }, - \\"version\\": \\"next\\" -}", - "docs-route-ff2.json": "{ - \\"docsSidebars\\": { - \\"version-1.0.1/docs\\": [ - { - \\"collapsed\\": true, - \\"type\\": \\"category\\", - \\"label\\": \\"Test\\", - \\"items\\": [ - { - \\"type\\": \\"link\\", - \\"label\\": \\"bar\\", - \\"href\\": \\"/docs/foo/bar\\" - } - ] - }, - { - \\"collapsed\\": true, - \\"type\\": \\"category\\", - \\"label\\": \\"Guides\\", - \\"items\\": [ - { - \\"type\\": \\"link\\", - \\"label\\": \\"hello\\", - \\"href\\": \\"/docs/\\" - } - ] - } - ] - }, - \\"permalinkToSidebar\\": { - \\"/docs/\\": \\"version-1.0.1/docs\\", - \\"/docs/foo/bar\\": \\"version-1.0.1/docs\\" - }, - \\"version\\": \\"1.0.1\\" -}", - "docs-with-slugs-route-335.json": "{ - \\"docsSidebars\\": {}, - \\"permalinkToSidebar\\": {}, - \\"version\\": \\"withSlugs\\" -}", - "site-docs-foo-bar-md-8c2.json": "{ - \\"unversionedId\\": \\"foo/bar\\", - \\"id\\": \\"foo/bar\\", - \\"isDocsHomePage\\": false, - \\"title\\": \\"bar\\", - \\"description\\": \\"This is next version of bar.\\", - \\"source\\": \\"@site/docs/foo/bar.md\\", - \\"slug\\": \\"/foo/barSlug\\", - \\"permalink\\": \\"/docs/next/foo/barSlug\\", - \\"version\\": \\"next\\", - \\"sidebar\\": \\"docs\\", - \\"next\\": { - \\"title\\": \\"hello\\", - \\"permalink\\": \\"/docs/next/\\" - } -}", - "site-docs-hello-md-9df.json": "{ - \\"unversionedId\\": \\"hello\\", - \\"id\\": \\"hello\\", - \\"isDocsHomePage\\": true, - \\"title\\": \\"hello\\", - \\"description\\": \\"Hello next !\\", - \\"source\\": \\"@site/docs/hello.md\\", - \\"slug\\": \\"/\\", - \\"permalink\\": \\"/docs/next/\\", - \\"version\\": \\"next\\", - \\"sidebar\\": \\"docs\\", - \\"previous\\": { - \\"title\\": \\"bar\\", - \\"permalink\\": \\"/docs/next/foo/barSlug\\" - } -}", - "site-docs-slugs-absolute-slug-md-4e8.json": "{ - \\"unversionedId\\": \\"slugs/absoluteSlug\\", - \\"id\\": \\"slugs/absoluteSlug\\", - \\"isDocsHomePage\\": false, - \\"title\\": \\"absoluteSlug\\", - \\"description\\": \\"Lorem\\", - \\"source\\": \\"@site/docs/slugs/absoluteSlug.md\\", - \\"slug\\": \\"/absoluteSlug\\", - \\"permalink\\": \\"/docs/next/absoluteSlug\\", - \\"version\\": \\"next\\" -}", - "site-docs-slugs-relative-slug-md-d1c.json": "{ - \\"unversionedId\\": \\"slugs/relativeSlug\\", - \\"id\\": \\"slugs/relativeSlug\\", - \\"isDocsHomePage\\": false, - \\"title\\": \\"relativeSlug\\", - \\"description\\": \\"Lorem\\", - \\"source\\": \\"@site/docs/slugs/relativeSlug.md\\", - \\"slug\\": \\"/slugs/relativeSlug\\", - \\"permalink\\": \\"/docs/next/slugs/relativeSlug\\", - \\"version\\": \\"next\\" -}", - "site-docs-slugs-resolved-slug-md-02b.json": "{ - \\"unversionedId\\": \\"slugs/resolvedSlug\\", - \\"id\\": \\"slugs/resolvedSlug\\", - \\"isDocsHomePage\\": false, - \\"title\\": \\"resolvedSlug\\", - \\"description\\": \\"Lorem\\", - \\"source\\": \\"@site/docs/slugs/resolvedSlug.md\\", - \\"slug\\": \\"/slugs/hey/resolvedSlug\\", - \\"permalink\\": \\"/docs/next/slugs/hey/resolvedSlug\\", - \\"version\\": \\"next\\" -}", - "site-docs-slugs-try-to-escape-slug-md-70d.json": "{ - \\"unversionedId\\": \\"slugs/tryToEscapeSlug\\", - \\"id\\": \\"slugs/tryToEscapeSlug\\", - \\"isDocsHomePage\\": false, - \\"title\\": \\"tryToEscapeSlug\\", - \\"description\\": \\"Lorem\\", - \\"source\\": \\"@site/docs/slugs/tryToEscapeSlug.md\\", - \\"slug\\": \\"/tryToEscapeSlug\\", - \\"permalink\\": \\"/docs/next/tryToEscapeSlug\\", - \\"version\\": \\"next\\" -}", - "site-versioned-docs-version-1-0-0-foo-bar-md-7a6.json": "{ - \\"unversionedId\\": \\"foo/bar\\", - \\"id\\": \\"version-1.0.0/foo/bar\\", - \\"isDocsHomePage\\": false, - \\"title\\": \\"bar\\", - \\"description\\": \\"Bar 1.0.0 !\\", - \\"source\\": \\"@site/versioned_docs/version-1.0.0/foo/bar.md\\", - \\"slug\\": \\"/foo/barSlug\\", - \\"permalink\\": \\"/docs/1.0.0/foo/barSlug\\", - \\"version\\": \\"1.0.0\\", - \\"sidebar\\": \\"version-1.0.0/docs\\", - \\"next\\": { - \\"title\\": \\"baz\\", - \\"permalink\\": \\"/docs/1.0.0/foo/baz\\" - } -}", - "site-versioned-docs-version-1-0-0-foo-baz-md-883.json": "{ - \\"unversionedId\\": \\"foo/baz\\", - \\"id\\": \\"version-1.0.0/foo/baz\\", - \\"isDocsHomePage\\": false, - \\"title\\": \\"baz\\", - \\"description\\": \\"Baz 1.0.0 ! This will be deleted in next subsequent versions.\\", - \\"source\\": \\"@site/versioned_docs/version-1.0.0/foo/baz.md\\", - \\"slug\\": \\"/foo/baz\\", - \\"permalink\\": \\"/docs/1.0.0/foo/baz\\", - \\"version\\": \\"1.0.0\\", - \\"sidebar\\": \\"version-1.0.0/docs\\", - \\"previous\\": { - \\"title\\": \\"bar\\", - \\"permalink\\": \\"/docs/1.0.0/foo/barSlug\\" - }, - \\"next\\": { - \\"title\\": \\"hello\\", - \\"permalink\\": \\"/docs/1.0.0/\\" - } -}", - "site-versioned-docs-version-1-0-0-hello-md-3ef.json": "{ - \\"unversionedId\\": \\"hello\\", - \\"id\\": \\"version-1.0.0/hello\\", - \\"isDocsHomePage\\": true, - \\"title\\": \\"hello\\", - \\"description\\": \\"Hello 1.0.0 !\\", - \\"source\\": \\"@site/versioned_docs/version-1.0.0/hello.md\\", - \\"slug\\": \\"/\\", - \\"permalink\\": \\"/docs/1.0.0/\\", - \\"version\\": \\"1.0.0\\", - \\"sidebar\\": \\"version-1.0.0/docs\\", - \\"previous\\": { - \\"title\\": \\"baz\\", - \\"permalink\\": \\"/docs/1.0.0/foo/baz\\" - } -}", - "site-versioned-docs-version-1-0-1-foo-bar-md-7a3.json": "{ - \\"unversionedId\\": \\"foo/bar\\", - \\"id\\": \\"version-1.0.1/foo/bar\\", - \\"isDocsHomePage\\": false, - \\"title\\": \\"bar\\", - \\"description\\": \\"Bar 1.0.1 !\\", - \\"source\\": \\"@site/versioned_docs/version-1.0.1/foo/bar.md\\", - \\"slug\\": \\"/foo/bar\\", - \\"permalink\\": \\"/docs/foo/bar\\", - \\"version\\": \\"1.0.1\\", - \\"sidebar\\": \\"version-1.0.1/docs\\", - \\"next\\": { - \\"title\\": \\"hello\\", - \\"permalink\\": \\"/docs/\\" - } -}", - "site-versioned-docs-version-1-0-1-hello-md-0c7.json": "{ - \\"unversionedId\\": \\"hello\\", - \\"id\\": \\"version-1.0.1/hello\\", - \\"isDocsHomePage\\": true, - \\"title\\": \\"hello\\", - \\"description\\": \\"Hello 1.0.1 !\\", - \\"source\\": \\"@site/versioned_docs/version-1.0.1/hello.md\\", - \\"slug\\": \\"/\\", - \\"permalink\\": \\"/docs/\\", - \\"version\\": \\"1.0.1\\", - \\"sidebar\\": \\"version-1.0.1/docs\\", - \\"previous\\": { - \\"title\\": \\"bar\\", - \\"permalink\\": \\"/docs/foo/bar\\" - } -}", - "site-versioned-docs-version-with-slugs-root-absolute-slug-md-4d2.json": "{ - \\"unversionedId\\": \\"rootAbsoluteSlug\\", - \\"id\\": \\"version-withSlugs/rootAbsoluteSlug\\", - \\"isDocsHomePage\\": false, - \\"title\\": \\"rootAbsoluteSlug\\", - \\"description\\": \\"Lorem\\", - \\"source\\": \\"@site/versioned_docs/version-withSlugs/rootAbsoluteSlug.md\\", - \\"slug\\": \\"/rootAbsoluteSlug\\", - \\"permalink\\": \\"/docs/withSlugs/rootAbsoluteSlug\\", - \\"version\\": \\"withSlugs\\" -}", - "site-versioned-docs-version-with-slugs-root-relative-slug-md-32a.json": "{ - \\"unversionedId\\": \\"rootRelativeSlug\\", - \\"id\\": \\"version-withSlugs/rootRelativeSlug\\", - \\"isDocsHomePage\\": false, - \\"title\\": \\"rootRelativeSlug\\", - \\"description\\": \\"Lorem\\", - \\"source\\": \\"@site/versioned_docs/version-withSlugs/rootRelativeSlug.md\\", - \\"slug\\": \\"/rootRelativeSlug\\", - \\"permalink\\": \\"/docs/withSlugs/rootRelativeSlug\\", - \\"version\\": \\"withSlugs\\" -}", - "site-versioned-docs-version-with-slugs-root-resolved-slug-md-aee.json": "{ - \\"unversionedId\\": \\"rootResolvedSlug\\", - \\"id\\": \\"version-withSlugs/rootResolvedSlug\\", - \\"isDocsHomePage\\": false, - \\"title\\": \\"rootResolvedSlug\\", - \\"description\\": \\"Lorem\\", - \\"source\\": \\"@site/versioned_docs/version-withSlugs/rootResolvedSlug.md\\", - \\"slug\\": \\"/hey/rootResolvedSlug\\", - \\"permalink\\": \\"/docs/withSlugs/hey/rootResolvedSlug\\", - \\"version\\": \\"withSlugs\\" -}", - "site-versioned-docs-version-with-slugs-root-try-to-escape-slug-md-b5d.json": "{ - \\"unversionedId\\": \\"rootTryToEscapeSlug\\", - \\"id\\": \\"version-withSlugs/rootTryToEscapeSlug\\", - \\"isDocsHomePage\\": false, - \\"title\\": \\"rootTryToEscapeSlug\\", - \\"description\\": \\"Lorem\\", - \\"source\\": \\"@site/versioned_docs/version-withSlugs/rootTryToEscapeSlug.md\\", - \\"slug\\": \\"/rootTryToEscapeSlug\\", - \\"permalink\\": \\"/docs/withSlugs/rootTryToEscapeSlug\\", - \\"version\\": \\"withSlugs\\" -}", - "site-versioned-docs-version-with-slugs-slugs-absolute-slug-md-47a.json": "{ - \\"unversionedId\\": \\"slugs/absoluteSlug\\", - \\"id\\": \\"version-withSlugs/slugs/absoluteSlug\\", - \\"isDocsHomePage\\": false, - \\"title\\": \\"absoluteSlug\\", - \\"description\\": \\"Lorem\\", - \\"source\\": \\"@site/versioned_docs/version-withSlugs/slugs/absoluteSlug.md\\", - \\"slug\\": \\"/absoluteSlug\\", - \\"permalink\\": \\"/docs/withSlugs/absoluteSlug\\", - \\"version\\": \\"withSlugs\\" -}", - "site-versioned-docs-version-with-slugs-slugs-relative-slug-md-a95.json": "{ - \\"unversionedId\\": \\"slugs/relativeSlug\\", - \\"id\\": \\"version-withSlugs/slugs/relativeSlug\\", - \\"isDocsHomePage\\": false, - \\"title\\": \\"relativeSlug\\", - \\"description\\": \\"Lorem\\", - \\"source\\": \\"@site/versioned_docs/version-withSlugs/slugs/relativeSlug.md\\", - \\"slug\\": \\"/slugs/relativeSlug\\", - \\"permalink\\": \\"/docs/withSlugs/slugs/relativeSlug\\", - \\"version\\": \\"withSlugs\\" -}", - "site-versioned-docs-version-with-slugs-slugs-resolved-slug-md-5a1.json": "{ - \\"unversionedId\\": \\"slugs/resolvedSlug\\", - \\"id\\": \\"version-withSlugs/slugs/resolvedSlug\\", - \\"isDocsHomePage\\": false, - \\"title\\": \\"resolvedSlug\\", - \\"description\\": \\"Lorem\\", - \\"source\\": \\"@site/versioned_docs/version-withSlugs/slugs/resolvedSlug.md\\", - \\"slug\\": \\"/slugs/hey/resolvedSlug\\", - \\"permalink\\": \\"/docs/withSlugs/slugs/hey/resolvedSlug\\", - \\"version\\": \\"withSlugs\\" -}", - "site-versioned-docs-version-with-slugs-slugs-try-to-escape-slug-md-4e1.json": "{ - \\"unversionedId\\": \\"slugs/tryToEscapeSlug\\", - \\"id\\": \\"version-withSlugs/slugs/tryToEscapeSlug\\", - \\"isDocsHomePage\\": false, - \\"title\\": \\"tryToEscapeSlug\\", - \\"description\\": \\"Lorem\\", - \\"source\\": \\"@site/versioned_docs/version-withSlugs/slugs/tryToEscapeSlug.md\\", - \\"slug\\": \\"/tryToEscapeSlug\\", - \\"permalink\\": \\"/docs/withSlugs/tryToEscapeSlug\\", - \\"version\\": \\"withSlugs\\" -}", -} -`; - -exports[`versioned website content: global data 1`] = ` -Object { - "pluginName": Object { - "pluginId": Object { - "latestVersionName": "1.0.1", - "path": "/docs", - "versions": Array [ - Object { - "docs": Array [ - Object { - "id": "foo/bar", - "path": "/docs/next/foo/barSlug", - }, - Object { - "id": "hello", - "path": "/docs/next/", - }, - Object { - "id": "slugs/absoluteSlug", - "path": "/docs/next/absoluteSlug", - }, - Object { - "id": "slugs/relativeSlug", - "path": "/docs/next/slugs/relativeSlug", - }, - Object { - "id": "slugs/resolvedSlug", - "path": "/docs/next/slugs/hey/resolvedSlug", - }, - Object { - "id": "slugs/tryToEscapeSlug", - "path": "/docs/next/tryToEscapeSlug", - }, - ], - "mainDocId": "hello", - "name": "next", - "path": "/docs/next", - }, - Object { - "docs": Array [ - Object { - "id": "foo/bar", - "path": "/docs/foo/bar", - }, - Object { - "id": "hello", - "path": "/docs/", - }, - ], - "mainDocId": "hello", - "name": "1.0.1", - "path": "/docs", - }, - Object { - "docs": Array [ - Object { - "id": "foo/bar", - "path": "/docs/1.0.0/foo/barSlug", - }, - Object { - "id": "foo/baz", - "path": "/docs/1.0.0/foo/baz", - }, - Object { - "id": "hello", - "path": "/docs/1.0.0/", - }, - ], - "mainDocId": "hello", - "name": "1.0.0", - "path": "/docs/1.0.0", - }, - Object { - "docs": Array [ - Object { - "id": "rootAbsoluteSlug", - "path": "/docs/withSlugs/rootAbsoluteSlug", - }, - Object { - "id": "rootRelativeSlug", - "path": "/docs/withSlugs/rootRelativeSlug", - }, - Object { - "id": "rootResolvedSlug", - "path": "/docs/withSlugs/hey/rootResolvedSlug", - }, - Object { - "id": "rootTryToEscapeSlug", - "path": "/docs/withSlugs/rootTryToEscapeSlug", - }, - Object { - "id": "slugs/absoluteSlug", - "path": "/docs/withSlugs/absoluteSlug", - }, - Object { - "id": "slugs/relativeSlug", - "path": "/docs/withSlugs/slugs/relativeSlug", - }, - Object { - "id": "slugs/resolvedSlug", - "path": "/docs/withSlugs/slugs/hey/resolvedSlug", - }, - Object { - "id": "slugs/tryToEscapeSlug", - "path": "/docs/withSlugs/tryToEscapeSlug", - }, - ], - "mainDocId": "rootAbsoluteSlug", - "name": "withSlugs", - "path": "/docs/withSlugs", - }, - ], - }, - }, -} -`; - -exports[`versioned website content: route config 1`] = ` -Array [ - Object { - "component": "@theme/DocPage", - "exact": false, - "modules": Object { - "docsMetadata": "~docs/docs-1-0-0-route-660.json", - }, - "path": "/docs/1.0.0", - "priority": undefined, - "routes": Array [ - Object { - "component": "@theme/DocItem", - "exact": true, - "modules": Object { - "content": "@site/versioned_docs/version-1.0.0/hello.md", - }, - "path": "/docs/1.0.0/", - }, - Object { - "component": "@theme/DocItem", - "exact": true, - "modules": Object { - "content": "@site/versioned_docs/version-1.0.0/foo/bar.md", - }, - "path": "/docs/1.0.0/foo/barSlug", - }, - Object { - "component": "@theme/DocItem", - "exact": true, - "modules": Object { - "content": "@site/versioned_docs/version-1.0.0/foo/baz.md", - }, - "path": "/docs/1.0.0/foo/baz", - }, - ], - }, - Object { - "component": "@theme/DocPage", - "exact": false, - "modules": Object { - "docsMetadata": "~docs/docs-next-route-1c8.json", - }, - "path": "/docs/next", - "priority": undefined, - "routes": Array [ - Object { - "component": "@theme/DocItem", - "exact": true, - "modules": Object { - "content": "@site/docs/hello.md", - }, - "path": "/docs/next/", - }, - Object { - "component": "@theme/DocItem", - "exact": true, - "modules": Object { - "content": "@site/docs/slugs/absoluteSlug.md", - }, - "path": "/docs/next/absoluteSlug", - }, - Object { - "component": "@theme/DocItem", - "exact": true, - "modules": Object { - "content": "@site/docs/foo/bar.md", - }, - "path": "/docs/next/foo/barSlug", - }, - Object { - "component": "@theme/DocItem", - "exact": true, - "modules": Object { - "content": "@site/docs/slugs/resolvedSlug.md", - }, - "path": "/docs/next/slugs/hey/resolvedSlug", - }, - Object { - "component": "@theme/DocItem", - "exact": true, - "modules": Object { - "content": "@site/docs/slugs/relativeSlug.md", - }, - "path": "/docs/next/slugs/relativeSlug", - }, - Object { - "component": "@theme/DocItem", - "exact": true, - "modules": Object { - "content": "@site/docs/slugs/tryToEscapeSlug.md", - }, - "path": "/docs/next/tryToEscapeSlug", - }, - ], - }, - Object { - "component": "@theme/DocPage", - "exact": false, - "modules": Object { - "docsMetadata": "~docs/docs-with-slugs-route-335.json", - }, - "path": "/docs/withSlugs", - "priority": undefined, - "routes": Array [ - Object { - "component": "@theme/DocItem", - "exact": true, - "modules": Object { - "content": "@site/versioned_docs/version-withSlugs/slugs/absoluteSlug.md", - }, - "path": "/docs/withSlugs/absoluteSlug", - }, - Object { - "component": "@theme/DocItem", - "exact": true, - "modules": Object { - "content": "@site/versioned_docs/version-withSlugs/rootResolvedSlug.md", - }, - "path": "/docs/withSlugs/hey/rootResolvedSlug", - }, - Object { - "component": "@theme/DocItem", - "exact": true, - "modules": Object { - "content": "@site/versioned_docs/version-withSlugs/rootAbsoluteSlug.md", - }, - "path": "/docs/withSlugs/rootAbsoluteSlug", - }, - Object { - "component": "@theme/DocItem", - "exact": true, - "modules": Object { - "content": "@site/versioned_docs/version-withSlugs/rootRelativeSlug.md", - }, - "path": "/docs/withSlugs/rootRelativeSlug", - }, - Object { - "component": "@theme/DocItem", - "exact": true, - "modules": Object { - "content": "@site/versioned_docs/version-withSlugs/rootTryToEscapeSlug.md", - }, - "path": "/docs/withSlugs/rootTryToEscapeSlug", - }, - Object { - "component": "@theme/DocItem", - "exact": true, - "modules": Object { - "content": "@site/versioned_docs/version-withSlugs/slugs/resolvedSlug.md", - }, - "path": "/docs/withSlugs/slugs/hey/resolvedSlug", - }, - Object { - "component": "@theme/DocItem", - "exact": true, - "modules": Object { - "content": "@site/versioned_docs/version-withSlugs/slugs/relativeSlug.md", - }, - "path": "/docs/withSlugs/slugs/relativeSlug", - }, - Object { - "component": "@theme/DocItem", - "exact": true, - "modules": Object { - "content": "@site/versioned_docs/version-withSlugs/slugs/tryToEscapeSlug.md", - }, - "path": "/docs/withSlugs/tryToEscapeSlug", - }, - ], - }, - Object { - "component": "@theme/DocPage", - "exact": false, - "modules": Object { - "docsMetadata": "~docs/docs-route-ff2.json", - }, - "path": "/docs", - "priority": -1, - "routes": Array [ - Object { - "component": "@theme/DocItem", - "exact": true, - "modules": Object { - "content": "@site/versioned_docs/version-1.0.1/hello.md", - }, - "path": "/docs/", - }, - Object { - "component": "@theme/DocItem", - "exact": true, - "modules": Object { - "content": "@site/versioned_docs/version-1.0.1/foo/bar.md", - }, - "path": "/docs/foo/bar", - }, - ], - }, -] -`; - -exports[`versioned website content: sidebars needed for each version 1`] = ` -Object { - "1.0.0": Set { - "version-1.0.0/docs", - }, - "1.0.1": Set { - "version-1.0.1/docs", - }, - "next": Set { - "docs", - }, -} -`; diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/index.test.ts b/packages/docusaurus-plugin-content-docs/src/__tests__/index.test.ts index 1be94fec64e2..0685d628fb8c 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/index.test.ts +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/index.test.ts @@ -161,8 +161,8 @@ describe('simple website', () => { expect(matchPattern).not.toEqual([]); expect(matchPattern).toMatchInlineSnapshot(` Array [ - "docs/**/*.{md,mdx}", "sidebars.json", + "docs/**/*.{md,mdx}", ] `); expect(isMatch('docs/hello.md', matchPattern)).toEqual(true); @@ -294,14 +294,14 @@ describe('versioned website', () => { expect(matchPattern).not.toEqual([]); expect(matchPattern).toMatchInlineSnapshot(` Array [ + "sidebars.json", "docs/**/*.{md,mdx}", "versioned_sidebars/version-1.0.1-sidebars.json", - "versioned_sidebars/version-1.0.0-sidebars.json", - "versioned_sidebars/version-withSlugs-sidebars.json", "versioned_docs/version-1.0.1/**/*.{md,mdx}", + "versioned_sidebars/version-1.0.0-sidebars.json", "versioned_docs/version-1.0.0/**/*.{md,mdx}", + "versioned_sidebars/version-withSlugs-sidebars.json", "versioned_docs/version-withSlugs/**/*.{md,mdx}", - "sidebars.json", ] `); expect(isMatch('docs/hello.md', matchPattern)).toEqual(true); @@ -518,10 +518,10 @@ describe('versioned website (community)', () => { expect(matchPattern).not.toEqual([]); expect(matchPattern).toMatchInlineSnapshot(` Array [ + "community_sidebars.json", "community/**/*.{md,mdx}", "community_versioned_sidebars/version-1.0.0-sidebars.json", "community_versioned_docs/version-1.0.0/**/*.{md,mdx}", - "community_sidebars.json", ] `); expect(isMatch('community/team.md', matchPattern)).toEqual(true); diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/metadata.test.ts b/packages/docusaurus-plugin-content-docs/src/__tests__/metadata.test.ts index 52ffc0afea23..a6f7767f8105 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/metadata.test.ts +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/metadata.test.ts @@ -33,7 +33,7 @@ function createTestHelpers({ ) { const metadata = await processMetadata({ source, - refDir, + docsDir: refDir, context, options, env, @@ -51,7 +51,7 @@ function createTestHelpers({ ) { const metadata = await processMetadata({ source, - refDir, + docsDir: refDir, context, options, env, @@ -281,7 +281,7 @@ describe('simple site', () => { await expect( processMetadata({ source: 'invalid-id.md', - refDir: path.join(badSiteDir, 'docs'), + docsDir: path.join(badSiteDir, 'docs'), context, options: { routeBasePath, @@ -299,7 +299,7 @@ describe('simple site', () => { await expect( processMetadata({ source: 'docWithSlug.md', - refDir: path.join(badSiteDir, 'docs'), + docsDir: path.join(badSiteDir, 'docs'), context, options: { routeBasePath, diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/pluginOptionSchema.test.ts b/packages/docusaurus-plugin-content-docs/src/__tests__/pluginOptionSchema.test.ts index 2ef791c8681e..9ea15ff81048 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/pluginOptionSchema.test.ts +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/pluginOptionSchema.test.ts @@ -19,6 +19,7 @@ describe('normalizeDocsPluginOptions', () => { test('should accept correctly defined user options', async () => { const userOptions = { + id: 'default', path: 'my-docs', // Path to data on filesystem, relative to site dir. routeBasePath: 'my-docs', // URL Route. homePageId: 'home', // Document id for docs home page. diff --git a/packages/docusaurus-plugin-content-docs/src/env.ts b/packages/docusaurus-plugin-content-docs/src/env.ts index 9e39c20f26e6..909e728d9937 100644 --- a/packages/docusaurus-plugin-content-docs/src/env.ts +++ b/packages/docusaurus-plugin-content-docs/src/env.ts @@ -7,7 +7,7 @@ import path from 'path'; import fs from 'fs-extra'; -import {VersioningEnv, Env, PluginOptions} from './types'; +import {VersioningEnv, Env, PluginOptions, VersionMetadata} from './types'; import { VERSIONS_JSON_FILE, VERSIONED_DOCS_DIR, @@ -83,12 +83,6 @@ function readVersionNames( return versions; } -type VersionMetadata = { - versionName: string; - docsPath: string; - sidebarPath: string; -}; - function createVersionMetadata({ versionName, siteDir, diff --git a/packages/docusaurus-plugin-content-docs/src/index.ts b/packages/docusaurus-plugin-content-docs/src/index.ts index 4a904ba72294..807569f04ffa 100644 --- a/packages/docusaurus-plugin-content-docs/src/index.ts +++ b/packages/docusaurus-plugin-content-docs/src/index.ts @@ -59,12 +59,14 @@ import { DocsVersion, GlobalVersion, GlobalDoc, + VersionMetadata, } from './types'; import {Configuration} from 'webpack'; import {docsVersion} from './version'; -import {VERSIONS_JSON_FILE} from './constants'; +import {CURRENT_VERSION_NAME, VERSIONS_JSON_FILE} from './constants'; import {PluginOptionSchema} from './pluginOptionSchema'; import {ValidationError} from '@hapi/joi'; +import {flatten} from 'lodash'; export default function pluginContentDocs( context: LoadContext, @@ -88,28 +90,35 @@ export default function pluginContentDocs( const aliasedSource = (source: string) => `~docs/${path.relative(pluginDataDirRoot, source)}`; + // TODO remove soon! // Versioning. const env = loadEnv(siteDir, pluginId, { disableVersioning: options.disableVersioning, }); const {versioning} = env; - const { - versions, - docsDir: versionedDir, - sidebarsDir: versionedSidebarsDir, - } = versioning; - const versionsNames = versions.map((version) => `version-${version}`); + const {versions, docsDir: versionedDir} = versioning; + + // TODO refactor + function getVersionPath(versionName: string) { + if (versionName === versioning.latestVersion) { + return ''; + } + if (versionName === CURRENT_VERSION_NAME) { + return 'next'; + } + return versionName; + } async function processDocsMetadata({ source, - refDir, + versionMetadata, }: { source: string; - refDir: string; + versionMetadata: VersionMetadata; }) { return processMetadata({ source, - refDir, + versionMetadata, context, options, env, @@ -160,93 +169,74 @@ export default function pluginContentDocs( }, getPathsToWatch() { - const {include} = options; - let globPattern = include.map((pattern) => `${docsDir}/${pattern}`); - if (versioning.enabled) { - const docsGlob = include - .map((pattern) => - versionsNames.map( - (versionName) => `${versionedDir}/${versionName}/${pattern}`, - ), - ) - .reduce((a, b) => a.concat(b), []); - const sidebarsGlob = versionsNames.map( - (versionName) => - `${versionedSidebarsDir}/${versionName}-sidebars.json`, - ); - globPattern = [...globPattern, ...sidebarsGlob, ...docsGlob]; + function getVersionPathsToWatch(version: VersionMetadata): string[] { + return [ + version.sidebarPath, + ...options.include.map((pattern) => `${version.docsPath}/${pattern}`), + ]; } - return [...globPattern, options.sidebarPath]; + + return flatten(versionsMetadata.map(getVersionPathsToWatch)); }, async loadContent() { - const {includeCurrentVersion, include, sidebarPath} = options; - - // Prepare metadata container. - const docsMetadataRaw: DocsMetadataRaw = {}; - const docsPromises = []; + const {include} = options; - // Metadata for current docs files. - if (includeCurrentVersion) { + async function withDocFiles(version: VersionMetadata) { const docsFiles = await globby(include, { - cwd: docsDir, + cwd: version.docsPath, }); - docsPromises.push( - Promise.all( - docsFiles.map(async (source) => { - const metadata: MetadataRaw = await processDocsMetadata({ - source, - refDir: docsDir, - }); - docsMetadataRaw[metadata.id] = metadata; - }), - ), - ); + return {version, docsFiles}; } - // Metadata for versioned docs. - if (versioning.enabled) { - const versionedGlob = include - .map((pattern) => - versionsNames.map((versionName) => `${versionName}/${pattern}`), - ) - .reduce((a, b) => a.concat(b), []); - const versionedFiles = await globby(versionedGlob, { - cwd: versionedDir, - }); - docsPromises.push( - Promise.all( - versionedFiles.map(async (source) => { - const metadata = await processDocsMetadata({ - source, - refDir: versionedDir, - }); - docsMetadataRaw[metadata.id] = metadata; - }), - ), + async function processVersionDocs( + versionMetadata: VersionMetadata, + ): Promise { + const {docsFiles} = await withDocFiles(versionMetadata); + return Promise.all( + docsFiles.map(async (source) => { + return processDocsMetadata({ + source, + versionMetadata, + }); + }), ); } - // Load the sidebars and create docs ordering. - const sidebarPaths = versionsNames.map( - (versionName) => `${versionedSidebarsDir}/${versionName}-sidebars.json`, - ); + // TODO refactor side-effectful + // Prepare metadata container. + const docsMetadataRaw: DocsMetadataRaw = {}; - if (includeCurrentVersion) { - sidebarPaths.unshift(sidebarPath); - } + await Promise.all( + versionsMetadata.map(async (version) => { + const versionDocs = await processVersionDocs(version); - const loadedSidebars: Sidebar = loadSidebars(sidebarPaths); - const order: Order = createOrder(loadedSidebars); + // TODO legacy side-effect, refactor! + versionDocs.forEach((versionDoc) => { + docsMetadataRaw[versionDoc.id] = versionDoc; + }); + }), + ); - await Promise.all(docsPromises); + const loadedSidebars: Sidebar = loadSidebars( + versionsMetadata.map((version) => version.sidebarPath), + ); + const order: Order = createOrder(loadedSidebars); // Construct inter-metadata relationship in docsMetadata. const docsMetadata: DocsMetadata = {}; const permalinkToSidebar: PermalinkToSidebar = {}; const versionToSidebars: VersionToSidebars = {}; - function toDocNavLink(doc: MetadataRaw) { + function toDocNavLink(docId: string) { + const doc = docsMetadataRaw[docId]; + if (!doc) { + throw new Error( + `no doc for id=${docId} + All ids= +- ${Object.keys(docsMetadataRaw).join('\n- ')}`, + ); + } return { title: doc.title, permalink: doc.permalink, @@ -256,10 +246,9 @@ export default function pluginContentDocs( Object.keys(docsMetadataRaw).forEach((currentID) => { const {next: nextID, previous: previousID, sidebar} = order[currentID] ?? {}; - const previous = previousID - ? toDocNavLink(docsMetadataRaw[previousID]!) - : undefined; - const next = nextID ? toDocNavLink(docsMetadataRaw[nextID]) : undefined; + + const previous = previousID ? toDocNavLink(previousID) : undefined; + const next = nextID ? toDocNavLink(nextID) : undefined; docsMetadata[currentID] = { ...docsMetadataRaw[currentID], @@ -467,11 +456,10 @@ Available document ids= Object.keys(docsMetadataByVersion).map(async (version) => { const docsMetadata = docsMetadataByVersion[version]; - const isLatestVersion = version === versioning.latestVersion; const docsBaseRoute = normalizeUrl([ baseUrl, routeBasePath, - isLatestVersion ? '' : version, + getVersionPath(version), ]); const docsBaseMetadata = createDocsBaseMetadata(version); @@ -494,7 +482,8 @@ Available document ids= pluginInstanceGlobalData.versions = sortBy( pluginInstanceGlobalData.versions, (versionMetadata: GlobalVersion) => { - const orderedVersionNames = ['next', ...versions]; + // TODO keep order of versions.json? + const orderedVersionNames = [CURRENT_VERSION_NAME, ...versions]; return orderedVersionNames.indexOf(versionMetadata.name!); }, ); diff --git a/packages/docusaurus-plugin-content-docs/src/metadata.ts b/packages/docusaurus-plugin-content-docs/src/metadata.ts index 74159f3c1414..af8722bbdc99 100644 --- a/packages/docusaurus-plugin-content-docs/src/metadata.ts +++ b/packages/docusaurus-plugin-content-docs/src/metadata.ts @@ -20,47 +20,10 @@ import { LastUpdateData, MetadataOptions, Env, - VersioningEnv, + VersionMetadata, } from './types'; import getSlug from './slug'; -import {escapeRegExp} from 'lodash'; - -function removeVersionPrefix(str: string, version: string): string { - return str.replace(new RegExp(`^version-${escapeRegExp(version)}/?`), ''); -} - -function inferVersion( - dirName: string, - versioning: VersioningEnv, -): string | undefined { - if (!versioning.enabled) { - return undefined; - } - if (/^version-/.test(dirName)) { - const inferredVersion = dirName - .split('/', 1) - .shift()! - .replace(/^version-/, ''); - if (inferredVersion && versioning.versions.includes(inferredVersion)) { - return inferredVersion; - } - throw new Error( - `Can't infer version from folder=${dirName} -Expected versions: -- ${versioning.versions.join('- ')}`, - ); - } else { - return 'next'; - } -} - -type Args = { - source: string; - refDir: string; - context: LoadContext; - options: MetadataOptions; - env: Env; -}; +import {CURRENT_VERSION_NAME} from './constants'; async function lastUpdated( filePath: string, @@ -91,29 +54,51 @@ async function lastUpdated( export default async function processMetadata({ source, - refDir, + versionMetadata, context, options, env, -}: Args): Promise { +}: { + source: string; + versionMetadata: VersionMetadata; + context: LoadContext; + options: MetadataOptions; + env: Env; +}): Promise { const {routeBasePath, editUrl, homePageId} = options; const {siteDir, baseUrl} = context; const {versioning} = env; - const filePath = path.join(refDir, source); + const filePath = path.join(versionMetadata.docsPath, source); const fileMarkdownPromise = parseMarkdownFile(filePath); const lastUpdatedPromise = lastUpdated(filePath, options); - const dirNameWithVersion = path.dirname(source); // ex: version-1.0.0/foo - const version = inferVersion(dirNameWithVersion, versioning); // ex: 1.0.0 - const dirNameWithoutVersion = // ex: foo - version && version !== 'next' - ? removeVersionPrefix(dirNameWithVersion, version) - : dirNameWithVersion; + const docsFileDirName = path.dirname(source); // ex: api/myDoc -> api + + console.log({source, docsFileDirName}); + + // TODO compatibility with legacy + const version = versionMetadata.versionName; + /* + versionMetadata.versionName === CURRENT_VERSION_NAME + ? 'next' + : versionMetadata.versionName; + + */ + + // TODO for legacy compatibility + function getVersionPath(versionName: string) { + if (!versioning.enabled || versionName === versioning.latestVersion) { + return ''; + } + if (versionName === CURRENT_VERSION_NAME) { + return 'next'; + } + return versionName; + } // The version portion of the url path. Eg: 'next', '1.0.0', and ''. - const versionPath = - version && version !== versioning.latestVersion ? version : ''; + const versionPath = getVersionPath(versionMetadata.versionName); const relativePath = path.relative(siteDir, filePath); @@ -122,19 +107,30 @@ export default async function processMetadata({ const {frontMatter = {}, excerpt} = await fileMarkdownPromise; const {sidebar_label, custom_edit_url} = frontMatter; - // Default base id is the file name. const baseID: string = frontMatter.id || path.basename(source, path.extname(source)); if (baseID.includes('/')) { - throw new Error('Document id cannot include "/".'); + throw new Error(`Document id [${baseID}]cannot include "/".`); } - // test for website/docs folder, not a versioned folder - // TODO legacy test, looks bad - const isCurrrentDocs = dirNameWithVersion === '.'; - const id = isCurrrentDocs ? baseID : `${dirNameWithVersion}/${baseID}`; - const unversionedId = version ? removeVersionPrefix(id, version) : id; + // TODO legacy retrocompatibility + // The same doc in 2 distinct version could keep the same id, + // we just need to namespace the data by version + const versionIdPart = + versionMetadata.versionName === CURRENT_VERSION_NAME + ? '' + : `version-${versionMetadata.versionName}/`; + + // TODO legacy retrocompatibility + // I think it's bad to affect the frontmatter id with the dirname + const dirNameIdPart = docsFileDirName === '.' ? '' : `${docsFileDirName}/`; + + // TODO legacy composite id, requires a breaking change to modify this + const id = `${versionIdPart}${dirNameIdPart}${baseID}`; + + const unversionedId = baseID; + // TODO remove soon, deprecated homePageId const isDocsHomePage = unversionedId === (homePageId ?? '_index'); if (frontMatter.slug && isDocsHomePage) { throw new Error( @@ -146,7 +142,7 @@ export default async function processMetadata({ ? '/' : getSlug({ baseID, - dirName: dirNameWithoutVersion, + dirName: docsFileDirName, frontmatterSlug: frontMatter.slug, }); diff --git a/packages/docusaurus-plugin-content-docs/src/pluginOptionSchema.ts b/packages/docusaurus-plugin-content-docs/src/pluginOptionSchema.ts index 228b3327fce5..d02128f2409d 100644 --- a/packages/docusaurus-plugin-content-docs/src/pluginOptionSchema.ts +++ b/packages/docusaurus-plugin-content-docs/src/pluginOptionSchema.ts @@ -13,7 +13,7 @@ import { URISchema, } from '@docusaurus/utils-validation'; -export const DEFAULT_OPTIONS: PluginOptions = { +export const DEFAULT_OPTIONS: Omit = { path: 'docs', // Path to data on filesystem, relative to site dir. routeBasePath: 'docs', // URL Route. homePageId: undefined, // TODO remove soon, deprecated diff --git a/packages/docusaurus-plugin-content-docs/src/types.ts b/packages/docusaurus-plugin-content-docs/src/types.ts index b9424a6e6ae8..bf8a30124407 100644 --- a/packages/docusaurus-plugin-content-docs/src/types.ts +++ b/packages/docusaurus-plugin-content-docs/src/types.ts @@ -8,6 +8,12 @@ // eslint-disable-next-line spaced-comment /// +export type VersionMetadata = { + versionName: string; + docsPath: string; + sidebarPath: string; +}; + export type DocsVersion = string | null; // null = unversioned sites export interface MetadataOptions { From 19d07fb1e6c5376011d92f37bfdb8369dae07cee Mon Sep 17 00:00:00 2001 From: slorber Date: Sat, 8 Aug 2020 22:13:55 +0200 Subject: [PATCH 05/30] refactors --- .../src/index.ts | 51 ++++++------------- .../src/metadata.ts | 21 ++------ 2 files changed, 20 insertions(+), 52 deletions(-) diff --git a/packages/docusaurus-plugin-content-docs/src/index.ts b/packages/docusaurus-plugin-content-docs/src/index.ts index 807569f04ffa..f39e5a5ea78d 100644 --- a/packages/docusaurus-plugin-content-docs/src/index.ts +++ b/packages/docusaurus-plugin-content-docs/src/index.ts @@ -66,7 +66,7 @@ import {docsVersion} from './version'; import {CURRENT_VERSION_NAME, VERSIONS_JSON_FILE} from './constants'; import {PluginOptionSchema} from './pluginOptionSchema'; import {ValidationError} from '@hapi/joi'; -import {flatten} from 'lodash'; +import {flatten, keyBy} from 'lodash'; export default function pluginContentDocs( context: LoadContext, @@ -75,7 +75,6 @@ export default function pluginContentDocs( const {siteDir, generatedFilesDir, baseUrl} = context; const versionsMetadata = readVersionsMetadata(siteDir, options); - console.log(versionsMetadata); const docsDir = path.resolve(siteDir, options.path); @@ -182,17 +181,12 @@ export default function pluginContentDocs( async loadContent() { const {include} = options; - async function withDocFiles(version: VersionMetadata) { - const docsFiles = await globby(include, { - cwd: version.docsPath, - }); - return {version, docsFiles}; - } - async function processVersionDocs( versionMetadata: VersionMetadata, ): Promise { - const {docsFiles} = await withDocFiles(versionMetadata); + const docsFiles = await globby(include, { + cwd: versionMetadata.docsPath, + }); return Promise.all( docsFiles.map(async (source) => { return processDocsMetadata({ @@ -203,21 +197,13 @@ export default function pluginContentDocs( ); } - // TODO refactor side-effectful - // Prepare metadata container. - const docsMetadataRaw: DocsMetadataRaw = {}; - - await Promise.all( - versionsMetadata.map(async (version) => { - const versionDocs = await processVersionDocs(version); - - // TODO legacy side-effect, refactor! - versionDocs.forEach((versionDoc) => { - docsMetadataRaw[versionDoc.id] = versionDoc; - }); - }), + // TODO, we should namespace docs by version! + const allDocs = flatten( + await Promise.all(versionsMetadata.map(processVersionDocs)), ); + const allDocsById: DocsMetadataRaw = keyBy(allDocs, (doc) => doc.id); + const loadedSidebars: Sidebar = loadSidebars( versionsMetadata.map((version) => version.sidebarPath), ); @@ -229,21 +215,14 @@ export default function pluginContentDocs( const versionToSidebars: VersionToSidebars = {}; function toDocNavLink(docId: string) { - const doc = docsMetadataRaw[docId]; - if (!doc) { - throw new Error( - `no doc for id=${docId} - All ids= -- ${Object.keys(docsMetadataRaw).join('\n- ')}`, - ); - } + const doc = allDocsById[docId]; return { title: doc.title, permalink: doc.permalink, }; } - Object.keys(docsMetadataRaw).forEach((currentID) => { + Object.keys(allDocsById).forEach((currentID) => { const {next: nextID, previous: previousID, sidebar} = order[currentID] ?? {}; @@ -251,14 +230,14 @@ export default function pluginContentDocs( const next = nextID ? toDocNavLink(nextID) : undefined; docsMetadata[currentID] = { - ...docsMetadataRaw[currentID], + ...allDocsById[currentID], sidebar, previous, next, }; // sourceToPermalink and permalinkToSidebar mapping. - const {source, permalink, version} = docsMetadataRaw[currentID]; + const {source, permalink, version} = allDocsById[currentID]; sourceToPermalink[source] = permalink; if (sidebar) { permalinkToSidebar[permalink] = sidebar; @@ -273,13 +252,13 @@ export default function pluginContentDocs( const convertDocLink = (item: SidebarItemDoc): SidebarItemLink => { const docId = item.id; - const docMetadata = docsMetadataRaw[docId]; + const docMetadata = allDocsById[docId]; if (!docMetadata) { throw new Error( `Bad sidebars file. The document id '${docId}' was used in the sidebar, but no document with this id could be found. Available document ids= -- ${Object.keys(docsMetadataRaw).sort().join('\n- ')}`, +- ${Object.keys(allDocsById).sort().join('\n- ')}`, ); } diff --git a/packages/docusaurus-plugin-content-docs/src/metadata.ts b/packages/docusaurus-plugin-content-docs/src/metadata.ts index af8722bbdc99..478b1f05c028 100644 --- a/packages/docusaurus-plugin-content-docs/src/metadata.ts +++ b/packages/docusaurus-plugin-content-docs/src/metadata.ts @@ -75,19 +75,10 @@ export default async function processMetadata({ const docsFileDirName = path.dirname(source); // ex: api/myDoc -> api - console.log({source, docsFileDirName}); - - // TODO compatibility with legacy - const version = versionMetadata.versionName; - /* - versionMetadata.versionName === CURRENT_VERSION_NAME - ? 'next' - : versionMetadata.versionName; - - */ + const {versionName} = versionMetadata; // TODO for legacy compatibility - function getVersionPath(versionName: string) { + function getVersionPath() { if (!versioning.enabled || versionName === versioning.latestVersion) { return ''; } @@ -98,11 +89,9 @@ export default async function processMetadata({ } // The version portion of the url path. Eg: 'next', '1.0.0', and ''. - const versionPath = getVersionPath(versionMetadata.versionName); - - const relativePath = path.relative(siteDir, filePath); + const versionPath = getVersionPath(); - const docsEditUrl = getEditUrl(relativePath, editUrl); + const docsEditUrl = getEditUrl(path.relative(siteDir, filePath), editUrl); const {frontMatter = {}, excerpt} = await fileMarkdownPromise; const {sidebar_label, custom_edit_url} = frontMatter; @@ -174,7 +163,7 @@ export default async function processMetadata({ slug: docSlug, permalink, editUrl: custom_edit_url !== undefined ? custom_edit_url : docsEditUrl, - version, + version: versionName, lastUpdatedBy, lastUpdatedAt, sidebar_label, From 0186a27680690be9a1ebf1ce707bfa8de65c4e0d Mon Sep 17 00:00:00 2001 From: slorber Date: Tue, 11 Aug 2020 22:57:06 +0200 Subject: [PATCH 06/30] stable docs refactor --- .../src/__tests__/metadata.test.ts | 4 +- .../docusaurus-plugin-content-docs/src/env.ts | 2 +- .../src/index.ts | 194 ++++++++---------- .../src/metadata.ts | 6 +- .../src/types.ts | 28 +-- .../src/theme/DocPage/index.tsx | 8 +- 6 files changed, 114 insertions(+), 128 deletions(-) diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/metadata.test.ts b/packages/docusaurus-plugin-content-docs/src/__tests__/metadata.test.ts index a6f7767f8105..98316c033599 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/metadata.test.ts +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/metadata.test.ts @@ -9,7 +9,7 @@ import path from 'path'; import {loadContext} from '@docusaurus/core/src/server/index'; import processMetadata from '../metadata'; import loadEnv from '../env'; -import {MetadataRaw, Env, MetadataOptions} from '../types'; +import {DocMetadataRaw, Env, MetadataOptions} from '../types'; import {LoadContext} from '@docusaurus/types'; import {DEFAULT_PLUGIN_ID} from '@docusaurus/core/lib/constants'; @@ -29,7 +29,7 @@ function createTestHelpers({ async function testMeta( refDir: string, source: string, - expectedMetadata: Omit, + expectedMetadata: Omit, ) { const metadata = await processMetadata({ source, diff --git a/packages/docusaurus-plugin-content-docs/src/env.ts b/packages/docusaurus-plugin-content-docs/src/env.ts index 909e728d9937..36da1058b46c 100644 --- a/packages/docusaurus-plugin-content-docs/src/env.ts +++ b/packages/docusaurus-plugin-content-docs/src/env.ts @@ -158,7 +158,7 @@ export default function loadEnv( const versioning: VersioningEnv = { enabled: false, versions: [], - latestVersion: null, + latestVersion: CURRENT_VERSION_NAME, docsDir: '', sidebarsDir: '', }; diff --git a/packages/docusaurus-plugin-content-docs/src/index.ts b/packages/docusaurus-plugin-content-docs/src/index.ts index f39e5a5ea78d..934e7d9caa0b 100644 --- a/packages/docusaurus-plugin-content-docs/src/index.ts +++ b/packages/docusaurus-plugin-content-docs/src/index.ts @@ -48,18 +48,20 @@ import { SidebarItemLink, SidebarItemDoc, DocsSidebar, - DocsBaseMetadata, - MetadataRaw, + VersionMetadataProp, + DocMetadataRaw, DocsMetadataRaw, - Metadata, + DocMetadata, VersionToSidebars, SidebarItem, DocsSidebarItem, GlobalPluginData, - DocsVersion, + VersionName, GlobalVersion, GlobalDoc, VersionMetadata, + DocNavLink, + OrderMetadata, } from './types'; import {Configuration} from 'webpack'; import {docsVersion} from './version'; @@ -68,6 +70,13 @@ import {PluginOptionSchema} from './pluginOptionSchema'; import {ValidationError} from '@hapi/joi'; import {flatten, keyBy} from 'lodash'; +function toDocNavLink(doc: DocMetadata): DocNavLink { + return { + title: doc.title, + permalink: doc.permalink, + }; +} + export default function pluginContentDocs( context: LoadContext, options: PluginOptions, @@ -75,6 +84,7 @@ export default function pluginContentDocs( const {siteDir, generatedFilesDir, baseUrl} = context; const versionsMetadata = readVersionsMetadata(siteDir, options); + const versionNames = versionsMetadata.map((version) => version.versionName); const docsDir = path.resolve(siteDir, options.path); @@ -90,22 +100,20 @@ export default function pluginContentDocs( `~docs/${path.relative(pluginDataDirRoot, source)}`; // TODO remove soon! - // Versioning. - const env = loadEnv(siteDir, pluginId, { + const legacyVersioningEnv = loadEnv(siteDir, pluginId, { disableVersioning: options.disableVersioning, }); - const {versioning} = env; - const {versions, docsDir: versionedDir} = versioning; + const {latestVersion} = legacyVersioningEnv.versioning; // TODO refactor - function getVersionPath(versionName: string) { - if (versionName === versioning.latestVersion) { + function getVersionPath(version: VersionName) { + if (version === latestVersion) { return ''; } - if (versionName === CURRENT_VERSION_NAME) { + if (version === CURRENT_VERSION_NAME) { return 'next'; } - return versionName; + return version; } async function processDocsMetadata({ @@ -120,7 +128,7 @@ export default function pluginContentDocs( versionMetadata, context, options, - env, + env: legacyVersioningEnv, }); } @@ -183,7 +191,7 @@ export default function pluginContentDocs( async function processVersionDocs( versionMetadata: VersionMetadata, - ): Promise { + ): Promise { const docsFiles = await globby(include, { cwd: versionMetadata.docsPath, }); @@ -207,46 +215,48 @@ export default function pluginContentDocs( const loadedSidebars: Sidebar = loadSidebars( versionsMetadata.map((version) => version.sidebarPath), ); + const order: Order = createOrder(loadedSidebars); + function getDocOrder(docId: string): OrderMetadata { + return order[docId] ?? {}; + } // Construct inter-metadata relationship in docsMetadata. const docsMetadata: DocsMetadata = {}; const permalinkToSidebar: PermalinkToSidebar = {}; const versionToSidebars: VersionToSidebars = {}; - function toDocNavLink(docId: string) { - const doc = allDocsById[docId]; - return { - title: doc.title, - permalink: doc.permalink, - }; - } - - Object.keys(allDocsById).forEach((currentID) => { + function addSidebarData(doc: DocMetadataRaw): DocMetadata { const {next: nextID, previous: previousID, sidebar} = - order[currentID] ?? {}; + order[doc.id] ?? {}; - const previous = previousID ? toDocNavLink(previousID) : undefined; - const next = nextID ? toDocNavLink(nextID) : undefined; + const previous = previousID + ? toDocNavLink(allDocsById[previousID]) + : undefined; + const next = nextID ? toDocNavLink(allDocsById[nextID]) : undefined; - docsMetadata[currentID] = { - ...allDocsById[currentID], + return { + ...doc, sidebar, previous, next, }; + } + + Object.keys(allDocsById).forEach((currentID) => { + const {sidebar} = getDocOrder(currentID); + + docsMetadata[currentID] = addSidebarData(allDocsById[currentID]); // sourceToPermalink and permalinkToSidebar mapping. const {source, permalink, version} = allDocsById[currentID]; sourceToPermalink[source] = permalink; if (sidebar) { permalinkToSidebar[permalink] = sidebar; - if (versioning.enabled && version) { - if (!versionToSidebars[version]) { - versionToSidebars[version] = new Set(); - } - versionToSidebars[version].add(sidebar); + if (!versionToSidebars[version]) { + versionToSidebars[version] = new Set(); } + versionToSidebars[version].add(sidebar); } }); @@ -311,18 +321,28 @@ Available document ids= const {docLayoutComponent, docItemComponent, routeBasePath} = options; const {addRoute, createData, setGlobalData} = actions; + const docsMetadataByVersion = groupBy( + // sort to ensure consistent output for tests + Object.values(content.docsMetadata).sort((a, b) => + a.id.localeCompare(b.id), + ), + 'version', + ); + + const allVersionNames = Object.keys(docsMetadataByVersion); + const pluginInstanceGlobalData: GlobalPluginData = { path: normalizeUrl([baseUrl, options.routeBasePath]), - latestVersionName: versioning.latestVersion, + latestVersionName: latestVersion, // Initialized empty, will be mutated versions: [], }; setGlobalData(pluginInstanceGlobalData); - const createDocsBaseMetadata = ( - version: DocsVersion, - ): DocsBaseMetadata => { + const createVersionMetadataProp = ( + version: VersionName, + ): VersionMetadataProp => { const {docsSidebars, permalinkToSidebar, versionToSidebars} = content; const neededSidebars: Set = versionToSidebars[version!] || new Set(); @@ -340,8 +360,8 @@ Available document ids= }; }; - const genRoutes = async ( - metadataItems: Metadata[], + const createDocRoutes = async ( + metadataItems: DocMetadata[], ): Promise => { const routes = await Promise.all( metadataItems.map(async (metadataItem) => { @@ -369,101 +389,67 @@ Available document ids= // We want latest version route to have lower priority // Otherwise `/docs/next/foo` would match // `/docs/:route` instead of `/docs/next/:route`. - const getVersionRoutePriority = (version: DocsVersion) => - version === versioning.latestVersion ? -1 : undefined; - - // This is the base route of the document root (for a doc given version) - // (/docs, /docs/next, /docs/1.0 etc...) - // The component applies the layout and renders the appropriate doc - const addVersionRoute = async ( - docsBasePath: string, - docsBaseMetadata: DocsBaseMetadata, - docs: Metadata[], - priority?: number, - ) => { - const docsBaseMetadataPath = await createData( - `${docuHash(normalizeUrl([docsBasePath, ':route']))}.json`, - JSON.stringify(docsBaseMetadata, null, 2), + const getVersionRoutePriority = (version: VersionName) => + version === latestVersion ? -1 : undefined; + + async function handleVersion(version: VersionName) { + const docs = docsMetadataByVersion[version]; + + const versionPath = normalizeUrl([ + baseUrl, + routeBasePath, + getVersionPath(version), + ]); + const versionMetadataProp = createVersionMetadataProp(version); + + const versionMetadataPropPath = await createData( + `${docuHash(normalizeUrl([versionPath, ':route']))}.json`, + JSON.stringify(versionMetadataProp, null, 2), ); - const docsRoutes = await genRoutes(docs); + const docsRoutes = await createDocRoutes(docs); - const mainDoc: Metadata = + const versionMainDoc: DocMetadata = docs.find( (doc) => doc.unversionedId === options.homePageId || doc.slug === '/', ) ?? docs[0]; - const toGlobalDataDoc = (doc: Metadata): GlobalDoc => ({ + const toGlobalDataDoc = (doc: DocMetadata): GlobalDoc => ({ id: doc.unversionedId, path: doc.permalink, }); pluginInstanceGlobalData.versions.push({ - name: docsBaseMetadata.version, - path: docsBasePath, - mainDocId: mainDoc.unversionedId, + name: versionMetadataProp.version, + path: versionPath, + mainDocId: versionMainDoc.unversionedId, docs: docs .map(toGlobalDataDoc) // stable ordering, useful for tests .sort((a, b) => a.id.localeCompare(b.id)), }); + const priority = getVersionRoutePriority(version); + addRoute({ - path: docsBasePath, + path: versionPath, exact: false, // allow matching /docs/* as well component: docLayoutComponent, // main docs component (DocPage) routes: docsRoutes, // subroute for each doc modules: { - docsMetadata: aliasedSource(docsBaseMetadataPath), + versionMetadata: aliasedSource(versionMetadataPropPath), }, priority, }); - }; - // If versioning is enabled, we cleverly chunk the generated routes - // to be by version and pick only needed base metadata. - if (versioning.enabled) { - const docsMetadataByVersion = groupBy( - // sort to ensure consistent output for tests - Object.values(content.docsMetadata).sort((a, b) => - a.id.localeCompare(b.id), - ), - 'version', - ); - - await Promise.all( - Object.keys(docsMetadataByVersion).map(async (version) => { - const docsMetadata = docsMetadataByVersion[version]; - - const docsBaseRoute = normalizeUrl([ - baseUrl, - routeBasePath, - getVersionPath(version), - ]); - const docsBaseMetadata = createDocsBaseMetadata(version); - - await addVersionRoute( - docsBaseRoute, - docsBaseMetadata, - docsMetadata, - getVersionRoutePriority(version), - ); - }), - ); - } else { - const docsMetadata = Object.values(content.docsMetadata); - const docsBaseMetadata = createDocsBaseMetadata(null); - const docsBaseRoute = normalizeUrl([baseUrl, routeBasePath]); - await addVersionRoute(docsBaseRoute, docsBaseMetadata, docsMetadata); } - // ensure version ordering on the global data (latest first) + await Promise.all(allVersionNames.map(handleVersion)); + pluginInstanceGlobalData.versions = sortBy( pluginInstanceGlobalData.versions, (versionMetadata: GlobalVersion) => { - // TODO keep order of versions.json? - const orderedVersionNames = [CURRENT_VERSION_NAME, ...versions]; - return orderedVersionNames.indexOf(versionMetadata.name!); + return versionNames.indexOf(versionMetadata.name!); }, ); }, @@ -490,7 +476,7 @@ Available document ids= rules: [ { test: /(\.mdx?)$/, - include: [docsDir, versionedDir].filter(Boolean), + include: versionsMetadata.map((vmd) => vmd.docsPath), use: [ getCacheLoader(isServer), getBabelLoader(isServer), @@ -517,7 +503,7 @@ Available document ids= siteDir, docsDir, sourceToPermalink, - versionedDir, + versionedDir: legacyVersioningEnv.versioning.docsDir, }, }, ].filter(Boolean), diff --git a/packages/docusaurus-plugin-content-docs/src/metadata.ts b/packages/docusaurus-plugin-content-docs/src/metadata.ts index 478b1f05c028..fc0581701c50 100644 --- a/packages/docusaurus-plugin-content-docs/src/metadata.ts +++ b/packages/docusaurus-plugin-content-docs/src/metadata.ts @@ -16,7 +16,7 @@ import {LoadContext} from '@docusaurus/types'; import lastUpdate from './lastUpdate'; import { - MetadataRaw, + DocMetadataRaw, LastUpdateData, MetadataOptions, Env, @@ -64,7 +64,7 @@ export default async function processMetadata({ context: LoadContext; options: MetadataOptions; env: Env; -}): Promise { +}): Promise { const {routeBasePath, editUrl, homePageId} = options; const {siteDir, baseUrl} = context; const {versioning} = env; @@ -153,7 +153,7 @@ export default async function processMetadata({ // NodeJS optimization. // Adding properties to object after instantiation will cause hidden // class transitions. - const metadata: MetadataRaw = { + const metadata: DocMetadataRaw = { unversionedId, id, isDocsHomePage, diff --git a/packages/docusaurus-plugin-content-docs/src/types.ts b/packages/docusaurus-plugin-content-docs/src/types.ts index bf8a30124407..6fc6f667f693 100644 --- a/packages/docusaurus-plugin-content-docs/src/types.ts +++ b/packages/docusaurus-plugin-content-docs/src/types.ts @@ -14,7 +14,7 @@ export type VersionMetadata = { sidebarPath: string; }; -export type DocsVersion = string | null; // null = unversioned sites +export type VersionName = string; export interface MetadataOptions { routeBasePath: string; @@ -124,7 +124,8 @@ export interface LastUpdateData { lastUpdatedBy?: string; } -export interface MetadataRaw extends LastUpdateData { +export interface DocMetadataRaw extends LastUpdateData { + version: VersionName; unversionedId: string; id: string; isDocsHomePage: boolean; @@ -135,26 +136,25 @@ export interface MetadataRaw extends LastUpdateData { permalink: string; sidebar_label?: string; editUrl?: string | null; - version?: string; } -export interface Paginator { +export interface DocNavLink { title: string; permalink: string; } -export interface Metadata extends MetadataRaw { +export interface DocMetadata extends DocMetadataRaw { sidebar?: string; - previous?: Paginator; - next?: Paginator; + previous?: DocNavLink; + next?: DocNavLink; } export interface DocsMetadata { - [id: string]: Metadata; + [id: string]: DocMetadata; } export interface DocsMetadataRaw { - [id: string]: MetadataRaw; + [id: string]: DocMetadataRaw; } export interface SourceToPermalink { @@ -177,16 +177,16 @@ export interface LoadedContent { versionToSidebars: VersionToSidebars; } -export type DocsBaseMetadata = Pick< +export type VersionMetadataProp = Pick< LoadedContent, 'docsSidebars' | 'permalinkToSidebar' > & { - version: string | null; + version: string; }; export type VersioningEnv = { enabled: boolean; - latestVersion: string | null; + latestVersion: string; versions: string[]; docsDir: string; sidebarsDir: string; @@ -203,7 +203,7 @@ export type GlobalDoc = { }; export type GlobalVersion = { - name: DocsVersion; + name: VersionName; path: string; mainDocId: string; // home doc (if docs homepage configured), or first doc docs: GlobalDoc[]; @@ -211,6 +211,6 @@ export type GlobalVersion = { export type GlobalPluginData = { path: string; - latestVersionName: DocsVersion; + latestVersionName: VersionName; versions: GlobalVersion[]; }; diff --git a/packages/docusaurus-theme-classic/src/theme/DocPage/index.tsx b/packages/docusaurus-theme-classic/src/theme/DocPage/index.tsx index 2ee000fb4da2..64ac1a1d7350 100644 --- a/packages/docusaurus-theme-classic/src/theme/DocPage/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/DocPage/index.tsx @@ -20,11 +20,11 @@ import styles from './styles.module.css'; function DocPageContent({ currentDocRoute, - docsMetadata, + versionMetadata, children, }): JSX.Element { const {siteConfig, isClient} = useDocusaurusContext(); - const {permalinkToSidebar, docsSidebars, version} = docsMetadata; + const {permalinkToSidebar, docsSidebars, version} = versionMetadata; const sidebarName = permalinkToSidebar[currentDocRoute.path]; const sidebar = docsSidebars[sidebarName]; return ( @@ -52,7 +52,7 @@ function DocPageContent({ function DocPage(props) { const { route: {routes: docRoutes}, - docsMetadata, + versionMetadata, location, } = props; const currentDocRoute = docRoutes.find((docRoute) => @@ -64,7 +64,7 @@ function DocPage(props) { return ( + versionMetadata={versionMetadata}> {renderRoutes(docRoutes)} ); From 9896e248943d983a92e172f7eabcd1eafccaa2b8 Mon Sep 17 00:00:00 2001 From: slorber Date: Tue, 11 Aug 2020 23:07:09 +0200 Subject: [PATCH 07/30] progress on refactor --- .../src/index.ts | 99 ++++++++++--------- 1 file changed, 50 insertions(+), 49 deletions(-) diff --git a/packages/docusaurus-plugin-content-docs/src/index.ts b/packages/docusaurus-plugin-content-docs/src/index.ts index 934e7d9caa0b..78b58e144664 100644 --- a/packages/docusaurus-plugin-content-docs/src/index.ts +++ b/packages/docusaurus-plugin-content-docs/src/index.ts @@ -70,13 +70,6 @@ import {PluginOptionSchema} from './pluginOptionSchema'; import {ValidationError} from '@hapi/joi'; import {flatten, keyBy} from 'lodash'; -function toDocNavLink(doc: DocMetadata): DocNavLink { - return { - title: doc.title, - permalink: doc.permalink, - }; -} - export default function pluginContentDocs( context: LoadContext, options: PluginOptions, @@ -230,10 +223,16 @@ export default function pluginContentDocs( const {next: nextID, previous: previousID, sidebar} = order[doc.id] ?? {}; - const previous = previousID - ? toDocNavLink(allDocsById[previousID]) - : undefined; - const next = nextID ? toDocNavLink(allDocsById[nextID]) : undefined; + function toDocNavLink(navDocId: string): DocNavLink { + const navDoc = allDocsById[navDocId]; + return { + title: navDoc.title, + permalink: navDoc.permalink, + }; + } + + const previous = previousID ? toDocNavLink(previousID) : undefined; + const next = nextID ? toDocNavLink(nextID) : undefined; return { ...doc, @@ -317,7 +316,7 @@ Available document ids= if (!content || Object.keys(content.docsMetadata).length === 0) { return; } - + const {docsSidebars, permalinkToSidebar, versionToSidebars} = content; const {docLayoutComponent, docItemComponent, routeBasePath} = options; const {addRoute, createData, setGlobalData} = actions; @@ -343,7 +342,6 @@ Available document ids= const createVersionMetadataProp = ( version: VersionName, ): VersionMetadataProp => { - const {docsSidebars, permalinkToSidebar, versionToSidebars} = content; const neededSidebars: Set = versionToSidebars[version!] || new Set(); @@ -462,6 +460,44 @@ Available document ids= warningsFilter: [VERSIONS_JSON_FILE], }; + // TODO instead of creating one mdx loader rule for all versions + // it may be simpler to create one mdx loader per version + // particularly to handle the markdown/linkify process + // (docsDir/versionedDir are a bit annoying here...) + function createMDXLoaderRule() { + return { + test: /(\.mdx?)$/, + include: versionsMetadata.map((vmd) => vmd.docsPath), + use: [ + getCacheLoader(isServer), + getBabelLoader(isServer), + { + loader: require.resolve('@docusaurus/mdx-loader'), + options: { + remarkPlugins, + rehypePlugins, + staticDir: path.join(siteDir, STATIC_DIR_NAME), + metadataPath: (mdxPath: string) => { + // Note that metadataPath must be the same/in-sync as + // the path from createData for each MDX. + const aliasedPath = aliasedSitePath(mdxPath, siteDir); + return path.join(dataDir, `${docuHash(aliasedPath)}.json`); + }, + }, + }, + { + loader: path.resolve(__dirname, './markdown/index.js'), + options: { + siteDir, + docsDir, + sourceToPermalink, + versionedDir: legacyVersioningEnv.versioning.docsDir, + }, + }, + ].filter(Boolean), + }; + } + return { stats, devServer: { @@ -473,42 +509,7 @@ Available document ids= }, }, module: { - rules: [ - { - test: /(\.mdx?)$/, - include: versionsMetadata.map((vmd) => vmd.docsPath), - use: [ - getCacheLoader(isServer), - getBabelLoader(isServer), - { - loader: require.resolve('@docusaurus/mdx-loader'), - options: { - remarkPlugins, - rehypePlugins, - staticDir: path.join(siteDir, STATIC_DIR_NAME), - metadataPath: (mdxPath: string) => { - // Note that metadataPath must be the same/in-sync as - // the path from createData for each MDX. - const aliasedPath = aliasedSitePath(mdxPath, siteDir); - return path.join( - dataDir, - `${docuHash(aliasedPath)}.json`, - ); - }, - }, - }, - { - loader: path.resolve(__dirname, './markdown/index.js'), - options: { - siteDir, - docsDir, - sourceToPermalink, - versionedDir: legacyVersioningEnv.versioning.docsDir, - }, - }, - ].filter(Boolean), - }, - ], + rules: [createMDXLoaderRule()], }, } as Configuration; }, From 03e59d88a284d4318d612cdd1f332080769b4c89 Mon Sep 17 00:00:00 2001 From: slorber Date: Tue, 11 Aug 2020 23:30:43 +0200 Subject: [PATCH 08/30] stable docs refactor --- .../src/index.ts | 109 +++++++++++++----- .../src/order.ts | 4 +- .../src/sidebars.ts | 10 +- .../src/types.ts | 15 ++- .../src/version.ts | 8 +- 5 files changed, 98 insertions(+), 48 deletions(-) diff --git a/packages/docusaurus-plugin-content-docs/src/index.ts b/packages/docusaurus-plugin-content-docs/src/index.ts index 78b58e144664..4801866db1a1 100644 --- a/packages/docusaurus-plugin-content-docs/src/index.ts +++ b/packages/docusaurus-plugin-content-docs/src/index.ts @@ -5,7 +5,6 @@ * LICENSE file in the root directory of this source tree. */ -import groupBy from 'lodash.groupby'; import pick from 'lodash.pick'; import pickBy from 'lodash.pickby'; import sortBy from 'lodash.sortby'; @@ -39,7 +38,7 @@ import loadEnv, {readVersionsMetadata} from './env'; import { PluginOptions, - Sidebar, + Sidebars, Order, DocsMetadata, LoadedContent, @@ -62,6 +61,7 @@ import { VersionMetadata, DocNavLink, OrderMetadata, + LoadedVersion, } from './types'; import {Configuration} from 'webpack'; import {docsVersion} from './version'; @@ -73,7 +73,7 @@ import {flatten, keyBy} from 'lodash'; export default function pluginContentDocs( context: LoadContext, options: PluginOptions, -): Plugin { +): Plugin { const {siteDir, generatedFilesDir, baseUrl} = context; const versionsMetadata = readVersionsMetadata(siteDir, options); @@ -188,14 +188,59 @@ export default function pluginContentDocs( const docsFiles = await globby(include, { cwd: versionMetadata.docsPath, }); - return Promise.all( - docsFiles.map(async (source) => { - return processDocsMetadata({ - source, - versionMetadata, - }); - }), + async function processVersionDoc(source: string) { + return processDocsMetadata({ + source, + versionMetadata, + }); + } + return Promise.all(docsFiles.map(processVersionDoc)); + } + + async function loadVersion( + versionMetadata: VersionMetadata, + ): Promise { + const docs: DocMetadataRaw[] = await processVersionDocs( + versionMetadata, ); + const docsById: DocsMetadataRaw = keyBy(docs, (doc) => doc.id); + + const sidebars = loadSidebars([versionMetadata.sidebarPath]); + const docsOrder: Order = createOrder(sidebars); + + // Add sidebar/next/previous to the docs + function addNavData(doc: DocMetadataRaw): DocMetadata { + const {next: nextID, previous: previousID, sidebar} = + docsOrder[doc.id] ?? {}; + + function toDocNavLink(navDocId: string): DocNavLink { + const navDoc = docsById[navDocId]; + return { + title: navDoc.title, + permalink: navDoc.permalink, + }; + } + + const previous = previousID ? toDocNavLink(previousID) : undefined; + const next = nextID ? toDocNavLink(nextID) : undefined; + + return { + ...doc, + sidebar, + previous, + next, + }; + } + + const loadedDocs = docs.map(addNavData); + + // sort to ensure consistent output for tests + loadedDocs.sort((a, b) => a.id.localeCompare(b.id)); + + return { + metadata: versionMetadata, + docs: docs.map(addNavData), + }; } // TODO, we should namespace docs by version! @@ -205,7 +250,7 @@ export default function pluginContentDocs( const allDocsById: DocsMetadataRaw = keyBy(allDocs, (doc) => doc.id); - const loadedSidebars: Sidebar = loadSidebars( + const loadedSidebars: Sidebars = loadSidebars( versionsMetadata.map((version) => version.sidebarPath), ); @@ -303,8 +348,13 @@ Available document ids= }, {}, ); + + const loadedVersions = await Promise.all( + versionsMetadata.map(loadVersion), + ); + return { - docsMetadata, + loadedVersions, docsDir, docsSidebars, permalinkToSidebar: objectWithKeySorted(permalinkToSidebar), @@ -313,23 +363,15 @@ Available document ids= }, async contentLoaded({content, actions}) { - if (!content || Object.keys(content.docsMetadata).length === 0) { - return; - } - const {docsSidebars, permalinkToSidebar, versionToSidebars} = content; + const { + loadedVersions, + docsSidebars, + permalinkToSidebar, + versionToSidebars, + } = content; const {docLayoutComponent, docItemComponent, routeBasePath} = options; const {addRoute, createData, setGlobalData} = actions; - const docsMetadataByVersion = groupBy( - // sort to ensure consistent output for tests - Object.values(content.docsMetadata).sort((a, b) => - a.id.localeCompare(b.id), - ), - 'version', - ); - - const allVersionNames = Object.keys(docsMetadataByVersion); - const pluginInstanceGlobalData: GlobalPluginData = { path: normalizeUrl([baseUrl, options.routeBasePath]), latestVersionName: latestVersion, @@ -390,15 +432,18 @@ Available document ids= const getVersionRoutePriority = (version: VersionName) => version === latestVersion ? -1 : undefined; - async function handleVersion(version: VersionName) { - const docs = docsMetadataByVersion[version]; + async function handleVersion(loadedVersion: LoadedVersion) { + const { + metadata: {versionName}, + docs, + } = loadedVersion; const versionPath = normalizeUrl([ baseUrl, routeBasePath, - getVersionPath(version), + getVersionPath(versionName), ]); - const versionMetadataProp = createVersionMetadataProp(version); + const versionMetadataProp = createVersionMetadataProp(versionName); const versionMetadataPropPath = await createData( `${docuHash(normalizeUrl([versionPath, ':route']))}.json`, @@ -428,7 +473,7 @@ Available document ids= .sort((a, b) => a.id.localeCompare(b.id)), }); - const priority = getVersionRoutePriority(version); + const priority = getVersionRoutePriority(versionName); addRoute({ path: versionPath, @@ -442,7 +487,7 @@ Available document ids= }); } - await Promise.all(allVersionNames.map(handleVersion)); + await Promise.all(loadedVersions.map(handleVersion)); pluginInstanceGlobalData.versions = sortBy( pluginInstanceGlobalData.versions, diff --git a/packages/docusaurus-plugin-content-docs/src/order.ts b/packages/docusaurus-plugin-content-docs/src/order.ts index 8cd545ec41c0..c18566d9bfd4 100644 --- a/packages/docusaurus-plugin-content-docs/src/order.ts +++ b/packages/docusaurus-plugin-content-docs/src/order.ts @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -import {Sidebar, SidebarItem, Order, SidebarItemDoc} from './types'; +import {Sidebars, SidebarItem, Order, SidebarItemDoc} from './types'; import {flatten} from 'lodash'; function getOrderedDocItems(items: SidebarItem[]): SidebarItemDoc[] { @@ -27,7 +27,7 @@ function getOrderedDocItems(items: SidebarItem[]): SidebarItemDoc[] { } // Build the docs meta such as next, previous, category and sidebar. -export default function createOrder(allSidebars: Sidebar = {}): Order { +export default function createOrder(allSidebars: Sidebars = {}): Order { const order: Order = {}; Object.keys(allSidebars).forEach((sidebarId) => { diff --git a/packages/docusaurus-plugin-content-docs/src/sidebars.ts b/packages/docusaurus-plugin-content-docs/src/sidebars.ts index 7afb576e5546..624484ca81bd 100644 --- a/packages/docusaurus-plugin-content-docs/src/sidebars.ts +++ b/packages/docusaurus-plugin-content-docs/src/sidebars.ts @@ -9,7 +9,7 @@ import flatMap from 'lodash.flatmap'; import fs from 'fs-extra'; import importFresh from 'import-fresh'; import { - Sidebar, + Sidebars, SidebarRaw, SidebarItem, SidebarItemCategoryRaw, @@ -158,9 +158,9 @@ function normalizeItem(item: SidebarItemRaw): SidebarItem[] { /** * Converts sidebars object to mapping to arrays of sidebar item objects. */ -function normalizeSidebar(sidebars: SidebarRaw): Sidebar { +function normalizeSidebar(sidebars: SidebarRaw): Sidebars { return Object.entries(sidebars).reduce( - (acc: Sidebar, [sidebarId, sidebar]) => { + (acc: Sidebars, [sidebarId, sidebar]) => { const normalizedSidebar: SidebarItemRaw[] = Array.isArray(sidebar) ? sidebar : normalizeCategoryShorthand(sidebar); @@ -173,12 +173,12 @@ function normalizeSidebar(sidebars: SidebarRaw): Sidebar { ); } -export default function loadSidebars(sidebarPaths?: string[]): Sidebar { +export default function loadSidebars(sidebarPaths?: string[]): Sidebars { // We don't want sidebars to be cached because of hot reloading. const allSidebars: SidebarRaw = {}; if (!sidebarPaths || !sidebarPaths.length) { - return {} as Sidebar; + return {} as Sidebars; } sidebarPaths.forEach((sidebarPath) => { diff --git a/packages/docusaurus-plugin-content-docs/src/types.ts b/packages/docusaurus-plugin-content-docs/src/types.ts index 6fc6f667f693..17d616597673 100644 --- a/packages/docusaurus-plugin-content-docs/src/types.ts +++ b/packages/docusaurus-plugin-content-docs/src/types.ts @@ -8,14 +8,14 @@ // eslint-disable-next-line spaced-comment /// +export type VersionName = string; + export type VersionMetadata = { - versionName: string; + versionName: VersionName; docsPath: string; sidebarPath: string; }; -export type VersionName = string; - export interface MetadataOptions { routeBasePath: string; homePageId?: string; @@ -92,7 +92,7 @@ export interface SidebarRaw { [sidebarId: string]: SidebarCategoryShorthandRaw | SidebarItemRaw[]; } -export interface Sidebar { +export interface Sidebars { [sidebarId: string]: SidebarItem[]; } @@ -169,8 +169,13 @@ export interface VersionToSidebars { [version: string]: Set; } +export type LoadedVersion = { + metadata: VersionMetadata; + docs: DocMetadata[]; +}; + export interface LoadedContent { - docsMetadata: DocsMetadata; + loadedVersions: LoadedVersion[]; docsDir: string; docsSidebars: DocsSidebar; permalinkToSidebar: PermalinkToSidebar; diff --git a/packages/docusaurus-plugin-content-docs/src/version.ts b/packages/docusaurus-plugin-content-docs/src/version.ts index 989d2fd0a729..d037ae4a5e1c 100644 --- a/packages/docusaurus-plugin-content-docs/src/version.ts +++ b/packages/docusaurus-plugin-content-docs/src/version.ts @@ -12,7 +12,7 @@ import { } from './env'; import fs from 'fs-extra'; import path from 'path'; -import {Sidebar, PathOptions, SidebarItem} from './types'; +import {Sidebars, PathOptions, SidebarItem} from './types'; import loadSidebars from './sidebars'; import {DEFAULT_PLUGIN_ID} from '@docusaurus/core/lib/constants'; @@ -89,7 +89,7 @@ export function docsVersion( // Load current sidebar and create a new versioned sidebars file. if (fs.existsSync(sidebarPath)) { - const loadedSidebars: Sidebar = loadSidebars([sidebarPath]); + const loadedSidebars: Sidebars = loadSidebars([sidebarPath]); // Transform id in original sidebar to versioned id. const normalizeItem = (item: SidebarItem): SidebarItem => { @@ -107,8 +107,8 @@ export function docsVersion( } }; - const versionedSidebar: Sidebar = Object.entries(loadedSidebars).reduce( - (acc: Sidebar, [sidebarId, sidebarItems]) => { + const versionedSidebar: Sidebars = Object.entries(loadedSidebars).reduce( + (acc: Sidebars, [sidebarId, sidebarItems]) => { const newVersionedSidebarId = `version-${version}/${sidebarId}`; acc[newVersionedSidebarId] = sidebarItems.map(normalizeItem); return acc; From 2db4083fa71e31c3c7177c4b652cf2cf8b0f6385 Mon Sep 17 00:00:00 2001 From: slorber Date: Wed, 12 Aug 2020 13:17:30 +0200 Subject: [PATCH 09/30] stable docs refactor --- .../src/index.ts | 222 +++++++----------- .../src/types.ts | 22 +- 2 files changed, 97 insertions(+), 147 deletions(-) diff --git a/packages/docusaurus-plugin-content-docs/src/index.ts b/packages/docusaurus-plugin-content-docs/src/index.ts index 4801866db1a1..47ca51b0fd14 100644 --- a/packages/docusaurus-plugin-content-docs/src/index.ts +++ b/packages/docusaurus-plugin-content-docs/src/index.ts @@ -5,8 +5,6 @@ * LICENSE file in the root directory of this source tree. */ -import pick from 'lodash.pick'; -import pickBy from 'lodash.pickby'; import sortBy from 'lodash.sortby'; import globby from 'globby'; import path from 'path'; @@ -17,12 +15,7 @@ import { STATIC_DIR_NAME, DEFAULT_PLUGIN_ID, } from '@docusaurus/core/lib/constants'; -import { - normalizeUrl, - docuHash, - objectWithKeySorted, - aliasedSitePath, -} from '@docusaurus/utils'; +import {normalizeUrl, docuHash, aliasedSitePath} from '@docusaurus/utils'; import { LoadContext, Plugin, @@ -40,7 +33,6 @@ import { PluginOptions, Sidebars, Order, - DocsMetadata, LoadedContent, SourceToPermalink, PermalinkToSidebar, @@ -70,6 +62,55 @@ import {PluginOptionSchema} from './pluginOptionSchema'; import {ValidationError} from '@hapi/joi'; import {flatten, keyBy} from 'lodash'; +function normalizeSidebars(loadedVersion: LoadedVersion): DocsSidebar { + const docsById = keyBy(loadedVersion.docs, (doc) => doc.id); + + const convertDocLink = (item: SidebarItemDoc): SidebarItemLink => { + const docId = item.id; + const docMetadata = docsById[docId]; + + if (!docMetadata) { + throw new Error( + `Bad sidebars file. The document id '${docId}' was used in the sidebar, but no document with this id could be found. +Available document ids= +- ${Object.keys(docsById).sort().join('\n- ')}`, + ); + } + + const {title, permalink, sidebar_label} = docMetadata; + + return { + type: 'link', + label: sidebar_label || title, + href: permalink, + }; + }; + + const normalizeItem = (item: SidebarItem): DocsSidebarItem => { + switch (item.type) { + case 'category': + return {...item, items: item.items.map(normalizeItem)}; + case 'ref': + case 'doc': + return convertDocLink(item); + case 'link': + default: + return item; + } + }; + + // Transform the sidebar so that all sidebar item will be in the + // form of 'link' or 'category' only. + // This is what will be passed as props to the UI component. + return Object.entries(loadedVersion.sidebars).reduce( + (acc: DocsSidebar, [sidebarId, sidebarItems]) => { + acc[sidebarId] = sidebarItems.map(normalizeItem); + return acc; + }, + {}, + ); +} + export default function pluginContentDocs( context: LoadContext, options: PluginOptions, @@ -182,7 +223,7 @@ export default function pluginContentDocs( async loadContent() { const {include} = options; - async function processVersionDocs( + async function loadVersionDocs( versionMetadata: VersionMetadata, ): Promise { const docsFiles = await globby(include, { @@ -200,9 +241,7 @@ export default function pluginContentDocs( async function loadVersion( versionMetadata: VersionMetadata, ): Promise { - const docs: DocMetadataRaw[] = await processVersionDocs( - versionMetadata, - ); + const docs: DocMetadataRaw[] = await loadVersionDocs(versionMetadata); const docsById: DocsMetadataRaw = keyBy(docs, (doc) => doc.id); const sidebars = loadSidebars([versionMetadata.sidebarPath]); @@ -210,25 +249,17 @@ export default function pluginContentDocs( // Add sidebar/next/previous to the docs function addNavData(doc: DocMetadataRaw): DocMetadata { - const {next: nextID, previous: previousID, sidebar} = + const {next: nextId, previous: previousId, sidebar} = docsOrder[doc.id] ?? {}; - - function toDocNavLink(navDocId: string): DocNavLink { - const navDoc = docsById[navDocId]; - return { - title: navDoc.title, - permalink: navDoc.permalink, - }; - } - - const previous = previousID ? toDocNavLink(previousID) : undefined; - const next = nextID ? toDocNavLink(nextID) : undefined; - + const toDocNavLink = (navDocId: string): DocNavLink => ({ + title: docsById[navDocId].title, + permalink: docsById[navDocId].permalink, + }); return { ...doc, sidebar, - previous, - next, + previous: previousId ? toDocNavLink(previousId) : undefined, + next: nextId ? toDocNavLink(nextId) : undefined, }; } @@ -237,15 +268,33 @@ export default function pluginContentDocs( // sort to ensure consistent output for tests loadedDocs.sort((a, b) => a.id.localeCompare(b.id)); + // TODO replace, not needed with global data? + const permalinkToSidebar: PermalinkToSidebar = {}; + + Object.values(loadedDocs).forEach((loadedDoc) => { + const {id: docId, source, permalink} = loadedDoc; + + const docSidebarName = docsOrder[docId]?.sidebar; + + // TODO annoying! + sourceToPermalink[source] = permalink; + + if (docSidebarName) { + permalinkToSidebar[permalink] = docSidebarName; + } + }); + return { - metadata: versionMetadata, + ...versionMetadata, + sidebars, + permalinkToSidebar, docs: docs.map(addNavData), }; } // TODO, we should namespace docs by version! const allDocs = flatten( - await Promise.all(versionsMetadata.map(processVersionDocs)), + await Promise.all(versionsMetadata.map(loadVersionDocs)), ); const allDocsById: DocsMetadataRaw = keyBy(allDocs, (doc) => doc.id); @@ -260,40 +309,14 @@ export default function pluginContentDocs( } // Construct inter-metadata relationship in docsMetadata. - const docsMetadata: DocsMetadata = {}; const permalinkToSidebar: PermalinkToSidebar = {}; const versionToSidebars: VersionToSidebars = {}; - function addSidebarData(doc: DocMetadataRaw): DocMetadata { - const {next: nextID, previous: previousID, sidebar} = - order[doc.id] ?? {}; - - function toDocNavLink(navDocId: string): DocNavLink { - const navDoc = allDocsById[navDocId]; - return { - title: navDoc.title, - permalink: navDoc.permalink, - }; - } - - const previous = previousID ? toDocNavLink(previousID) : undefined; - const next = nextID ? toDocNavLink(nextID) : undefined; - - return { - ...doc, - sidebar, - previous, - next, - }; - } - - Object.keys(allDocsById).forEach((currentID) => { - const {sidebar} = getDocOrder(currentID); - - docsMetadata[currentID] = addSidebarData(allDocsById[currentID]); + Object.keys(allDocsById).forEach((docId) => { + const {sidebar} = getDocOrder(docId); // sourceToPermalink and permalinkToSidebar mapping. - const {source, permalink, version} = allDocsById[currentID]; + const {source, permalink, version} = allDocsById[docId]; sourceToPermalink[source] = permalink; if (sidebar) { permalinkToSidebar[permalink] = sidebar; @@ -304,71 +327,17 @@ export default function pluginContentDocs( } }); - const convertDocLink = (item: SidebarItemDoc): SidebarItemLink => { - const docId = item.id; - const docMetadata = allDocsById[docId]; - - if (!docMetadata) { - throw new Error( - `Bad sidebars file. The document id '${docId}' was used in the sidebar, but no document with this id could be found. -Available document ids= -- ${Object.keys(allDocsById).sort().join('\n- ')}`, - ); - } - - const {title, permalink, sidebar_label} = docMetadata; - - return { - type: 'link', - label: sidebar_label || title, - href: permalink, - }; - }; - - const normalizeItem = (item: SidebarItem): DocsSidebarItem => { - switch (item.type) { - case 'category': - return {...item, items: item.items.map(normalizeItem)}; - case 'ref': - case 'doc': - return convertDocLink(item); - case 'link': - default: - return item; - } - }; - - // Transform the sidebar so that all sidebar item will be in the - // form of 'link' or 'category' only. - // This is what will be passed as props to the UI component. - const docsSidebars: DocsSidebar = Object.entries(loadedSidebars).reduce( - (acc: DocsSidebar, [sidebarId, sidebarItems]) => { - acc[sidebarId] = sidebarItems.map(normalizeItem); - return acc; - }, - {}, - ); - const loadedVersions = await Promise.all( versionsMetadata.map(loadVersion), ); return { loadedVersions, - docsDir, - docsSidebars, - permalinkToSidebar: objectWithKeySorted(permalinkToSidebar), - versionToSidebars, }; }, async contentLoaded({content, actions}) { - const { - loadedVersions, - docsSidebars, - permalinkToSidebar, - versionToSidebars, - } = content; + const {loadedVersions} = content; const {docLayoutComponent, docItemComponent, routeBasePath} = options; const {addRoute, createData, setGlobalData} = actions; @@ -382,21 +351,12 @@ Available document ids= setGlobalData(pluginInstanceGlobalData); const createVersionMetadataProp = ( - version: VersionName, + loadedVersion: LoadedVersion, ): VersionMetadataProp => { - const neededSidebars: Set = - versionToSidebars[version!] || new Set(); - return { - docsSidebars: version - ? pick(docsSidebars, Array.from(neededSidebars)) - : docsSidebars, - permalinkToSidebar: version - ? pickBy(permalinkToSidebar, (sidebar) => - neededSidebars.has(sidebar), - ) - : permalinkToSidebar, - version, + version: loadedVersion.versionName, + docsSidebars: normalizeSidebars(loadedVersion), + permalinkToSidebar: loadedVersion.permalinkToSidebar, }; }; @@ -433,17 +393,14 @@ Available document ids= version === latestVersion ? -1 : undefined; async function handleVersion(loadedVersion: LoadedVersion) { - const { - metadata: {versionName}, - docs, - } = loadedVersion; + const {versionName, docs} = loadedVersion; const versionPath = normalizeUrl([ baseUrl, routeBasePath, getVersionPath(versionName), ]); - const versionMetadataProp = createVersionMetadataProp(versionName); + const versionMetadataProp = createVersionMetadataProp(loadedVersion); const versionMetadataPropPath = await createData( `${docuHash(normalizeUrl([versionPath, ':route']))}.json`, @@ -452,6 +409,7 @@ Available document ids= const docsRoutes = await createDocRoutes(docs); + // TODO bad algo! const versionMainDoc: DocMetadata = docs.find( (doc) => diff --git a/packages/docusaurus-plugin-content-docs/src/types.ts b/packages/docusaurus-plugin-content-docs/src/types.ts index 17d616597673..29f570d2ee45 100644 --- a/packages/docusaurus-plugin-content-docs/src/types.ts +++ b/packages/docusaurus-plugin-content-docs/src/types.ts @@ -149,10 +149,6 @@ export interface DocMetadata extends DocMetadataRaw { next?: DocNavLink; } -export interface DocsMetadata { - [id: string]: DocMetadata; -} - export interface DocsMetadataRaw { [id: string]: DocMetadataRaw; } @@ -169,24 +165,20 @@ export interface VersionToSidebars { [version: string]: Set; } -export type LoadedVersion = { - metadata: VersionMetadata; +export type LoadedVersion = VersionMetadata & { docs: DocMetadata[]; + sidebars: Sidebars; + permalinkToSidebar: Record; }; export interface LoadedContent { loadedVersions: LoadedVersion[]; - docsDir: string; - docsSidebars: DocsSidebar; - permalinkToSidebar: PermalinkToSidebar; - versionToSidebars: VersionToSidebars; } -export type VersionMetadataProp = Pick< - LoadedContent, - 'docsSidebars' | 'permalinkToSidebar' -> & { - version: string; +export type VersionMetadataProp = { + version: VersionName; + docsSidebars: DocsSidebar; + permalinkToSidebar: PermalinkToSidebar; }; export type VersioningEnv = { From b5a27d2ee5487453bb94e6d6d5a88df6eea4318f Mon Sep 17 00:00:00 2001 From: slorber Date: Wed, 12 Aug 2020 14:03:42 +0200 Subject: [PATCH 10/30] stable docs refactor --- .../src/__tests__/metadata.test.ts | 4 +- .../src/globalData.ts | 27 +++ .../src/index.ts | 177 ++++++------------ .../src/metadata.ts | 6 +- .../src/types.ts | 12 +- 5 files changed, 90 insertions(+), 136 deletions(-) create mode 100644 packages/docusaurus-plugin-content-docs/src/globalData.ts diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/metadata.test.ts b/packages/docusaurus-plugin-content-docs/src/__tests__/metadata.test.ts index 98316c033599..eb9553007396 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/metadata.test.ts +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/metadata.test.ts @@ -9,7 +9,7 @@ import path from 'path'; import {loadContext} from '@docusaurus/core/src/server/index'; import processMetadata from '../metadata'; import loadEnv from '../env'; -import {DocMetadataRaw, Env, MetadataOptions} from '../types'; +import {DocMetadataBase, Env, MetadataOptions} from '../types'; import {LoadContext} from '@docusaurus/types'; import {DEFAULT_PLUGIN_ID} from '@docusaurus/core/lib/constants'; @@ -29,7 +29,7 @@ function createTestHelpers({ async function testMeta( refDir: string, source: string, - expectedMetadata: Omit, + expectedMetadata: Omit, ) { const metadata = await processMetadata({ source, diff --git a/packages/docusaurus-plugin-content-docs/src/globalData.ts b/packages/docusaurus-plugin-content-docs/src/globalData.ts new file mode 100644 index 000000000000..ab6d1ccb47e8 --- /dev/null +++ b/packages/docusaurus-plugin-content-docs/src/globalData.ts @@ -0,0 +1,27 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import {DocMetadata, GlobalDoc, LoadedVersion, GlobalVersion} from './types'; + +export function toGlobalDataDoc(doc: DocMetadata): GlobalDoc { + return { + id: doc.unversionedId, + path: doc.permalink, + }; +} + +export function toGlobalDataVersion(version: LoadedVersion): GlobalVersion { + return { + name: version.versionName, + path: version.versionPath, + mainDocId: version.mainDocId, + docs: version.docs + .map(toGlobalDataDoc) + // stable ordering, useful for tests + .sort((a, b) => a.id.localeCompare(b.id)), + }; +} diff --git a/packages/docusaurus-plugin-content-docs/src/index.ts b/packages/docusaurus-plugin-content-docs/src/index.ts index 47ca51b0fd14..7601f42c1cff 100644 --- a/packages/docusaurus-plugin-content-docs/src/index.ts +++ b/packages/docusaurus-plugin-content-docs/src/index.ts @@ -5,7 +5,6 @@ * LICENSE file in the root directory of this source tree. */ -import sortBy from 'lodash.sortby'; import globby from 'globby'; import path from 'path'; import chalk from 'chalk'; @@ -31,7 +30,6 @@ import loadEnv, {readVersionsMetadata} from './env'; import { PluginOptions, - Sidebars, Order, LoadedContent, SourceToPermalink, @@ -40,19 +38,15 @@ import { SidebarItemDoc, DocsSidebar, VersionMetadataProp, - DocMetadataRaw, + DocMetadataBase, DocsMetadataRaw, DocMetadata, - VersionToSidebars, SidebarItem, DocsSidebarItem, GlobalPluginData, VersionName, - GlobalVersion, - GlobalDoc, VersionMetadata, DocNavLink, - OrderMetadata, LoadedVersion, } from './types'; import {Configuration} from 'webpack'; @@ -61,6 +55,7 @@ import {CURRENT_VERSION_NAME, VERSIONS_JSON_FILE} from './constants'; import {PluginOptionSchema} from './pluginOptionSchema'; import {ValidationError} from '@hapi/joi'; import {flatten, keyBy} from 'lodash'; +import {toGlobalDataVersion} from './globalData'; function normalizeSidebars(loadedVersion: LoadedVersion): DocsSidebar { const docsById = keyBy(loadedVersion.docs, (doc) => doc.id); @@ -118,7 +113,6 @@ export default function pluginContentDocs( const {siteDir, generatedFilesDir, baseUrl} = context; const versionsMetadata = readVersionsMetadata(siteDir, options); - const versionNames = versionsMetadata.map((version) => version.versionName); const docsDir = path.resolve(siteDir, options.path); @@ -139,8 +133,8 @@ export default function pluginContentDocs( }); const {latestVersion} = legacyVersioningEnv.versioning; - // TODO refactor - function getVersionPath(version: VersionName) { + // TODO refactor, retrocompatibility + function getVersionPathPart(version: VersionName) { if (version === latestVersion) { return ''; } @@ -225,7 +219,7 @@ export default function pluginContentDocs( async function loadVersionDocs( versionMetadata: VersionMetadata, - ): Promise { + ): Promise { const docsFiles = await globby(include, { cwd: versionMetadata.docsPath, }); @@ -241,14 +235,14 @@ export default function pluginContentDocs( async function loadVersion( versionMetadata: VersionMetadata, ): Promise { - const docs: DocMetadataRaw[] = await loadVersionDocs(versionMetadata); + const docs: DocMetadataBase[] = await loadVersionDocs(versionMetadata); const docsById: DocsMetadataRaw = keyBy(docs, (doc) => doc.id); const sidebars = loadSidebars([versionMetadata.sidebarPath]); const docsOrder: Order = createOrder(sidebars); // Add sidebar/next/previous to the docs - function addNavData(doc: DocMetadataRaw): DocMetadata { + function addNavData(doc: DocMetadataBase): DocMetadata { const {next: nextId, previous: previousId, sidebar} = docsOrder[doc.id] ?? {}; const toDocNavLink = (navDocId: string): DocNavLink => ({ @@ -268,88 +262,56 @@ export default function pluginContentDocs( // sort to ensure consistent output for tests loadedDocs.sort((a, b) => a.id.localeCompare(b.id)); - // TODO replace, not needed with global data? - const permalinkToSidebar: PermalinkToSidebar = {}; - + // TODO annoying side effect! Object.values(loadedDocs).forEach((loadedDoc) => { - const {id: docId, source, permalink} = loadedDoc; - - const docSidebarName = docsOrder[docId]?.sidebar; - - // TODO annoying! + const {source, permalink} = loadedDoc; sourceToPermalink[source] = permalink; + }); + // TODO replace with global state logic? + const permalinkToSidebar: PermalinkToSidebar = {}; + Object.values(loadedDocs).forEach((loadedDoc) => { + const {id: docId, permalink} = loadedDoc; + const docSidebarName = docsOrder[docId]?.sidebar; if (docSidebarName) { permalinkToSidebar[permalink] = docSidebarName; } }); + // TODO compute in versionMetadata? + const versionPath = normalizeUrl([ + baseUrl, + options.routeBasePath, + getVersionPathPart(versionMetadata.versionName), + ]); + + // TODO bad algo! + const mainDoc: DocMetadata = + docs.find( + (doc) => + doc.unversionedId === options.homePageId || doc.slug === '/', + ) ?? docs[0]; + return { ...versionMetadata, + versionPath, + mainDocId: mainDoc.unversionedId, sidebars, permalinkToSidebar, docs: docs.map(addNavData), }; } - // TODO, we should namespace docs by version! - const allDocs = flatten( - await Promise.all(versionsMetadata.map(loadVersionDocs)), - ); - - const allDocsById: DocsMetadataRaw = keyBy(allDocs, (doc) => doc.id); - - const loadedSidebars: Sidebars = loadSidebars( - versionsMetadata.map((version) => version.sidebarPath), - ); - - const order: Order = createOrder(loadedSidebars); - function getDocOrder(docId: string): OrderMetadata { - return order[docId] ?? {}; - } - - // Construct inter-metadata relationship in docsMetadata. - const permalinkToSidebar: PermalinkToSidebar = {}; - const versionToSidebars: VersionToSidebars = {}; - - Object.keys(allDocsById).forEach((docId) => { - const {sidebar} = getDocOrder(docId); - - // sourceToPermalink and permalinkToSidebar mapping. - const {source, permalink, version} = allDocsById[docId]; - sourceToPermalink[source] = permalink; - if (sidebar) { - permalinkToSidebar[permalink] = sidebar; - if (!versionToSidebars[version]) { - versionToSidebars[version] = new Set(); - } - versionToSidebars[version].add(sidebar); - } - }); - - const loadedVersions = await Promise.all( - versionsMetadata.map(loadVersion), - ); - return { - loadedVersions, + loadedVersions: await Promise.all(versionsMetadata.map(loadVersion)), }; }, async contentLoaded({content, actions}) { const {loadedVersions} = content; - const {docLayoutComponent, docItemComponent, routeBasePath} = options; + const {docLayoutComponent, docItemComponent} = options; const {addRoute, createData, setGlobalData} = actions; - const pluginInstanceGlobalData: GlobalPluginData = { - path: normalizeUrl([baseUrl, options.routeBasePath]), - latestVersionName: latestVersion, - // Initialized empty, will be mutated - versions: [], - }; - - setGlobalData(pluginInstanceGlobalData); - const createVersionMetadataProp = ( loadedVersion: LoadedVersion, ): VersionMetadataProp => { @@ -361,10 +323,10 @@ export default function pluginContentDocs( }; const createDocRoutes = async ( - metadataItems: DocMetadata[], + docs: DocMetadata[], ): Promise => { const routes = await Promise.all( - metadataItems.map(async (metadataItem) => { + docs.map(async (metadataItem) => { await createData( // Note that this created data path must be in sync with // metadataPath provided to mdx-loader. @@ -386,73 +348,40 @@ export default function pluginContentDocs( return routes.sort((a, b) => a.path.localeCompare(b.path)); }; - // We want latest version route to have lower priority - // Otherwise `/docs/next/foo` would match - // `/docs/:route` instead of `/docs/next/:route`. - const getVersionRoutePriority = (version: VersionName) => - version === latestVersion ? -1 : undefined; - async function handleVersion(loadedVersion: LoadedVersion) { - const {versionName, docs} = loadedVersion; - - const versionPath = normalizeUrl([ - baseUrl, - routeBasePath, - getVersionPath(versionName), - ]); const versionMetadataProp = createVersionMetadataProp(loadedVersion); const versionMetadataPropPath = await createData( - `${docuHash(normalizeUrl([versionPath, ':route']))}.json`, + `${docuHash(`${loadedVersion.versionName}`)}.json`, JSON.stringify(versionMetadataProp, null, 2), ); - const docsRoutes = await createDocRoutes(docs); - - // TODO bad algo! - const versionMainDoc: DocMetadata = - docs.find( - (doc) => - doc.unversionedId === options.homePageId || doc.slug === '/', - ) ?? docs[0]; - - const toGlobalDataDoc = (doc: DocMetadata): GlobalDoc => ({ - id: doc.unversionedId, - path: doc.permalink, - }); - - pluginInstanceGlobalData.versions.push({ - name: versionMetadataProp.version, - path: versionPath, - mainDocId: versionMainDoc.unversionedId, - docs: docs - .map(toGlobalDataDoc) - // stable ordering, useful for tests - .sort((a, b) => a.id.localeCompare(b.id)), - }); - - const priority = getVersionRoutePriority(versionName); - addRoute({ - path: versionPath, - exact: false, // allow matching /docs/* as well - component: docLayoutComponent, // main docs component (DocPage) - routes: docsRoutes, // subroute for each doc + path: loadedVersion.versionPath, + // allow matching /docs/* as well + exact: false, + // main docs component (DocPage) + component: docLayoutComponent, + // sub-routes for each doc + routes: await createDocRoutes(loadedVersion.docs), modules: { versionMetadata: aliasedSource(versionMetadataPropPath), }, - priority, + // Because /docs/:route` should always be after `/docs/versionName/:route`. + priority: + getVersionPathPart(loadedVersion.versionName) === '' + ? -1 + : undefined, }); } await Promise.all(loadedVersions.map(handleVersion)); - pluginInstanceGlobalData.versions = sortBy( - pluginInstanceGlobalData.versions, - (versionMetadata: GlobalVersion) => { - return versionNames.indexOf(versionMetadata.name!); - }, - ); + setGlobalData({ + path: normalizeUrl([baseUrl, options.routeBasePath]), + latestVersionName: latestVersion, + versions: loadedVersions.map(toGlobalDataVersion), + }); }, configureWebpack(_config, isServer, utils) { diff --git a/packages/docusaurus-plugin-content-docs/src/metadata.ts b/packages/docusaurus-plugin-content-docs/src/metadata.ts index fc0581701c50..515946f54ff5 100644 --- a/packages/docusaurus-plugin-content-docs/src/metadata.ts +++ b/packages/docusaurus-plugin-content-docs/src/metadata.ts @@ -16,7 +16,7 @@ import {LoadContext} from '@docusaurus/types'; import lastUpdate from './lastUpdate'; import { - DocMetadataRaw, + DocMetadataBase, LastUpdateData, MetadataOptions, Env, @@ -64,7 +64,7 @@ export default async function processMetadata({ context: LoadContext; options: MetadataOptions; env: Env; -}): Promise { +}): Promise { const {routeBasePath, editUrl, homePageId} = options; const {siteDir, baseUrl} = context; const {versioning} = env; @@ -153,7 +153,7 @@ export default async function processMetadata({ // NodeJS optimization. // Adding properties to object after instantiation will cause hidden // class transitions. - const metadata: DocMetadataRaw = { + const metadata: DocMetadataBase = { unversionedId, id, isDocsHomePage, diff --git a/packages/docusaurus-plugin-content-docs/src/types.ts b/packages/docusaurus-plugin-content-docs/src/types.ts index 29f570d2ee45..e9bad29e1385 100644 --- a/packages/docusaurus-plugin-content-docs/src/types.ts +++ b/packages/docusaurus-plugin-content-docs/src/types.ts @@ -124,7 +124,7 @@ export interface LastUpdateData { lastUpdatedBy?: string; } -export interface DocMetadataRaw extends LastUpdateData { +export interface DocMetadataBase extends LastUpdateData { version: VersionName; unversionedId: string; id: string; @@ -143,14 +143,14 @@ export interface DocNavLink { permalink: string; } -export interface DocMetadata extends DocMetadataRaw { +export interface DocMetadata extends DocMetadataBase { sidebar?: string; previous?: DocNavLink; next?: DocNavLink; } export interface DocsMetadataRaw { - [id: string]: DocMetadataRaw; + [id: string]: DocMetadataBase; } export interface SourceToPermalink { @@ -161,11 +161,9 @@ export interface PermalinkToSidebar { [permalink: string]: string; } -export interface VersionToSidebars { - [version: string]: Set; -} - export type LoadedVersion = VersionMetadata & { + versionPath: string; + mainDocId: string; docs: DocMetadata[]; sidebars: Sidebars; permalinkToSidebar: Record; From ba4dec6af8d3ec2b515fa3dc00271bc000e411d9 Mon Sep 17 00:00:00 2001 From: slorber Date: Wed, 12 Aug 2020 14:19:12 +0200 Subject: [PATCH 11/30] attempt to fix admonition :( --- .../docusaurus-plugin-content-docs/src/index.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/docusaurus-plugin-content-docs/src/index.ts b/packages/docusaurus-plugin-content-docs/src/index.ts index 7601f42c1cff..0e23fd4c4ab8 100644 --- a/packages/docusaurus-plugin-content-docs/src/index.ts +++ b/packages/docusaurus-plugin-content-docs/src/index.ts @@ -477,14 +477,17 @@ export function validateOptions({ } // @ts-expect-error: TODO bad OptionValidationContext, need refactor - const validatedOptions: PluginOptions = validate(PluginOptionSchema, options); + const normalizedOptions: PluginOptions = validate( + PluginOptionSchema, + options, + ); - if (options.admonitions) { - validatedOptions.remarkPlugins = validatedOptions.remarkPlugins.concat([ - [admonitions, options.admonitions], + if (normalizedOptions.admonitions) { + normalizedOptions.remarkPlugins = normalizedOptions.remarkPlugins.concat([ + [admonitions, normalizedOptions.admonitions], ]); } // @ts-expect-error: TODO bad OptionValidationContext, need refactor - return validatedOptions; + return normalizedOptions; } From d0b1d773934e39b1772832769266017225ce2e9a Mon Sep 17 00:00:00 2001 From: slorber Date: Wed, 12 Aug 2020 14:52:46 +0200 Subject: [PATCH 12/30] configureWebpack docs: better typing --- .../src/index.ts | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/packages/docusaurus-plugin-content-docs/src/index.ts b/packages/docusaurus-plugin-content-docs/src/index.ts index 0e23fd4c4ab8..cf9a4a85ca2b 100644 --- a/packages/docusaurus-plugin-content-docs/src/index.ts +++ b/packages/docusaurus-plugin-content-docs/src/index.ts @@ -49,12 +49,12 @@ import { DocNavLink, LoadedVersion, } from './types'; -import {Configuration} from 'webpack'; +import {RuleSetRule} from 'webpack'; import {docsVersion} from './version'; import {CURRENT_VERSION_NAME, VERSIONS_JSON_FILE} from './constants'; import {PluginOptionSchema} from './pluginOptionSchema'; import {ValidationError} from '@hapi/joi'; -import {flatten, keyBy} from 'lodash'; +import {flatten, keyBy, compact} from 'lodash'; import {toGlobalDataVersion} from './globalData'; function normalizeSidebars(loadedVersion: LoadedVersion): DocsSidebar { @@ -387,20 +387,16 @@ export default function pluginContentDocs( configureWebpack(_config, isServer, utils) { const {getBabelLoader, getCacheLoader} = utils; const {rehypePlugins, remarkPlugins} = options; - // Suppress warnings about non-existing of versions file. - const stats = { - warningsFilter: [VERSIONS_JSON_FILE], - }; // TODO instead of creating one mdx loader rule for all versions // it may be simpler to create one mdx loader per version // particularly to handle the markdown/linkify process // (docsDir/versionedDir are a bit annoying here...) - function createMDXLoaderRule() { + function createMDXLoaderRule(): RuleSetRule { return { test: /(\.mdx?)$/, include: versionsMetadata.map((vmd) => vmd.docsPath), - use: [ + use: compact([ getCacheLoader(isServer), getBabelLoader(isServer), { @@ -426,10 +422,15 @@ export default function pluginContentDocs( versionedDir: legacyVersioningEnv.versioning.docsDir, }, }, - ].filter(Boolean), + ]), }; } + // Suppress warnings about non-existing of versions file. + const stats = { + warningsFilter: [VERSIONS_JSON_FILE], + }; + return { stats, devServer: { @@ -443,7 +444,7 @@ export default function pluginContentDocs( module: { rules: [createMDXLoaderRule()], }, - } as Configuration; + }; }, }; } From d8a8facbc1570ed8d739293a6619867938944e76 Mon Sep 17 00:00:00 2001 From: slorber Date: Wed, 12 Aug 2020 15:18:02 +0200 Subject: [PATCH 13/30] more refactors --- .../src/index.ts | 74 ++----------------- .../src/sidebars.ts | 12 ++- .../src/types.ts | 45 ++++++----- .../src/versionMetadataProp.ts | 70 ++++++++++++++++++ 4 files changed, 104 insertions(+), 97 deletions(-) create mode 100644 packages/docusaurus-plugin-content-docs/src/versionMetadataProp.ts diff --git a/packages/docusaurus-plugin-content-docs/src/index.ts b/packages/docusaurus-plugin-content-docs/src/index.ts index cf9a4a85ca2b..e6c8df99d05f 100644 --- a/packages/docusaurus-plugin-content-docs/src/index.ts +++ b/packages/docusaurus-plugin-content-docs/src/index.ts @@ -34,15 +34,9 @@ import { LoadedContent, SourceToPermalink, PermalinkToSidebar, - SidebarItemLink, - SidebarItemDoc, - DocsSidebar, - VersionMetadataProp, DocMetadataBase, DocsMetadataRaw, DocMetadata, - SidebarItem, - DocsSidebarItem, GlobalPluginData, VersionName, VersionMetadata, @@ -56,55 +50,7 @@ import {PluginOptionSchema} from './pluginOptionSchema'; import {ValidationError} from '@hapi/joi'; import {flatten, keyBy, compact} from 'lodash'; import {toGlobalDataVersion} from './globalData'; - -function normalizeSidebars(loadedVersion: LoadedVersion): DocsSidebar { - const docsById = keyBy(loadedVersion.docs, (doc) => doc.id); - - const convertDocLink = (item: SidebarItemDoc): SidebarItemLink => { - const docId = item.id; - const docMetadata = docsById[docId]; - - if (!docMetadata) { - throw new Error( - `Bad sidebars file. The document id '${docId}' was used in the sidebar, but no document with this id could be found. -Available document ids= -- ${Object.keys(docsById).sort().join('\n- ')}`, - ); - } - - const {title, permalink, sidebar_label} = docMetadata; - - return { - type: 'link', - label: sidebar_label || title, - href: permalink, - }; - }; - - const normalizeItem = (item: SidebarItem): DocsSidebarItem => { - switch (item.type) { - case 'category': - return {...item, items: item.items.map(normalizeItem)}; - case 'ref': - case 'doc': - return convertDocLink(item); - case 'link': - default: - return item; - } - }; - - // Transform the sidebar so that all sidebar item will be in the - // form of 'link' or 'category' only. - // This is what will be passed as props to the UI component. - return Object.entries(loadedVersion.sidebars).reduce( - (acc: DocsSidebar, [sidebarId, sidebarItems]) => { - acc[sidebarId] = sidebarItems.map(normalizeItem); - return acc; - }, - {}, - ); -} +import {toVersionMetadataProp} from './versionMetadataProp'; export default function pluginContentDocs( context: LoadContext, @@ -312,16 +258,6 @@ export default function pluginContentDocs( const {docLayoutComponent, docItemComponent} = options; const {addRoute, createData, setGlobalData} = actions; - const createVersionMetadataProp = ( - loadedVersion: LoadedVersion, - ): VersionMetadataProp => { - return { - version: loadedVersion.versionName, - docsSidebars: normalizeSidebars(loadedVersion), - permalinkToSidebar: loadedVersion.permalinkToSidebar, - }; - }; - const createDocRoutes = async ( docs: DocMetadata[], ): Promise => { @@ -349,11 +285,11 @@ export default function pluginContentDocs( }; async function handleVersion(loadedVersion: LoadedVersion) { - const versionMetadataProp = createVersionMetadataProp(loadedVersion); - const versionMetadataPropPath = await createData( - `${docuHash(`${loadedVersion.versionName}`)}.json`, - JSON.stringify(versionMetadataProp, null, 2), + `${docuHash( + `version-${loadedVersion.versionName}-metadata-prop`, + )}.json`, + JSON.stringify(toVersionMetadataProp(loadedVersion), null, 2), ); addRoute({ diff --git a/packages/docusaurus-plugin-content-docs/src/sidebars.ts b/packages/docusaurus-plugin-content-docs/src/sidebars.ts index 624484ca81bd..1a4b973cb591 100644 --- a/packages/docusaurus-plugin-content-docs/src/sidebars.ts +++ b/packages/docusaurus-plugin-content-docs/src/sidebars.ts @@ -10,7 +10,6 @@ import fs from 'fs-extra'; import importFresh from 'import-fresh'; import { Sidebars, - SidebarRaw, SidebarItem, SidebarItemCategoryRaw, SidebarItemRaw, @@ -19,6 +18,11 @@ import { SidebarCategoryShorthandRaw, } from './types'; +// Sidebar given by user that is not normalized yet. e.g: sidebars.json +export interface SidebarJson { + [sidebarId: string]: SidebarCategoryShorthandRaw | SidebarItemRaw[]; +} + function isCategoryShorthand( item: SidebarItemRaw, ): item is SidebarCategoryShorthandRaw { @@ -158,7 +162,7 @@ function normalizeItem(item: SidebarItemRaw): SidebarItem[] { /** * Converts sidebars object to mapping to arrays of sidebar item objects. */ -function normalizeSidebar(sidebars: SidebarRaw): Sidebars { +function normalizeSidebar(sidebars: SidebarJson): Sidebars { return Object.entries(sidebars).reduce( (acc: Sidebars, [sidebarId, sidebar]) => { const normalizedSidebar: SidebarItemRaw[] = Array.isArray(sidebar) @@ -175,7 +179,7 @@ function normalizeSidebar(sidebars: SidebarRaw): Sidebars { export default function loadSidebars(sidebarPaths?: string[]): Sidebars { // We don't want sidebars to be cached because of hot reloading. - const allSidebars: SidebarRaw = {}; + const allSidebars: SidebarJson = {}; if (!sidebarPaths || !sidebarPaths.length) { return {} as Sidebars; @@ -183,7 +187,7 @@ export default function loadSidebars(sidebarPaths?: string[]): Sidebars { sidebarPaths.forEach((sidebarPath) => { if (sidebarPath && fs.existsSync(sidebarPath)) { - const sidebar = importFresh(sidebarPath) as SidebarRaw; + const sidebar = importFresh(sidebarPath) as SidebarJson; Object.assign(allSidebars, sidebar); } }); diff --git a/packages/docusaurus-plugin-content-docs/src/types.ts b/packages/docusaurus-plugin-content-docs/src/types.ts index e9bad29e1385..ea254fa8f22e 100644 --- a/packages/docusaurus-plugin-content-docs/src/types.ts +++ b/packages/docusaurus-plugin-content-docs/src/types.ts @@ -87,28 +87,10 @@ export interface SidebarCategoryShorthandRaw { [sidebarCategory: string]: SidebarItemRaw[]; } -// Sidebar given by user that is not normalized yet. e.g: sidebars.json -export interface SidebarRaw { - [sidebarId: string]: SidebarCategoryShorthandRaw | SidebarItemRaw[]; -} - export interface Sidebars { [sidebarId: string]: SidebarItem[]; } -export interface DocsSidebarItemCategory { - type: 'category'; - label: string; - items: DocsSidebarItem[]; - collapsed?: boolean; -} - -export type DocsSidebarItem = SidebarItemLink | DocsSidebarItemCategory; - -export interface DocsSidebar { - [sidebarId: string]: DocsSidebarItem[]; -} - export interface OrderMetadata { previous?: string; next?: string; @@ -173,12 +155,6 @@ export interface LoadedContent { loadedVersions: LoadedVersion[]; } -export type VersionMetadataProp = { - version: VersionName; - docsSidebars: DocsSidebar; - permalinkToSidebar: PermalinkToSidebar; -}; - export type VersioningEnv = { enabled: boolean; latestVersion: string; @@ -209,3 +185,24 @@ export type GlobalPluginData = { latestVersionName: VersionName; versions: GlobalVersion[]; }; + +export type PropVersionMetadata = { + version: VersionName; + docsSidebars: PropSidebars; + permalinkToSidebar: PermalinkToSidebar; +}; + +export type PropSidebarItemLink = SidebarItemLink; // same + +export interface PropSidebarItemCategory { + type: 'category'; + label: string; + items: PropSidebarItemLink[]; + collapsed?: boolean; +} + +export type PropSidebarItem = PropSidebarItemLink | PropSidebarItemCategory; + +export interface PropSidebars { + [sidebarId: string]: PropSidebarItem[]; +} diff --git a/packages/docusaurus-plugin-content-docs/src/versionMetadataProp.ts b/packages/docusaurus-plugin-content-docs/src/versionMetadataProp.ts new file mode 100644 index 000000000000..cdc1d94ba27c --- /dev/null +++ b/packages/docusaurus-plugin-content-docs/src/versionMetadataProp.ts @@ -0,0 +1,70 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import { + LoadedVersion, + PropSidebars, + SidebarItemDoc, + SidebarItemLink, + PropVersionMetadata, + SidebarItem, + PropSidebarItem, +} from './types'; +import {keyBy, mapValues} from 'lodash'; + +function toSidebarsProp(loadedVersion: LoadedVersion): PropSidebars { + const docsById = keyBy(loadedVersion.docs, (doc) => doc.id); + + const convertDocLink = (item: SidebarItemDoc): SidebarItemLink => { + const docId = item.id; + const docMetadata = docsById[docId]; + + if (!docMetadata) { + throw new Error( + `Bad sidebars file. The document id '${docId}' was used in the sidebar, but no document with this id could be found. +Available document ids= +- ${Object.keys(docsById).sort().join('\n- ')}`, + ); + } + + const {title, permalink, sidebar_label} = docMetadata; + + return { + type: 'link', + label: sidebar_label || title, + href: permalink, + }; + }; + + const normalizeItem = (item: SidebarItem): PropSidebarItem => { + switch (item.type) { + case 'category': + return {...item, items: item.items.map(normalizeItem)}; + case 'ref': + case 'doc': + return convertDocLink(item); + case 'link': + default: + return item; + } + }; + + // Transform the sidebar so that all sidebar item will be in the + // form of 'link' or 'category' only. + // This is what will be passed as props to the UI component. + return mapValues(loadedVersion.sidebars, (items) => items.map(normalizeItem)); +} + +export function toVersionMetadataProp( + loadedVersion: LoadedVersion, +): PropVersionMetadata { + return { + version: loadedVersion.versionName, + docsSidebars: toSidebarsProp(loadedVersion), + permalinkToSidebar: loadedVersion.permalinkToSidebar, + }; +} From 2a3a313d63a590a48cfdacdee19163ea2665b7c1 Mon Sep 17 00:00:00 2001 From: slorber Date: Wed, 12 Aug 2020 15:29:21 +0200 Subject: [PATCH 14/30] rename cli --- .../{version.test.ts => cli.test.ts} | 49 ++++++++++++------- .../src/{version.ts => cli.ts} | 2 +- .../src/index.ts | 4 +- 3 files changed, 35 insertions(+), 20 deletions(-) rename packages/docusaurus-plugin-content-docs/src/__tests__/{version.test.ts => cli.test.ts} (89%) rename packages/docusaurus-plugin-content-docs/src/{version.ts => cli.ts} (99%) diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/version.test.ts b/packages/docusaurus-plugin-content-docs/src/__tests__/cli.test.ts similarity index 89% rename from packages/docusaurus-plugin-content-docs/src/__tests__/version.test.ts rename to packages/docusaurus-plugin-content-docs/src/__tests__/cli.test.ts index bd5cd1f0e3ca..217eb6571423 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/version.test.ts +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/cli.test.ts @@ -6,7 +6,7 @@ */ import path from 'path'; -import {docsVersion} from '../version'; +import {cliDocsVersion} from '../cli'; import {PathOptions} from '../types'; import fs from 'fs-extra'; import { @@ -28,17 +28,22 @@ describe('docsVersion', () => { test('no version tag provided', () => { expect(() => - docsVersion(null, simpleSiteDir, DEFAULT_PLUGIN_ID, DEFAULT_OPTIONS), + cliDocsVersion(null, simpleSiteDir, DEFAULT_PLUGIN_ID, DEFAULT_OPTIONS), ).toThrowErrorMatchingInlineSnapshot( `"[docs] No version tag specified!. Pass the version you wish to create as an argument. Ex: 1.0.0"`, ); expect(() => - docsVersion(undefined, simpleSiteDir, DEFAULT_PLUGIN_ID, DEFAULT_OPTIONS), + cliDocsVersion( + undefined, + simpleSiteDir, + DEFAULT_PLUGIN_ID, + DEFAULT_OPTIONS, + ), ).toThrowErrorMatchingInlineSnapshot( `"[docs] No version tag specified!. Pass the version you wish to create as an argument. Ex: 1.0.0"`, ); expect(() => - docsVersion('', simpleSiteDir, DEFAULT_PLUGIN_ID, DEFAULT_OPTIONS), + cliDocsVersion('', simpleSiteDir, DEFAULT_PLUGIN_ID, DEFAULT_OPTIONS), ).toThrowErrorMatchingInlineSnapshot( `"[docs] No version tag specified!. Pass the version you wish to create as an argument. Ex: 1.0.0"`, ); @@ -46,12 +51,17 @@ describe('docsVersion', () => { test('version tag should not have slash', () => { expect(() => - docsVersion('foo/bar', simpleSiteDir, DEFAULT_PLUGIN_ID, DEFAULT_OPTIONS), + cliDocsVersion( + 'foo/bar', + simpleSiteDir, + DEFAULT_PLUGIN_ID, + DEFAULT_OPTIONS, + ), ).toThrowErrorMatchingInlineSnapshot( `"[docs] Invalid version tag specified! Do not include slash (/) or (\\\\). Try something like: 1.0.0"`, ); expect(() => - docsVersion( + cliDocsVersion( 'foo\\bar', simpleSiteDir, DEFAULT_PLUGIN_ID, @@ -64,7 +74,7 @@ describe('docsVersion', () => { test('version tag should not be too long', () => { expect(() => - docsVersion( + cliDocsVersion( 'a'.repeat(255), simpleSiteDir, DEFAULT_PLUGIN_ID, @@ -77,12 +87,12 @@ describe('docsVersion', () => { test('version tag should not be a dot or two dots', () => { expect(() => - docsVersion('..', simpleSiteDir, DEFAULT_PLUGIN_ID, DEFAULT_OPTIONS), + cliDocsVersion('..', simpleSiteDir, DEFAULT_PLUGIN_ID, DEFAULT_OPTIONS), ).toThrowErrorMatchingInlineSnapshot( `"[docs] Invalid version tag specified! Do not name your version \\".\\" or \\"..\\". Try something like: 1.0.0"`, ); expect(() => - docsVersion('.', simpleSiteDir, DEFAULT_PLUGIN_ID, DEFAULT_OPTIONS), + cliDocsVersion('.', simpleSiteDir, DEFAULT_PLUGIN_ID, DEFAULT_OPTIONS), ).toThrowErrorMatchingInlineSnapshot( `"[docs] Invalid version tag specified! Do not name your version \\".\\" or \\"..\\". Try something like: 1.0.0"`, ); @@ -90,7 +100,7 @@ describe('docsVersion', () => { test('version tag should be a valid pathname', () => { expect(() => - docsVersion( + cliDocsVersion( '', simpleSiteDir, DEFAULT_PLUGIN_ID, @@ -100,7 +110,7 @@ describe('docsVersion', () => { `"[docs] Invalid version tag specified! Please ensure its a valid pathname too. Try something like: 1.0.0"`, ); expect(() => - docsVersion( + cliDocsVersion( 'foo\x00bar', simpleSiteDir, DEFAULT_PLUGIN_ID, @@ -110,7 +120,12 @@ describe('docsVersion', () => { `"[docs] Invalid version tag specified! Please ensure its a valid pathname too. Try something like: 1.0.0"`, ); expect(() => - docsVersion('foo:bar', simpleSiteDir, DEFAULT_PLUGIN_ID, DEFAULT_OPTIONS), + cliDocsVersion( + 'foo:bar', + simpleSiteDir, + DEFAULT_PLUGIN_ID, + DEFAULT_OPTIONS, + ), ).toThrowErrorMatchingInlineSnapshot( `"[docs] Invalid version tag specified! Please ensure its a valid pathname too. Try something like: 1.0.0"`, ); @@ -118,7 +133,7 @@ describe('docsVersion', () => { test('version tag already exist', () => { expect(() => - docsVersion( + cliDocsVersion( '1.0.0', versionedSiteDir, DEFAULT_PLUGIN_ID, @@ -132,7 +147,7 @@ describe('docsVersion', () => { test('no docs file to version', () => { const emptySiteDir = path.join(fixtureDir, 'empty-site'); expect(() => - docsVersion('1.0.0', emptySiteDir, DEFAULT_PLUGIN_ID, DEFAULT_OPTIONS), + cliDocsVersion('1.0.0', emptySiteDir, DEFAULT_PLUGIN_ID, DEFAULT_OPTIONS), ).toThrowErrorMatchingInlineSnapshot( `"[docs] There is no docs to version !"`, ); @@ -159,7 +174,7 @@ describe('docsVersion', () => { path: 'docs', sidebarPath: path.join(simpleSiteDir, 'sidebars.json'), }; - docsVersion('1.0.0', simpleSiteDir, DEFAULT_PLUGIN_ID, options); + cliDocsVersion('1.0.0', simpleSiteDir, DEFAULT_PLUGIN_ID, options); expect(copyMock).toHaveBeenCalledWith( path.join(simpleSiteDir, options.path), path.join( @@ -207,7 +222,7 @@ describe('docsVersion', () => { path: 'docs', sidebarPath: path.join(versionedSiteDir, 'sidebars.json'), }; - docsVersion('2.0.0', versionedSiteDir, DEFAULT_PLUGIN_ID, options); + cliDocsVersion('2.0.0', versionedSiteDir, DEFAULT_PLUGIN_ID, options); expect(copyMock).toHaveBeenCalledWith( path.join(versionedSiteDir, options.path), path.join( @@ -257,7 +272,7 @@ describe('docsVersion', () => { path: 'community', sidebarPath: path.join(versionedSiteDir, 'community_sidebars.json'), }; - docsVersion('2.0.0', versionedSiteDir, pluginId, options); + cliDocsVersion('2.0.0', versionedSiteDir, pluginId, options); expect(copyMock).toHaveBeenCalledWith( path.join(versionedSiteDir, options.path), path.join( diff --git a/packages/docusaurus-plugin-content-docs/src/version.ts b/packages/docusaurus-plugin-content-docs/src/cli.ts similarity index 99% rename from packages/docusaurus-plugin-content-docs/src/version.ts rename to packages/docusaurus-plugin-content-docs/src/cli.ts index d037ae4a5e1c..ad756c08d9e2 100644 --- a/packages/docusaurus-plugin-content-docs/src/version.ts +++ b/packages/docusaurus-plugin-content-docs/src/cli.ts @@ -18,7 +18,7 @@ import {DEFAULT_PLUGIN_ID} from '@docusaurus/core/lib/constants'; // Tests depend on non-default export for mocking. // eslint-disable-next-line import/prefer-default-export -export function docsVersion( +export function cliDocsVersion( version: string | null | undefined, siteDir: string, pluginId: string, diff --git a/packages/docusaurus-plugin-content-docs/src/index.ts b/packages/docusaurus-plugin-content-docs/src/index.ts index e6c8df99d05f..506e8327b156 100644 --- a/packages/docusaurus-plugin-content-docs/src/index.ts +++ b/packages/docusaurus-plugin-content-docs/src/index.ts @@ -44,7 +44,7 @@ import { LoadedVersion, } from './types'; import {RuleSetRule} from 'webpack'; -import {docsVersion} from './version'; +import {cliDocsVersion} from './cli'; import {CURRENT_VERSION_NAME, VERSIONS_JSON_FILE} from './constants'; import {PluginOptionSchema} from './pluginOptionSchema'; import {ValidationError} from '@hapi/joi'; @@ -134,7 +134,7 @@ export default function pluginContentDocs( .arguments('') .description(commandDescription) .action((version) => { - docsVersion(version, siteDir, pluginId, { + cliDocsVersion(version, siteDir, pluginId, { path: options.path, sidebarPath: options.sidebarPath, }); From d53f108f0fff96fa0f1233b4f1e0228778391562 Mon Sep 17 00:00:00 2001 From: slorber Date: Wed, 12 Aug 2020 17:07:42 +0200 Subject: [PATCH 15/30] refactor docs metadata processing => move to pure function --- ...{version.test.ts.snap => cli.test.ts.snap} | 0 .../{metadata.test.ts => docs.test.ts} | 10 +- .../src/__tests__/lastUpdate.test.ts | 14 +-- .../src/{metadata.ts => docs.ts} | 61 +++++++--- .../src/index.ts | 37 ++---- .../src/lastUpdate.ts | 2 +- .../src/types.ts | 113 ++++++++++-------- 7 files changed, 126 insertions(+), 111 deletions(-) rename packages/docusaurus-plugin-content-docs/src/__tests__/__snapshots__/{version.test.ts.snap => cli.test.ts.snap} (100%) rename packages/docusaurus-plugin-content-docs/src/__tests__/{metadata.test.ts => docs.test.ts} (98%) rename packages/docusaurus-plugin-content-docs/src/{metadata.ts => docs.ts} (75%) diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/__snapshots__/version.test.ts.snap b/packages/docusaurus-plugin-content-docs/src/__tests__/__snapshots__/cli.test.ts.snap similarity index 100% rename from packages/docusaurus-plugin-content-docs/src/__tests__/__snapshots__/version.test.ts.snap rename to packages/docusaurus-plugin-content-docs/src/__tests__/__snapshots__/cli.test.ts.snap diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/metadata.test.ts b/packages/docusaurus-plugin-content-docs/src/__tests__/docs.test.ts similarity index 98% rename from packages/docusaurus-plugin-content-docs/src/__tests__/metadata.test.ts rename to packages/docusaurus-plugin-content-docs/src/__tests__/docs.test.ts index eb9553007396..46bf99fb4972 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/metadata.test.ts +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/docs.test.ts @@ -7,7 +7,7 @@ import path from 'path'; import {loadContext} from '@docusaurus/core/src/server/index'; -import processMetadata from '../metadata'; +import {processDocMetadata} from '../docs'; import loadEnv from '../env'; import {DocMetadataBase, Env, MetadataOptions} from '../types'; import {LoadContext} from '@docusaurus/types'; @@ -31,7 +31,7 @@ function createTestHelpers({ source: string, expectedMetadata: Omit, ) { - const metadata = await processMetadata({ + const metadata = await processDocMetadata({ source, docsDir: refDir, context, @@ -49,7 +49,7 @@ function createTestHelpers({ source: string, expectedPermalink: string, ) { - const metadata = await processMetadata({ + const metadata = await processDocMetadata({ source, docsDir: refDir, context, @@ -279,7 +279,7 @@ describe('simple site', () => { const badSiteDir = path.join(fixtureDir, 'bad-id-site'); await expect( - processMetadata({ + processDocMetadata({ source: 'invalid-id.md', docsDir: path.join(badSiteDir, 'docs'), context, @@ -297,7 +297,7 @@ describe('simple site', () => { const badSiteDir = path.join(fixtureDir, 'bad-slug-on-doc-home-site'); await expect( - processMetadata({ + processDocMetadata({ source: 'docWithSlug.md', docsDir: path.join(badSiteDir, 'docs'), context, diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/lastUpdate.test.ts b/packages/docusaurus-plugin-content-docs/src/__tests__/lastUpdate.test.ts index 6c66b5da9c84..6c106ded9a49 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/lastUpdate.test.ts +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/lastUpdate.test.ts @@ -9,7 +9,7 @@ import fs from 'fs'; import path from 'path'; import shell from 'shelljs'; -import lastUpdate from '../lastUpdate'; +import {getFileLastUpdate} from '../lastUpdate'; describe('lastUpdate', () => { const existingFilePath = path.join( @@ -17,7 +17,7 @@ describe('lastUpdate', () => { '__fixtures__/simple-site/docs/hello.md', ); test('existing test file in repository with Git timestamp', async () => { - const lastUpdateData = await lastUpdate(existingFilePath); + const lastUpdateData = await getFileLastUpdate(existingFilePath); expect(lastUpdateData).not.toBeNull(); const {author, timestamp} = lastUpdateData; @@ -36,29 +36,29 @@ describe('lastUpdate', () => { '__fixtures__', '.nonExisting', ); - expect(await lastUpdate(nonExistingFilePath)).toBeNull(); + expect(await getFileLastUpdate(nonExistingFilePath)).toBeNull(); expect(consoleMock).toHaveBeenCalledTimes(1); expect(consoleMock).toHaveBeenCalledWith( new Error( `Command failed with exit code 128: git log -1 --format=%ct, %an ${nonExistingFilePath}`, ), ); - expect(await lastUpdate(null)).toBeNull(); - expect(await lastUpdate(undefined)).toBeNull(); + expect(await getFileLastUpdate(null)).toBeNull(); + expect(await getFileLastUpdate(undefined)).toBeNull(); consoleMock.mockRestore(); }); test('temporary created file that has no git timestamp', async () => { const tempFilePath = path.join(__dirname, '__fixtures__', '.temp'); fs.writeFileSync(tempFilePath, 'Lorem ipsum :)'); - expect(await lastUpdate(tempFilePath)).toBeNull(); + expect(await getFileLastUpdate(tempFilePath)).toBeNull(); fs.unlinkSync(tempFilePath); }); test('Git does not exist', async () => { const mock = jest.spyOn(shell, 'which').mockImplementationOnce(() => null); const consoleMock = jest.spyOn(console, 'warn').mockImplementation(); - const lastUpdateData = await lastUpdate(existingFilePath); + const lastUpdateData = await getFileLastUpdate(existingFilePath); expect(lastUpdateData).toBeNull(); expect(consoleMock).toHaveBeenLastCalledWith( 'Sorry, the docs plugin last update options require Git.', diff --git a/packages/docusaurus-plugin-content-docs/src/metadata.ts b/packages/docusaurus-plugin-content-docs/src/docs.ts similarity index 75% rename from packages/docusaurus-plugin-content-docs/src/metadata.ts rename to packages/docusaurus-plugin-content-docs/src/docs.ts index 515946f54ff5..a67502bcf7eb 100644 --- a/packages/docusaurus-plugin-content-docs/src/metadata.ts +++ b/packages/docusaurus-plugin-content-docs/src/docs.ts @@ -6,35 +6,39 @@ */ import path from 'path'; +import fs from 'fs-extra'; import { - parseMarkdownFile, aliasedSitePath, normalizeUrl, getEditUrl, + parseMarkdownString, } from '@docusaurus/utils'; import {LoadContext} from '@docusaurus/types'; -import lastUpdate from './lastUpdate'; +import {getFileLastUpdate} from './lastUpdate'; import { DocMetadataBase, LastUpdateData, MetadataOptions, Env, VersionMetadata, + DocFile, + PluginOptions, } from './types'; import getSlug from './slug'; import {CURRENT_VERSION_NAME} from './constants'; +import globby from 'globby'; -async function lastUpdated( +async function readLastUpdateData( filePath: string, - options: MetadataOptions, + options: Pick, ): Promise { const {showLastUpdateAuthor, showLastUpdateTime} = options; if (showLastUpdateAuthor || showLastUpdateTime) { // Use fake data in dev for faster development. const fileLastUpdateData = process.env.NODE_ENV === 'production' - ? await lastUpdate(filePath) + ? await getFileLastUpdate(filePath) : { author: 'Author', timestamp: 1539502055, @@ -52,28 +56,51 @@ async function lastUpdated( return {}; } -export default async function processMetadata({ - source, +export async function readVersionDocs( + versionMetadata: VersionMetadata, + options: Pick< + PluginOptions, + 'include' | 'showLastUpdateAuthor' | 'showLastUpdateTime' + >, +): Promise { + const sources = await globby(options.include, { + cwd: versionMetadata.docsPath, + }); + + async function readDoc(source: string): Promise { + const filePath = path.join(versionMetadata.docsPath, source); + const [content, lastUpdate] = await Promise.all([ + fs.readFile(filePath, 'utf-8'), + readLastUpdateData(filePath, options), + ]); + return {source, content, lastUpdate}; + } + + return Promise.all(sources.map(readDoc)); +} + +export function processDocMetadata({ + docFile, versionMetadata, context, options, env, }: { - source: string; + docFile: DocFile; versionMetadata: VersionMetadata; context: LoadContext; options: MetadataOptions; env: Env; -}): Promise { +}): DocMetadataBase { + const {source, content, lastUpdate} = docFile; const {routeBasePath, editUrl, homePageId} = options; const {siteDir, baseUrl} = context; const {versioning} = env; const filePath = path.join(versionMetadata.docsPath, source); - const fileMarkdownPromise = parseMarkdownFile(filePath); - const lastUpdatedPromise = lastUpdated(filePath, options); - - const docsFileDirName = path.dirname(source); // ex: api/myDoc -> api + // ex: api/myDoc -> api + // ex: myDoc -> . + const docsFileDirName = path.dirname(source); const {versionName} = versionMetadata; @@ -93,7 +120,7 @@ export default async function processMetadata({ const docsEditUrl = getEditUrl(path.relative(siteDir, filePath), editUrl); - const {frontMatter = {}, excerpt} = await fileMarkdownPromise; + const {frontMatter = {}, excerpt} = parseMarkdownString(content); const {sidebar_label, custom_edit_url} = frontMatter; const baseID: string = @@ -147,8 +174,6 @@ export default async function processMetadata({ docSlug, ]); - const {lastUpdatedAt, lastUpdatedBy} = await lastUpdatedPromise; - // Assign all of object properties during instantiation (if possible) for // NodeJS optimization. // Adding properties to object after instantiation will cause hidden @@ -164,8 +189,8 @@ export default async function processMetadata({ permalink, editUrl: custom_edit_url !== undefined ? custom_edit_url : docsEditUrl, version: versionName, - lastUpdatedBy, - lastUpdatedAt, + lastUpdatedBy: lastUpdate.lastUpdatedBy, + lastUpdatedAt: lastUpdate.lastUpdatedAt, sidebar_label, }; diff --git a/packages/docusaurus-plugin-content-docs/src/index.ts b/packages/docusaurus-plugin-content-docs/src/index.ts index 506e8327b156..b85e82202745 100644 --- a/packages/docusaurus-plugin-content-docs/src/index.ts +++ b/packages/docusaurus-plugin-content-docs/src/index.ts @@ -5,7 +5,6 @@ * LICENSE file in the root directory of this source tree. */ -import globby from 'globby'; import path from 'path'; import chalk from 'chalk'; @@ -25,7 +24,7 @@ import { import createOrder from './order'; import loadSidebars from './sidebars'; -import processMetadata from './metadata'; +import {readVersionDocs, processDocMetadata} from './docs'; import loadEnv, {readVersionsMetadata} from './env'; import { @@ -42,6 +41,7 @@ import { VersionMetadata, DocNavLink, LoadedVersion, + DocFile, } from './types'; import {RuleSetRule} from 'webpack'; import {cliDocsVersion} from './cli'; @@ -90,22 +90,6 @@ export default function pluginContentDocs( return version; } - async function processDocsMetadata({ - source, - versionMetadata, - }: { - source: string; - versionMetadata: VersionMetadata; - }) { - return processMetadata({ - source, - versionMetadata, - context, - options, - env: legacyVersioningEnv, - }); - } - return { name: 'docusaurus-plugin-content-docs', @@ -161,21 +145,20 @@ export default function pluginContentDocs( }, async loadContent() { - const {include} = options; - async function loadVersionDocs( versionMetadata: VersionMetadata, ): Promise { - const docsFiles = await globby(include, { - cwd: versionMetadata.docsPath, - }); - async function processVersionDoc(source: string) { - return processDocsMetadata({ - source, + const docFiles = await readVersionDocs(versionMetadata, options); + async function processVersionDoc(docFile: DocFile) { + return processDocMetadata({ + docFile, versionMetadata, + context, + options, + env: legacyVersioningEnv, }); } - return Promise.all(docsFiles.map(processVersionDoc)); + return Promise.all(docFiles.map(processVersionDoc)); } async function loadVersion( diff --git a/packages/docusaurus-plugin-content-docs/src/lastUpdate.ts b/packages/docusaurus-plugin-content-docs/src/lastUpdate.ts index 8c5a982c46f7..2ce8e71ffb2f 100644 --- a/packages/docusaurus-plugin-content-docs/src/lastUpdate.ts +++ b/packages/docusaurus-plugin-content-docs/src/lastUpdate.ts @@ -14,7 +14,7 @@ const GIT_COMMIT_TIMESTAMP_AUTHOR_REGEX = /^(\d+), (.+)$/; let showedGitRequirementError = false; -export default async function getFileLastUpdate( +export async function getFileLastUpdate( filePath?: string, ): Promise { if (!filePath) { diff --git a/packages/docusaurus-plugin-content-docs/src/types.ts b/packages/docusaurus-plugin-content-docs/src/types.ts index ea254fa8f22e..aa329d4f08ac 100644 --- a/packages/docusaurus-plugin-content-docs/src/types.ts +++ b/packages/docusaurus-plugin-content-docs/src/types.ts @@ -8,6 +8,12 @@ // eslint-disable-next-line spaced-comment /// +export type DocFile = { + source: string; + content: string; + lastUpdate: LastUpdateData; +}; + export type VersionName = string; export type VersionMetadata = { @@ -16,56 +22,57 @@ export type VersionMetadata = { sidebarPath: string; }; -export interface MetadataOptions { +export type MetadataOptions = { routeBasePath: string; homePageId?: string; editUrl?: string; showLastUpdateTime?: boolean; showLastUpdateAuthor?: boolean; -} +}; -export interface PathOptions { +export type PathOptions = { path: string; sidebarPath: string; -} +}; -export interface PluginOptions extends MetadataOptions, PathOptions { - id: string; - include: string[]; - docLayoutComponent: string; - docItemComponent: string; - remarkPlugins: ([Function, object] | Function)[]; - rehypePlugins: string[]; - admonitions: any; - disableVersioning: boolean; - excludeNextVersionDocs?: boolean; - includeCurrentVersion: boolean; -} +export type PluginOptions = MetadataOptions & + PathOptions & { + id: string; + include: string[]; + docLayoutComponent: string; + docItemComponent: string; + remarkPlugins: ([Function, object] | Function)[]; + rehypePlugins: string[]; + admonitions: any; + disableVersioning: boolean; + excludeNextVersionDocs?: boolean; + includeCurrentVersion: boolean; + }; export type SidebarItemDoc = { type: 'doc' | 'ref'; id: string; }; -export interface SidebarItemLink { +export type SidebarItemLink = { type: 'link'; href: string; label: string; -} +}; -export interface SidebarItemCategory { +export type SidebarItemCategory = { type: 'category'; label: string; items: SidebarItem[]; collapsed: boolean; -} +}; -export interface SidebarItemCategoryRaw { +export type SidebarItemCategoryRaw = { type: 'category'; label: string; items: SidebarItemRaw[]; collapsed?: boolean; -} +}; export type SidebarItem = | SidebarItemDoc @@ -83,30 +90,30 @@ export type SidebarItemRaw = [key: string]: unknown; }; -export interface SidebarCategoryShorthandRaw { +export type SidebarCategoryShorthandRaw = { [sidebarCategory: string]: SidebarItemRaw[]; -} +}; -export interface Sidebars { +export type Sidebars = { [sidebarId: string]: SidebarItem[]; -} +}; -export interface OrderMetadata { +export type OrderMetadata = { previous?: string; next?: string; sidebar?: string; -} +}; -export interface Order { +export type Order = { [id: string]: OrderMetadata; -} +}; -export interface LastUpdateData { +export type LastUpdateData = { lastUpdatedAt?: number; lastUpdatedBy?: string; -} +}; -export interface DocMetadataBase extends LastUpdateData { +export type DocMetadataBase = LastUpdateData & { version: VersionName; unversionedId: string; id: string; @@ -118,30 +125,30 @@ export interface DocMetadataBase extends LastUpdateData { permalink: string; sidebar_label?: string; editUrl?: string | null; -} +}; -export interface DocNavLink { +export type DocNavLink = { title: string; permalink: string; -} +}; -export interface DocMetadata extends DocMetadataBase { +export type DocMetadata = DocMetadataBase & { sidebar?: string; previous?: DocNavLink; next?: DocNavLink; -} +}; -export interface DocsMetadataRaw { +export type DocsMetadataRaw = { [id: string]: DocMetadataBase; -} +}; -export interface SourceToPermalink { +export type SourceToPermalink = { [source: string]: string; -} +}; -export interface PermalinkToSidebar { +export type PermalinkToSidebar = { [permalink: string]: string; -} +}; export type LoadedVersion = VersionMetadata & { versionPath: string; @@ -151,9 +158,9 @@ export type LoadedVersion = VersionMetadata & { permalinkToSidebar: Record; }; -export interface LoadedContent { +export type LoadedContent = { loadedVersions: LoadedVersion[]; -} +}; export type VersioningEnv = { enabled: boolean; @@ -163,10 +170,10 @@ export type VersioningEnv = { sidebarsDir: string; }; -export interface Env { +export type Env = { versioning: VersioningEnv; // TODO: translation -} +}; export type GlobalDoc = { id: string; @@ -194,15 +201,15 @@ export type PropVersionMetadata = { export type PropSidebarItemLink = SidebarItemLink; // same -export interface PropSidebarItemCategory { +export type PropSidebarItemCategory = { type: 'category'; label: string; - items: PropSidebarItemLink[]; + items: PropSidebarItem[]; collapsed?: boolean; -} +}; export type PropSidebarItem = PropSidebarItemLink | PropSidebarItemCategory; -export interface PropSidebars { +export type PropSidebars = { [sidebarId: string]: PropSidebarItem[]; -} +}; From bdcccae1e2154a332eaa3fa3112d3d2e1279c9b6 Mon Sep 17 00:00:00 2001 From: slorber Date: Wed, 12 Aug 2020 18:22:08 +0200 Subject: [PATCH 16/30] stable docs refactor --- .eslintrc.js | 1 + .../src/docs.ts | 39 +--- .../docusaurus-plugin-content-docs/src/env.ts | 202 ++++++++++++++---- .../src/index.ts | 46 ++-- .../src/types.ts | 10 +- 5 files changed, 186 insertions(+), 112 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 24869c41ec0b..dbf43124daaf 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -87,6 +87,7 @@ module.exports = { {functions: false, classes: false, variables: true}, ], 'no-unused-vars': OFF, + 'no-nested-ternary': WARNING, '@typescript-eslint/no-unused-vars': [ERROR, {argsIgnorePattern: '^_'}], '@typescript-eslint/ban-ts-comment': [ ERROR, diff --git a/packages/docusaurus-plugin-content-docs/src/docs.ts b/packages/docusaurus-plugin-content-docs/src/docs.ts index a67502bcf7eb..762c16c7a7ba 100644 --- a/packages/docusaurus-plugin-content-docs/src/docs.ts +++ b/packages/docusaurus-plugin-content-docs/src/docs.ts @@ -20,7 +20,6 @@ import { DocMetadataBase, LastUpdateData, MetadataOptions, - Env, VersionMetadata, DocFile, PluginOptions, @@ -64,11 +63,11 @@ export async function readVersionDocs( >, ): Promise { const sources = await globby(options.include, { - cwd: versionMetadata.docsPath, + cwd: versionMetadata.docsDirPath, }); async function readDoc(source: string): Promise { - const filePath = path.join(versionMetadata.docsPath, source); + const filePath = path.join(versionMetadata.docsDirPath, source); const [content, lastUpdate] = await Promise.all([ fs.readFile(filePath, 'utf-8'), readLastUpdateData(filePath, options), @@ -84,40 +83,21 @@ export function processDocMetadata({ versionMetadata, context, options, - env, }: { docFile: DocFile; versionMetadata: VersionMetadata; context: LoadContext; options: MetadataOptions; - env: Env; }): DocMetadataBase { const {source, content, lastUpdate} = docFile; - const {routeBasePath, editUrl, homePageId} = options; - const {siteDir, baseUrl} = context; - const {versioning} = env; - const filePath = path.join(versionMetadata.docsPath, source); + const {editUrl, homePageId} = options; + const {siteDir} = context; + const filePath = path.join(versionMetadata.docsDirPath, source); // ex: api/myDoc -> api // ex: myDoc -> . const docsFileDirName = path.dirname(source); - const {versionName} = versionMetadata; - - // TODO for legacy compatibility - function getVersionPath() { - if (!versioning.enabled || versionName === versioning.latestVersion) { - return ''; - } - if (versionName === CURRENT_VERSION_NAME) { - return 'next'; - } - return versionName; - } - - // The version portion of the url path. Eg: 'next', '1.0.0', and ''. - const versionPath = getVersionPath(); - const docsEditUrl = getEditUrl(path.relative(siteDir, filePath), editUrl); const {frontMatter = {}, excerpt} = parseMarkdownString(content); @@ -167,12 +147,7 @@ export function processDocMetadata({ const description: string = frontMatter.description || excerpt; - const permalink = normalizeUrl([ - baseUrl, - routeBasePath, - versionPath, - docSlug, - ]); + const permalink = normalizeUrl([versionMetadata.versionPath, docSlug]); // Assign all of object properties during instantiation (if possible) for // NodeJS optimization. @@ -188,7 +163,7 @@ export function processDocMetadata({ slug: docSlug, permalink, editUrl: custom_edit_url !== undefined ? custom_edit_url : docsEditUrl, - version: versionName, + version: versionMetadata.versionName, lastUpdatedBy: lastUpdate.lastUpdatedBy, lastUpdatedAt: lastUpdate.lastUpdatedAt, sidebar_label, diff --git a/packages/docusaurus-plugin-content-docs/src/env.ts b/packages/docusaurus-plugin-content-docs/src/env.ts index 36da1058b46c..2de37f7a26a0 100644 --- a/packages/docusaurus-plugin-content-docs/src/env.ts +++ b/packages/docusaurus-plugin-content-docs/src/env.ts @@ -16,6 +16,8 @@ import { } from './constants'; import {DEFAULT_PLUGIN_ID} from '@docusaurus/core/lib/constants'; +import {LoadContext} from '@docusaurus/types'; +import {normalizeUrl} from '@docusaurus/utils'; // retro-compatibility: no prefix for the default plugin id function addPluginIdPrefix(fileOrDir: string, pluginId: string): string { @@ -47,92 +49,204 @@ export function getVersionsFilePath(siteDir: string, pluginId: string): string { return path.join(siteDir, addPluginIdPrefix(VERSIONS_JSON_FILE, pluginId)); } +function ensureValidVersionString(version: unknown): asserts version is string { + if (typeof version !== 'string') { + throw new Error( + `versions should be strings. Found type=[${typeof version}] for version=[${version}]`, + ); + } + // Should we forbid versions with special chars like / ? + if (version.trim().length === 0) { + throw new Error(`Invalid version=[${version}]`); + } +} + +function ensureValidVersionArray( + versionArray: unknown, +): asserts versionArray is string[] { + if (!(versionArray instanceof Array)) { + throw new Error( + `The versions file should contain an array of versions! ${JSON.stringify( + versionArray, + )}`, + ); + } + + versionArray.forEach(ensureValidVersionString); +} + +// TODO not easy to make async due to many deps function readVersionsFile(siteDir: string, pluginId: string): string[] | null { const versionsFilePath = getVersionsFilePath(siteDir, pluginId); if (fs.existsSync(versionsFilePath)) { const content = JSON.parse(fs.readFileSync(versionsFilePath, 'utf8')); - if ( - content instanceof Array && - content.every((version) => typeof version === 'string') - ) { - return content; - } else { - throw new Error( - `The versions file should contain an array of versions! ${versionsFilePath}`, - ); - } + ensureValidVersionArray(content); + return content; } else { return null; } } +// TODO not easy to make async due to many deps function readVersionNames( siteDir: string, - {id: pluginId, disableVersioning, includeCurrentVersion}: PluginOptions, + options: Pick< + PluginOptions, + 'id' | 'disableVersioning' | 'includeCurrentVersion' + >, ): string[] { - const versions = disableVersioning + const versions = options.disableVersioning ? [] - : readVersionsFile(siteDir, pluginId) ?? []; + : readVersionsFile(siteDir, options.id) ?? []; // We add the current version at the beginning, unless // - user don't want to // - it's been explicitly added to versions.json - if (includeCurrentVersion && !versions.includes(CURRENT_VERSION_NAME)) { + if ( + options.includeCurrentVersion && + !versions.includes(CURRENT_VERSION_NAME) + ) { versions.unshift(CURRENT_VERSION_NAME); } + + if (versions.length === 0) { + throw new Error( + "It is not possible to use docs without any version. You shouldn't use 'includeCurrentVersion: false' on an unversioned site", + ); + } + return versions; } +function getVersionMetadataPaths({ + versionName, + context, + options, +}: { + versionName: string; + context: Pick; + options: Pick; +}): Pick { + const isCurrentVersion = versionName === CURRENT_VERSION_NAME; + + const docsDirPath = isCurrentVersion + ? path.resolve(context.siteDir, options.path) + : path.join( + getVersionedDocsDirPath(context.siteDir, options.id), + `version-${versionName}`, + ); + + const sidebarFilePath = isCurrentVersion + ? path.resolve(context.siteDir, options.sidebarPath) + : path.join( + getVersionedSidebarsDirPath(context.siteDir, options.id), + `version-${versionName}-sidebars.json`, + ); + + return {docsDirPath, sidebarFilePath}; +} + function createVersionMetadata({ versionName, - siteDir, + isLast, + context, options, }: { versionName: string; - siteDir: string; - options: PluginOptions; + isLast: boolean; + context: Pick; + options: Pick; }): VersionMetadata { - if (versionName === CURRENT_VERSION_NAME) { - const docsPath = path.resolve(siteDir, options.path); - const sidebarPath = path.resolve(siteDir, options.sidebarPath); - return {versionName, docsPath, sidebarPath}; - } else { - const docsPath = path.join( - getVersionedDocsDirPath(siteDir, options.id), - `version-${versionName}`, - ); - const sidebarPath = path.join( - getVersionedSidebarsDirPath(siteDir, options.id), - `version-${versionName}-sidebars.json`, - ); - return {versionName, docsPath, sidebarPath}; - } + const {sidebarFilePath, docsDirPath} = getVersionMetadataPaths({ + versionName, + context, + options, + }); + + // TODO hardcoded for retro-compatibility + // TODO Need to make this configurable + const versionLabel = + versionName === CURRENT_VERSION_NAME ? 'Next' : versionName; + const versionPathPart = isLast + ? '' + : versionName === CURRENT_VERSION_NAME + ? 'next' + : versionName; + + const versionPath = normalizeUrl([ + context.baseUrl, + options.routeBasePath, + versionPathPart, + ]); + + // Because /docs/:route` should always be after `/docs/versionName/:route`. + const routePriority = versionPathPart === '' ? -1 : undefined; + + return { + versionName, + versionLabel, + versionPath, + isLast, + routePriority, + sidebarFilePath, + docsDirPath, + }; } function checkVersionMetadataPaths({ versionName, - docsPath, - sidebarPath, + docsDirPath, + sidebarFilePath, }: VersionMetadata) { - if (!fs.existsSync(docsPath)) { + if (!fs.existsSync(docsDirPath)) { throw new Error( - `The docs folder does not exist for version [${versionName}]. A docs folder is expected to be found at ${docsPath}`, + `The docs folder does not exist for version [${versionName}]. A docs folder is expected to be found at ${docsDirPath}`, ); } - if (!fs.existsSync(sidebarPath)) { + if (!fs.existsSync(sidebarFilePath)) { throw new Error( - `The sidebar file does not exist for version [${versionName}]. A sidebar file is expected to be found at ${sidebarPath}`, + `The sidebar file does not exist for version [${versionName}]. A sidebar file is expected to be found at ${sidebarFilePath}`, ); } } -export function readVersionsMetadata( - siteDir: string, - options: PluginOptions, -): VersionMetadata[] { - const versionNames = readVersionNames(siteDir, options); +// TODO for retrocompatibility with existing behavior +// We should make this configurable +// "last version" is not a very good concept nor api surface +function getLastVersionName(versionNames: string[]) { + if (versionNames.length === 1) { + return versionNames[1]; + } else { + return versionNames.filter( + (versionName) => versionName !== CURRENT_VERSION_NAME, + )[0]; + } +} + +export function readVersionsMetadata({ + context, + options, +}: { + context: Pick; + options: Pick< + PluginOptions, + | 'id' + | 'path' + | 'sidebarPath' + | 'routeBasePath' + | 'includeCurrentVersion' + | 'disableVersioning' + >; +}): VersionMetadata[] { + const versionNames = readVersionNames(context.siteDir, options); + const lastVersionName = getLastVersionName(versionNames); const versionsMetadata = versionNames.map((versionName) => - createVersionMetadata({versionName, siteDir, options}), + createVersionMetadata({ + versionName, + isLast: versionName === lastVersionName, + context, + options, + }), ); versionsMetadata.forEach(checkVersionMetadataPaths); return versionsMetadata; diff --git a/packages/docusaurus-plugin-content-docs/src/index.ts b/packages/docusaurus-plugin-content-docs/src/index.ts index b85e82202745..07f044ac83f7 100644 --- a/packages/docusaurus-plugin-content-docs/src/index.ts +++ b/packages/docusaurus-plugin-content-docs/src/index.ts @@ -37,7 +37,6 @@ import { DocsMetadataRaw, DocMetadata, GlobalPluginData, - VersionName, VersionMetadata, DocNavLink, LoadedVersion, @@ -45,7 +44,7 @@ import { } from './types'; import {RuleSetRule} from 'webpack'; import {cliDocsVersion} from './cli'; -import {CURRENT_VERSION_NAME, VERSIONS_JSON_FILE} from './constants'; +import {VERSIONS_JSON_FILE} from './constants'; import {PluginOptionSchema} from './pluginOptionSchema'; import {ValidationError} from '@hapi/joi'; import {flatten, keyBy, compact} from 'lodash'; @@ -58,7 +57,7 @@ export default function pluginContentDocs( ): Plugin { const {siteDir, generatedFilesDir, baseUrl} = context; - const versionsMetadata = readVersionsMetadata(siteDir, options); + const versionsMetadata = readVersionsMetadata({context, options}); const docsDir = path.resolve(siteDir, options.path); @@ -79,17 +78,6 @@ export default function pluginContentDocs( }); const {latestVersion} = legacyVersioningEnv.versioning; - // TODO refactor, retrocompatibility - function getVersionPathPart(version: VersionName) { - if (version === latestVersion) { - return ''; - } - if (version === CURRENT_VERSION_NAME) { - return 'next'; - } - return version; - } - return { name: 'docusaurus-plugin-content-docs', @@ -136,8 +124,10 @@ export default function pluginContentDocs( getPathsToWatch() { function getVersionPathsToWatch(version: VersionMetadata): string[] { return [ - version.sidebarPath, - ...options.include.map((pattern) => `${version.docsPath}/${pattern}`), + version.sidebarFilePath, + ...options.include.map( + (pattern) => `${version.docsDirPath}/${pattern}`, + ), ]; } @@ -155,7 +145,6 @@ export default function pluginContentDocs( versionMetadata, context, options, - env: legacyVersioningEnv, }); } return Promise.all(docFiles.map(processVersionDoc)); @@ -167,7 +156,7 @@ export default function pluginContentDocs( const docs: DocMetadataBase[] = await loadVersionDocs(versionMetadata); const docsById: DocsMetadataRaw = keyBy(docs, (doc) => doc.id); - const sidebars = loadSidebars([versionMetadata.sidebarPath]); + const sidebars = loadSidebars([versionMetadata.sidebarFilePath]); const docsOrder: Order = createOrder(sidebars); // Add sidebar/next/previous to the docs @@ -207,14 +196,8 @@ export default function pluginContentDocs( } }); - // TODO compute in versionMetadata? - const versionPath = normalizeUrl([ - baseUrl, - options.routeBasePath, - getVersionPathPart(versionMetadata.versionName), - ]); - - // TODO bad algo! + // TODO bad legacy algo! docs[0] gives a random doc + // We should fallback to the first doc of the first sidebar instead const mainDoc: DocMetadata = docs.find( (doc) => @@ -223,7 +206,6 @@ export default function pluginContentDocs( return { ...versionMetadata, - versionPath, mainDocId: mainDoc.unversionedId, sidebars, permalinkToSidebar, @@ -286,11 +268,7 @@ export default function pluginContentDocs( modules: { versionMetadata: aliasedSource(versionMetadataPropPath), }, - // Because /docs/:route` should always be after `/docs/versionName/:route`. - priority: - getVersionPathPart(loadedVersion.versionName) === '' - ? -1 - : undefined, + priority: loadedVersion.routePriority, }); } @@ -314,7 +292,7 @@ export default function pluginContentDocs( function createMDXLoaderRule(): RuleSetRule { return { test: /(\.mdx?)$/, - include: versionsMetadata.map((vmd) => vmd.docsPath), + include: versionsMetadata.map((vmd) => vmd.docsDirPath), use: compact([ getCacheLoader(isServer), getBabelLoader(isServer), @@ -336,6 +314,8 @@ export default function pluginContentDocs( loader: path.resolve(__dirname, './markdown/index.js'), options: { siteDir, + + // TODO legacy attributes, need refactor docsDir, sourceToPermalink, versionedDir: legacyVersioningEnv.versioning.docsDir, diff --git a/packages/docusaurus-plugin-content-docs/src/types.ts b/packages/docusaurus-plugin-content-docs/src/types.ts index aa329d4f08ac..fc00914414ff 100644 --- a/packages/docusaurus-plugin-content-docs/src/types.ts +++ b/packages/docusaurus-plugin-content-docs/src/types.ts @@ -17,9 +17,13 @@ export type DocFile = { export type VersionName = string; export type VersionMetadata = { - versionName: VersionName; - docsPath: string; - sidebarPath: string; + versionName: VersionName; // 1.0.0 + versionLabel: string; // Version 1.0.0 + versionPath: string; // /baseUrl/docs/1.0.0 + isLast: boolean; + docsDirPath: string; // versioned_docs/1.0.0 + sidebarFilePath: string; // versioned_sidebars/1.0.0.json + routePriority: number | undefined; // -1 for the latest docs }; export type MetadataOptions = { From d4baa1ba46901a3f6a876e84789a51a2f7d39f15 Mon Sep 17 00:00:00 2001 From: slorber Date: Wed, 12 Aug 2020 18:48:06 +0200 Subject: [PATCH 17/30] stable docs refactor --- .../src/__tests__/index.test.ts | 22 +++--- ...inOptionSchema.test.ts => options.test.ts} | 20 +++--- .../__tests__}/docsClientUtils.test.ts | 55 +++++++-------- .../src/client/docsClientUtils.ts | 4 +- .../docusaurus-plugin-content-docs/src/env.ts | 48 +------------ .../src/globalData.ts | 7 +- .../src/index.ts | 70 ++----------------- .../src/{pluginOptionSchema.ts => options.ts} | 48 ++++++++++++- .../src/theme/hooks/useVersioning.ts | 1 + .../src/types.ts | 16 +---- 10 files changed, 108 insertions(+), 183 deletions(-) rename packages/docusaurus-plugin-content-docs/src/__tests__/{pluginOptionSchema.test.ts => options.test.ts} (85%) rename packages/docusaurus-plugin-content-docs/src/{__tests__/client => client/__tests__}/docsClientUtils.test.ts (91%) rename packages/docusaurus-plugin-content-docs/src/{pluginOptionSchema.ts => options.ts} (57%) diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/index.test.ts b/packages/docusaurus-plugin-content-docs/src/__tests__/index.test.ts index 0685d628fb8c..6aae038132e3 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/index.test.ts +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/index.test.ts @@ -19,8 +19,8 @@ import {posixPath} from '@docusaurus/utils'; import {sortConfig} from '@docusaurus/core/src/server/plugins'; import {DEFAULT_PLUGIN_ID} from '@docusaurus/core/lib/constants'; -import * as version from '../version'; -import {PluginOptionSchema} from '../pluginOptionSchema'; +import * as cliDocs from '../cli'; +import {OptionsSchema} from '../options'; import {normalizePluginOptions} from '@docusaurus/utils-validation'; const createFakeActions = (contentDir: string) => { @@ -79,7 +79,7 @@ test('site with wrong sidebar file', async () => { const sidebarPath = path.join(siteDir, 'wrong-sidebars.json'); const plugin = pluginContentDocs( context, - normalizePluginOptions(PluginOptionSchema, { + normalizePluginOptions(OptionsSchema, { sidebarPath, }), ); @@ -94,7 +94,7 @@ describe('empty/no docs website', () => { await fs.ensureDir(path.join(siteDir, 'docs')); const plugin = pluginContentDocs( context, - normalizePluginOptions(PluginOptionSchema, {}), + normalizePluginOptions(OptionsSchema, {}), ); const content = await plugin.loadContent(); const {docsMetadata, docsSidebars} = content; @@ -116,7 +116,7 @@ describe('empty/no docs website', () => { expect(() => pluginContentDocs( context, - normalizePluginOptions(PluginOptionSchema, { + normalizePluginOptions(OptionsSchema, { path: '/path/does/not/exist/', }), ), @@ -133,7 +133,7 @@ describe('simple website', () => { const pluginPath = 'docs'; const plugin = pluginContentDocs( context, - normalizePluginOptions(PluginOptionSchema, { + normalizePluginOptions(OptionsSchema, { path: pluginPath, sidebarPath, homePageId: 'hello', @@ -142,7 +142,7 @@ describe('simple website', () => { const pluginContentDir = path.join(context.generatedFilesDir, plugin.name); test('extendCli - docsVersion', () => { - const mock = jest.spyOn(version, 'docsVersion').mockImplementation(); + const mock = jest.spyOn(cliDocs, 'docsVersion').mockImplementation(); const cli = new commander.Command(); plugin.extendCli(cli); cli.parse(['node', 'test', 'docs:version', '1.0.0']); @@ -260,7 +260,7 @@ describe('versioned website', () => { const routeBasePath = 'docs'; const plugin = pluginContentDocs( context, - normalizePluginOptions(PluginOptionSchema, { + normalizePluginOptions(OptionsSchema, { routeBasePath, sidebarPath, homePageId: 'hello', @@ -275,7 +275,7 @@ describe('versioned website', () => { }); test('extendCli - docsVersion', () => { - const mock = jest.spyOn(version, 'docsVersion').mockImplementation(); + const mock = jest.spyOn(cliDocs, 'docsVersion').mockImplementation(); const cli = new commander.Command(); plugin.extendCli(cli); cli.parse(['node', 'test', 'docs:version', '2.0.0']); @@ -483,7 +483,7 @@ describe('versioned website (community)', () => { const pluginId = 'community'; const plugin = pluginContentDocs( context, - normalizePluginOptions(PluginOptionSchema, { + normalizePluginOptions(OptionsSchema, { id: 'community', path: 'community', routeBasePath, @@ -499,7 +499,7 @@ describe('versioned website (community)', () => { }); test('extendCli - docsVersion', () => { - const mock = jest.spyOn(version, 'docsVersion').mockImplementation(); + const mock = jest.spyOn(cliDocs, 'docsVersion').mockImplementation(); const cli = new commander.Command(); plugin.extendCli(cli); cli.parse(['node', 'test', `docs:version:${pluginId}`, '2.0.0']); diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/pluginOptionSchema.test.ts b/packages/docusaurus-plugin-content-docs/src/__tests__/options.test.ts similarity index 85% rename from packages/docusaurus-plugin-content-docs/src/__tests__/pluginOptionSchema.test.ts rename to packages/docusaurus-plugin-content-docs/src/__tests__/options.test.ts index 11e73957cb2c..ef23a1c84987 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/pluginOptionSchema.test.ts +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/options.test.ts @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -import {PluginOptionSchema, DEFAULT_OPTIONS} from '../pluginOptionSchema'; +import {OptionsSchema, DEFAULT_OPTIONS} from '../options'; import {normalizePluginOptions} from '@docusaurus/utils-validation'; // the type of remark/rehype plugins is function @@ -14,7 +14,7 @@ const markdownPluginsObjectStub = {}; describe('normalizeDocsPluginOptions', () => { test('should return default options for undefined user options', async () => { - const {value, error} = await PluginOptionSchema.validate({}); + const {value, error} = await OptionsSchema.validate({}); expect(value).toEqual(DEFAULT_OPTIONS); expect(error).toBe(undefined); }); @@ -38,7 +38,7 @@ describe('normalizeDocsPluginOptions', () => { includeCurrentVersion: false, disableVersioning: true, }; - const {value, error} = await PluginOptionSchema.validate(userOptions); + const {value, error} = await OptionsSchema.validate(userOptions); expect(value).toEqual(userOptions); expect(error).toBe(undefined); }); @@ -52,14 +52,14 @@ describe('normalizeDocsPluginOptions', () => { [markdownPluginsFunctionStub, {option1: '42'}], ], }; - const {value, error} = await PluginOptionSchema.validate(userOptions); + const {value, error} = await OptionsSchema.validate(userOptions); expect(value).toEqual(userOptions); expect(error).toBe(undefined); }); test('should reject invalid remark plugin options', () => { expect(() => { - normalizePluginOptions(PluginOptionSchema, { + normalizePluginOptions(OptionsSchema, { remarkPlugins: [[{option1: '42'}, markdownPluginsFunctionStub]], }); }).toThrowErrorMatchingInlineSnapshot( @@ -69,7 +69,7 @@ describe('normalizeDocsPluginOptions', () => { test('should reject invalid rehype plugin options', () => { expect(() => { - normalizePluginOptions(PluginOptionSchema, { + normalizePluginOptions(OptionsSchema, { rehypePlugins: [ [ markdownPluginsFunctionStub, @@ -85,7 +85,7 @@ describe('normalizeDocsPluginOptions', () => { test('should reject bad path inputs', () => { expect(() => { - normalizePluginOptions(PluginOptionSchema, { + normalizePluginOptions(OptionsSchema, { path: 2, }); }).toThrowErrorMatchingInlineSnapshot(`"\\"path\\" must be a string"`); @@ -93,7 +93,7 @@ describe('normalizeDocsPluginOptions', () => { test('should reject bad include inputs', () => { expect(() => { - normalizePluginOptions(PluginOptionSchema, { + normalizePluginOptions(OptionsSchema, { include: '**/*.{md,mdx}', }); }).toThrowErrorMatchingInlineSnapshot(`"\\"include\\" must be an array"`); @@ -101,7 +101,7 @@ describe('normalizeDocsPluginOptions', () => { test('should reject bad showLastUpdateTime inputs', () => { expect(() => { - normalizePluginOptions(PluginOptionSchema, { + normalizePluginOptions(OptionsSchema, { showLastUpdateTime: 'true', }); }).toThrowErrorMatchingInlineSnapshot( @@ -111,7 +111,7 @@ describe('normalizeDocsPluginOptions', () => { test('should reject bad remarkPlugins input', () => { expect(() => { - normalizePluginOptions(PluginOptionSchema, { + normalizePluginOptions(OptionsSchema, { remarkPlugins: 'remark-math', }); }).toThrowErrorMatchingInlineSnapshot( diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/client/docsClientUtils.test.ts b/packages/docusaurus-plugin-content-docs/src/client/__tests__/docsClientUtils.test.ts similarity index 91% rename from packages/docusaurus-plugin-content-docs/src/__tests__/client/docsClientUtils.test.ts rename to packages/docusaurus-plugin-content-docs/src/client/__tests__/docsClientUtils.test.ts index 08129b9fea70..cf12adcd49c2 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/client/docsClientUtils.test.ts +++ b/packages/docusaurus-plugin-content-docs/src/client/__tests__/docsClientUtils.test.ts @@ -12,7 +12,7 @@ import { getActiveDocContext, getActiveVersion, getDocVersionSuggestions, -} from '../../client/docsClientUtils'; +} from '../docsClientUtils'; import {GlobalPluginData, GlobalVersion} from '../../types'; import {shuffle} from 'lodash'; @@ -21,12 +21,10 @@ describe('docsClientUtils', () => { const data: Record = { pluginIosId: { path: '/ios', - latestVersionName: 'xyz', versions: [], }, pluginAndroidId: { path: '/android', - latestVersionName: 'xyz', versions: [], }, }; @@ -55,19 +53,25 @@ describe('docsClientUtils', () => { const versions: GlobalVersion[] = [ { name: 'version1', + label: 'version1', path: '/???', + isLast: false, docs: [], mainDocId: '???', }, { name: 'version2', + label: 'version2', path: '/???', + isLast: true, docs: [], mainDocId: '???', }, { name: 'version3', + label: 'version3', path: '/???', + isLast: false, docs: [], mainDocId: '???', }, @@ -76,52 +80,35 @@ describe('docsClientUtils', () => { expect( getLatestVersion({ path: '???', - latestVersionName: 'does not exist', versions, }), - ).toEqual(undefined); - expect( - getLatestVersion({ - path: '???', - latestVersionName: 'version1', - versions, - })?.name, - ).toEqual('version1'); - expect( - getLatestVersion({ - path: '???', - latestVersionName: 'version2', - versions, - })?.name, - ).toEqual('version2'); - expect( - getLatestVersion({ - path: '???', - latestVersionName: 'version3', - versions, - })?.name, - ).toEqual('version3'); + ).toEqual(versions[1]); }); test('getActiveVersion', () => { const data: GlobalPluginData = { path: 'docs', - latestVersionName: 'version2', versions: [ { name: 'next', + label: 'next', + isLast: false, path: '/docs/next', docs: [], mainDocId: '???', }, { name: 'version2', + label: 'version2', + isLast: true, path: '/docs', docs: [], mainDocId: '???', }, { name: 'version1', + label: 'version1', + isLast: false, path: '/docs/version1', docs: [], mainDocId: '???', @@ -146,7 +133,9 @@ describe('docsClientUtils', () => { test('getActiveDocContext', () => { const versionNext: GlobalVersion = { name: 'next', + label: 'next', path: '/docs/next', + isLast: false, mainDocId: 'doc1', docs: [ { @@ -162,6 +151,8 @@ describe('docsClientUtils', () => { const version2: GlobalVersion = { name: 'version2', + label: 'version2', + isLast: true, path: '/docs', mainDocId: 'doc1', docs: [ @@ -178,7 +169,9 @@ describe('docsClientUtils', () => { const version1: GlobalVersion = { name: 'version1', + label: 'version1', path: '/docs/version1', + isLast: false, mainDocId: 'doc1', docs: [ { @@ -197,7 +190,6 @@ describe('docsClientUtils', () => { const data: GlobalPluginData = { path: 'docs', - latestVersionName: 'version2', versions, }; @@ -270,6 +262,8 @@ describe('docsClientUtils', () => { test('getDocVersionSuggestions', () => { const versionNext: GlobalVersion = { name: 'next', + label: 'next', + isLast: false, path: '/docs/next', mainDocId: 'doc1', docs: [ @@ -286,7 +280,9 @@ describe('docsClientUtils', () => { const version2: GlobalVersion = { name: 'version2', + label: 'version2', path: '/docs', + isLast: true, mainDocId: 'doc1', docs: [ { @@ -302,6 +298,8 @@ describe('docsClientUtils', () => { const version1: GlobalVersion = { name: 'version1', + label: 'version1', + isLast: false, path: '/docs/version1', mainDocId: 'doc1', docs: [ @@ -321,7 +319,6 @@ describe('docsClientUtils', () => { const data: GlobalPluginData = { path: 'docs', - latestVersionName: 'version2', versions, }; diff --git a/packages/docusaurus-plugin-content-docs/src/client/docsClientUtils.ts b/packages/docusaurus-plugin-content-docs/src/client/docsClientUtils.ts index 1692169f64d1..6adbc0d98511 100644 --- a/packages/docusaurus-plugin-content-docs/src/client/docsClientUtils.ts +++ b/packages/docusaurus-plugin-content-docs/src/client/docsClientUtils.ts @@ -49,9 +49,7 @@ export type ActiveDocContext = { }; export const getLatestVersion = (data: GlobalPluginData): Version => { - return data.versions.find( - (version) => version.name === data.latestVersionName, - )!; + return data.versions.find((version) => version.isLast)!; }; // Note: return undefined on doc-unrelated pages, diff --git a/packages/docusaurus-plugin-content-docs/src/env.ts b/packages/docusaurus-plugin-content-docs/src/env.ts index 2de37f7a26a0..18f3a537edd0 100644 --- a/packages/docusaurus-plugin-content-docs/src/env.ts +++ b/packages/docusaurus-plugin-content-docs/src/env.ts @@ -7,7 +7,7 @@ import path from 'path'; import fs from 'fs-extra'; -import {VersioningEnv, Env, PluginOptions, VersionMetadata} from './types'; +import {PluginOptions, VersionMetadata} from './types'; import { VERSIONS_JSON_FILE, VERSIONED_DOCS_DIR, @@ -251,49 +251,3 @@ export function readVersionsMetadata({ versionsMetadata.forEach(checkVersionMetadataPaths); return versionsMetadata; } - -// TODO remove soon -type EnvOptions = Partial<{ - disableVersioning: boolean; - includeCurrentVersion: boolean; -}>; -export default function loadEnv( - siteDir: string, - pluginId: string, - options: EnvOptions = {disableVersioning: false}, -): Env { - if (!siteDir) { - throw new Error('unexpected, missing siteDir'); - } - if (!pluginId) { - throw new Error('unexpected, missing pluginId'); - } - - const versioning: VersioningEnv = { - enabled: false, - versions: [], - latestVersion: CURRENT_VERSION_NAME, - docsDir: '', - sidebarsDir: '', - }; - - const versionsJSONFile = getVersionsFilePath(siteDir, pluginId); - if (fs.existsSync(versionsJSONFile)) { - if (!options.disableVersioning) { - const parsedVersions = JSON.parse( - fs.readFileSync(versionsJSONFile, 'utf8'), - ); - if (parsedVersions && parsedVersions.length > 0) { - versioning.latestVersion = parsedVersions[0]; - versioning.enabled = true; - versioning.versions = parsedVersions; - versioning.docsDir = getVersionedDocsDirPath(siteDir, pluginId); - versioning.sidebarsDir = getVersionedSidebarsDirPath(siteDir, pluginId); - } - } - } - - return { - versioning, - }; -} diff --git a/packages/docusaurus-plugin-content-docs/src/globalData.ts b/packages/docusaurus-plugin-content-docs/src/globalData.ts index ab6d1ccb47e8..80d9bc8e9301 100644 --- a/packages/docusaurus-plugin-content-docs/src/globalData.ts +++ b/packages/docusaurus-plugin-content-docs/src/globalData.ts @@ -17,11 +17,10 @@ export function toGlobalDataDoc(doc: DocMetadata): GlobalDoc { export function toGlobalDataVersion(version: LoadedVersion): GlobalVersion { return { name: version.versionName, + label: version.versionLabel, + isLast: version.isLast, path: version.versionPath, mainDocId: version.mainDocId, - docs: version.docs - .map(toGlobalDataDoc) - // stable ordering, useful for tests - .sort((a, b) => a.id.localeCompare(b.id)), + docs: version.docs.map(toGlobalDataDoc), }; } diff --git a/packages/docusaurus-plugin-content-docs/src/index.ts b/packages/docusaurus-plugin-content-docs/src/index.ts index 07f044ac83f7..63a8eafef30c 100644 --- a/packages/docusaurus-plugin-content-docs/src/index.ts +++ b/packages/docusaurus-plugin-content-docs/src/index.ts @@ -6,26 +6,18 @@ */ import path from 'path'; -import chalk from 'chalk'; -import admonitions from 'remark-admonitions'; import { STATIC_DIR_NAME, DEFAULT_PLUGIN_ID, } from '@docusaurus/core/lib/constants'; import {normalizeUrl, docuHash, aliasedSitePath} from '@docusaurus/utils'; -import { - LoadContext, - Plugin, - RouteConfig, - OptionValidationContext, - ValidationResult, -} from '@docusaurus/types'; +import {LoadContext, Plugin, RouteConfig} from '@docusaurus/types'; import createOrder from './order'; import loadSidebars from './sidebars'; import {readVersionDocs, processDocMetadata} from './docs'; -import loadEnv, {readVersionsMetadata} from './env'; +import {readVersionsMetadata, getVersionedDocsDirPath} from './env'; import { PluginOptions, @@ -45,8 +37,7 @@ import { import {RuleSetRule} from 'webpack'; import {cliDocsVersion} from './cli'; import {VERSIONS_JSON_FILE} from './constants'; -import {PluginOptionSchema} from './pluginOptionSchema'; -import {ValidationError} from '@hapi/joi'; +import {OptionsSchema} from './options'; import {flatten, keyBy, compact} from 'lodash'; import {toGlobalDataVersion} from './globalData'; import {toVersionMetadataProp} from './versionMetadataProp'; @@ -54,7 +45,7 @@ import {toVersionMetadataProp} from './versionMetadataProp'; export default function pluginContentDocs( context: LoadContext, options: PluginOptions, -): Plugin { +): Plugin { const {siteDir, generatedFilesDir, baseUrl} = context; const versionsMetadata = readVersionsMetadata({context, options}); @@ -72,12 +63,6 @@ export default function pluginContentDocs( const aliasedSource = (source: string) => `~docs/${path.relative(pluginDataDirRoot, source)}`; - // TODO remove soon! - const legacyVersioningEnv = loadEnv(siteDir, pluginId, { - disableVersioning: options.disableVersioning, - }); - const {latestVersion} = legacyVersioningEnv.versioning; - return { name: 'docusaurus-plugin-content-docs', @@ -276,7 +261,6 @@ export default function pluginContentDocs( setGlobalData({ path: normalizeUrl([baseUrl, options.routeBasePath]), - latestVersionName: latestVersion, versions: loadedVersions.map(toGlobalDataVersion), }); }, @@ -318,7 +302,7 @@ export default function pluginContentDocs( // TODO legacy attributes, need refactor docsDir, sourceToPermalink, - versionedDir: legacyVersioningEnv.versioning.docsDir, + versionedDir: getVersionedDocsDirPath(siteDir, options.id), }, }, ]), @@ -348,46 +332,4 @@ export default function pluginContentDocs( }; } -export function validateOptions({ - validate, - options, -}: OptionValidationContext): ValidationResult< - PluginOptions, - ValidationError -> { - // TODO remove homePageId before end of 2020 - // "slug: /" is better because the home doc can be different across versions - if (options.homePageId) { - console.log( - chalk.red( - `The docs plugin option homePageId=${options.homePageId} is deprecated. To make a doc the "home", prefer frontmatter: "slug: /"`, - ), - ); - } - - if (typeof options.excludeNextVersionDocs !== 'undefined') { - console.log( - chalk.red( - `The docs plugin option excludeNextVersionDocs=${ - options.excludeNextVersionDocs - } is deprecated. Use the includeCurrentVersion=${!options.excludeNextVersionDocs} option instead!"`, - ), - ); - options.includeCurrentVersion = !options.excludeNextVersionDocs; - } - - // @ts-expect-error: TODO bad OptionValidationContext, need refactor - const normalizedOptions: PluginOptions = validate( - PluginOptionSchema, - options, - ); - - if (normalizedOptions.admonitions) { - normalizedOptions.remarkPlugins = normalizedOptions.remarkPlugins.concat([ - [admonitions, normalizedOptions.admonitions], - ]); - } - - // @ts-expect-error: TODO bad OptionValidationContext, need refactor - return normalizedOptions; -} +export {validateOptions} from './options'; diff --git a/packages/docusaurus-plugin-content-docs/src/pluginOptionSchema.ts b/packages/docusaurus-plugin-content-docs/src/options.ts similarity index 57% rename from packages/docusaurus-plugin-content-docs/src/pluginOptionSchema.ts rename to packages/docusaurus-plugin-content-docs/src/options.ts index f6ceb60c5d98..c7464f2c274c 100644 --- a/packages/docusaurus-plugin-content-docs/src/pluginOptionSchema.ts +++ b/packages/docusaurus-plugin-content-docs/src/options.ts @@ -12,6 +12,10 @@ import { AdmonitionsSchema, URISchema, } from '@docusaurus/utils-validation'; +import {OptionValidationContext, ValidationResult} from '@docusaurus/types'; +import {ValidationError} from '@hapi/joi'; +import chalk from 'chalk'; +import admonitions from 'remark-admonitions'; export const DEFAULT_OPTIONS: Omit = { path: 'docs', // Path to data on filesystem, relative to site dir. @@ -31,7 +35,7 @@ export const DEFAULT_OPTIONS: Omit = { disableVersioning: false, }; -export const PluginOptionSchema = Joi.object({ +export const OptionsSchema = Joi.object({ path: Joi.string().default(DEFAULT_OPTIONS.path), editUrl: URISchema, routeBasePath: Joi.string().allow('').default(DEFAULT_OPTIONS.routeBasePath), @@ -55,3 +59,45 @@ export const PluginOptionSchema = Joi.object({ ), disableVersioning: Joi.bool().default(DEFAULT_OPTIONS.disableVersioning), }); + +// TODO bad validation function types +export function validateOptions({ + validate, + options, +}: OptionValidationContext): ValidationResult< + PluginOptions, + ValidationError +> { + // TODO remove homePageId before end of 2020 + // "slug: /" is better because the home doc can be different across versions + if (options.homePageId) { + console.log( + chalk.red( + `The docs plugin option homePageId=${options.homePageId} is deprecated. To make a doc the "home", prefer frontmatter: "slug: /"`, + ), + ); + } + + if (typeof options.excludeNextVersionDocs !== 'undefined') { + console.log( + chalk.red( + `The docs plugin option excludeNextVersionDocs=${ + options.excludeNextVersionDocs + } is deprecated. Use the includeCurrentVersion=${!options.excludeNextVersionDocs} option instead!"`, + ), + ); + options.includeCurrentVersion = !options.excludeNextVersionDocs; + } + + // @ts-expect-error: TODO bad OptionValidationContext, need refactor + const normalizedOptions: PluginOptions = validate(OptionsSchema, options); + + if (normalizedOptions.admonitions) { + normalizedOptions.remarkPlugins = normalizedOptions.remarkPlugins.concat([ + [admonitions, normalizedOptions.admonitions], + ]); + } + + // @ts-expect-error: TODO bad OptionValidationContext, need refactor + return normalizedOptions; +} diff --git a/packages/docusaurus-plugin-content-docs/src/theme/hooks/useVersioning.ts b/packages/docusaurus-plugin-content-docs/src/theme/hooks/useVersioning.ts index 419ee5585706..afc278336ca6 100644 --- a/packages/docusaurus-plugin-content-docs/src/theme/hooks/useVersioning.ts +++ b/packages/docusaurus-plugin-content-docs/src/theme/hooks/useVersioning.ts @@ -14,6 +14,7 @@ try { versions = []; } +// TODO deprecate in favor of useDocs.ts instead function useVersioning(): { versioningEnabled: boolean; versions: string[]; diff --git a/packages/docusaurus-plugin-content-docs/src/types.ts b/packages/docusaurus-plugin-content-docs/src/types.ts index fc00914414ff..17f268384c80 100644 --- a/packages/docusaurus-plugin-content-docs/src/types.ts +++ b/packages/docusaurus-plugin-content-docs/src/types.ts @@ -166,19 +166,6 @@ export type LoadedContent = { loadedVersions: LoadedVersion[]; }; -export type VersioningEnv = { - enabled: boolean; - latestVersion: string; - versions: string[]; - docsDir: string; - sidebarsDir: string; -}; - -export type Env = { - versioning: VersioningEnv; - // TODO: translation -}; - export type GlobalDoc = { id: string; path: string; @@ -186,6 +173,8 @@ export type GlobalDoc = { export type GlobalVersion = { name: VersionName; + label: string; + isLast: boolean; path: string; mainDocId: string; // home doc (if docs homepage configured), or first doc docs: GlobalDoc[]; @@ -193,7 +182,6 @@ export type GlobalVersion = { export type GlobalPluginData = { path: string; - latestVersionName: VersionName; versions: GlobalVersion[]; }; From 6341d6b67069883115d37ceea1c69abe0c52c369 Mon Sep 17 00:00:00 2001 From: slorber Date: Wed, 12 Aug 2020 18:57:40 +0200 Subject: [PATCH 18/30] named exports --- .../src/__tests__/order.test.ts | 20 ++++++++++++++++++- .../src/__tests__/sidebars.test.ts | 2 +- .../docusaurus-plugin-content-docs/src/cli.ts | 2 +- .../src/index.ts | 4 ++-- .../src/order.ts | 2 +- .../src/sidebars.ts | 2 +- 6 files changed, 25 insertions(+), 7 deletions(-) diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/order.test.ts b/packages/docusaurus-plugin-content-docs/src/__tests__/order.test.ts index 1f6bc057c29d..4f94bc38e7eb 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/order.test.ts +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/order.test.ts @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -import createOrder from '../order'; +import {createOrder} from '../order'; describe('createOrder', () => { test('multiple sidebars with subcategory', () => { @@ -13,15 +13,18 @@ describe('createOrder', () => { docs: [ { type: 'category', + collapsed: false, label: 'Category1', items: [ { type: 'category', + collapsed: false, label: 'Subcategory 1', items: [{type: 'doc', id: 'doc1'}], }, { type: 'category', + collapsed: false, label: 'Subcategory 2', items: [{type: 'doc', id: 'doc2'}], }, @@ -29,6 +32,7 @@ describe('createOrder', () => { }, { type: 'category', + collapsed: false, label: 'Category2', items: [ {type: 'doc', id: 'doc3'}, @@ -39,6 +43,7 @@ describe('createOrder', () => { otherDocs: [ { type: 'category', + collapsed: false, label: 'Category1', items: [{type: 'doc', id: 'doc5'}], }, @@ -77,6 +82,7 @@ describe('createOrder', () => { docs: [ { type: 'category', + collapsed: false, label: 'Category1', items: [ {type: 'doc', id: 'doc1'}, @@ -85,6 +91,7 @@ describe('createOrder', () => { }, { type: 'category', + collapsed: false, label: 'Category2', items: [ {type: 'doc', id: 'doc3'}, @@ -95,6 +102,7 @@ describe('createOrder', () => { otherDocs: [ { type: 'category', + collapsed: false, label: 'Category1', items: [{type: 'doc', id: 'doc5'}], }, @@ -134,6 +142,7 @@ describe('createOrder', () => { docs: [ { type: 'category', + collapsed: false, label: 'Category1', items: [{type: 'doc', id: 'doc1'}], }, @@ -141,11 +150,13 @@ describe('createOrder', () => { 'version-1.2.3-docs': [ { type: 'category', + collapsed: false, label: 'Category1', items: [{type: 'doc', id: 'version-1.2.3-doc2'}], }, { type: 'category', + collapsed: false, label: 'Category2', items: [{type: 'doc', id: 'version-1.2.3-doc1'}], }, @@ -175,20 +186,24 @@ describe('createOrder', () => { docs: [ { type: 'category', + collapsed: false, label: 'Category1', items: [ { type: 'category', + collapsed: false, label: 'Subcategory 1', items: [{type: 'link', href: '//example.com', label: 'bar'}], }, { type: 'category', + collapsed: false, label: 'Subcategory 2', items: [{type: 'doc', id: 'doc2'}], }, { type: 'category', + collapsed: false, label: 'Subcategory 1', items: [{type: 'link', href: '//example2.com', label: 'baz'}], }, @@ -196,6 +211,7 @@ describe('createOrder', () => { }, { type: 'category', + collapsed: false, label: 'Category2', items: [ {type: 'doc', id: 'doc3'}, @@ -206,6 +222,7 @@ describe('createOrder', () => { otherDocs: [ { type: 'category', + collapsed: false, label: 'Category1', items: [{type: 'doc', id: 'doc5'}], }, @@ -233,6 +250,7 @@ describe('createOrder', () => { test('edge cases', () => { expect(createOrder({})).toEqual({}); expect(createOrder(undefined)).toEqual({}); + expect(() => createOrder(null)).toThrowErrorMatchingInlineSnapshot( `"Cannot convert undefined or null to object"`, ); diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/sidebars.test.ts b/packages/docusaurus-plugin-content-docs/src/__tests__/sidebars.test.ts index 1d322bba4463..4445e7e867bc 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/sidebars.test.ts +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/sidebars.test.ts @@ -6,7 +6,7 @@ */ import path from 'path'; -import loadSidebars from '../sidebars'; +import {loadSidebars} from '../sidebars'; /* eslint-disable global-require, import/no-dynamic-require */ diff --git a/packages/docusaurus-plugin-content-docs/src/cli.ts b/packages/docusaurus-plugin-content-docs/src/cli.ts index ad756c08d9e2..2bebd0794daa 100644 --- a/packages/docusaurus-plugin-content-docs/src/cli.ts +++ b/packages/docusaurus-plugin-content-docs/src/cli.ts @@ -13,7 +13,7 @@ import { import fs from 'fs-extra'; import path from 'path'; import {Sidebars, PathOptions, SidebarItem} from './types'; -import loadSidebars from './sidebars'; +import {loadSidebars} from './sidebars'; import {DEFAULT_PLUGIN_ID} from '@docusaurus/core/lib/constants'; // Tests depend on non-default export for mocking. diff --git a/packages/docusaurus-plugin-content-docs/src/index.ts b/packages/docusaurus-plugin-content-docs/src/index.ts index 63a8eafef30c..f0d6c24dc105 100644 --- a/packages/docusaurus-plugin-content-docs/src/index.ts +++ b/packages/docusaurus-plugin-content-docs/src/index.ts @@ -14,8 +14,8 @@ import { import {normalizeUrl, docuHash, aliasedSitePath} from '@docusaurus/utils'; import {LoadContext, Plugin, RouteConfig} from '@docusaurus/types'; -import createOrder from './order'; -import loadSidebars from './sidebars'; +import {createOrder} from './order'; +import {loadSidebars} from './sidebars'; import {readVersionDocs, processDocMetadata} from './docs'; import {readVersionsMetadata, getVersionedDocsDirPath} from './env'; diff --git a/packages/docusaurus-plugin-content-docs/src/order.ts b/packages/docusaurus-plugin-content-docs/src/order.ts index c18566d9bfd4..2d268a1e8f3d 100644 --- a/packages/docusaurus-plugin-content-docs/src/order.ts +++ b/packages/docusaurus-plugin-content-docs/src/order.ts @@ -27,7 +27,7 @@ function getOrderedDocItems(items: SidebarItem[]): SidebarItemDoc[] { } // Build the docs meta such as next, previous, category and sidebar. -export default function createOrder(allSidebars: Sidebars = {}): Order { +export function createOrder(allSidebars: Sidebars = {}): Order { const order: Order = {}; Object.keys(allSidebars).forEach((sidebarId) => { diff --git a/packages/docusaurus-plugin-content-docs/src/sidebars.ts b/packages/docusaurus-plugin-content-docs/src/sidebars.ts index 1a4b973cb591..c7432e1879fd 100644 --- a/packages/docusaurus-plugin-content-docs/src/sidebars.ts +++ b/packages/docusaurus-plugin-content-docs/src/sidebars.ts @@ -177,7 +177,7 @@ function normalizeSidebar(sidebars: SidebarJson): Sidebars { ); } -export default function loadSidebars(sidebarPaths?: string[]): Sidebars { +export function loadSidebars(sidebarPaths?: string[]): Sidebars { // We don't want sidebars to be cached because of hot reloading. const allSidebars: SidebarJson = {}; From e3d6484e3a42d169562a97f330e6109e85bbba11 Mon Sep 17 00:00:00 2001 From: slorber Date: Wed, 12 Aug 2020 19:23:31 +0200 Subject: [PATCH 19/30] basic sidebars refactor --- .../src/__tests__/sidebars.test.ts | 71 +++++++----- .../docusaurus-plugin-content-docs/src/cli.ts | 2 +- .../src/index.ts | 2 +- .../src/order.ts | 26 +---- .../src/sidebars.ts | 108 +++++++++++------- .../src/types.ts | 26 +---- 6 files changed, 117 insertions(+), 118 deletions(-) diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/sidebars.test.ts b/packages/docusaurus-plugin-content-docs/src/__tests__/sidebars.test.ts index 4445e7e867bc..6caf442bd683 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/sidebars.test.ts +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/sidebars.test.ts @@ -14,13 +14,13 @@ describe('loadSidebars', () => { const fixtureDir = path.join(__dirname, '__fixtures__', 'sidebars'); test('sidebars with known sidebar item type', async () => { const sidebarPath = path.join(fixtureDir, 'sidebars.json'); - const result = loadSidebars([sidebarPath]); + const result = loadSidebars(sidebarPath); expect(result).toMatchSnapshot(); }); test('sidebars with deep level of category', async () => { const sidebarPath = path.join(fixtureDir, 'sidebars-category.js'); - const result = loadSidebars([sidebarPath]); + const result = loadSidebars(sidebarPath); expect(result).toMatchSnapshot(); }); @@ -30,8 +30,8 @@ describe('loadSidebars', () => { fixtureDir, 'sidebars-category-shorthand.js', ); - const sidebar1 = loadSidebars([sidebarPath1]); - const sidebar2 = loadSidebars([sidebarPath2]); + const sidebar1 = loadSidebars(sidebarPath1); + const sidebar2 = loadSidebars(sidebarPath2); expect(sidebar1).toEqual(sidebar2); }); @@ -40,9 +40,7 @@ describe('loadSidebars', () => { fixtureDir, 'sidebars-category-wrong-items.json', ); - expect(() => - loadSidebars([sidebarPath]), - ).toThrowErrorMatchingInlineSnapshot( + expect(() => loadSidebars(sidebarPath)).toThrowErrorMatchingInlineSnapshot( `"Error loading {\\"type\\":\\"category\\",\\"label\\":\\"Category Label\\",\\"items\\":\\"doc1\\"}. \\"items\\" must be an array."`, ); }); @@ -52,9 +50,7 @@ describe('loadSidebars', () => { fixtureDir, 'sidebars-category-wrong-label.json', ); - expect(() => - loadSidebars([sidebarPath]), - ).toThrowErrorMatchingInlineSnapshot( + expect(() => loadSidebars(sidebarPath)).toThrowErrorMatchingInlineSnapshot( `"Error loading {\\"type\\":\\"category\\",\\"label\\":true,\\"items\\":[\\"doc1\\"]}. \\"label\\" must be a string."`, ); }); @@ -64,9 +60,7 @@ describe('loadSidebars', () => { fixtureDir, 'sidebars-doc-id-not-string.json', ); - expect(() => - loadSidebars([sidebarPath]), - ).toThrowErrorMatchingInlineSnapshot( + expect(() => loadSidebars(sidebarPath)).toThrowErrorMatchingInlineSnapshot( `"Error loading {\\"type\\":\\"doc\\",\\"id\\":[\\"doc1\\"]}. \\"id\\" must be a string."`, ); }); @@ -76,60 +70,75 @@ describe('loadSidebars', () => { fixtureDir, 'sidebars-first-level-not-category.js', ); - const result = loadSidebars([sidebarPath]); + const result = loadSidebars(sidebarPath); expect(result).toMatchSnapshot(); }); test('sidebars link', async () => { const sidebarPath = path.join(fixtureDir, 'sidebars-link.json'); - const result = loadSidebars([sidebarPath]); + const result = loadSidebars(sidebarPath); expect(result).toMatchSnapshot(); }); test('sidebars link wrong label', async () => { const sidebarPath = path.join(fixtureDir, 'sidebars-link-wrong-label.json'); - expect(() => - loadSidebars([sidebarPath]), - ).toThrowErrorMatchingInlineSnapshot( + expect(() => loadSidebars(sidebarPath)).toThrowErrorMatchingInlineSnapshot( `"Error loading {\\"type\\":\\"link\\",\\"label\\":false,\\"href\\":\\"https://github.com\\"}. \\"label\\" must be a string."`, ); }); test('sidebars link wrong href', async () => { const sidebarPath = path.join(fixtureDir, 'sidebars-link-wrong-href.json'); - expect(() => - loadSidebars([sidebarPath]), - ).toThrowErrorMatchingInlineSnapshot( + expect(() => loadSidebars(sidebarPath)).toThrowErrorMatchingInlineSnapshot( `"Error loading {\\"type\\":\\"link\\",\\"label\\":\\"GitHub\\",\\"href\\":[\\"example.com\\"]}. \\"href\\" must be a string."`, ); }); test('sidebars with unknown sidebar item type', async () => { const sidebarPath = path.join(fixtureDir, 'sidebars-unknown-type.json'); - expect(() => - loadSidebars([sidebarPath]), - ).toThrowErrorMatchingInlineSnapshot( + expect(() => loadSidebars(sidebarPath)).toThrowErrorMatchingInlineSnapshot( `"Unknown sidebar item type [superman]. Sidebar item={\\"type\\":\\"superman\\"} "`, ); }); test('sidebars with known sidebar item type but wrong field', async () => { const sidebarPath = path.join(fixtureDir, 'sidebars-wrong-field.json'); + expect(() => loadSidebars(sidebarPath)).toThrowErrorMatchingInlineSnapshot( + `"Unknown sidebar item keys: href. Item: {\\"type\\":\\"category\\",\\"label\\":\\"category\\",\\"href\\":\\"https://github.com\\"}"`, + ); + }); + + test('unexisting path', () => { + expect(() => loadSidebars('badpath')).toThrowErrorMatchingInlineSnapshot( + `"No sidebar file exist at path: badpath"`, + ); + }); + + test('undefined path', () => { expect(() => - loadSidebars([sidebarPath]), + loadSidebars( + // @ts-expect-error: bad arg + undefined, + ), ).toThrowErrorMatchingInlineSnapshot( - `"Unknown sidebar item keys: href. Item: {\\"type\\":\\"category\\",\\"label\\":\\"category\\",\\"href\\":\\"https://github.com\\"}"`, + `"sidebarFilePath not provided: undefined"`, ); }); - test('no sidebars', () => { - const result = loadSidebars(null); - expect(result).toEqual({}); + test('null path', () => { + expect(() => + loadSidebars( + // @ts-expect-error: bad arg + null, + ), + ).toThrowErrorMatchingInlineSnapshot( + `"sidebarFilePath not provided: null"`, + ); }); test('sidebars with category.collapsed property', async () => { const sidebarPath = path.join(fixtureDir, 'sidebars-collapsed.json'); - const result = loadSidebars([sidebarPath]); + const result = loadSidebars(sidebarPath); expect(result).toMatchSnapshot(); }); @@ -138,7 +147,7 @@ describe('loadSidebars', () => { fixtureDir, 'sidebars-collapsed-first-level.json', ); - const result = loadSidebars([sidebarPath]); + const result = loadSidebars(sidebarPath); expect(result).toMatchSnapshot(); }); }); diff --git a/packages/docusaurus-plugin-content-docs/src/cli.ts b/packages/docusaurus-plugin-content-docs/src/cli.ts index 2bebd0794daa..15e16eeb4dd3 100644 --- a/packages/docusaurus-plugin-content-docs/src/cli.ts +++ b/packages/docusaurus-plugin-content-docs/src/cli.ts @@ -89,7 +89,7 @@ export function cliDocsVersion( // Load current sidebar and create a new versioned sidebars file. if (fs.existsSync(sidebarPath)) { - const loadedSidebars: Sidebars = loadSidebars([sidebarPath]); + const loadedSidebars: Sidebars = loadSidebars(sidebarPath); // Transform id in original sidebar to versioned id. const normalizeItem = (item: SidebarItem): SidebarItem => { diff --git a/packages/docusaurus-plugin-content-docs/src/index.ts b/packages/docusaurus-plugin-content-docs/src/index.ts index f0d6c24dc105..33584ab18f59 100644 --- a/packages/docusaurus-plugin-content-docs/src/index.ts +++ b/packages/docusaurus-plugin-content-docs/src/index.ts @@ -141,7 +141,7 @@ export default function pluginContentDocs( const docs: DocMetadataBase[] = await loadVersionDocs(versionMetadata); const docsById: DocsMetadataRaw = keyBy(docs, (doc) => doc.id); - const sidebars = loadSidebars([versionMetadata.sidebarFilePath]); + const sidebars = loadSidebars(versionMetadata.sidebarFilePath); const docsOrder: Order = createOrder(sidebars); // Add sidebar/next/previous to the docs diff --git a/packages/docusaurus-plugin-content-docs/src/order.ts b/packages/docusaurus-plugin-content-docs/src/order.ts index 2d268a1e8f3d..2bcefe6ca63d 100644 --- a/packages/docusaurus-plugin-content-docs/src/order.ts +++ b/packages/docusaurus-plugin-content-docs/src/order.ts @@ -5,35 +5,17 @@ * LICENSE file in the root directory of this source tree. */ -import {Sidebars, SidebarItem, Order, SidebarItemDoc} from './types'; -import {flatten} from 'lodash'; - -function getOrderedDocItems(items: SidebarItem[]): SidebarItemDoc[] { - function getDocItemsRecursion(item: SidebarItem): SidebarItemDoc[] { - if (item.type === 'doc') { - return [item]; - } - if (item.type === 'category') { - return getOrderedDocItems(item.items); - } - // Refs and links should not be shown in navigation. - if (item.type === 'ref' || item.type === 'link') { - return []; - } - throw new Error(`unknown sidebar item type = ${item.type}`); - } - - return flatten(items.map(getDocItemsRecursion)); -} +import {Sidebars, Order} from './types'; +import {collectSidebarDocItems} from './sidebars'; // Build the docs meta such as next, previous, category and sidebar. -export function createOrder(allSidebars: Sidebars = {}): Order { +export function createOrder(allSidebars: Sidebars): Order { const order: Order = {}; Object.keys(allSidebars).forEach((sidebarId) => { const sidebar = allSidebars[sidebarId]; - const docIds: string[] = getOrderedDocItems(sidebar).map( + const docIds: string[] = collectSidebarDocItems(sidebar).map( (docItem) => docItem.id, ); diff --git a/packages/docusaurus-plugin-content-docs/src/sidebars.ts b/packages/docusaurus-plugin-content-docs/src/sidebars.ts index c7432e1879fd..6f413982ebfd 100644 --- a/packages/docusaurus-plugin-content-docs/src/sidebars.ts +++ b/packages/docusaurus-plugin-content-docs/src/sidebars.ts @@ -11,21 +11,44 @@ import importFresh from 'import-fresh'; import { Sidebars, SidebarItem, - SidebarItemCategoryRaw, - SidebarItemRaw, SidebarItemLink, SidebarItemDoc, - SidebarCategoryShorthandRaw, + Sidebar, } from './types'; +import {mapValues, flatten} from 'lodash'; + +type SidebarItemCategoryJSON = { + type: 'category'; + label: string; + items: SidebarItemJSON[]; + collapsed?: boolean; +}; + +type SidebarItemJSON = + | string + | SidebarCategoryShorthandJSON + | SidebarItemDoc + | SidebarItemLink + | SidebarItemCategoryJSON + | { + type: string; + [key: string]: unknown; + }; + +type SidebarCategoryShorthandJSON = { + [sidebarCategory: string]: SidebarItemJSON[]; +}; + +type SidebarJSON = SidebarCategoryShorthandJSON | SidebarItemJSON[]; // Sidebar given by user that is not normalized yet. e.g: sidebars.json -export interface SidebarJson { - [sidebarId: string]: SidebarCategoryShorthandRaw | SidebarItemRaw[]; -} +type SidebarsJSON = { + [sidebarId: string]: SidebarJSON; +}; function isCategoryShorthand( - item: SidebarItemRaw, -): item is SidebarCategoryShorthandRaw { + item: SidebarItemJSON, +): item is SidebarCategoryShorthandJSON { return typeof item !== 'string' && !item.type; } @@ -36,8 +59,8 @@ const defaultCategoryCollapsedValue = true; * Convert {category1: [item1,item2]} shorthand syntax to long-form syntax */ function normalizeCategoryShorthand( - sidebar: SidebarCategoryShorthandRaw, -): SidebarItemCategoryRaw[] { + sidebar: SidebarCategoryShorthandJSON, +): SidebarItemCategoryJSON[] { return Object.entries(sidebar).map(([label, items]) => ({ type: 'category', collapsed: defaultCategoryCollapsedValue, @@ -69,7 +92,7 @@ function assertItem( function assertIsCategory( item: unknown, -): asserts item is SidebarItemCategoryRaw { +): asserts item is SidebarItemCategoryJSON { assertItem(item, ['items', 'label', 'collapsed']); if (typeof item.label !== 'string') { throw new Error( @@ -116,7 +139,7 @@ function assertIsLink(item: unknown): asserts item is SidebarItemLink { * Normalizes recursively item and all its children. Ensures that at the end * each item will be an object with the corresponding type. */ -function normalizeItem(item: SidebarItemRaw): SidebarItem[] { +function normalizeItem(item: SidebarItemJSON): SidebarItem[] { if (typeof item === 'string') { return [ { @@ -159,38 +182,45 @@ function normalizeItem(item: SidebarItemRaw): SidebarItem[] { } } -/** - * Converts sidebars object to mapping to arrays of sidebar item objects. - */ -function normalizeSidebar(sidebars: SidebarJson): Sidebars { - return Object.entries(sidebars).reduce( - (acc: Sidebars, [sidebarId, sidebar]) => { - const normalizedSidebar: SidebarItemRaw[] = Array.isArray(sidebar) - ? sidebar - : normalizeCategoryShorthand(sidebar); - - acc[sidebarId] = flatMap(normalizedSidebar, normalizeItem); - - return acc; - }, - {}, - ); +function normalizeSidebar(sidebar: SidebarJSON) { + const normalizedSidebar: SidebarItemJSON[] = Array.isArray(sidebar) + ? sidebar + : normalizeCategoryShorthand(sidebar); + + return flatMap(normalizedSidebar, normalizeItem); } -export function loadSidebars(sidebarPaths?: string[]): Sidebars { - // We don't want sidebars to be cached because of hot reloading. - const allSidebars: SidebarJson = {}; +function normalizeSidebars(sidebars: SidebarsJSON): Sidebars { + return mapValues(sidebars, normalizeSidebar); +} - if (!sidebarPaths || !sidebarPaths.length) { - return {} as Sidebars; +// TODO refactor: make async +export function loadSidebars(sidebarFilePath: string): Sidebars { + if (!sidebarFilePath) { + throw new Error(`sidebarFilePath not provided: ${sidebarFilePath}`); } + if (!fs.existsSync(sidebarFilePath)) { + throw new Error(`No sidebar file exist at path: ${sidebarFilePath}`); + } + // We don't want sidebars to be cached because of hot reloading. + const sidebarJson = importFresh(sidebarFilePath) as SidebarsJSON; + return normalizeSidebars(sidebarJson); +} - sidebarPaths.forEach((sidebarPath) => { - if (sidebarPath && fs.existsSync(sidebarPath)) { - const sidebar = importFresh(sidebarPath) as SidebarJson; - Object.assign(allSidebars, sidebar); +export function collectSidebarDocItems(sidebar: Sidebar): SidebarItemDoc[] { + function collectRecursive(item: SidebarItem): SidebarItemDoc[] { + if (item.type === 'doc') { + return [item]; } - }); + if (item.type === 'category') { + return flatten(item.items.map(collectRecursive)); + } + // Refs and links should not be shown in navigation. + if (item.type === 'ref' || item.type === 'link') { + return []; + } + throw new Error(`unknown sidebar item type = ${item.type}`); + } - return normalizeSidebar(allSidebars); + return flatten(sidebar.map(collectRecursive)); } diff --git a/packages/docusaurus-plugin-content-docs/src/types.ts b/packages/docusaurus-plugin-content-docs/src/types.ts index 17f268384c80..50d5aaf07eb8 100644 --- a/packages/docusaurus-plugin-content-docs/src/types.ts +++ b/packages/docusaurus-plugin-content-docs/src/types.ts @@ -71,36 +71,14 @@ export type SidebarItemCategory = { collapsed: boolean; }; -export type SidebarItemCategoryRaw = { - type: 'category'; - label: string; - items: SidebarItemRaw[]; - collapsed?: boolean; -}; - export type SidebarItem = | SidebarItemDoc | SidebarItemLink | SidebarItemCategory; -export type SidebarItemRaw = - | string - | SidebarCategoryShorthandRaw - | SidebarItemDoc - | SidebarItemLink - | SidebarItemCategoryRaw - | { - type: string; - [key: string]: unknown; - }; +export type Sidebar = SidebarItem[]; -export type SidebarCategoryShorthandRaw = { - [sidebarCategory: string]: SidebarItemRaw[]; -}; - -export type Sidebars = { - [sidebarId: string]: SidebarItem[]; -}; +export type Sidebars = Record; export type OrderMetadata = { previous?: string; From 9f2513c28993d72e3377943ea13f1c8de64dc521 Mon Sep 17 00:00:00 2001 From: slorber Date: Wed, 12 Aug 2020 19:49:51 +0200 Subject: [PATCH 20/30] add getElementsAround utils --- .../src/__tests__/index.test.ts | 35 +++++++++++++++++++ packages/docusaurus-utils/src/index.ts | 19 ++++++++++ 2 files changed, 54 insertions(+) diff --git a/packages/docusaurus-utils/src/__tests__/index.test.ts b/packages/docusaurus-utils/src/__tests__/index.test.ts index 1861b72d97fa..5efd04d99f73 100644 --- a/packages/docusaurus-utils/src/__tests__/index.test.ts +++ b/packages/docusaurus-utils/src/__tests__/index.test.ts @@ -26,6 +26,7 @@ import { removePrefix, getFilePathForRoutePath, addLeadingSlash, + getElementsAround, } from '../index'; describe('load utils', () => { @@ -477,3 +478,37 @@ describe('getFilePathForRoutePath', () => { ); }); }); + +describe('getElementsAround', () => { + test.only('can return elements around', () => { + expect(getElementsAround(['a', 'b', 'c', 'd'], 0)).toEqual({ + previous: undefined, + next: 'b', + }); + expect(getElementsAround(['a', 'b', 'c', 'd'], 1)).toEqual({ + previous: 'a', + next: 'c', + }); + expect(getElementsAround(['a', 'b', 'c', 'd'], 2)).toEqual({ + previous: 'b', + next: 'd', + }); + expect(getElementsAround(['a', 'b', 'c', 'd'], 3)).toEqual({ + previous: 'c', + next: undefined, + }); + }); + + test.only('throws if bad index is provided', () => { + expect(() => + getElementsAround(['a', 'b', 'c', 'd'], -1), + ).toThrowErrorMatchingInlineSnapshot( + `"Valid aroundIndex for array (of size 4) are between 0 and 3, but you provided aroundIndex=-1"`, + ); + expect(() => + getElementsAround(['a', 'b', 'c', 'd'], 4), + ).toThrowErrorMatchingInlineSnapshot( + `"Valid aroundIndex for array (of size 4) are between 0 and 3, but you provided aroundIndex=4"`, + ); + }); +}); diff --git a/packages/docusaurus-utils/src/index.ts b/packages/docusaurus-utils/src/index.ts index baaba7e9824f..6b85cd1408d5 100644 --- a/packages/docusaurus-utils/src/index.ts +++ b/packages/docusaurus-utils/src/index.ts @@ -403,3 +403,22 @@ export function getFilePathForRoutePath(routePath: string): string { const filePath = path.dirname(routePath); return path.join(filePath, `${fileName}/index.html`); } + +export function getElementsAround( + array: T[], + aroundIndex: number, +): { + next: T | undefined; + previous: T | undefined; +} { + const min = 0; + const max = array.length - 1; + if (aroundIndex < min || aroundIndex > max) { + throw new Error( + `Valid aroundIndex for array (of size ${array.length}) are between ${min} and ${max}, but you provided aroundIndex=${aroundIndex}`, + ); + } + const previous = aroundIndex === min ? undefined : array[aroundIndex - 1]; + const next = aroundIndex === max ? undefined : array[aroundIndex + 1]; + return {previous, next}; +} From 78dfd7f197d62307831c196344622deb1d0f0c10 Mon Sep 17 00:00:00 2001 From: slorber Date: Wed, 12 Aug 2020 20:55:27 +0200 Subject: [PATCH 21/30] refactor sidebar + ordering/navigation logic --- .../src/__tests__/order.test.ts | 258 ------------------ .../src/__tests__/sidebars.test.ts | 178 +++++++++++- .../src/index.ts | 82 ++++-- .../src/order.ts | 45 --- .../src/sidebars.ts | 55 ++++ .../src/types.ts | 8 - .../src/__tests__/index.test.ts | 4 +- 7 files changed, 287 insertions(+), 343 deletions(-) delete mode 100644 packages/docusaurus-plugin-content-docs/src/__tests__/order.test.ts delete mode 100644 packages/docusaurus-plugin-content-docs/src/order.ts diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/order.test.ts b/packages/docusaurus-plugin-content-docs/src/__tests__/order.test.ts deleted file mode 100644 index 4f94bc38e7eb..000000000000 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/order.test.ts +++ /dev/null @@ -1,258 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -import {createOrder} from '../order'; - -describe('createOrder', () => { - test('multiple sidebars with subcategory', () => { - const result = createOrder({ - docs: [ - { - type: 'category', - collapsed: false, - label: 'Category1', - items: [ - { - type: 'category', - collapsed: false, - label: 'Subcategory 1', - items: [{type: 'doc', id: 'doc1'}], - }, - { - type: 'category', - collapsed: false, - label: 'Subcategory 2', - items: [{type: 'doc', id: 'doc2'}], - }, - ], - }, - { - type: 'category', - collapsed: false, - label: 'Category2', - items: [ - {type: 'doc', id: 'doc3'}, - {type: 'doc', id: 'doc4'}, - ], - }, - ], - otherDocs: [ - { - type: 'category', - collapsed: false, - label: 'Category1', - items: [{type: 'doc', id: 'doc5'}], - }, - ], - }); - expect(result).toEqual({ - doc1: { - next: 'doc2', - previous: undefined, - sidebar: 'docs', - }, - doc2: { - next: 'doc3', - previous: 'doc1', - sidebar: 'docs', - }, - doc3: { - next: 'doc4', - previous: 'doc2', - sidebar: 'docs', - }, - doc4: { - next: undefined, - previous: 'doc3', - sidebar: 'docs', - }, - doc5: { - next: undefined, - previous: undefined, - sidebar: 'otherDocs', - }, - }); - }); - test('multiple sidebars without subcategory', () => { - const result = createOrder({ - docs: [ - { - type: 'category', - collapsed: false, - label: 'Category1', - items: [ - {type: 'doc', id: 'doc1'}, - {type: 'doc', id: 'doc2'}, - ], - }, - { - type: 'category', - collapsed: false, - label: 'Category2', - items: [ - {type: 'doc', id: 'doc3'}, - {type: 'doc', id: 'doc4'}, - ], - }, - ], - otherDocs: [ - { - type: 'category', - collapsed: false, - label: 'Category1', - items: [{type: 'doc', id: 'doc5'}], - }, - ], - }); - expect(result).toEqual({ - doc1: { - next: 'doc2', - previous: undefined, - sidebar: 'docs', - }, - doc2: { - next: 'doc3', - previous: 'doc1', - sidebar: 'docs', - }, - doc3: { - next: 'doc4', - previous: 'doc2', - sidebar: 'docs', - }, - doc4: { - next: undefined, - previous: 'doc3', - sidebar: 'docs', - }, - doc5: { - next: undefined, - previous: undefined, - sidebar: 'otherDocs', - }, - }); - }); - - test('versioned sidebars', () => { - const result = createOrder({ - docs: [ - { - type: 'category', - collapsed: false, - label: 'Category1', - items: [{type: 'doc', id: 'doc1'}], - }, - ], - 'version-1.2.3-docs': [ - { - type: 'category', - collapsed: false, - label: 'Category1', - items: [{type: 'doc', id: 'version-1.2.3-doc2'}], - }, - { - type: 'category', - collapsed: false, - label: 'Category2', - items: [{type: 'doc', id: 'version-1.2.3-doc1'}], - }, - ], - }); - expect(result).toEqual({ - doc1: { - next: undefined, - previous: undefined, - sidebar: 'docs', - }, - 'version-1.2.3-doc1': { - next: undefined, - previous: 'version-1.2.3-doc2', - sidebar: 'version-1.2.3-docs', - }, - 'version-1.2.3-doc2': { - next: 'version-1.2.3-doc1', - previous: undefined, - sidebar: 'version-1.2.3-docs', - }, - }); - }); - - test('multiple sidebars with subcategories, refs and external links', () => { - const result = createOrder({ - docs: [ - { - type: 'category', - collapsed: false, - label: 'Category1', - items: [ - { - type: 'category', - collapsed: false, - label: 'Subcategory 1', - items: [{type: 'link', href: '//example.com', label: 'bar'}], - }, - { - type: 'category', - collapsed: false, - label: 'Subcategory 2', - items: [{type: 'doc', id: 'doc2'}], - }, - { - type: 'category', - collapsed: false, - label: 'Subcategory 1', - items: [{type: 'link', href: '//example2.com', label: 'baz'}], - }, - ], - }, - { - type: 'category', - collapsed: false, - label: 'Category2', - items: [ - {type: 'doc', id: 'doc3'}, - {type: 'ref', id: 'doc4'}, - ], - }, - ], - otherDocs: [ - { - type: 'category', - collapsed: false, - label: 'Category1', - items: [{type: 'doc', id: 'doc5'}], - }, - ], - }); - expect(result).toEqual({ - doc2: { - next: 'doc3', - previous: undefined, - sidebar: 'docs', - }, - doc3: { - next: undefined, - previous: 'doc2', - sidebar: 'docs', - }, - doc5: { - next: undefined, - previous: undefined, - sidebar: 'otherDocs', - }, - }); - }); - - test('edge cases', () => { - expect(createOrder({})).toEqual({}); - expect(createOrder(undefined)).toEqual({}); - - expect(() => createOrder(null)).toThrowErrorMatchingInlineSnapshot( - `"Cannot convert undefined or null to object"`, - ); - }); -}); diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/sidebars.test.ts b/packages/docusaurus-plugin-content-docs/src/__tests__/sidebars.test.ts index 6caf442bd683..ec8f6fb4c36b 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/sidebars.test.ts +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/sidebars.test.ts @@ -6,7 +6,13 @@ */ import path from 'path'; -import {loadSidebars} from '../sidebars'; +import { + loadSidebars, + collectSidebarDocItems, + collectSidebarsDocIds, + createSidebarsUtils, +} from '../sidebars'; +import {Sidebar, Sidebars} from '../types'; /* eslint-disable global-require, import/no-dynamic-require */ @@ -151,3 +157,173 @@ describe('loadSidebars', () => { expect(result).toMatchSnapshot(); }); }); + +describe('collectSidebarDocItems', () => { + test('can collect recursively', async () => { + const sidebar: Sidebar = [ + { + type: 'category', + collapsed: false, + label: 'Category1', + items: [ + { + type: 'category', + collapsed: false, + label: 'Subcategory 1', + items: [{type: 'doc', id: 'doc1'}], + }, + { + type: 'category', + collapsed: false, + label: 'Subcategory 2', + items: [ + {type: 'doc', id: 'doc2'}, + { + type: 'category', + collapsed: false, + label: 'Sub sub category 1', + items: [{type: 'doc', id: 'doc3'}], + }, + ], + }, + ], + }, + { + type: 'category', + collapsed: false, + label: 'Category2', + items: [ + {type: 'doc', id: 'doc4'}, + {type: 'doc', id: 'doc5'}, + ], + }, + ]; + + expect(collectSidebarDocItems(sidebar).map((doc) => doc.id)).toEqual([ + 'doc1', + 'doc2', + 'doc3', + 'doc4', + 'doc5', + ]); + }); +}); + +describe('collectSidebarsDocItems', () => { + test('can collect sidebars doc items', async () => { + const sidebar1: Sidebar = [ + { + type: 'category', + collapsed: false, + label: 'Category1', + items: [ + { + type: 'category', + collapsed: false, + label: 'Subcategory 1', + items: [{type: 'doc', id: 'doc1'}], + }, + {type: 'doc', id: 'doc2'}, + ], + }, + ]; + + const sidebar2: Sidebar = [ + { + type: 'category', + collapsed: false, + label: 'Category2', + items: [ + {type: 'doc', id: 'doc3'}, + {type: 'doc', id: 'doc4'}, + ], + }, + ]; + + const sidebar3: Sidebar = [ + {type: 'doc', id: 'doc5'}, + {type: 'doc', id: 'doc6'}, + ]; + expect(collectSidebarsDocIds({sidebar1, sidebar2, sidebar3})).toEqual({ + sidebar1: ['doc1', 'doc2'], + sidebar2: ['doc3', 'doc4'], + sidebar3: ['doc5', 'doc6'], + }); + }); +}); + +describe('createSidebarsUtils', () => { + const sidebar1: Sidebar = [ + { + type: 'category', + collapsed: false, + label: 'Category1', + items: [ + { + type: 'category', + collapsed: false, + label: 'Subcategory 1', + items: [{type: 'doc', id: 'doc1'}], + }, + {type: 'doc', id: 'doc2'}, + ], + }, + ]; + + const sidebar2: Sidebar = [ + { + type: 'category', + collapsed: false, + label: 'Category2', + items: [ + {type: 'doc', id: 'doc3'}, + {type: 'doc', id: 'doc4'}, + ], + }, + ]; + + const sidebars: Sidebars = {sidebar1, sidebar2}; + + const { + getFirstDocIdOfFirstSidebar, + getSidebarNameByDocId, + getDocNavigation, + } = createSidebarsUtils(sidebars); + + test('getSidebarNameByDocId', async () => { + expect(getFirstDocIdOfFirstSidebar()).toEqual('doc1'); + }); + + test('getSidebarNameByDocId', async () => { + expect(getSidebarNameByDocId('doc1')).toEqual('sidebar1'); + expect(getSidebarNameByDocId('doc2')).toEqual('sidebar1'); + expect(getSidebarNameByDocId('doc3')).toEqual('sidebar2'); + expect(getSidebarNameByDocId('doc4')).toEqual('sidebar2'); + expect(getSidebarNameByDocId('doc5')).toEqual(undefined); + expect(getSidebarNameByDocId('doc6')).toEqual(undefined); + }); + + test('getDocNavigation', async () => { + expect(getDocNavigation('doc1')).toEqual({ + sidebarName: 'sidebar1', + previousId: undefined, + nextId: 'doc2', + }); + expect(getDocNavigation('doc2')).toEqual({ + sidebarName: 'sidebar1', + previousId: 'doc1', + nextId: undefined, + }); + + expect(getDocNavigation('doc3')).toEqual({ + sidebarName: 'sidebar2', + previousId: undefined, + nextId: 'doc4', + }); + expect(getDocNavigation('doc4')).toEqual({ + sidebarName: 'sidebar2', + previousId: 'doc3', + nextId: undefined, + }); + }); +}); diff --git a/packages/docusaurus-plugin-content-docs/src/index.ts b/packages/docusaurus-plugin-content-docs/src/index.ts index 33584ab18f59..cd788a29461e 100644 --- a/packages/docusaurus-plugin-content-docs/src/index.ts +++ b/packages/docusaurus-plugin-content-docs/src/index.ts @@ -14,19 +14,16 @@ import { import {normalizeUrl, docuHash, aliasedSitePath} from '@docusaurus/utils'; import {LoadContext, Plugin, RouteConfig} from '@docusaurus/types'; -import {createOrder} from './order'; -import {loadSidebars} from './sidebars'; +import {loadSidebars, createSidebarsUtils} from './sidebars'; import {readVersionDocs, processDocMetadata} from './docs'; import {readVersionsMetadata, getVersionedDocsDirPath} from './env'; import { PluginOptions, - Order, LoadedContent, SourceToPermalink, PermalinkToSidebar, DocMetadataBase, - DocsMetadataRaw, DocMetadata, GlobalPluginData, VersionMetadata, @@ -120,10 +117,19 @@ export default function pluginContentDocs( }, async loadContent() { - async function loadVersionDocs( + async function loadVersionDocsBase( versionMetadata: VersionMetadata, ): Promise { const docFiles = await readVersionDocs(versionMetadata, options); + if (docFiles.length === 0) { + throw new Error( + `Docs version has no docs! At least once document is required!\n For version=${JSON.stringify( + versionMetadata, + null, + 2, + )}`, + ); + } async function processVersionDoc(docFile: DocFile) { return processDocMetadata({ docFile, @@ -138,60 +144,78 @@ export default function pluginContentDocs( async function loadVersion( versionMetadata: VersionMetadata, ): Promise { - const docs: DocMetadataBase[] = await loadVersionDocs(versionMetadata); - const docsById: DocsMetadataRaw = keyBy(docs, (doc) => doc.id); - const sidebars = loadSidebars(versionMetadata.sidebarFilePath); - const docsOrder: Order = createOrder(sidebars); + const sidebarsUtils = createSidebarsUtils(sidebars); + + const docsBase: DocMetadataBase[] = await loadVersionDocsBase( + versionMetadata, + ); + const docsBaseById: Record = keyBy( + docsBase, + (doc) => doc.id, + ); // Add sidebar/next/previous to the docs function addNavData(doc: DocMetadataBase): DocMetadata { - const {next: nextId, previous: previousId, sidebar} = - docsOrder[doc.id] ?? {}; + const { + sidebarName, + previousId, + nextId, + } = sidebarsUtils.getDocNavigation(doc.id); const toDocNavLink = (navDocId: string): DocNavLink => ({ - title: docsById[navDocId].title, - permalink: docsById[navDocId].permalink, + title: docsBaseById[navDocId].title, + permalink: docsBaseById[navDocId].permalink, }); return { ...doc, - sidebar, + sidebar: sidebarName, previous: previousId ? toDocNavLink(previousId) : undefined, next: nextId ? toDocNavLink(nextId) : undefined, }; } - const loadedDocs = docs.map(addNavData); + const docs = docsBase.map(addNavData); // sort to ensure consistent output for tests - loadedDocs.sort((a, b) => a.id.localeCompare(b.id)); + docs.sort((a, b) => a.id.localeCompare(b.id)); // TODO annoying side effect! - Object.values(loadedDocs).forEach((loadedDoc) => { + Object.values(docs).forEach((loadedDoc) => { const {source, permalink} = loadedDoc; sourceToPermalink[source] = permalink; }); - // TODO replace with global state logic? + // TODO useful? replace with global state logic? const permalinkToSidebar: PermalinkToSidebar = {}; - Object.values(loadedDocs).forEach((loadedDoc) => { - const {id: docId, permalink} = loadedDoc; - const docSidebarName = docsOrder[docId]?.sidebar; - if (docSidebarName) { - permalinkToSidebar[permalink] = docSidebarName; + Object.values(docs).forEach((doc) => { + if (doc.sidebar) { + permalinkToSidebar[doc.permalink] = doc.sidebar; } }); - // TODO bad legacy algo! docs[0] gives a random doc - // We should fallback to the first doc of the first sidebar instead - const mainDoc: DocMetadata = - docs.find( + // The "main doc" is the "version entry point" + // We browse this doc by clicking on a version: + // - the doc at / + // - the first doc of the first sidebar + // - a random doc (if no docs are in any sidebar... edge case) + function getMainDoc(): DocMetadata { + const versionHomeDoc = docs.find( (doc) => doc.unversionedId === options.homePageId || doc.slug === '/', - ) ?? docs[0]; + ); + const firstDocIdOfFirstSidebar = sidebarsUtils.getFirstDocIdOfFirstSidebar(); + if (versionHomeDoc) { + return versionHomeDoc; + } else if (firstDocIdOfFirstSidebar) { + return docs.find((doc) => doc.id === firstDocIdOfFirstSidebar)!; + } else { + return docs[0]; + } + } return { ...versionMetadata, - mainDocId: mainDoc.unversionedId, + mainDocId: getMainDoc().unversionedId, sidebars, permalinkToSidebar, docs: docs.map(addNavData), diff --git a/packages/docusaurus-plugin-content-docs/src/order.ts b/packages/docusaurus-plugin-content-docs/src/order.ts deleted file mode 100644 index 2bcefe6ca63d..000000000000 --- a/packages/docusaurus-plugin-content-docs/src/order.ts +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -import {Sidebars, Order} from './types'; -import {collectSidebarDocItems} from './sidebars'; - -// Build the docs meta such as next, previous, category and sidebar. -export function createOrder(allSidebars: Sidebars): Order { - const order: Order = {}; - - Object.keys(allSidebars).forEach((sidebarId) => { - const sidebar = allSidebars[sidebarId]; - - const docIds: string[] = collectSidebarDocItems(sidebar).map( - (docItem) => docItem.id, - ); - - // eslint-disable-next-line - for (let i = 0; i < docIds.length; i++) { - const id = docIds[i]; - let previous; - let next; - - if (i > 0) { - previous = docIds[i - 1]; - } - - if (i < docIds.length - 1) { - next = docIds[i + 1]; - } - - order[id] = { - previous, - next, - sidebar: sidebarId, - }; - } - }); - - return order; -} diff --git a/packages/docusaurus-plugin-content-docs/src/sidebars.ts b/packages/docusaurus-plugin-content-docs/src/sidebars.ts index 6f413982ebfd..9fed7529764d 100644 --- a/packages/docusaurus-plugin-content-docs/src/sidebars.ts +++ b/packages/docusaurus-plugin-content-docs/src/sidebars.ts @@ -16,6 +16,7 @@ import { Sidebar, } from './types'; import {mapValues, flatten} from 'lodash'; +import {getElementsAround} from '@docusaurus/utils'; type SidebarItemCategoryJSON = { type: 'category'; @@ -207,6 +208,7 @@ export function loadSidebars(sidebarFilePath: string): Sidebars { return normalizeSidebars(sidebarJson); } +// traverse the sidebar tree in depth to find all doc items, in correct order export function collectSidebarDocItems(sidebar: Sidebar): SidebarItemDoc[] { function collectRecursive(item: SidebarItem): SidebarItemDoc[] { if (item.type === 'doc') { @@ -224,3 +226,56 @@ export function collectSidebarDocItems(sidebar: Sidebar): SidebarItemDoc[] { return flatten(sidebar.map(collectRecursive)); } + +export function collectSidebarsDocIds( + sidebars: Sidebars, +): Record { + return mapValues(sidebars, (sidebar) => { + return collectSidebarDocItems(sidebar).map((docItem) => docItem.id); + }); +} + +export function createSidebarsUtils(sidebars: Sidebars) { + const sidebarNameToDocIds = collectSidebarsDocIds(sidebars); + + function getFirstDocIdOfFirstSidebar(): string | undefined { + return Object.values(sidebarNameToDocIds)[0]?.[0]; + } + + function getSidebarNameByDocId(docId: string): string | undefined { + // TODO lookup speed can be optimized + const entry = Object.entries( + sidebarNameToDocIds, + ).find(([_sidebarName, docIds]) => docIds.includes(docId)); + + return entry?.[0]; + } + + function getDocNavigation( + docId: string, + ): { + sidebarName: string | undefined; + previousId: string | undefined; + nextId: string | undefined; + } { + const sidebarName = getSidebarNameByDocId(docId); + if (sidebarName) { + const docIds = sidebarNameToDocIds[sidebarName]; + const currentIndex = docIds.indexOf(docId); + const {previous, next} = getElementsAround(docIds, currentIndex); + return { + sidebarName, + previousId: previous, + nextId: next, + }; + } else { + return { + sidebarName: undefined, + previousId: undefined, + nextId: undefined, + }; + } + } + + return {getFirstDocIdOfFirstSidebar, getSidebarNameByDocId, getDocNavigation}; +} diff --git a/packages/docusaurus-plugin-content-docs/src/types.ts b/packages/docusaurus-plugin-content-docs/src/types.ts index 50d5aaf07eb8..889dab70ffce 100644 --- a/packages/docusaurus-plugin-content-docs/src/types.ts +++ b/packages/docusaurus-plugin-content-docs/src/types.ts @@ -86,10 +86,6 @@ export type OrderMetadata = { sidebar?: string; }; -export type Order = { - [id: string]: OrderMetadata; -}; - export type LastUpdateData = { lastUpdatedAt?: number; lastUpdatedBy?: string; @@ -120,10 +116,6 @@ export type DocMetadata = DocMetadataBase & { next?: DocNavLink; }; -export type DocsMetadataRaw = { - [id: string]: DocMetadataBase; -}; - export type SourceToPermalink = { [source: string]: string; }; diff --git a/packages/docusaurus-utils/src/__tests__/index.test.ts b/packages/docusaurus-utils/src/__tests__/index.test.ts index 5efd04d99f73..9a14101a53ae 100644 --- a/packages/docusaurus-utils/src/__tests__/index.test.ts +++ b/packages/docusaurus-utils/src/__tests__/index.test.ts @@ -480,7 +480,7 @@ describe('getFilePathForRoutePath', () => { }); describe('getElementsAround', () => { - test.only('can return elements around', () => { + test('can return elements around', () => { expect(getElementsAround(['a', 'b', 'c', 'd'], 0)).toEqual({ previous: undefined, next: 'b', @@ -499,7 +499,7 @@ describe('getElementsAround', () => { }); }); - test.only('throws if bad index is provided', () => { + test('throws if bad index is provided', () => { expect(() => getElementsAround(['a', 'b', 'c', 'd'], -1), ).toThrowErrorMatchingInlineSnapshot( From 9f607afbd0c34d4e9daf5dd7b448b77dbe892370 Mon Sep 17 00:00:00 2001 From: slorber Date: Wed, 12 Aug 2020 21:02:34 +0200 Subject: [PATCH 22/30] stable retrocompatible refactor --- .../src/theme/DocVersionSuggestions/index.tsx | 27 ++++++++++--------- .../DocsVersionDropdownNavbarItem.tsx | 10 ++----- .../NavbarItem/DocsVersionNavbarItem.tsx | 6 +---- .../src/validateThemeConfig.js | 1 - 4 files changed, 18 insertions(+), 26 deletions(-) diff --git a/packages/docusaurus-theme-classic/src/theme/DocVersionSuggestions/index.tsx b/packages/docusaurus-theme-classic/src/theme/DocVersionSuggestions/index.tsx index 7654f22aeb22..f047b60b250d 100644 --- a/packages/docusaurus-theme-classic/src/theme/DocVersionSuggestions/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/DocVersionSuggestions/index.tsx @@ -52,18 +52,21 @@ function DocVersionSuggestions(): JSX.Element { return (
- {activeVersionName === 'next' ? ( -
- This is unreleased documentation for {siteTitle}{' '} - {activeVersionName} version. -
- ) : ( -
- This is documentation for {siteTitle}{' '} - v{activeVersionName}, which is no longer actively - maintained. -
- )} + { + // TODO need refactoring + activeVersionName === 'current' ? ( +
+ This is unreleased documentation for {siteTitle}{' '} + {activeVersionName} version. +
+ ) : ( +
+ This is documentation for {siteTitle}{' '} + v{activeVersionName}, which is no longer actively + maintained. +
+ ) + }
For up-to-date documentation, see the{' '} diff --git a/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx b/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx index 275d6cfe9cd7..e9fd19496347 100644 --- a/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx +++ b/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx @@ -13,16 +13,12 @@ import { useActiveDocContext, } from '@theme/hooks/useDocs'; -const versionLabel = (version, nextVersionLabel) => - version.name === 'next' ? nextVersionLabel : version.name; - const getVersionMainDoc = (version) => version.docs.find((doc) => doc.id === version.mainDocId); export default function DocsVersionDropdownNavbarItem({ mobile, docsPluginId, - nextVersionLabel, ...props }) { const activeDocContext = useActiveDocContext(docsPluginId); @@ -37,7 +33,7 @@ export default function DocsVersionDropdownNavbarItem({ getVersionMainDoc(version); return { isNavLink: true, - label: versionLabel(version, nextVersionLabel), + label: version.label, to: versionDoc.path, isActive: () => version === activeDocContext?.activeVersion, }; @@ -46,9 +42,7 @@ export default function DocsVersionDropdownNavbarItem({ const dropdownVersion = activeDocContext.activeVersion ?? latestVersion; // Mobile is handled a bit differently - const dropdownLabel = mobile - ? 'Versions' - : versionLabel(dropdownVersion, nextVersionLabel); + const dropdownLabel = mobile ? 'Versions' : dropdownVersion.label; const dropdownTo = mobile ? undefined : getVersionMainDoc(dropdownVersion).path; diff --git a/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionNavbarItem.tsx b/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionNavbarItem.tsx index 11bd3e037f29..58eb426febac 100644 --- a/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionNavbarItem.tsx +++ b/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionNavbarItem.tsx @@ -12,20 +12,16 @@ import {useActiveVersion, useLatestVersion} from '@theme/hooks/useDocs'; const getVersionMainDoc = (version) => version.docs.find((doc) => doc.id === version.mainDocId); -const versionLabel = (version, nextVersionLabel) => - version.name === 'next' ? nextVersionLabel : version.name; - export default function DocsVersionNavbarItem({ label: staticLabel, to: staticTo, docsPluginId, - nextVersionLabel, ...props }) { const activeVersion = useActiveVersion(docsPluginId); const latestVersion = useLatestVersion(docsPluginId); const version = activeVersion ?? latestVersion; - const label = staticLabel ?? versionLabel(version, nextVersionLabel); + const label = staticLabel ?? version.label; const path = staticTo ?? getVersionMainDoc(version).path; return ; } diff --git a/packages/docusaurus-theme-classic/src/validateThemeConfig.js b/packages/docusaurus-theme-classic/src/validateThemeConfig.js index 1267db7c56dc..38153e3b59ab 100644 --- a/packages/docusaurus-theme-classic/src/validateThemeConfig.js +++ b/packages/docusaurus-theme-classic/src/validateThemeConfig.js @@ -50,7 +50,6 @@ const DocsVersionNavbarItemSchema = Joi.object({ label: Joi.string(), to: Joi.string(), docsPluginId: Joi.string(), - nextVersionLabel: Joi.string().default('Next'), }); const DocsVersionDropdownNavbarItemSchema = Joi.object({ From be48c5feb9907db861412fe6d0435daa6c6393fd Mon Sep 17 00:00:00 2001 From: slorber Date: Thu, 13 Aug 2020 17:21:16 +0200 Subject: [PATCH 23/30] add proper versions metadata tests --- .../src/__tests__/env.test.ts | 58 --- .../src/__tests__/versions.test.ts | 345 ++++++++++++++++++ .../src/index.ts | 4 +- .../src/options.ts | 2 +- .../src/{versionMetadataProp.ts => props.ts} | 0 .../src/{env.ts => versions.ts} | 18 +- 6 files changed, 360 insertions(+), 67 deletions(-) delete mode 100644 packages/docusaurus-plugin-content-docs/src/__tests__/env.test.ts create mode 100644 packages/docusaurus-plugin-content-docs/src/__tests__/versions.test.ts rename packages/docusaurus-plugin-content-docs/src/{versionMetadataProp.ts => props.ts} (100%) rename packages/docusaurus-plugin-content-docs/src/{env.ts => versions.ts} (90%) diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/env.test.ts b/packages/docusaurus-plugin-content-docs/src/__tests__/env.test.ts deleted file mode 100644 index 017ab47bab4e..000000000000 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/env.test.ts +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -import path from 'path'; -import loadEnv from '../env'; -import {DEFAULT_PLUGIN_ID} from '@docusaurus/core/lib/constants'; - -describe('loadEnv', () => { - test('website with versioning disabled', () => { - const siteDir = path.join(__dirname, '__fixtures__', 'simple-site'); - const env = loadEnv(siteDir, DEFAULT_PLUGIN_ID); - expect(env.versioning.enabled).toBe(false); - expect(env.versioning.versions).toStrictEqual([]); - }); - - test('website with versioning enabled', () => { - const siteDir = path.join(__dirname, '__fixtures__', 'versioned-site'); - const env = loadEnv(siteDir, DEFAULT_PLUGIN_ID); - expect(env.versioning.enabled).toBe(true); - expect(env.versioning.latestVersion).toBe('1.0.1'); - expect(env.versioning.versions).toStrictEqual([ - '1.0.1', - '1.0.0', - 'withSlugs', - ]); - }); - - test('website with versioning enabled, 2nd docs plugin instance', () => { - const siteDir = path.join(__dirname, '__fixtures__', 'versioned-site'); - const env = loadEnv(siteDir, 'community'); - expect(env.versioning.enabled).toBe(true); - expect(env.versioning.latestVersion).toBe('1.0.0'); - expect(env.versioning.versions).toStrictEqual(['1.0.0']); - }); - - test('website with versioning but disabled', () => { - const siteDir = path.join(__dirname, '__fixtures__', 'versioned-site'); - const env = loadEnv(siteDir, DEFAULT_PLUGIN_ID, {disableVersioning: true}); - expect(env.versioning.enabled).toBe(false); - expect(env.versioning.versions).toStrictEqual([]); - }); - - test('website with invalid versions.json file', () => { - const siteDir = path.join(__dirname, '__fixtures__', 'versioned-site'); - const mock = jest.spyOn(JSON, 'parse').mockImplementationOnce(() => { - return { - invalid: 'json', - }; - }); - const env = loadEnv(siteDir, DEFAULT_PLUGIN_ID); - expect(env.versioning.enabled).toBe(false); - mock.mockRestore(); - }); -}); diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/versions.test.ts b/packages/docusaurus-plugin-content-docs/src/__tests__/versions.test.ts new file mode 100644 index 000000000000..bba60d89f2e8 --- /dev/null +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/versions.test.ts @@ -0,0 +1,345 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import path from 'path'; +import { + getVersionsFilePath, + getVersionedDocsDirPath, + getVersionedSidebarsDirPath, + readVersionsMetadata, +} from '../versions'; +import {DEFAULT_OPTIONS} from '../options'; +import {DEFAULT_PLUGIN_ID} from '@docusaurus/core/lib/constants'; +import {VersionMetadata} from '../types'; + +describe('version paths', () => { + test('getVersionedDocsDirPath', () => { + expect(getVersionsFilePath('someSiteDir', DEFAULT_PLUGIN_ID)).toBe( + 'someSiteDir/versions.json', + ); + expect(getVersionsFilePath('otherSite/dir', 'pluginId')).toBe( + 'otherSite/dir/pluginId_versions.json', + ); + }); + + test('getVersionedDocsDirPath', () => { + expect(getVersionedDocsDirPath('someSiteDir', DEFAULT_PLUGIN_ID)).toBe( + 'someSiteDir/versioned_docs', + ); + expect(getVersionedDocsDirPath('otherSite/dir', 'pluginId')).toBe( + 'otherSite/dir/pluginId_versioned_docs', + ); + }); + + test('getVersionedSidebarsDirPath', () => { + expect(getVersionedSidebarsDirPath('someSiteDir', DEFAULT_PLUGIN_ID)).toBe( + 'someSiteDir/versioned_sidebars', + ); + expect(getVersionedSidebarsDirPath('otherSite/dir', 'pluginId')).toBe( + 'otherSite/dir/pluginId_versioned_sidebars', + ); + }); +}); + +describe('simple site', () => { + const simpleSiteDir = path.resolve( + path.join(__dirname, '__fixtures__', 'simple-site'), + ); + const defaultOptions = { + id: DEFAULT_PLUGIN_ID, + ...DEFAULT_OPTIONS, + }; + const defaultContext = { + siteDir: simpleSiteDir, + baseUrl: '/', + }; + + const vCurrent: VersionMetadata = { + docsDirPath: path.join(simpleSiteDir, 'docs'), + isLast: true, + routePriority: -1, + sidebarFilePath: path.join(simpleSiteDir, 'sidebars.json'), + versionLabel: 'Next', + versionName: 'current', + versionPath: '/docs', + }; + + test('readVersionsMetadata simple site', () => { + const versionsMetadata = readVersionsMetadata({ + options: defaultOptions, + context: defaultContext, + }); + + expect(versionsMetadata).toEqual([vCurrent]); + }); + + test('readVersionsMetadata simple site with base url', () => { + const versionsMetadata = readVersionsMetadata({ + options: defaultOptions, + context: { + ...defaultContext, + baseUrl: '/myBaseUrl', + }, + }); + + expect(versionsMetadata).toEqual([ + { + ...vCurrent, + versionPath: '/myBaseUrl/docs', + }, + ]); + }); + + test('readVersionsMetadata simple site with base url', () => { + expect(() => + readVersionsMetadata({ + options: {...defaultOptions, disableVersioning: true}, + context: defaultContext, + }), + ).toThrowErrorMatchingInlineSnapshot( + `"Docs: using disableVersioning=true option on a non-versioned site does not make sense"`, + ); + }); + + test('readVersionsMetadata simple site with base url', () => { + expect(() => + readVersionsMetadata({ + options: {...defaultOptions, includeCurrentVersion: false}, + context: defaultContext, + }), + ).toThrowErrorMatchingInlineSnapshot( + `"It is not possible to use docs without any version. Please check the configuration of these options: includeCurrentVersion=false disableVersioning=false"`, + ); + }); +}); + +describe('versioned site, pluginId=default', () => { + const versionedSiteDir = path.resolve( + path.join(__dirname, '__fixtures__', 'versioned-site'), + ); + const defaultOptions = { + id: DEFAULT_PLUGIN_ID, + ...DEFAULT_OPTIONS, + }; + const defaultContext = { + siteDir: versionedSiteDir, + baseUrl: '/', + }; + + const vCurrent: VersionMetadata = { + docsDirPath: path.join(versionedSiteDir, 'docs'), + isLast: false, + routePriority: undefined, + sidebarFilePath: path.join(versionedSiteDir, 'sidebars.json'), + versionLabel: 'Next', + versionName: 'current', + versionPath: '/docs/next', + }; + + const v101: VersionMetadata = { + docsDirPath: path.join(versionedSiteDir, 'versioned_docs/version-1.0.1'), + isLast: true, + routePriority: -1, + sidebarFilePath: path.join( + versionedSiteDir, + 'versioned_sidebars/version-1.0.1-sidebars.json', + ), + versionLabel: '1.0.1', + versionName: '1.0.1', + versionPath: '/docs', + }; + + const v100: VersionMetadata = { + docsDirPath: path.join(versionedSiteDir, 'versioned_docs/version-1.0.0'), + isLast: false, + routePriority: undefined, + sidebarFilePath: path.join( + versionedSiteDir, + 'versioned_sidebars/version-1.0.0-sidebars.json', + ), + versionLabel: '1.0.0', + versionName: '1.0.0', + versionPath: '/docs/1.0.0', + }; + + const vwithSlugs: VersionMetadata = { + docsDirPath: path.join( + versionedSiteDir, + 'versioned_docs/version-withSlugs', + ), + isLast: false, + routePriority: undefined, + sidebarFilePath: path.join( + versionedSiteDir, + 'versioned_sidebars/version-withSlugs-sidebars.json', + ), + versionLabel: 'withSlugs', + versionName: 'withSlugs', + versionPath: '/docs/withSlugs', + }; + + test('readVersionsMetadata versioned site', () => { + const versionsMetadata = readVersionsMetadata({ + options: defaultOptions, + context: defaultContext, + }); + + expect(versionsMetadata).toEqual([vCurrent, v101, v100, vwithSlugs]); + }); + + test('readVersionsMetadata versioned site with includeCurrentVersion=false', () => { + const versionsMetadata = readVersionsMetadata({ + options: {...defaultOptions, includeCurrentVersion: false}, + context: defaultContext, + }); + + expect(versionsMetadata).toEqual([ + // vCurrent removed + v101, + v100, + vwithSlugs, + ]); + }); + + test('readVersionsMetadata versioned site with disableVersioning', () => { + const versionsMetadata = readVersionsMetadata({ + options: {...defaultOptions, disableVersioning: true}, + context: defaultContext, + }); + + expect(versionsMetadata).toEqual([ + {...vCurrent, isLast: true, routePriority: -1, versionPath: '/docs'}, + ]); + }); + + test('readVersionsMetadata versioned site with all versions disabled', () => { + expect(() => + readVersionsMetadata({ + options: { + ...defaultOptions, + includeCurrentVersion: false, + disableVersioning: true, + }, + context: defaultContext, + }), + ).toThrowErrorMatchingInlineSnapshot( + `"It is not possible to use docs without any version. Please check the configuration of these options: includeCurrentVersion=false disableVersioning=true"`, + ); + }); + + test('readVersionsMetadata versioned site with invalid versions.json file', () => { + const mock = jest.spyOn(JSON, 'parse').mockImplementationOnce(() => { + return { + invalid: 'json', + }; + }); + + expect(() => { + readVersionsMetadata({ + options: defaultOptions, + context: defaultContext, + }); + }).toThrowErrorMatchingInlineSnapshot( + `"The versions file should contain an array of versions! Found content={\\"invalid\\":\\"json\\"}"`, + ); + mock.mockRestore(); + }); +}); + +describe('versioned site, pluginId=community', () => { + const versionedSiteDir = path.resolve( + path.join(__dirname, '__fixtures__', 'versioned-site'), + ); + const defaultOptions = { + ...DEFAULT_OPTIONS, + id: 'community', + path: 'community', + routeBasePath: 'communityBasePath', + }; + const defaultContext = { + siteDir: versionedSiteDir, + baseUrl: '/', + }; + + const vCurrent: VersionMetadata = { + docsDirPath: path.join(versionedSiteDir, 'community'), + isLast: false, + routePriority: undefined, + sidebarFilePath: path.join(versionedSiteDir, 'sidebars.json'), + versionLabel: 'Next', + versionName: 'current', + versionPath: '/communityBasePath/next', + }; + + const v100: VersionMetadata = { + docsDirPath: path.join( + versionedSiteDir, + 'community_versioned_docs/version-1.0.0', + ), + isLast: true, + routePriority: -1, + sidebarFilePath: path.join( + versionedSiteDir, + 'community_versioned_sidebars/version-1.0.0-sidebars.json', + ), + versionLabel: '1.0.0', + versionName: '1.0.0', + versionPath: '/communityBasePath', + }; + + test('readVersionsMetadata versioned site (community)', () => { + const versionsMetadata = readVersionsMetadata({ + options: defaultOptions, + context: defaultContext, + }); + + expect(versionsMetadata).toEqual([vCurrent, v100]); + }); + + test('readVersionsMetadata versioned site (community) with includeCurrentVersion=false', () => { + const versionsMetadata = readVersionsMetadata({ + options: {...defaultOptions, includeCurrentVersion: false}, + context: defaultContext, + }); + + expect(versionsMetadata).toEqual([ + // vCurrent removed + v100, + ]); + }); + + test('readVersionsMetadata versioned site (community) with disableVersioning', () => { + const versionsMetadata = readVersionsMetadata({ + options: {...defaultOptions, disableVersioning: true}, + context: defaultContext, + }); + + expect(versionsMetadata).toEqual([ + { + ...vCurrent, + isLast: true, + routePriority: -1, + versionPath: '/communityBasePath', + }, + ]); + }); + + test('readVersionsMetadata versioned site (community) with all versions disabled', () => { + expect(() => + readVersionsMetadata({ + options: { + ...defaultOptions, + includeCurrentVersion: false, + disableVersioning: true, + }, + context: defaultContext, + }), + ).toThrowErrorMatchingInlineSnapshot( + `"It is not possible to use docs without any version. Please check the configuration of these options: includeCurrentVersion=false disableVersioning=true"`, + ); + }); +}); diff --git a/packages/docusaurus-plugin-content-docs/src/index.ts b/packages/docusaurus-plugin-content-docs/src/index.ts index cd788a29461e..2dd69a227d1e 100644 --- a/packages/docusaurus-plugin-content-docs/src/index.ts +++ b/packages/docusaurus-plugin-content-docs/src/index.ts @@ -16,7 +16,7 @@ import {LoadContext, Plugin, RouteConfig} from '@docusaurus/types'; import {loadSidebars, createSidebarsUtils} from './sidebars'; import {readVersionDocs, processDocMetadata} from './docs'; -import {readVersionsMetadata, getVersionedDocsDirPath} from './env'; +import {readVersionsMetadata, getVersionedDocsDirPath} from './versions'; import { PluginOptions, @@ -37,7 +37,7 @@ import {VERSIONS_JSON_FILE} from './constants'; import {OptionsSchema} from './options'; import {flatten, keyBy, compact} from 'lodash'; import {toGlobalDataVersion} from './globalData'; -import {toVersionMetadataProp} from './versionMetadataProp'; +import {toVersionMetadataProp} from './props'; export default function pluginContentDocs( context: LoadContext, diff --git a/packages/docusaurus-plugin-content-docs/src/options.ts b/packages/docusaurus-plugin-content-docs/src/options.ts index c7464f2c274c..e036651dd700 100644 --- a/packages/docusaurus-plugin-content-docs/src/options.ts +++ b/packages/docusaurus-plugin-content-docs/src/options.ts @@ -22,7 +22,7 @@ export const DEFAULT_OPTIONS: Omit = { routeBasePath: 'docs', // URL Route. homePageId: undefined, // TODO remove soon, deprecated include: ['**/*.{md,mdx}'], // Extensions to include. - sidebarPath: '', // Path to sidebar configuration for showing a list of markdown pages. + sidebarPath: 'sidebars.json', // Path to sidebar configuration for showing a list of markdown pages. docLayoutComponent: '@theme/DocPage', docItemComponent: '@theme/DocItem', remarkPlugins: [], diff --git a/packages/docusaurus-plugin-content-docs/src/versionMetadataProp.ts b/packages/docusaurus-plugin-content-docs/src/props.ts similarity index 100% rename from packages/docusaurus-plugin-content-docs/src/versionMetadataProp.ts rename to packages/docusaurus-plugin-content-docs/src/props.ts diff --git a/packages/docusaurus-plugin-content-docs/src/env.ts b/packages/docusaurus-plugin-content-docs/src/versions.ts similarity index 90% rename from packages/docusaurus-plugin-content-docs/src/env.ts rename to packages/docusaurus-plugin-content-docs/src/versions.ts index 18f3a537edd0..cc61ab3b30e8 100644 --- a/packages/docusaurus-plugin-content-docs/src/env.ts +++ b/packages/docusaurus-plugin-content-docs/src/versions.ts @@ -66,7 +66,7 @@ function ensureValidVersionArray( ): asserts versionArray is string[] { if (!(versionArray instanceof Array)) { throw new Error( - `The versions file should contain an array of versions! ${JSON.stringify( + `The versions file should contain an array of versions! Found content=${JSON.stringify( versionArray, )}`, ); @@ -95,9 +95,15 @@ function readVersionNames( 'id' | 'disableVersioning' | 'includeCurrentVersion' >, ): string[] { - const versions = options.disableVersioning - ? [] - : readVersionsFile(siteDir, options.id) ?? []; + const versionFileContent = readVersionsFile(siteDir, options.id); + + if (!versionFileContent && options.disableVersioning) { + throw new Error( + `Docs: using disableVersioning=${options.disableVersioning} option on a non-versioned site does not make sense`, + ); + } + + const versions = options.disableVersioning ? [] : versionFileContent ?? []; // We add the current version at the beginning, unless // - user don't want to @@ -111,7 +117,7 @@ function readVersionNames( if (versions.length === 0) { throw new Error( - "It is not possible to use docs without any version. You shouldn't use 'includeCurrentVersion: false' on an unversioned site", + `It is not possible to use docs without any version. Please check the configuration of these options: includeCurrentVersion=${options.includeCurrentVersion} disableVersioning=${options.disableVersioning}`, ); } @@ -215,7 +221,7 @@ function checkVersionMetadataPaths({ // "last version" is not a very good concept nor api surface function getLastVersionName(versionNames: string[]) { if (versionNames.length === 1) { - return versionNames[1]; + return versionNames[0]; } else { return versionNames.filter( (versionName) => versionName !== CURRENT_VERSION_NAME, From de1830e92879347003d89559869eb8861e9a4832 Mon Sep 17 00:00:00 2001 From: slorber Date: Thu, 13 Aug 2020 18:49:37 +0200 Subject: [PATCH 24/30] fix docs metadata tests --- .../package.json | 5 +- .../src/__tests__/cli.test.ts | 2 +- .../src/__tests__/docs.test.ts | 413 ++++++++++-------- .../src/__tests__/index.test.ts | 4 +- .../src/__tests__/options.test.ts | 1 - .../docusaurus-plugin-content-docs/src/cli.ts | 2 +- .../src/docs.ts | 40 +- yarn.lock | 10 + 8 files changed, 284 insertions(+), 193 deletions(-) diff --git a/packages/docusaurus-plugin-content-docs/package.json b/packages/docusaurus-plugin-content-docs/package.json index b009540e134c..b24a95022e48 100644 --- a/packages/docusaurus-plugin-content-docs/package.json +++ b/packages/docusaurus-plugin-content-docs/package.json @@ -14,6 +14,7 @@ "devDependencies": { "@docusaurus/module-type-aliases": "^2.0.0-alpha.61", "@types/hapi__joi": "^17.1.2", + "@types/picomatch": "^2.2.1", "commander": "^5.0.0", "picomatch": "^2.1.1" }, @@ -37,7 +38,9 @@ "lodash.pickby": "^4.6.0", "lodash.sortby": "^4.6.0", "remark-admonitions": "^1.2.1", - "shelljs": "^0.8.4" + "shelljs": "^0.8.4", + "utility-types": "^3.10.0", + "webpack": "^4.41.2" }, "peerDependencies": { "react": "^16.8.4", diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/cli.test.ts b/packages/docusaurus-plugin-content-docs/src/__tests__/cli.test.ts index 217eb6571423..c7d93d47b301 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/cli.test.ts +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/cli.test.ts @@ -13,7 +13,7 @@ import { getVersionedDocsDirPath, getVersionsFilePath, getVersionedSidebarsDirPath, -} from '../env'; +} from '../versions'; import {DEFAULT_PLUGIN_ID} from '@docusaurus/core/lib/constants'; const fixtureDir = path.join(__dirname, '__fixtures__'); diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/docs.test.ts b/packages/docusaurus-plugin-content-docs/src/__tests__/docs.test.ts index 46bf99fb4972..df1028a97f17 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/docs.test.ts +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/docs.test.ts @@ -7,78 +7,154 @@ import path from 'path'; import {loadContext} from '@docusaurus/core/src/server/index'; -import {processDocMetadata} from '../docs'; -import loadEnv from '../env'; -import {DocMetadataBase, Env, MetadataOptions} from '../types'; +import {processDocMetadata, readVersionDocs, readDocFile} from '../docs'; +import {readVersionsMetadata} from '../versions'; +import { + DocFile, + DocMetadataBase, + MetadataOptions, + VersionMetadata, +} from '../types'; import {LoadContext} from '@docusaurus/types'; import {DEFAULT_PLUGIN_ID} from '@docusaurus/core/lib/constants'; +import {DEFAULT_OPTIONS} from '../options'; +import {Optional} from 'utility-types'; const fixtureDir = path.join(__dirname, '__fixtures__'); -function createTestHelpers({ +const createFakeDocFile = ({ + source, + frontmatter = {}, + markdown = 'some markdown content', +}: { + source: string; + frontmatter?: Record; + markdown?: string; +}): DocFile => { + const content = `--- +${Object.entries(frontmatter) + .map(([key, value]) => `${key}: ${value}`) + .join('\n')} +--- +${markdown} +`; + return { + source, + content, + lastUpdate: {}, + }; +}; + +function createTestUtils({ siteDir, context, - env, + versionMetadata, options, }: { siteDir: string; context: LoadContext; - env: Env; + versionMetadata: VersionMetadata; options: MetadataOptions; }) { + async function readDoc(docFileSource: string) { + return readDocFile(versionMetadata.docsDirPath, docFileSource, options); + } + function processDocFile(docFile: DocFile) { + return processDocMetadata({ + docFile, + versionMetadata, + options, + context, + }); + } async function testMeta( - refDir: string, - source: string, - expectedMetadata: Omit, + docFileSource: string, + expectedMetadata: Optional< + DocMetadataBase, + 'source' | 'lastUpdatedBy' | 'lastUpdatedAt' | 'sidebar_label' | 'editUrl' + >, ) { + const docFile = await readDoc(docFileSource); const metadata = await processDocMetadata({ - source, - docsDir: refDir, + docFile, + versionMetadata, context, options, - env, }); expect(metadata).toEqual({ + lastUpdatedBy: undefined, + lastUpdatedAt: undefined, + sidebar_label: undefined, + editUrl: undefined, + source: path.join( + '@site', + path.relative(siteDir, versionMetadata.docsDirPath), + docFileSource, + ), ...expectedMetadata, - source: path.join('@site', path.relative(siteDir, refDir), source), }); } - async function testSlug( - refDir: string, - source: string, - expectedPermalink: string, - ) { + async function testSlug(docFileSource: string, expectedPermalink: string) { + const docFile = await readDoc(docFileSource); const metadata = await processDocMetadata({ - source, - docsDir: refDir, + docFile, + versionMetadata, context, options, - env, }); expect(metadata.permalink).toEqual(expectedPermalink); } - return {testMeta, testSlug}; + return {processDocFile, testMeta, testSlug}; } describe('simple site', () => { const siteDir = path.join(fixtureDir, 'simple-site'); const context = loadContext(siteDir); - const routeBasePath = 'docs'; - const docsDir = path.resolve(siteDir, routeBasePath); - const env = loadEnv(siteDir, DEFAULT_PLUGIN_ID); - const options = {routeBasePath}; + const options = { + id: DEFAULT_PLUGIN_ID, + ...DEFAULT_OPTIONS, + }; + const versionsMetadata = readVersionsMetadata({ + context, + options: { + id: DEFAULT_PLUGIN_ID, + ...DEFAULT_OPTIONS, + }, + }); + expect(versionsMetadata.length).toEqual(1); + const [currentVersion] = versionsMetadata; - const {testMeta, testSlug} = createTestHelpers({ + const defaultTestUtils = createTestUtils({ siteDir, context, options, - env, + versionMetadata: currentVersion, + }); + + test('readVersionDocs', async () => { + const docs = await readVersionDocs(currentVersion, options); + expect(docs.map((doc) => doc.source)).toMatchObject([ + 'hello.md', + 'ipsum.md', + 'lorem.md', + 'rootAbsoluteSlug.md', + 'rootRelativeSlug.md', + 'rootResolvedSlug.md', + 'rootTryToEscapeSlug.md', + 'foo/bar.md', + 'foo/baz.md', + 'slugs/absoluteSlug.md', + 'slugs/relativeSlug.md', + 'slugs/resolvedSlug.md', + 'slugs/tryToEscapeSlug.md', + ]); }); test('normal docs', async () => { - await testMeta(docsDir, path.join('foo', 'bar.md'), { + await defaultTestUtils.testMeta(path.join('foo', 'bar.md'), { + version: 'current', id: 'foo/bar', unversionedId: 'foo/bar', isDocsHomePage: false, @@ -87,7 +163,8 @@ describe('simple site', () => { title: 'Bar', description: 'This is custom description', }); - await testMeta(docsDir, path.join('hello.md'), { + await defaultTestUtils.testMeta(path.join('hello.md'), { + version: 'current', id: 'hello', unversionedId: 'hello', isDocsHomePage: false, @@ -99,17 +176,15 @@ describe('simple site', () => { }); test('homePageId doc', async () => { - const {testMeta: testMetaLocal} = createTestHelpers({ + const testUtilsLocal = createTestUtils({ siteDir, - options: { - routeBasePath, - homePageId: 'hello', - }, context, - env, + options: {...options, homePageId: 'hello'}, + versionMetadata: currentVersion, }); - await testMetaLocal(docsDir, path.join('hello.md'), { + await testUtilsLocal.testMeta(path.join('hello.md'), { + version: 'current', id: 'hello', unversionedId: 'hello', isDocsHomePage: true, @@ -121,17 +196,15 @@ describe('simple site', () => { }); test('homePageId doc nested', async () => { - const {testMeta: testMetaLocal} = createTestHelpers({ + const testUtilsLocal = createTestUtils({ siteDir, - options: { - routeBasePath, - homePageId: 'foo/bar', - }, context, - env, + options: {...options, homePageId: 'foo/bar'}, + versionMetadata: currentVersion, }); - await testMetaLocal(docsDir, path.join('foo', 'bar.md'), { + await testUtilsLocal.testMeta(path.join('foo', 'bar.md'), { + version: 'current', id: 'foo/bar', unversionedId: 'foo/bar', isDocsHomePage: true, @@ -143,17 +216,18 @@ describe('simple site', () => { }); test('docs with editUrl', async () => { - const {testMeta: testMetaLocal} = createTestHelpers({ + const testUtilsLocal = createTestUtils({ siteDir, + context, options: { - routeBasePath, + ...options, editUrl: 'https://github.com/facebook/docusaurus/edit/master/website', }, - context, - env, + versionMetadata: currentVersion, }); - await testMetaLocal(docsDir, path.join('foo', 'baz.md'), { + await testUtilsLocal.testMeta(path.join('foo', 'baz.md'), { + version: 'current', id: 'foo/baz', unversionedId: 'foo/baz', isDocsHomePage: false, @@ -167,7 +241,8 @@ describe('simple site', () => { }); test('docs with custom editUrl & unrelated frontmatter', async () => { - await testMeta(docsDir, 'lorem.md', { + await defaultTestUtils.testMeta('lorem.md', { + version: 'current', id: 'lorem', unversionedId: 'lorem', isDocsHomePage: false, @@ -180,18 +255,19 @@ describe('simple site', () => { }); test('docs with last update time and author', async () => { - const {testMeta: testMetaLocal} = createTestHelpers({ + const testUtilsLocal = createTestUtils({ siteDir, + context, options: { - routeBasePath, + ...options, showLastUpdateAuthor: true, showLastUpdateTime: true, }, - context, - env, + versionMetadata: currentVersion, }); - await testMetaLocal(docsDir, 'lorem.md', { + await testUtilsLocal.testMeta('lorem.md', { + version: 'current', id: 'lorem', unversionedId: 'lorem', isDocsHomePage: false, @@ -205,110 +281,78 @@ describe('simple site', () => { }); }); - test('docs with null custom_edit_url', async () => { - const {testMeta: testMetaLocal} = createTestHelpers({ - siteDir, - options: { - routeBasePath, - showLastUpdateAuthor: true, - showLastUpdateTime: true, - }, - context, - env, - }); - - await testMetaLocal(docsDir, 'ipsum.md', { - id: 'ipsum', - unversionedId: 'ipsum', - isDocsHomePage: false, - permalink: '/docs/ipsum', - slug: '/ipsum', - title: 'ipsum', - editUrl: null, - description: 'Lorem ipsum.', - lastUpdatedAt: 1539502055, - lastUpdatedBy: 'Author', - }); - }); - test('docs with slugs', async () => { - await testSlug( - docsDir, + await defaultTestUtils.testSlug( path.join('rootRelativeSlug.md'), '/docs/rootRelativeSlug', ); - await testSlug( - docsDir, + await defaultTestUtils.testSlug( path.join('rootAbsoluteSlug.md'), '/docs/rootAbsoluteSlug', ); - await testSlug( - docsDir, + await defaultTestUtils.testSlug( path.join('rootResolvedSlug.md'), '/docs/hey/rootResolvedSlug', ); - await testSlug( - docsDir, + await defaultTestUtils.testSlug( path.join('rootTryToEscapeSlug.md'), '/docs/rootTryToEscapeSlug', ); - await testSlug( - docsDir, + await defaultTestUtils.testSlug( path.join('slugs', 'absoluteSlug.md'), '/docs/absoluteSlug', ); - await testSlug( - docsDir, + await defaultTestUtils.testSlug( path.join('slugs', 'relativeSlug.md'), '/docs/slugs/relativeSlug', ); - await testSlug( - docsDir, + await defaultTestUtils.testSlug( path.join('slugs', 'resolvedSlug.md'), '/docs/slugs/hey/resolvedSlug', ); - await testSlug( - docsDir, + await defaultTestUtils.testSlug( path.join('slugs', 'tryToEscapeSlug.md'), '/docs/tryToEscapeSlug', ); }); - test('docs with invalid id', async () => { - const badSiteDir = path.join(fixtureDir, 'bad-id-site'); - - await expect( - processDocMetadata({ - source: 'invalid-id.md', - docsDir: path.join(badSiteDir, 'docs'), - context, - options: { - routeBasePath, - }, - env, - }), - ).rejects.toThrowErrorMatchingInlineSnapshot( - `"Document id cannot include \\"/\\"."`, + test('docs with invalid id', () => { + expect(() => { + defaultTestUtils.processDocFile( + createFakeDocFile({ + source: 'some/fake/path', + frontmatter: { + id: 'Hello/world', + }, + }), + ); + }).toThrowErrorMatchingInlineSnapshot( + `"Document id [Hello/world] cannot include \\"/\\"."`, ); }); test('docs with slug on doc home', async () => { - const badSiteDir = path.join(fixtureDir, 'bad-slug-on-doc-home-site'); - - await expect( - processDocMetadata({ - source: 'docWithSlug.md', - docsDir: path.join(badSiteDir, 'docs'), - context, - options: { - routeBasePath, - homePageId: 'docWithSlug', - }, - env, - }), - ).rejects.toThrowErrorMatchingInlineSnapshot( - `"The docs homepage (homePageId=docWithSlug) is not allowed to have a frontmatter slug=docWithSlug.html => you have to chooser either homePageId or slug, not both"`, + const testUtilsLocal = createTestUtils({ + siteDir, + context, + options: { + ...options, + homePageId: 'homePageId', + }, + versionMetadata: currentVersion, + }); + expect(() => { + testUtilsLocal.processDocFile( + createFakeDocFile({ + source: 'homePageId', + frontmatter: { + slug: '/x/y', + }, + }), + ); + }).toThrowErrorMatchingInlineSnapshot( + `"The docs homepage (homePageId=homePageId) is not allowed to have a frontmatter slug=/x/y => you have to chooser either homePageId or slug, not both"`, ); }); }); @@ -316,21 +360,54 @@ describe('simple site', () => { describe('versioned site', () => { const siteDir = path.join(fixtureDir, 'versioned-site'); const context = loadContext(siteDir); - const routeBasePath = 'docs'; - const docsDir = path.resolve(siteDir, routeBasePath); - const env = loadEnv(siteDir, DEFAULT_PLUGIN_ID); - const {docsDir: versionedDir} = env.versioning; - const options = {routeBasePath}; + const options = { + id: DEFAULT_PLUGIN_ID, + ...DEFAULT_OPTIONS, + }; + const versionsMetadata = readVersionsMetadata({ + context, + options: { + id: DEFAULT_PLUGIN_ID, + ...DEFAULT_OPTIONS, + }, + }); + expect(versionsMetadata.length).toEqual(4); + const [ + currentVersion, + version101, + version100, + versionWithSlugs, + ] = versionsMetadata; + + const currentVersionTestUtils = createTestUtils({ + siteDir, + context, + options, + versionMetadata: currentVersion, + }); + const version101TestUtils = createTestUtils({ + siteDir, + context, + options, + versionMetadata: version101, + }); + + const version100TestUtils = createTestUtils({ + siteDir, + context, + options, + versionMetadata: version100, + }); - const {testMeta, testSlug} = createTestHelpers({ + const versionWithSlugsTestUtils = createTestUtils({ siteDir, context, options, - env, + versionMetadata: versionWithSlugs, }); test('next docs', async () => { - await testMeta(docsDir, path.join('foo', 'bar.md'), { + await currentVersionTestUtils.testMeta(path.join('foo', 'bar.md'), { id: 'foo/bar', unversionedId: 'foo/bar', isDocsHomePage: false, @@ -338,9 +415,9 @@ describe('versioned site', () => { slug: '/foo/barSlug', title: 'bar', description: 'This is next version of bar.', - version: 'next', + version: 'current', }); - await testMeta(docsDir, path.join('hello.md'), { + await currentVersionTestUtils.testMeta(path.join('hello.md'), { id: 'hello', unversionedId: 'hello', isDocsHomePage: false, @@ -348,12 +425,12 @@ describe('versioned site', () => { slug: '/hello', title: 'hello', description: 'Hello next !', - version: 'next', + version: 'current', }); }); test('versioned docs', async () => { - await testMeta(versionedDir, path.join('version-1.0.0', 'foo', 'bar.md'), { + await version100TestUtils.testMeta(path.join('foo', 'bar.md'), { id: 'version-1.0.0/foo/bar', unversionedId: 'foo/bar', isDocsHomePage: false, @@ -363,7 +440,7 @@ describe('versioned site', () => { description: 'Bar 1.0.0 !', version: '1.0.0', }); - await testMeta(versionedDir, path.join('version-1.0.0', 'hello.md'), { + await version100TestUtils.testMeta(path.join('hello.md'), { id: 'version-1.0.0/hello', unversionedId: 'hello', isDocsHomePage: false, @@ -373,7 +450,7 @@ describe('versioned site', () => { description: 'Hello 1.0.0 !', version: '1.0.0', }); - await testMeta(versionedDir, path.join('version-1.0.1', 'foo', 'bar.md'), { + await version101TestUtils.testMeta(path.join('foo', 'bar.md'), { id: 'version-1.0.1/foo/bar', unversionedId: 'foo/bar', isDocsHomePage: false, @@ -383,7 +460,7 @@ describe('versioned site', () => { description: 'Bar 1.0.1 !', version: '1.0.1', }); - await testMeta(versionedDir, path.join('version-1.0.1', 'hello.md'), { + await version101TestUtils.testMeta(path.join('hello.md'), { id: 'version-1.0.1/hello', unversionedId: 'hello', isDocsHomePage: false, @@ -396,68 +473,56 @@ describe('versioned site', () => { }); test('next doc slugs', async () => { - await testSlug( - docsDir, + await currentVersionTestUtils.testSlug( path.join('slugs', 'absoluteSlug.md'), '/docs/next/absoluteSlug', ); - await testSlug( - docsDir, + await currentVersionTestUtils.testSlug( path.join('slugs', 'relativeSlug.md'), '/docs/next/slugs/relativeSlug', ); - await testSlug( - docsDir, + await currentVersionTestUtils.testSlug( path.join('slugs', 'resolvedSlug.md'), '/docs/next/slugs/hey/resolvedSlug', ); - await testSlug( - docsDir, + await currentVersionTestUtils.testSlug( path.join('slugs', 'tryToEscapeSlug.md'), '/docs/next/tryToEscapeSlug', ); }); test('versioned doc slugs', async () => { - await testSlug( - versionedDir, - path.join('version-withSlugs', 'rootAbsoluteSlug.md'), + await versionWithSlugsTestUtils.testSlug( + path.join('rootAbsoluteSlug.md'), '/docs/withSlugs/rootAbsoluteSlug', ); - await testSlug( - versionedDir, - path.join('version-withSlugs', 'rootRelativeSlug.md'), + await versionWithSlugsTestUtils.testSlug( + path.join('rootRelativeSlug.md'), '/docs/withSlugs/rootRelativeSlug', ); - await testSlug( - versionedDir, - path.join('version-withSlugs', 'rootResolvedSlug.md'), + await versionWithSlugsTestUtils.testSlug( + path.join('rootResolvedSlug.md'), '/docs/withSlugs/hey/rootResolvedSlug', ); - await testSlug( - versionedDir, - path.join('version-withSlugs', 'rootTryToEscapeSlug.md'), + await versionWithSlugsTestUtils.testSlug( + path.join('rootTryToEscapeSlug.md'), '/docs/withSlugs/rootTryToEscapeSlug', ); - await testSlug( - versionedDir, - path.join('version-withSlugs', 'slugs', 'absoluteSlug.md'), + await versionWithSlugsTestUtils.testSlug( + path.join('slugs', 'absoluteSlug.md'), '/docs/withSlugs/absoluteSlug', ); - await testSlug( - versionedDir, - path.join('version-withSlugs', 'slugs', 'relativeSlug.md'), + await versionWithSlugsTestUtils.testSlug( + path.join('slugs', 'relativeSlug.md'), '/docs/withSlugs/slugs/relativeSlug', ); - await testSlug( - versionedDir, - path.join('version-withSlugs', 'slugs', 'resolvedSlug.md'), + await versionWithSlugsTestUtils.testSlug( + path.join('slugs', 'resolvedSlug.md'), '/docs/withSlugs/slugs/hey/resolvedSlug', ); - await testSlug( - versionedDir, - path.join('version-withSlugs', 'slugs', 'tryToEscapeSlug.md'), + await versionWithSlugsTestUtils.testSlug( + path.join('slugs', 'tryToEscapeSlug.md'), '/docs/withSlugs/tryToEscapeSlug', ); }); diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/index.test.ts b/packages/docusaurus-plugin-content-docs/src/__tests__/index.test.ts index 6aae038132e3..113481521cbd 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/index.test.ts +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/index.test.ts @@ -11,7 +11,6 @@ import {isMatch} from 'picomatch'; import commander from 'commander'; import fs from 'fs-extra'; import pluginContentDocs from '../index'; -import loadEnv from '../env'; import {loadContext} from '@docusaurus/core/src/server/index'; import {applyConfigureWebpack} from '@docusaurus/core/src/webpack/utils'; import {RouteConfig} from '@docusaurus/types'; @@ -23,6 +22,9 @@ import * as cliDocs from '../cli'; import {OptionsSchema} from '../options'; import {normalizePluginOptions} from '@docusaurus/utils-validation'; +// TODO remove +function loadEnv(..._args: any[]): any {} + const createFakeActions = (contentDir: string) => { const routeConfigs: RouteConfig[] = []; const dataContainer: any = {}; diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/options.test.ts b/packages/docusaurus-plugin-content-docs/src/__tests__/options.test.ts index ef23a1c84987..d065896f1c32 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/options.test.ts +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/options.test.ts @@ -21,7 +21,6 @@ describe('normalizeDocsPluginOptions', () => { test('should accept correctly defined user options', async () => { const userOptions = { - id: 'default', path: 'my-docs', // Path to data on filesystem, relative to site dir. routeBasePath: 'my-docs', // URL Route. homePageId: 'home', // Document id for docs home page. diff --git a/packages/docusaurus-plugin-content-docs/src/cli.ts b/packages/docusaurus-plugin-content-docs/src/cli.ts index 15e16eeb4dd3..c0ea5546615b 100644 --- a/packages/docusaurus-plugin-content-docs/src/cli.ts +++ b/packages/docusaurus-plugin-content-docs/src/cli.ts @@ -9,7 +9,7 @@ import { getVersionsFilePath, getVersionedDocsDirPath, getVersionedSidebarsDirPath, -} from './env'; +} from './versions'; import fs from 'fs-extra'; import path from 'path'; import {Sidebars, PathOptions, SidebarItem} from './types'; diff --git a/packages/docusaurus-plugin-content-docs/src/docs.ts b/packages/docusaurus-plugin-content-docs/src/docs.ts index 762c16c7a7ba..e10e19bc7a52 100644 --- a/packages/docusaurus-plugin-content-docs/src/docs.ts +++ b/packages/docusaurus-plugin-content-docs/src/docs.ts @@ -28,9 +28,14 @@ import getSlug from './slug'; import {CURRENT_VERSION_NAME} from './constants'; import globby from 'globby'; +type LastUpdateOptions = Pick< + PluginOptions, + 'showLastUpdateAuthor' | 'showLastUpdateTime' +>; + async function readLastUpdateData( filePath: string, - options: Pick, + options: LastUpdateOptions, ): Promise { const {showLastUpdateAuthor, showLastUpdateTime} = options; if (showLastUpdateAuthor || showLastUpdateTime) { @@ -55,6 +60,19 @@ async function readLastUpdateData( return {}; } +export async function readDocFile( + docsDirPath: string, + source: string, + options: LastUpdateOptions, +): Promise { + const filePath = path.join(docsDirPath, source); + const [content, lastUpdate] = await Promise.all([ + fs.readFile(filePath, 'utf-8'), + readLastUpdateData(filePath, options), + ]); + return {source, content, lastUpdate}; +} + export async function readVersionDocs( versionMetadata: VersionMetadata, options: Pick< @@ -65,17 +83,11 @@ export async function readVersionDocs( const sources = await globby(options.include, { cwd: versionMetadata.docsDirPath, }); - - async function readDoc(source: string): Promise { - const filePath = path.join(versionMetadata.docsDirPath, source); - const [content, lastUpdate] = await Promise.all([ - fs.readFile(filePath, 'utf-8'), - readLastUpdateData(filePath, options), - ]); - return {source, content, lastUpdate}; - } - - return Promise.all(sources.map(readDoc)); + return Promise.all( + sources.map((source) => + readDocFile(versionMetadata.docsDirPath, source, options), + ), + ); } export function processDocMetadata({ @@ -106,7 +118,7 @@ export function processDocMetadata({ const baseID: string = frontMatter.id || path.basename(source, path.extname(source)); if (baseID.includes('/')) { - throw new Error(`Document id [${baseID}]cannot include "/".`); + throw new Error(`Document id [${baseID}] cannot include "/".`); } // TODO legacy retrocompatibility @@ -124,7 +136,7 @@ export function processDocMetadata({ // TODO legacy composite id, requires a breaking change to modify this const id = `${versionIdPart}${dirNameIdPart}${baseID}`; - const unversionedId = baseID; + const unversionedId = `${dirNameIdPart}${baseID}`; // TODO remove soon, deprecated homePageId const isDocsHomePage = unversionedId === (homePageId ?? '_index'); diff --git a/yarn.lock b/yarn.lock index 3a4f32a547e6..a661d15170c5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4067,6 +4067,11 @@ resolved "https://registry.yarnpkg.com/@types/parse5/-/parse5-5.0.3.tgz#e7b5aebbac150f8b5fdd4a46e7f0bd8e65e19109" integrity sha512-kUNnecmtkunAoQ3CnjmMkzNU/gtxG8guhi+Fk2U/kOpIKjIMKnXGp4IJCgQJrXSgMsWYimYG4TGjz/UzbGEBTw== +"@types/picomatch@^2.2.1": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@types/picomatch/-/picomatch-2.2.1.tgz#f9e5a5e6ad03996832975ab7eadfa35791ca2a8f" + integrity sha512-26/tQcDmJXYHiaWAAIjnTVL5nwrT+IVaqFZIbBImAuKk/r/j1r/1hmZ7uaOzG6IknqP3QHcNNQ6QO8Vp28lUoA== + "@types/prettier@^1.19.0": version "1.19.1" resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-1.19.1.tgz#33509849f8e679e4add158959fdb086440e9553f" @@ -21191,6 +21196,11 @@ utila@^0.4.0, utila@~0.4: resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c" integrity sha1-ihagXURWV6Oupe7MWxKk+lN5dyw= +utility-types@^3.10.0: + version "3.10.0" + resolved "https://registry.yarnpkg.com/utility-types/-/utility-types-3.10.0.tgz#ea4148f9a741015f05ed74fd615e1d20e6bed82b" + integrity sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg== + utils-merge@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" From d30b88f812b289908bd4e04dc45bfbf546b75643 Mon Sep 17 00:00:00 2001 From: slorber Date: Fri, 14 Aug 2020 16:34:31 +0200 Subject: [PATCH 25/30] fix docs tests --- .../__fixtures__/empty-site/sidebars.json | 1 + .../version-withSlugs-sidebars.json | 7 +- .../__snapshots__/index.test.ts.snap | 1171 +++++++++++++++-- .../src/__tests__/cli.test.ts | 64 +- .../src/__tests__/index.test.ts | 330 ++--- .../docusaurus-plugin-content-docs/src/cli.ts | 2 +- .../src/index.ts | 13 +- .../src/props.ts | 2 +- .../src/sidebars.ts | 24 +- 9 files changed, 1328 insertions(+), 286 deletions(-) create mode 100644 packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/empty-site/sidebars.json diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/empty-site/sidebars.json b/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/empty-site/sidebars.json new file mode 100644 index 000000000000..0967ef424bce --- /dev/null +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/empty-site/sidebars.json @@ -0,0 +1 @@ +{} diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/versioned-site/versioned_sidebars/version-withSlugs-sidebars.json b/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/versioned-site/versioned_sidebars/version-withSlugs-sidebars.json index 98a2d8a4f74b..ca469922c09c 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/versioned-site/versioned_sidebars/version-withSlugs-sidebars.json +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/versioned-site/versioned_sidebars/version-withSlugs-sidebars.json @@ -1,10 +1,5 @@ { "version-1.0.1/docs": { - "Test": [ - "version-1.0.1/foo/bar" - ], - "Guides": [ - "version-1.0.1/hello" - ] + "Test": ["version-withSlugs/rootAbsoluteSlug"] } } diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/__snapshots__/index.test.ts.snap b/packages/docusaurus-plugin-content-docs/src/__tests__/__snapshots__/index.test.ts.snap index 87bb00a0d64a..8a93f780e515 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/__snapshots__/index.test.ts.snap +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/__snapshots__/index.test.ts.snap @@ -10,14 +10,12 @@ Object { "collapsed": true, "items": Array [ Object { - "href": "/docs/foo/bar", - "label": "Bar", - "type": "link", + "id": "foo/bar", + "type": "doc", }, Object { - "href": "/docs/foo/bazSlug.html", - "label": "baz", - "type": "link", + "id": "foo/baz", + "type": "doc", }, ], "label": "foo", @@ -29,9 +27,8 @@ Object { "type": "link", }, Object { - "href": "/docs/", - "label": "Hello, World !", - "type": "link", + "id": "hello", + "type": "ref", }, ], "label": "Test", @@ -41,9 +38,8 @@ Object { "collapsed": true, "items": Array [ Object { - "href": "/docs/", - "label": "Hello, World !", - "type": "link", + "id": "hello", + "type": "doc", }, ], "label": "Guides", @@ -57,7 +53,6 @@ exports[`simple website content 2`] = ` Object { "pluginName": Object { "pluginId": Object { - "latestVersionName": null, "path": "/docs", "versions": Array [ Object { @@ -115,8 +110,10 @@ Object { "path": "/docs/tryToEscapeSlug", }, ], + "isLast": true, + "label": "Next", "mainDocId": "hello", - "name": null, + "name": "current", "path": "/docs", }, ], @@ -127,64 +124,6 @@ Object { exports[`simple website content: data 1`] = ` Object { - "docs-route-ff2.json": "{ - \\"docsSidebars\\": { - \\"docs\\": [ - { - \\"collapsed\\": true, - \\"type\\": \\"category\\", - \\"label\\": \\"Test\\", - \\"items\\": [ - { - \\"collapsed\\": true, - \\"type\\": \\"category\\", - \\"label\\": \\"foo\\", - \\"items\\": [ - { - \\"type\\": \\"link\\", - \\"label\\": \\"Bar\\", - \\"href\\": \\"/docs/foo/bar\\" - }, - { - \\"type\\": \\"link\\", - \\"label\\": \\"baz\\", - \\"href\\": \\"/docs/foo/bazSlug.html\\" - } - ] - }, - { - \\"type\\": \\"link\\", - \\"label\\": \\"Github\\", - \\"href\\": \\"https://github.com\\" - }, - { - \\"type\\": \\"link\\", - \\"label\\": \\"Hello, World !\\", - \\"href\\": \\"/docs/\\" - } - ] - }, - { - \\"collapsed\\": true, - \\"type\\": \\"category\\", - \\"label\\": \\"Guides\\", - \\"items\\": [ - { - \\"type\\": \\"link\\", - \\"label\\": \\"Hello, World !\\", - \\"href\\": \\"/docs/\\" - } - ] - } - ] - }, - \\"permalinkToSidebar\\": { - \\"/docs/\\": \\"docs\\", - \\"/docs/foo/bar\\": \\"docs\\", - \\"/docs/foo/bazSlug.html\\": \\"docs\\" - }, - \\"version\\": null -}", "site-docs-foo-bar-md-8c2.json": "{ \\"unversionedId\\": \\"foo/bar\\", \\"id\\": \\"foo/bar\\", @@ -194,6 +133,7 @@ Object { \\"source\\": \\"@site/docs/foo/bar.md\\", \\"slug\\": \\"/foo/bar\\", \\"permalink\\": \\"/docs/foo/bar\\", + \\"version\\": \\"current\\", \\"sidebar\\": \\"docs\\", \\"next\\": { \\"title\\": \\"baz\\", @@ -209,6 +149,7 @@ Object { \\"source\\": \\"@site/docs/foo/baz.md\\", \\"slug\\": \\"/foo/bazSlug.html\\", \\"permalink\\": \\"/docs/foo/bazSlug.html\\", + \\"version\\": \\"current\\", \\"sidebar\\": \\"docs\\", \\"previous\\": { \\"title\\": \\"Bar\\", @@ -228,6 +169,7 @@ Object { \\"source\\": \\"@site/docs/hello.md\\", \\"slug\\": \\"/\\", \\"permalink\\": \\"/docs/\\", + \\"version\\": \\"current\\", \\"sidebar\\": \\"docs\\", \\"previous\\": { \\"title\\": \\"baz\\", @@ -243,7 +185,8 @@ Object { \\"source\\": \\"@site/docs/ipsum.md\\", \\"slug\\": \\"/ipsum\\", \\"permalink\\": \\"/docs/ipsum\\", - \\"editUrl\\": null + \\"editUrl\\": null, + \\"version\\": \\"current\\" }", "site-docs-lorem-md-b27.json": "{ \\"unversionedId\\": \\"lorem\\", @@ -254,7 +197,8 @@ Object { \\"source\\": \\"@site/docs/lorem.md\\", \\"slug\\": \\"/lorem\\", \\"permalink\\": \\"/docs/lorem\\", - \\"editUrl\\": \\"https://github.com/customUrl/docs/lorem.md\\" + \\"editUrl\\": \\"https://github.com/customUrl/docs/lorem.md\\", + \\"version\\": \\"current\\" }", "site-docs-root-absolute-slug-md-db5.json": "{ \\"unversionedId\\": \\"rootAbsoluteSlug\\", @@ -264,7 +208,8 @@ Object { \\"description\\": \\"Lorem\\", \\"source\\": \\"@site/docs/rootAbsoluteSlug.md\\", \\"slug\\": \\"/rootAbsoluteSlug\\", - \\"permalink\\": \\"/docs/rootAbsoluteSlug\\" + \\"permalink\\": \\"/docs/rootAbsoluteSlug\\", + \\"version\\": \\"current\\" }", "site-docs-root-relative-slug-md-3dd.json": "{ \\"unversionedId\\": \\"rootRelativeSlug\\", @@ -274,7 +219,8 @@ Object { \\"description\\": \\"Lorem\\", \\"source\\": \\"@site/docs/rootRelativeSlug.md\\", \\"slug\\": \\"/rootRelativeSlug\\", - \\"permalink\\": \\"/docs/rootRelativeSlug\\" + \\"permalink\\": \\"/docs/rootRelativeSlug\\", + \\"version\\": \\"current\\" }", "site-docs-root-resolved-slug-md-4d1.json": "{ \\"unversionedId\\": \\"rootResolvedSlug\\", @@ -284,7 +230,8 @@ Object { \\"description\\": \\"Lorem\\", \\"source\\": \\"@site/docs/rootResolvedSlug.md\\", \\"slug\\": \\"/hey/rootResolvedSlug\\", - \\"permalink\\": \\"/docs/hey/rootResolvedSlug\\" + \\"permalink\\": \\"/docs/hey/rootResolvedSlug\\", + \\"version\\": \\"current\\" }", "site-docs-root-try-to-escape-slug-md-9ee.json": "{ \\"unversionedId\\": \\"rootTryToEscapeSlug\\", @@ -294,7 +241,8 @@ Object { \\"description\\": \\"Lorem\\", \\"source\\": \\"@site/docs/rootTryToEscapeSlug.md\\", \\"slug\\": \\"/rootTryToEscapeSlug\\", - \\"permalink\\": \\"/docs/rootTryToEscapeSlug\\" + \\"permalink\\": \\"/docs/rootTryToEscapeSlug\\", + \\"version\\": \\"current\\" }", "site-docs-slugs-absolute-slug-md-4e8.json": "{ \\"unversionedId\\": \\"slugs/absoluteSlug\\", @@ -304,7 +252,8 @@ Object { \\"description\\": \\"Lorem\\", \\"source\\": \\"@site/docs/slugs/absoluteSlug.md\\", \\"slug\\": \\"/absoluteSlug\\", - \\"permalink\\": \\"/docs/absoluteSlug\\" + \\"permalink\\": \\"/docs/absoluteSlug\\", + \\"version\\": \\"current\\" }", "site-docs-slugs-relative-slug-md-d1c.json": "{ \\"unversionedId\\": \\"slugs/relativeSlug\\", @@ -314,7 +263,8 @@ Object { \\"description\\": \\"Lorem\\", \\"source\\": \\"@site/docs/slugs/relativeSlug.md\\", \\"slug\\": \\"/slugs/relativeSlug\\", - \\"permalink\\": \\"/docs/slugs/relativeSlug\\" + \\"permalink\\": \\"/docs/slugs/relativeSlug\\", + \\"version\\": \\"current\\" }", "site-docs-slugs-resolved-slug-md-02b.json": "{ \\"unversionedId\\": \\"slugs/resolvedSlug\\", @@ -324,7 +274,8 @@ Object { \\"description\\": \\"Lorem\\", \\"source\\": \\"@site/docs/slugs/resolvedSlug.md\\", \\"slug\\": \\"/slugs/hey/resolvedSlug\\", - \\"permalink\\": \\"/docs/slugs/hey/resolvedSlug\\" + \\"permalink\\": \\"/docs/slugs/hey/resolvedSlug\\", + \\"version\\": \\"current\\" }", "site-docs-slugs-try-to-escape-slug-md-70d.json": "{ \\"unversionedId\\": \\"slugs/tryToEscapeSlug\\", @@ -334,7 +285,66 @@ Object { \\"description\\": \\"Lorem\\", \\"source\\": \\"@site/docs/slugs/tryToEscapeSlug.md\\", \\"slug\\": \\"/tryToEscapeSlug\\", - \\"permalink\\": \\"/docs/tryToEscapeSlug\\" + \\"permalink\\": \\"/docs/tryToEscapeSlug\\", + \\"version\\": \\"current\\" +}", + "version-current-metadata-prop-751.json": "{ + \\"version\\": \\"current\\", + \\"docsSidebars\\": { + \\"docs\\": [ + { + \\"collapsed\\": true, + \\"type\\": \\"category\\", + \\"label\\": \\"Test\\", + \\"items\\": [ + { + \\"collapsed\\": true, + \\"type\\": \\"category\\", + \\"label\\": \\"foo\\", + \\"items\\": [ + { + \\"type\\": \\"link\\", + \\"label\\": \\"Bar\\", + \\"href\\": \\"/docs/foo/bar\\" + }, + { + \\"type\\": \\"link\\", + \\"label\\": \\"baz\\", + \\"href\\": \\"/docs/foo/bazSlug.html\\" + } + ] + }, + { + \\"type\\": \\"link\\", + \\"label\\": \\"Github\\", + \\"href\\": \\"https://github.com\\" + }, + { + \\"type\\": \\"link\\", + \\"label\\": \\"Hello, World !\\", + \\"href\\": \\"/docs/\\" + } + ] + }, + { + \\"collapsed\\": true, + \\"type\\": \\"category\\", + \\"label\\": \\"Guides\\", + \\"items\\": [ + { + \\"type\\": \\"link\\", + \\"label\\": \\"Hello, World !\\", + \\"href\\": \\"/docs/\\" + } + ] + } + ] + }, + \\"permalinkToSidebar\\": { + \\"/docs/foo/bar\\": \\"docs\\", + \\"/docs/foo/bazSlug.html\\": \\"docs\\", + \\"/docs/\\": \\"docs\\" + } }", } `; @@ -343,7 +353,6 @@ exports[`simple website content: global data 1`] = ` Object { "pluginName": Object { "pluginId": Object { - "latestVersionName": null, "path": "/docs", "versions": Array [ Object { @@ -401,8 +410,10 @@ Object { "path": "/docs/tryToEscapeSlug", }, ], + "isLast": true, + "label": "Next", "mainDocId": "hello", - "name": null, + "name": "current", "path": "/docs", }, ], @@ -417,10 +428,10 @@ Array [ "component": "@theme/DocPage", "exact": false, "modules": Object { - "docsMetadata": "~docs/docs-route-ff2.json", + "versionMetadata": "~docs/version-current-metadata-prop-751.json", }, "path": "/docs", - "priority": undefined, + "priority": -1, "routes": Array [ Object { "component": "@theme/DocItem", @@ -532,7 +543,10 @@ Array [ `; exports[`site with wrong sidebar file 1`] = ` -"Bad sidebars file. The document id 'goku' was used in the sidebar, but no document with this id could be found. +"Bad sidebars file. +These sidebar document ids do not exist: +- goku\`, + Available document ids= - foo/bar - foo/baz @@ -548,3 +562,986 @@ Available document ids= - slugs/resolvedSlug - slugs/tryToEscapeSlug" `; + +exports[`versioned website (community) content: 100 version sidebars 1`] = ` +Object { + "version-1.0.0/community": Array [ + Object { + "id": "version-1.0.0/team", + "type": "doc", + }, + ], +} +`; + +exports[`versioned website (community) content: current version sidebars 1`] = ` +Object { + "community": Array [ + Object { + "id": "team", + "type": "doc", + }, + ], +} +`; + +exports[`versioned website (community) content: data 1`] = ` +Object { + "site-community-team-md-9d8.json": "{ + \\"unversionedId\\": \\"team\\", + \\"id\\": \\"team\\", + \\"isDocsHomePage\\": false, + \\"title\\": \\"team\\", + \\"description\\": \\"Team current version\\", + \\"source\\": \\"@site/community/team.md\\", + \\"slug\\": \\"/team\\", + \\"permalink\\": \\"/community/next/team\\", + \\"version\\": \\"current\\", + \\"sidebar\\": \\"community\\" +}", + "site-community-versioned-docs-version-1-0-0-team-md-359.json": "{ + \\"unversionedId\\": \\"team\\", + \\"id\\": \\"version-1.0.0/team\\", + \\"isDocsHomePage\\": false, + \\"title\\": \\"team\\", + \\"description\\": \\"Team 1.0.0\\", + \\"source\\": \\"@site/community_versioned_docs/version-1.0.0/team.md\\", + \\"slug\\": \\"/team\\", + \\"permalink\\": \\"/community/team\\", + \\"version\\": \\"1.0.0\\", + \\"sidebar\\": \\"version-1.0.0/community\\" +}", + "version-1-0-0-metadata-prop-608.json": "{ + \\"version\\": \\"1.0.0\\", + \\"docsSidebars\\": { + \\"version-1.0.0/community\\": [ + { + \\"type\\": \\"link\\", + \\"label\\": \\"team\\", + \\"href\\": \\"/community/team\\" + } + ] + }, + \\"permalinkToSidebar\\": { + \\"/community/team\\": \\"version-1.0.0/community\\" + } +}", + "version-current-metadata-prop-751.json": "{ + \\"version\\": \\"current\\", + \\"docsSidebars\\": { + \\"community\\": [ + { + \\"type\\": \\"link\\", + \\"label\\": \\"team\\", + \\"href\\": \\"/community/next/team\\" + } + ] + }, + \\"permalinkToSidebar\\": { + \\"/community/next/team\\": \\"community\\" + } +}", +} +`; + +exports[`versioned website (community) content: global data 1`] = ` +Object { + "pluginName": Object { + "pluginId": Object { + "path": "/community", + "versions": Array [ + Object { + "docs": Array [ + Object { + "id": "team", + "path": "/community/next/team", + }, + ], + "isLast": false, + "label": "Next", + "mainDocId": "team", + "name": "current", + "path": "/community/next", + }, + Object { + "docs": Array [ + Object { + "id": "team", + "path": "/community/team", + }, + ], + "isLast": true, + "label": "1.0.0", + "mainDocId": "team", + "name": "1.0.0", + "path": "/community", + }, + ], + }, + }, +} +`; + +exports[`versioned website (community) content: route config 1`] = ` +Array [ + Object { + "component": "@theme/DocPage", + "exact": false, + "modules": Object { + "versionMetadata": "~docs/version-current-metadata-prop-751.json", + }, + "path": "/community/next", + "priority": undefined, + "routes": Array [ + Object { + "component": "@theme/DocItem", + "exact": true, + "modules": Object { + "content": "@site/community/team.md", + }, + "path": "/community/next/team", + }, + ], + }, + Object { + "component": "@theme/DocPage", + "exact": false, + "modules": Object { + "versionMetadata": "~docs/version-1-0-0-metadata-prop-608.json", + }, + "path": "/community", + "priority": -1, + "routes": Array [ + Object { + "component": "@theme/DocItem", + "exact": true, + "modules": Object { + "content": "@site/community_versioned_docs/version-1.0.0/team.md", + }, + "path": "/community/team", + }, + ], + }, +] +`; + +exports[`versioned website content: 100 version sidebars 1`] = ` +Object { + "version-1.0.0/docs": Array [ + Object { + "collapsed": true, + "items": Array [ + Object { + "id": "version-1.0.0/foo/bar", + "type": "doc", + }, + Object { + "id": "version-1.0.0/foo/baz", + "type": "doc", + }, + ], + "label": "Test", + "type": "category", + }, + Object { + "collapsed": true, + "items": Array [ + Object { + "id": "version-1.0.0/hello", + "type": "doc", + }, + ], + "label": "Guides", + "type": "category", + }, + ], +} +`; + +exports[`versioned website content: 101 version sidebars 1`] = ` +Object { + "version-1.0.1/docs": Array [ + Object { + "collapsed": true, + "items": Array [ + Object { + "id": "version-1.0.1/foo/bar", + "type": "doc", + }, + ], + "label": "Test", + "type": "category", + }, + Object { + "collapsed": true, + "items": Array [ + Object { + "id": "version-1.0.1/hello", + "type": "doc", + }, + ], + "label": "Guides", + "type": "category", + }, + ], +} +`; + +exports[`versioned website content: current version sidebars 1`] = ` +Object { + "docs": Array [ + Object { + "collapsed": true, + "items": Array [ + Object { + "id": "foo/bar", + "type": "doc", + }, + ], + "label": "Test", + "type": "category", + }, + Object { + "collapsed": true, + "items": Array [ + Object { + "id": "hello", + "type": "doc", + }, + ], + "label": "Guides", + "type": "category", + }, + ], +} +`; + +exports[`versioned website content: data 1`] = ` +Object { + "site-docs-foo-bar-md-8c2.json": "{ + \\"unversionedId\\": \\"foo/bar\\", + \\"id\\": \\"foo/bar\\", + \\"isDocsHomePage\\": false, + \\"title\\": \\"bar\\", + \\"description\\": \\"This is next version of bar.\\", + \\"source\\": \\"@site/docs/foo/bar.md\\", + \\"slug\\": \\"/foo/barSlug\\", + \\"permalink\\": \\"/docs/next/foo/barSlug\\", + \\"version\\": \\"current\\", + \\"sidebar\\": \\"docs\\", + \\"next\\": { + \\"title\\": \\"hello\\", + \\"permalink\\": \\"/docs/next/\\" + } +}", + "site-docs-hello-md-9df.json": "{ + \\"unversionedId\\": \\"hello\\", + \\"id\\": \\"hello\\", + \\"isDocsHomePage\\": true, + \\"title\\": \\"hello\\", + \\"description\\": \\"Hello next !\\", + \\"source\\": \\"@site/docs/hello.md\\", + \\"slug\\": \\"/\\", + \\"permalink\\": \\"/docs/next/\\", + \\"version\\": \\"current\\", + \\"sidebar\\": \\"docs\\", + \\"previous\\": { + \\"title\\": \\"bar\\", + \\"permalink\\": \\"/docs/next/foo/barSlug\\" + } +}", + "site-docs-slugs-absolute-slug-md-4e8.json": "{ + \\"unversionedId\\": \\"slugs/absoluteSlug\\", + \\"id\\": \\"slugs/absoluteSlug\\", + \\"isDocsHomePage\\": false, + \\"title\\": \\"absoluteSlug\\", + \\"description\\": \\"Lorem\\", + \\"source\\": \\"@site/docs/slugs/absoluteSlug.md\\", + \\"slug\\": \\"/absoluteSlug\\", + \\"permalink\\": \\"/docs/next/absoluteSlug\\", + \\"version\\": \\"current\\" +}", + "site-docs-slugs-relative-slug-md-d1c.json": "{ + \\"unversionedId\\": \\"slugs/relativeSlug\\", + \\"id\\": \\"slugs/relativeSlug\\", + \\"isDocsHomePage\\": false, + \\"title\\": \\"relativeSlug\\", + \\"description\\": \\"Lorem\\", + \\"source\\": \\"@site/docs/slugs/relativeSlug.md\\", + \\"slug\\": \\"/slugs/relativeSlug\\", + \\"permalink\\": \\"/docs/next/slugs/relativeSlug\\", + \\"version\\": \\"current\\" +}", + "site-docs-slugs-resolved-slug-md-02b.json": "{ + \\"unversionedId\\": \\"slugs/resolvedSlug\\", + \\"id\\": \\"slugs/resolvedSlug\\", + \\"isDocsHomePage\\": false, + \\"title\\": \\"resolvedSlug\\", + \\"description\\": \\"Lorem\\", + \\"source\\": \\"@site/docs/slugs/resolvedSlug.md\\", + \\"slug\\": \\"/slugs/hey/resolvedSlug\\", + \\"permalink\\": \\"/docs/next/slugs/hey/resolvedSlug\\", + \\"version\\": \\"current\\" +}", + "site-docs-slugs-try-to-escape-slug-md-70d.json": "{ + \\"unversionedId\\": \\"slugs/tryToEscapeSlug\\", + \\"id\\": \\"slugs/tryToEscapeSlug\\", + \\"isDocsHomePage\\": false, + \\"title\\": \\"tryToEscapeSlug\\", + \\"description\\": \\"Lorem\\", + \\"source\\": \\"@site/docs/slugs/tryToEscapeSlug.md\\", + \\"slug\\": \\"/tryToEscapeSlug\\", + \\"permalink\\": \\"/docs/next/tryToEscapeSlug\\", + \\"version\\": \\"current\\" +}", + "site-versioned-docs-version-1-0-0-foo-bar-md-7a6.json": "{ + \\"unversionedId\\": \\"foo/bar\\", + \\"id\\": \\"version-1.0.0/foo/bar\\", + \\"isDocsHomePage\\": false, + \\"title\\": \\"bar\\", + \\"description\\": \\"Bar 1.0.0 !\\", + \\"source\\": \\"@site/versioned_docs/version-1.0.0/foo/bar.md\\", + \\"slug\\": \\"/foo/barSlug\\", + \\"permalink\\": \\"/docs/1.0.0/foo/barSlug\\", + \\"version\\": \\"1.0.0\\", + \\"sidebar\\": \\"version-1.0.0/docs\\", + \\"next\\": { + \\"title\\": \\"baz\\", + \\"permalink\\": \\"/docs/1.0.0/foo/baz\\" + } +}", + "site-versioned-docs-version-1-0-0-foo-baz-md-883.json": "{ + \\"unversionedId\\": \\"foo/baz\\", + \\"id\\": \\"version-1.0.0/foo/baz\\", + \\"isDocsHomePage\\": false, + \\"title\\": \\"baz\\", + \\"description\\": \\"Baz 1.0.0 ! This will be deleted in next subsequent versions.\\", + \\"source\\": \\"@site/versioned_docs/version-1.0.0/foo/baz.md\\", + \\"slug\\": \\"/foo/baz\\", + \\"permalink\\": \\"/docs/1.0.0/foo/baz\\", + \\"version\\": \\"1.0.0\\", + \\"sidebar\\": \\"version-1.0.0/docs\\", + \\"previous\\": { + \\"title\\": \\"bar\\", + \\"permalink\\": \\"/docs/1.0.0/foo/barSlug\\" + }, + \\"next\\": { + \\"title\\": \\"hello\\", + \\"permalink\\": \\"/docs/1.0.0/\\" + } +}", + "site-versioned-docs-version-1-0-0-hello-md-3ef.json": "{ + \\"unversionedId\\": \\"hello\\", + \\"id\\": \\"version-1.0.0/hello\\", + \\"isDocsHomePage\\": true, + \\"title\\": \\"hello\\", + \\"description\\": \\"Hello 1.0.0 !\\", + \\"source\\": \\"@site/versioned_docs/version-1.0.0/hello.md\\", + \\"slug\\": \\"/\\", + \\"permalink\\": \\"/docs/1.0.0/\\", + \\"version\\": \\"1.0.0\\", + \\"sidebar\\": \\"version-1.0.0/docs\\", + \\"previous\\": { + \\"title\\": \\"baz\\", + \\"permalink\\": \\"/docs/1.0.0/foo/baz\\" + } +}", + "site-versioned-docs-version-1-0-1-foo-bar-md-7a3.json": "{ + \\"unversionedId\\": \\"foo/bar\\", + \\"id\\": \\"version-1.0.1/foo/bar\\", + \\"isDocsHomePage\\": false, + \\"title\\": \\"bar\\", + \\"description\\": \\"Bar 1.0.1 !\\", + \\"source\\": \\"@site/versioned_docs/version-1.0.1/foo/bar.md\\", + \\"slug\\": \\"/foo/bar\\", + \\"permalink\\": \\"/docs/foo/bar\\", + \\"version\\": \\"1.0.1\\", + \\"sidebar\\": \\"version-1.0.1/docs\\", + \\"next\\": { + \\"title\\": \\"hello\\", + \\"permalink\\": \\"/docs/\\" + } +}", + "site-versioned-docs-version-1-0-1-hello-md-0c7.json": "{ + \\"unversionedId\\": \\"hello\\", + \\"id\\": \\"version-1.0.1/hello\\", + \\"isDocsHomePage\\": true, + \\"title\\": \\"hello\\", + \\"description\\": \\"Hello 1.0.1 !\\", + \\"source\\": \\"@site/versioned_docs/version-1.0.1/hello.md\\", + \\"slug\\": \\"/\\", + \\"permalink\\": \\"/docs/\\", + \\"version\\": \\"1.0.1\\", + \\"sidebar\\": \\"version-1.0.1/docs\\", + \\"previous\\": { + \\"title\\": \\"bar\\", + \\"permalink\\": \\"/docs/foo/bar\\" + } +}", + "site-versioned-docs-version-with-slugs-root-absolute-slug-md-4d2.json": "{ + \\"unversionedId\\": \\"rootAbsoluteSlug\\", + \\"id\\": \\"version-withSlugs/rootAbsoluteSlug\\", + \\"isDocsHomePage\\": false, + \\"title\\": \\"rootAbsoluteSlug\\", + \\"description\\": \\"Lorem\\", + \\"source\\": \\"@site/versioned_docs/version-withSlugs/rootAbsoluteSlug.md\\", + \\"slug\\": \\"/rootAbsoluteSlug\\", + \\"permalink\\": \\"/docs/withSlugs/rootAbsoluteSlug\\", + \\"version\\": \\"withSlugs\\", + \\"sidebar\\": \\"version-1.0.1/docs\\" +}", + "site-versioned-docs-version-with-slugs-root-relative-slug-md-32a.json": "{ + \\"unversionedId\\": \\"rootRelativeSlug\\", + \\"id\\": \\"version-withSlugs/rootRelativeSlug\\", + \\"isDocsHomePage\\": false, + \\"title\\": \\"rootRelativeSlug\\", + \\"description\\": \\"Lorem\\", + \\"source\\": \\"@site/versioned_docs/version-withSlugs/rootRelativeSlug.md\\", + \\"slug\\": \\"/rootRelativeSlug\\", + \\"permalink\\": \\"/docs/withSlugs/rootRelativeSlug\\", + \\"version\\": \\"withSlugs\\" +}", + "site-versioned-docs-version-with-slugs-root-resolved-slug-md-aee.json": "{ + \\"unversionedId\\": \\"rootResolvedSlug\\", + \\"id\\": \\"version-withSlugs/rootResolvedSlug\\", + \\"isDocsHomePage\\": false, + \\"title\\": \\"rootResolvedSlug\\", + \\"description\\": \\"Lorem\\", + \\"source\\": \\"@site/versioned_docs/version-withSlugs/rootResolvedSlug.md\\", + \\"slug\\": \\"/hey/rootResolvedSlug\\", + \\"permalink\\": \\"/docs/withSlugs/hey/rootResolvedSlug\\", + \\"version\\": \\"withSlugs\\" +}", + "site-versioned-docs-version-with-slugs-root-try-to-escape-slug-md-b5d.json": "{ + \\"unversionedId\\": \\"rootTryToEscapeSlug\\", + \\"id\\": \\"version-withSlugs/rootTryToEscapeSlug\\", + \\"isDocsHomePage\\": false, + \\"title\\": \\"rootTryToEscapeSlug\\", + \\"description\\": \\"Lorem\\", + \\"source\\": \\"@site/versioned_docs/version-withSlugs/rootTryToEscapeSlug.md\\", + \\"slug\\": \\"/rootTryToEscapeSlug\\", + \\"permalink\\": \\"/docs/withSlugs/rootTryToEscapeSlug\\", + \\"version\\": \\"withSlugs\\" +}", + "site-versioned-docs-version-with-slugs-slugs-absolute-slug-md-47a.json": "{ + \\"unversionedId\\": \\"slugs/absoluteSlug\\", + \\"id\\": \\"version-withSlugs/slugs/absoluteSlug\\", + \\"isDocsHomePage\\": false, + \\"title\\": \\"absoluteSlug\\", + \\"description\\": \\"Lorem\\", + \\"source\\": \\"@site/versioned_docs/version-withSlugs/slugs/absoluteSlug.md\\", + \\"slug\\": \\"/absoluteSlug\\", + \\"permalink\\": \\"/docs/withSlugs/absoluteSlug\\", + \\"version\\": \\"withSlugs\\" +}", + "site-versioned-docs-version-with-slugs-slugs-relative-slug-md-a95.json": "{ + \\"unversionedId\\": \\"slugs/relativeSlug\\", + \\"id\\": \\"version-withSlugs/slugs/relativeSlug\\", + \\"isDocsHomePage\\": false, + \\"title\\": \\"relativeSlug\\", + \\"description\\": \\"Lorem\\", + \\"source\\": \\"@site/versioned_docs/version-withSlugs/slugs/relativeSlug.md\\", + \\"slug\\": \\"/slugs/relativeSlug\\", + \\"permalink\\": \\"/docs/withSlugs/slugs/relativeSlug\\", + \\"version\\": \\"withSlugs\\" +}", + "site-versioned-docs-version-with-slugs-slugs-resolved-slug-md-5a1.json": "{ + \\"unversionedId\\": \\"slugs/resolvedSlug\\", + \\"id\\": \\"version-withSlugs/slugs/resolvedSlug\\", + \\"isDocsHomePage\\": false, + \\"title\\": \\"resolvedSlug\\", + \\"description\\": \\"Lorem\\", + \\"source\\": \\"@site/versioned_docs/version-withSlugs/slugs/resolvedSlug.md\\", + \\"slug\\": \\"/slugs/hey/resolvedSlug\\", + \\"permalink\\": \\"/docs/withSlugs/slugs/hey/resolvedSlug\\", + \\"version\\": \\"withSlugs\\" +}", + "site-versioned-docs-version-with-slugs-slugs-try-to-escape-slug-md-4e1.json": "{ + \\"unversionedId\\": \\"slugs/tryToEscapeSlug\\", + \\"id\\": \\"version-withSlugs/slugs/tryToEscapeSlug\\", + \\"isDocsHomePage\\": false, + \\"title\\": \\"tryToEscapeSlug\\", + \\"description\\": \\"Lorem\\", + \\"source\\": \\"@site/versioned_docs/version-withSlugs/slugs/tryToEscapeSlug.md\\", + \\"slug\\": \\"/tryToEscapeSlug\\", + \\"permalink\\": \\"/docs/withSlugs/tryToEscapeSlug\\", + \\"version\\": \\"withSlugs\\" +}", + "version-1-0-0-metadata-prop-608.json": "{ + \\"version\\": \\"1.0.0\\", + \\"docsSidebars\\": { + \\"version-1.0.0/docs\\": [ + { + \\"collapsed\\": true, + \\"type\\": \\"category\\", + \\"label\\": \\"Test\\", + \\"items\\": [ + { + \\"type\\": \\"link\\", + \\"label\\": \\"bar\\", + \\"href\\": \\"/docs/1.0.0/foo/barSlug\\" + }, + { + \\"type\\": \\"link\\", + \\"label\\": \\"baz\\", + \\"href\\": \\"/docs/1.0.0/foo/baz\\" + } + ] + }, + { + \\"collapsed\\": true, + \\"type\\": \\"category\\", + \\"label\\": \\"Guides\\", + \\"items\\": [ + { + \\"type\\": \\"link\\", + \\"label\\": \\"hello\\", + \\"href\\": \\"/docs/1.0.0/\\" + } + ] + } + ] + }, + \\"permalinkToSidebar\\": { + \\"/docs/1.0.0/foo/barSlug\\": \\"version-1.0.0/docs\\", + \\"/docs/1.0.0/foo/baz\\": \\"version-1.0.0/docs\\", + \\"/docs/1.0.0/\\": \\"version-1.0.0/docs\\" + } +}", + "version-1-0-1-metadata-prop-e87.json": "{ + \\"version\\": \\"1.0.1\\", + \\"docsSidebars\\": { + \\"version-1.0.1/docs\\": [ + { + \\"collapsed\\": true, + \\"type\\": \\"category\\", + \\"label\\": \\"Test\\", + \\"items\\": [ + { + \\"type\\": \\"link\\", + \\"label\\": \\"bar\\", + \\"href\\": \\"/docs/foo/bar\\" + } + ] + }, + { + \\"collapsed\\": true, + \\"type\\": \\"category\\", + \\"label\\": \\"Guides\\", + \\"items\\": [ + { + \\"type\\": \\"link\\", + \\"label\\": \\"hello\\", + \\"href\\": \\"/docs/\\" + } + ] + } + ] + }, + \\"permalinkToSidebar\\": { + \\"/docs/foo/bar\\": \\"version-1.0.1/docs\\", + \\"/docs/\\": \\"version-1.0.1/docs\\" + } +}", + "version-current-metadata-prop-751.json": "{ + \\"version\\": \\"current\\", + \\"docsSidebars\\": { + \\"docs\\": [ + { + \\"collapsed\\": true, + \\"type\\": \\"category\\", + \\"label\\": \\"Test\\", + \\"items\\": [ + { + \\"type\\": \\"link\\", + \\"label\\": \\"bar\\", + \\"href\\": \\"/docs/next/foo/barSlug\\" + } + ] + }, + { + \\"collapsed\\": true, + \\"type\\": \\"category\\", + \\"label\\": \\"Guides\\", + \\"items\\": [ + { + \\"type\\": \\"link\\", + \\"label\\": \\"hello\\", + \\"href\\": \\"/docs/next/\\" + } + ] + } + ] + }, + \\"permalinkToSidebar\\": { + \\"/docs/next/foo/barSlug\\": \\"docs\\", + \\"/docs/next/\\": \\"docs\\" + } +}", + "version-with-slugs-metadata-prop-2bf.json": "{ + \\"version\\": \\"withSlugs\\", + \\"docsSidebars\\": { + \\"version-1.0.1/docs\\": [ + { + \\"collapsed\\": true, + \\"type\\": \\"category\\", + \\"label\\": \\"Test\\", + \\"items\\": [ + { + \\"type\\": \\"link\\", + \\"label\\": \\"rootAbsoluteSlug\\", + \\"href\\": \\"/docs/withSlugs/rootAbsoluteSlug\\" + } + ] + } + ] + }, + \\"permalinkToSidebar\\": { + \\"/docs/withSlugs/rootAbsoluteSlug\\": \\"version-1.0.1/docs\\" + } +}", +} +`; + +exports[`versioned website content: global data 1`] = ` +Object { + "pluginName": Object { + "pluginId": Object { + "path": "/docs", + "versions": Array [ + Object { + "docs": Array [ + Object { + "id": "foo/bar", + "path": "/docs/next/foo/barSlug", + }, + Object { + "id": "hello", + "path": "/docs/next/", + }, + Object { + "id": "slugs/absoluteSlug", + "path": "/docs/next/absoluteSlug", + }, + Object { + "id": "slugs/relativeSlug", + "path": "/docs/next/slugs/relativeSlug", + }, + Object { + "id": "slugs/resolvedSlug", + "path": "/docs/next/slugs/hey/resolvedSlug", + }, + Object { + "id": "slugs/tryToEscapeSlug", + "path": "/docs/next/tryToEscapeSlug", + }, + ], + "isLast": false, + "label": "Next", + "mainDocId": "hello", + "name": "current", + "path": "/docs/next", + }, + Object { + "docs": Array [ + Object { + "id": "foo/bar", + "path": "/docs/foo/bar", + }, + Object { + "id": "hello", + "path": "/docs/", + }, + ], + "isLast": true, + "label": "1.0.1", + "mainDocId": "hello", + "name": "1.0.1", + "path": "/docs", + }, + Object { + "docs": Array [ + Object { + "id": "foo/bar", + "path": "/docs/1.0.0/foo/barSlug", + }, + Object { + "id": "foo/baz", + "path": "/docs/1.0.0/foo/baz", + }, + Object { + "id": "hello", + "path": "/docs/1.0.0/", + }, + ], + "isLast": false, + "label": "1.0.0", + "mainDocId": "hello", + "name": "1.0.0", + "path": "/docs/1.0.0", + }, + Object { + "docs": Array [ + Object { + "id": "rootAbsoluteSlug", + "path": "/docs/withSlugs/rootAbsoluteSlug", + }, + Object { + "id": "rootRelativeSlug", + "path": "/docs/withSlugs/rootRelativeSlug", + }, + Object { + "id": "rootResolvedSlug", + "path": "/docs/withSlugs/hey/rootResolvedSlug", + }, + Object { + "id": "rootTryToEscapeSlug", + "path": "/docs/withSlugs/rootTryToEscapeSlug", + }, + Object { + "id": "slugs/absoluteSlug", + "path": "/docs/withSlugs/absoluteSlug", + }, + Object { + "id": "slugs/relativeSlug", + "path": "/docs/withSlugs/slugs/relativeSlug", + }, + Object { + "id": "slugs/resolvedSlug", + "path": "/docs/withSlugs/slugs/hey/resolvedSlug", + }, + Object { + "id": "slugs/tryToEscapeSlug", + "path": "/docs/withSlugs/tryToEscapeSlug", + }, + ], + "isLast": false, + "label": "withSlugs", + "mainDocId": "rootAbsoluteSlug", + "name": "withSlugs", + "path": "/docs/withSlugs", + }, + ], + }, + }, +} +`; + +exports[`versioned website content: route config 1`] = ` +Array [ + Object { + "component": "@theme/DocPage", + "exact": false, + "modules": Object { + "versionMetadata": "~docs/version-1-0-0-metadata-prop-608.json", + }, + "path": "/docs/1.0.0", + "priority": undefined, + "routes": Array [ + Object { + "component": "@theme/DocItem", + "exact": true, + "modules": Object { + "content": "@site/versioned_docs/version-1.0.0/hello.md", + }, + "path": "/docs/1.0.0/", + }, + Object { + "component": "@theme/DocItem", + "exact": true, + "modules": Object { + "content": "@site/versioned_docs/version-1.0.0/foo/bar.md", + }, + "path": "/docs/1.0.0/foo/barSlug", + }, + Object { + "component": "@theme/DocItem", + "exact": true, + "modules": Object { + "content": "@site/versioned_docs/version-1.0.0/foo/baz.md", + }, + "path": "/docs/1.0.0/foo/baz", + }, + ], + }, + Object { + "component": "@theme/DocPage", + "exact": false, + "modules": Object { + "versionMetadata": "~docs/version-current-metadata-prop-751.json", + }, + "path": "/docs/next", + "priority": undefined, + "routes": Array [ + Object { + "component": "@theme/DocItem", + "exact": true, + "modules": Object { + "content": "@site/docs/hello.md", + }, + "path": "/docs/next/", + }, + Object { + "component": "@theme/DocItem", + "exact": true, + "modules": Object { + "content": "@site/docs/slugs/absoluteSlug.md", + }, + "path": "/docs/next/absoluteSlug", + }, + Object { + "component": "@theme/DocItem", + "exact": true, + "modules": Object { + "content": "@site/docs/foo/bar.md", + }, + "path": "/docs/next/foo/barSlug", + }, + Object { + "component": "@theme/DocItem", + "exact": true, + "modules": Object { + "content": "@site/docs/slugs/resolvedSlug.md", + }, + "path": "/docs/next/slugs/hey/resolvedSlug", + }, + Object { + "component": "@theme/DocItem", + "exact": true, + "modules": Object { + "content": "@site/docs/slugs/relativeSlug.md", + }, + "path": "/docs/next/slugs/relativeSlug", + }, + Object { + "component": "@theme/DocItem", + "exact": true, + "modules": Object { + "content": "@site/docs/slugs/tryToEscapeSlug.md", + }, + "path": "/docs/next/tryToEscapeSlug", + }, + ], + }, + Object { + "component": "@theme/DocPage", + "exact": false, + "modules": Object { + "versionMetadata": "~docs/version-with-slugs-metadata-prop-2bf.json", + }, + "path": "/docs/withSlugs", + "priority": undefined, + "routes": Array [ + Object { + "component": "@theme/DocItem", + "exact": true, + "modules": Object { + "content": "@site/versioned_docs/version-withSlugs/slugs/absoluteSlug.md", + }, + "path": "/docs/withSlugs/absoluteSlug", + }, + Object { + "component": "@theme/DocItem", + "exact": true, + "modules": Object { + "content": "@site/versioned_docs/version-withSlugs/rootResolvedSlug.md", + }, + "path": "/docs/withSlugs/hey/rootResolvedSlug", + }, + Object { + "component": "@theme/DocItem", + "exact": true, + "modules": Object { + "content": "@site/versioned_docs/version-withSlugs/rootAbsoluteSlug.md", + }, + "path": "/docs/withSlugs/rootAbsoluteSlug", + }, + Object { + "component": "@theme/DocItem", + "exact": true, + "modules": Object { + "content": "@site/versioned_docs/version-withSlugs/rootRelativeSlug.md", + }, + "path": "/docs/withSlugs/rootRelativeSlug", + }, + Object { + "component": "@theme/DocItem", + "exact": true, + "modules": Object { + "content": "@site/versioned_docs/version-withSlugs/rootTryToEscapeSlug.md", + }, + "path": "/docs/withSlugs/rootTryToEscapeSlug", + }, + Object { + "component": "@theme/DocItem", + "exact": true, + "modules": Object { + "content": "@site/versioned_docs/version-withSlugs/slugs/resolvedSlug.md", + }, + "path": "/docs/withSlugs/slugs/hey/resolvedSlug", + }, + Object { + "component": "@theme/DocItem", + "exact": true, + "modules": Object { + "content": "@site/versioned_docs/version-withSlugs/slugs/relativeSlug.md", + }, + "path": "/docs/withSlugs/slugs/relativeSlug", + }, + Object { + "component": "@theme/DocItem", + "exact": true, + "modules": Object { + "content": "@site/versioned_docs/version-withSlugs/slugs/tryToEscapeSlug.md", + }, + "path": "/docs/withSlugs/tryToEscapeSlug", + }, + ], + }, + Object { + "component": "@theme/DocPage", + "exact": false, + "modules": Object { + "versionMetadata": "~docs/version-1-0-1-metadata-prop-e87.json", + }, + "path": "/docs", + "priority": -1, + "routes": Array [ + Object { + "component": "@theme/DocItem", + "exact": true, + "modules": Object { + "content": "@site/versioned_docs/version-1.0.1/hello.md", + }, + "path": "/docs/", + }, + Object { + "component": "@theme/DocItem", + "exact": true, + "modules": Object { + "content": "@site/versioned_docs/version-1.0.1/foo/bar.md", + }, + "path": "/docs/foo/bar", + }, + ], + }, +] +`; + +exports[`versioned website content: withSlugs version sidebars 1`] = ` +Object { + "version-1.0.1/docs": Array [ + Object { + "collapsed": true, + "items": Array [ + Object { + "id": "version-withSlugs/rootAbsoluteSlug", + "type": "doc", + }, + ], + "label": "Test", + "type": "category", + }, + ], +} +`; diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/cli.test.ts b/packages/docusaurus-plugin-content-docs/src/__tests__/cli.test.ts index c7d93d47b301..b296aa17a34f 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/cli.test.ts +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/cli.test.ts @@ -6,7 +6,7 @@ */ import path from 'path'; -import {cliDocsVersion} from '../cli'; +import {cliDocsVersionCommand} from '../cli'; import {PathOptions} from '../types'; import fs from 'fs-extra'; import { @@ -28,12 +28,17 @@ describe('docsVersion', () => { test('no version tag provided', () => { expect(() => - cliDocsVersion(null, simpleSiteDir, DEFAULT_PLUGIN_ID, DEFAULT_OPTIONS), + cliDocsVersionCommand( + null, + simpleSiteDir, + DEFAULT_PLUGIN_ID, + DEFAULT_OPTIONS, + ), ).toThrowErrorMatchingInlineSnapshot( `"[docs] No version tag specified!. Pass the version you wish to create as an argument. Ex: 1.0.0"`, ); expect(() => - cliDocsVersion( + cliDocsVersionCommand( undefined, simpleSiteDir, DEFAULT_PLUGIN_ID, @@ -43,7 +48,12 @@ describe('docsVersion', () => { `"[docs] No version tag specified!. Pass the version you wish to create as an argument. Ex: 1.0.0"`, ); expect(() => - cliDocsVersion('', simpleSiteDir, DEFAULT_PLUGIN_ID, DEFAULT_OPTIONS), + cliDocsVersionCommand( + '', + simpleSiteDir, + DEFAULT_PLUGIN_ID, + DEFAULT_OPTIONS, + ), ).toThrowErrorMatchingInlineSnapshot( `"[docs] No version tag specified!. Pass the version you wish to create as an argument. Ex: 1.0.0"`, ); @@ -51,7 +61,7 @@ describe('docsVersion', () => { test('version tag should not have slash', () => { expect(() => - cliDocsVersion( + cliDocsVersionCommand( 'foo/bar', simpleSiteDir, DEFAULT_PLUGIN_ID, @@ -61,7 +71,7 @@ describe('docsVersion', () => { `"[docs] Invalid version tag specified! Do not include slash (/) or (\\\\). Try something like: 1.0.0"`, ); expect(() => - cliDocsVersion( + cliDocsVersionCommand( 'foo\\bar', simpleSiteDir, DEFAULT_PLUGIN_ID, @@ -74,7 +84,7 @@ describe('docsVersion', () => { test('version tag should not be too long', () => { expect(() => - cliDocsVersion( + cliDocsVersionCommand( 'a'.repeat(255), simpleSiteDir, DEFAULT_PLUGIN_ID, @@ -87,12 +97,22 @@ describe('docsVersion', () => { test('version tag should not be a dot or two dots', () => { expect(() => - cliDocsVersion('..', simpleSiteDir, DEFAULT_PLUGIN_ID, DEFAULT_OPTIONS), + cliDocsVersionCommand( + '..', + simpleSiteDir, + DEFAULT_PLUGIN_ID, + DEFAULT_OPTIONS, + ), ).toThrowErrorMatchingInlineSnapshot( `"[docs] Invalid version tag specified! Do not name your version \\".\\" or \\"..\\". Try something like: 1.0.0"`, ); expect(() => - cliDocsVersion('.', simpleSiteDir, DEFAULT_PLUGIN_ID, DEFAULT_OPTIONS), + cliDocsVersionCommand( + '.', + simpleSiteDir, + DEFAULT_PLUGIN_ID, + DEFAULT_OPTIONS, + ), ).toThrowErrorMatchingInlineSnapshot( `"[docs] Invalid version tag specified! Do not name your version \\".\\" or \\"..\\". Try something like: 1.0.0"`, ); @@ -100,7 +120,7 @@ describe('docsVersion', () => { test('version tag should be a valid pathname', () => { expect(() => - cliDocsVersion( + cliDocsVersionCommand( '', simpleSiteDir, DEFAULT_PLUGIN_ID, @@ -110,7 +130,7 @@ describe('docsVersion', () => { `"[docs] Invalid version tag specified! Please ensure its a valid pathname too. Try something like: 1.0.0"`, ); expect(() => - cliDocsVersion( + cliDocsVersionCommand( 'foo\x00bar', simpleSiteDir, DEFAULT_PLUGIN_ID, @@ -120,7 +140,7 @@ describe('docsVersion', () => { `"[docs] Invalid version tag specified! Please ensure its a valid pathname too. Try something like: 1.0.0"`, ); expect(() => - cliDocsVersion( + cliDocsVersionCommand( 'foo:bar', simpleSiteDir, DEFAULT_PLUGIN_ID, @@ -133,7 +153,7 @@ describe('docsVersion', () => { test('version tag already exist', () => { expect(() => - cliDocsVersion( + cliDocsVersionCommand( '1.0.0', versionedSiteDir, DEFAULT_PLUGIN_ID, @@ -147,7 +167,12 @@ describe('docsVersion', () => { test('no docs file to version', () => { const emptySiteDir = path.join(fixtureDir, 'empty-site'); expect(() => - cliDocsVersion('1.0.0', emptySiteDir, DEFAULT_PLUGIN_ID, DEFAULT_OPTIONS), + cliDocsVersionCommand( + '1.0.0', + emptySiteDir, + DEFAULT_PLUGIN_ID, + DEFAULT_OPTIONS, + ), ).toThrowErrorMatchingInlineSnapshot( `"[docs] There is no docs to version !"`, ); @@ -174,7 +199,7 @@ describe('docsVersion', () => { path: 'docs', sidebarPath: path.join(simpleSiteDir, 'sidebars.json'), }; - cliDocsVersion('1.0.0', simpleSiteDir, DEFAULT_PLUGIN_ID, options); + cliDocsVersionCommand('1.0.0', simpleSiteDir, DEFAULT_PLUGIN_ID, options); expect(copyMock).toHaveBeenCalledWith( path.join(simpleSiteDir, options.path), path.join( @@ -222,7 +247,12 @@ describe('docsVersion', () => { path: 'docs', sidebarPath: path.join(versionedSiteDir, 'sidebars.json'), }; - cliDocsVersion('2.0.0', versionedSiteDir, DEFAULT_PLUGIN_ID, options); + cliDocsVersionCommand( + '2.0.0', + versionedSiteDir, + DEFAULT_PLUGIN_ID, + options, + ); expect(copyMock).toHaveBeenCalledWith( path.join(versionedSiteDir, options.path), path.join( @@ -272,7 +302,7 @@ describe('docsVersion', () => { path: 'community', sidebarPath: path.join(versionedSiteDir, 'community_sidebars.json'), }; - cliDocsVersion('2.0.0', versionedSiteDir, pluginId, options); + cliDocsVersionCommand('2.0.0', versionedSiteDir, pluginId, options); expect(copyMock).toHaveBeenCalledWith( path.join(versionedSiteDir, options.path), path.join( diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/index.test.ts b/packages/docusaurus-plugin-content-docs/src/__tests__/index.test.ts index 113481521cbd..0aadf6ade96d 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/index.test.ts +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/index.test.ts @@ -6,9 +6,10 @@ */ import path from 'path'; -import {validate} from 'webpack'; import {isMatch} from 'picomatch'; import commander from 'commander'; +import {kebabCase} from 'lodash'; + import fs from 'fs-extra'; import pluginContentDocs from '../index'; import {loadContext} from '@docusaurus/core/src/server/index'; @@ -21,9 +22,23 @@ import {DEFAULT_PLUGIN_ID} from '@docusaurus/core/lib/constants'; import * as cliDocs from '../cli'; import {OptionsSchema} from '../options'; import {normalizePluginOptions} from '@docusaurus/utils-validation'; +import {DocMetadata, LoadedVersion} from '../types'; +import {toSidebarsProp} from '../props'; + +// @ts-expect-error: TODO typedefs missing? +import {validate} from 'webpack'; -// TODO remove -function loadEnv(..._args: any[]): any {} +function findDocById(version: LoadedVersion, unversionedId: string) { + return version.docs.find((item) => item.unversionedId === unversionedId); +} +const defaultDocMetadata: Partial = { + next: undefined, + previous: undefined, + editUrl: undefined, + lastUpdatedAt: undefined, + lastUpdatedBy: undefined, + sidebar_label: undefined, +}; const createFakeActions = (contentDir: string) => { const routeConfigs: RouteConfig[] = []; @@ -43,20 +58,34 @@ const createFakeActions = (contentDir: string) => { }, }; + // query by prefix, because files have a hash at the end + // so it's not convenient to query by full filename + const getCreatedDataByPrefix = (prefix: string) => { + const entry = Object.entries(dataContainer).find(([key]) => + key.startsWith(prefix), + ); + if (!entry) { + throw new Error(`No created entry found for prefix=[${prefix}] +Entries created: +- ${Object.keys(dataContainer).join('\n- ')} + `); + } + return JSON.parse(entry[1] as string); + }; + // Extra fns useful for tests! const utils = { getGlobalData: () => globalDataContainer, getRouteConfigs: () => routeConfigs, - // query by prefix, because files have a hash at the end - // so it's not convenient to query by full filename - getCreatedDataByPrefix: (prefix: string) => { - const entry = Object.entries(dataContainer).find(([key]) => - key.startsWith(prefix), + + checkVersionMetadataPropCreated: (version: LoadedVersion) => { + const versionMetadataProp = getCreatedDataByPrefix( + `version-${kebabCase(version.versionName)}-metadata-prop`, + ); + expect(versionMetadataProp.docsSidebars).toEqual(toSidebarsProp(version)); + expect(versionMetadataProp.permalinkToSidebar).toEqual( + version.permalinkToSidebar, ); - if (!entry) { - throw new Error(`No entry found for prefix=${prefix}`); - } - return JSON.parse(entry[1] as string); }, expectSnapshot: () => { @@ -85,7 +114,7 @@ test('site with wrong sidebar file', async () => { sidebarPath, }), ); - await expect(plugin.loadContent()).rejects.toThrowErrorMatchingSnapshot(); + await expect(plugin.loadContent!()).rejects.toThrowErrorMatchingSnapshot(); }); describe('empty/no docs website', () => { @@ -98,20 +127,11 @@ describe('empty/no docs website', () => { context, normalizePluginOptions(OptionsSchema, {}), ); - const content = await plugin.loadContent(); - const {docsMetadata, docsSidebars} = content; - expect(docsMetadata).toMatchInlineSnapshot(`Object {}`); - expect(docsSidebars).toMatchInlineSnapshot(`Object {}`); - - const pluginContentDir = path.join(context.generatedFilesDir, plugin.name); - const {actions, utils} = createFakeActions(pluginContentDir); - - await plugin.contentLoaded({ - content, - actions, - }); - - expect(utils.getRouteConfigs()).toEqual([]); + await expect( + plugin.loadContent!(), + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"Docs version current has no docs! At least one doc should exist at /Users/sebastienlorber/Desktop/projects/docusaurus/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/empty-site/docs"`, + ); }); test('docs folder does not exist', async () => { @@ -132,11 +152,10 @@ describe('simple website', () => { const siteDir = path.join(__dirname, '__fixtures__', 'simple-site'); const context = loadContext(siteDir); const sidebarPath = path.join(siteDir, 'sidebars.json'); - const pluginPath = 'docs'; const plugin = pluginContentDocs( context, normalizePluginOptions(OptionsSchema, { - path: pluginPath, + path: 'docs', sidebarPath, homePageId: 'hello', }), @@ -144,19 +163,23 @@ describe('simple website', () => { const pluginContentDir = path.join(context.generatedFilesDir, plugin.name); test('extendCli - docsVersion', () => { - const mock = jest.spyOn(cliDocs, 'docsVersion').mockImplementation(); + const mock = jest + .spyOn(cliDocs, 'cliDocsVersionCommand') + .mockImplementation(); const cli = new commander.Command(); - plugin.extendCli(cli); + // @ts-expect-error: TODO annoying type incompatibility + plugin.extendCli!(cli); cli.parse(['node', 'test', 'docs:version', '1.0.0']); + expect(mock).toHaveBeenCalledTimes(1); expect(mock).toHaveBeenCalledWith('1.0.0', siteDir, DEFAULT_PLUGIN_ID, { - path: pluginPath, + path: 'docs', sidebarPath, }); mock.mockRestore(); }); test('getPathToWatch', () => { - const pathToWatch = plugin.getPathsToWatch(); + const pathToWatch = plugin.getPathsToWatch!(); const matchPattern = pathToWatch.map((filepath) => posixPath(path.relative(siteDir, filepath)), ); @@ -196,15 +219,13 @@ describe('simple website', () => { }); test('content', async () => { - const content = await plugin.loadContent(); - const { - docsMetadata, - docsSidebars, - versionToSidebars, - permalinkToSidebar, - } = content; - expect(versionToSidebars).toEqual({}); - expect(docsMetadata.hello).toEqual({ + const content = await plugin.loadContent!(); + expect(content.loadedVersions.length).toEqual(1); + const [currentVersion] = content.loadedVersions; + + expect(findDocById(currentVersion, 'hello')).toEqual({ + ...defaultDocMetadata, + version: 'current', id: 'hello', unversionedId: 'hello', isDocsHomePage: true, @@ -215,12 +236,18 @@ describe('simple website', () => { permalink: '/docs/foo/bazSlug.html', }, sidebar: 'docs', - source: path.join('@site', pluginPath, 'hello.md'), + source: path.join( + '@site', + path.relative(siteDir, currentVersion.docsDirPath), + 'hello.md', + ), title: 'Hello, World !', description: 'Hi, Endilie here :)', }); - expect(docsMetadata['foo/bar']).toEqual({ + expect(findDocById(currentVersion, 'foo/bar')).toEqual({ + ...defaultDocMetadata, + version: 'current', id: 'foo/bar', unversionedId: 'foo/bar', isDocsHomePage: false, @@ -231,26 +258,30 @@ describe('simple website', () => { permalink: '/docs/foo/bar', slug: '/foo/bar', sidebar: 'docs', - source: path.join('@site', pluginPath, 'foo', 'bar.md'), + source: path.join( + '@site', + path.relative(siteDir, currentVersion.docsDirPath), + 'foo', + 'bar.md', + ), title: 'Bar', description: 'This is custom description', }); - expect(docsSidebars).toMatchSnapshot(); + expect(currentVersion.sidebars).toMatchSnapshot(); const {actions, utils} = createFakeActions(pluginContentDir); - await plugin.contentLoaded({ + await plugin.contentLoaded!({ content, actions, + allContent: {}, }); - // There is only one nested docs route for simple site - const baseMetadata = utils.getCreatedDataByPrefix('docs-route-'); - expect(baseMetadata.docsSidebars).toEqual(docsSidebars); - expect(baseMetadata.permalinkToSidebar).toEqual(permalinkToSidebar); + utils.checkVersionMetadataPropCreated(currentVersion); utils.expectSnapshot(); + expect(utils.getGlobalData()).toMatchSnapshot(); }); }); @@ -268,19 +299,18 @@ describe('versioned website', () => { homePageId: 'hello', }), ); - const env = loadEnv(siteDir, DEFAULT_PLUGIN_ID); - const {docsDir: versionedDir} = env.versioning; - const pluginContentDir = path.join(context.generatedFilesDir, plugin.name); - test('isVersioned', () => { - expect(env.versioning.enabled).toEqual(true); - }); + const pluginContentDir = path.join(context.generatedFilesDir, plugin.name); test('extendCli - docsVersion', () => { - const mock = jest.spyOn(cliDocs, 'docsVersion').mockImplementation(); + const mock = jest + .spyOn(cliDocs, 'cliDocsVersionCommand') + .mockImplementation(); const cli = new commander.Command(); - plugin.extendCli(cli); + // @ts-expect-error: TODO annoying type incompatibility + plugin.extendCli!(cli); cli.parse(['node', 'test', 'docs:version', '2.0.0']); + expect(mock).toHaveBeenCalledTimes(1); expect(mock).toHaveBeenCalledWith('2.0.0', siteDir, DEFAULT_PLUGIN_ID, { path: routeBasePath, sidebarPath, @@ -289,7 +319,7 @@ describe('versioned website', () => { }); test('getPathToWatch', () => { - const pathToWatch = plugin.getPathsToWatch(); + const pathToWatch = plugin.getPathsToWatch!(); const matchPattern = pathToWatch.map((filepath) => posixPath(path.relative(siteDir, filepath)), ); @@ -339,50 +369,65 @@ describe('versioned website', () => { }); test('content', async () => { - const content = await plugin.loadContent(); - const { - docsMetadata, - docsSidebars, - versionToSidebars, - permalinkToSidebar, - } = content; + const content = await plugin.loadContent!(); + expect(content.loadedVersions.length).toEqual(4); + const [ + currentVersion, + version101, + version100, + versionWithSlugs, + ] = content.loadedVersions; // foo/baz.md only exists in version -1.0.0 - expect(docsMetadata['foo/baz']).toBeUndefined(); - expect(docsMetadata['version-1.0.1/foo/baz']).toBeUndefined(); - expect(docsMetadata['foo/bar']).toEqual({ + expect(findDocById(currentVersion, 'foo/baz')).toBeUndefined(); + expect(findDocById(version101, 'foo/baz')).toBeUndefined(); + expect(findDocById(versionWithSlugs, 'foo/baz')).toBeUndefined(); + + expect(findDocById(currentVersion, 'foo/bar')).toEqual({ + ...defaultDocMetadata, id: 'foo/bar', unversionedId: 'foo/bar', isDocsHomePage: false, permalink: '/docs/next/foo/barSlug', slug: '/foo/barSlug', - source: path.join('@site', routeBasePath, 'foo', 'bar.md'), + source: path.join( + '@site', + path.relative(siteDir, currentVersion.docsDirPath), + 'foo', + 'bar.md', + ), title: 'bar', description: 'This is next version of bar.', - version: 'next', + version: 'current', sidebar: 'docs', next: { title: 'hello', permalink: '/docs/next/', }, }); - expect(docsMetadata.hello).toEqual({ + expect(findDocById(currentVersion, 'hello')).toEqual({ + ...defaultDocMetadata, id: 'hello', unversionedId: 'hello', isDocsHomePage: true, permalink: '/docs/next/', slug: '/', - source: path.join('@site', routeBasePath, 'hello.md'), + source: path.join( + '@site', + path.relative(siteDir, currentVersion.docsDirPath), + 'hello.md', + ), title: 'hello', description: 'Hello next !', - version: 'next', + version: 'current', sidebar: 'docs', previous: { title: 'bar', permalink: '/docs/next/foo/barSlug', }, }); - expect(docsMetadata['version-1.0.1/hello']).toEqual({ + expect(findDocById(version101, 'hello')).toEqual({ + ...defaultDocMetadata, id: 'version-1.0.1/hello', unversionedId: 'hello', isDocsHomePage: true, @@ -390,8 +435,7 @@ describe('versioned website', () => { slug: '/', source: path.join( '@site', - path.relative(siteDir, versionedDir), - 'version-1.0.1', + path.relative(siteDir, version101.docsDirPath), 'hello.md', ), title: 'hello', @@ -403,7 +447,8 @@ describe('versioned website', () => { permalink: '/docs/foo/bar', }, }); - expect(docsMetadata['version-1.0.0/foo/baz']).toEqual({ + expect(findDocById(version100, 'foo/baz')).toEqual({ + ...defaultDocMetadata, id: 'version-1.0.0/foo/baz', unversionedId: 'foo/baz', isDocsHomePage: false, @@ -411,8 +456,7 @@ describe('versioned website', () => { slug: '/foo/baz', source: path.join( '@site', - path.relative(siteDir, versionedDir), - 'version-1.0.0', + path.relative(siteDir, version100.docsDirPath), 'foo', 'baz.md', ), @@ -431,47 +475,24 @@ describe('versioned website', () => { }, }); - expect(docsSidebars).toMatchSnapshot('all sidebars'); - expect(versionToSidebars).toMatchSnapshot( - 'sidebars needed for each version', + expect(currentVersion.sidebars).toMatchSnapshot('current version sidebars'); + expect(version101.sidebars).toMatchSnapshot('101 version sidebars'); + expect(version100.sidebars).toMatchSnapshot('100 version sidebars'); + expect(versionWithSlugs.sidebars).toMatchSnapshot( + 'withSlugs version sidebars', ); + const {actions, utils} = createFakeActions(pluginContentDir); - await plugin.contentLoaded({ + await plugin.contentLoaded!({ content, actions, + allContent: {}, }); - // The created base metadata for each nested docs route is smartly chunked/ splitted across version - const latestVersionBaseMetadata = utils.getCreatedDataByPrefix( - 'docs-route-', - ); - expect(latestVersionBaseMetadata).toMatchSnapshot( - 'base metadata for latest version', - ); - expect(latestVersionBaseMetadata.docsSidebars).not.toEqual(docsSidebars); - expect(latestVersionBaseMetadata.permalinkToSidebar).not.toEqual( - permalinkToSidebar, - ); - const nextVersionBaseMetadata = utils.getCreatedDataByPrefix( - 'docs-next-route-', - ); - expect(nextVersionBaseMetadata).toMatchSnapshot( - 'base metadata for next version', - ); - expect(nextVersionBaseMetadata.docsSidebars).not.toEqual(docsSidebars); - expect(nextVersionBaseMetadata.permalinkToSidebar).not.toEqual( - permalinkToSidebar, - ); - const firstVersionBaseMetadata = utils.getCreatedDataByPrefix( - 'docs-1-0-0-route-', - ); - expect(firstVersionBaseMetadata).toMatchSnapshot( - 'base metadata for first version', - ); - expect(nextVersionBaseMetadata.docsSidebars).not.toEqual(docsSidebars); - expect(nextVersionBaseMetadata.permalinkToSidebar).not.toEqual( - permalinkToSidebar, - ); + utils.checkVersionMetadataPropCreated(currentVersion); + utils.checkVersionMetadataPropCreated(version101); + utils.checkVersionMetadataPropCreated(version100); + utils.checkVersionMetadataPropCreated(versionWithSlugs); utils.expectSnapshot(); }); @@ -492,19 +513,17 @@ describe('versioned website (community)', () => { sidebarPath, }), ); - const env = loadEnv(siteDir, pluginId); - const {docsDir: versionedDir} = env.versioning; const pluginContentDir = path.join(context.generatedFilesDir, plugin.name); - test('isVersioned', () => { - expect(env.versioning.enabled).toEqual(true); - }); - test('extendCli - docsVersion', () => { - const mock = jest.spyOn(cliDocs, 'docsVersion').mockImplementation(); + const mock = jest + .spyOn(cliDocs, 'cliDocsVersionCommand') + .mockImplementation(); const cli = new commander.Command(); - plugin.extendCli(cli); + // @ts-expect-error: TODO annoying type incompatibility + plugin.extendCli!(cli); cli.parse(['node', 'test', `docs:version:${pluginId}`, '2.0.0']); + expect(mock).toHaveBeenCalledTimes(1); expect(mock).toHaveBeenCalledWith('2.0.0', siteDir, pluginId, { path: routeBasePath, sidebarPath, @@ -513,7 +532,7 @@ describe('versioned website (community)', () => { }); test('getPathToWatch', () => { - const pathToWatch = plugin.getPathsToWatch(); + const pathToWatch = plugin.getPathsToWatch!(); const matchPattern = pathToWatch.map((filepath) => posixPath(path.relative(siteDir, filepath)), ); @@ -549,27 +568,29 @@ describe('versioned website (community)', () => { }); test('content', async () => { - const content = await plugin.loadContent(); - const { - docsMetadata, - docsSidebars, - versionToSidebars, - permalinkToSidebar, - } = content; - - expect(docsMetadata.team).toEqual({ + const content = await plugin.loadContent!(); + expect(content.loadedVersions.length).toEqual(2); + const [currentVersion, version100] = content.loadedVersions; + + expect(findDocById(currentVersion, 'team')).toEqual({ + ...defaultDocMetadata, id: 'team', unversionedId: 'team', isDocsHomePage: false, permalink: '/community/next/team', slug: '/team', - source: path.join('@site', routeBasePath, 'team.md'), + source: path.join( + '@site', + path.relative(siteDir, currentVersion.docsDirPath), + 'team.md', + ), title: 'team', description: 'Team current version', - version: 'next', + version: 'current', sidebar: 'community', }); - expect(docsMetadata['version-1.0.0/team']).toEqual({ + expect(findDocById(version100, 'team')).toEqual({ + ...defaultDocMetadata, id: 'version-1.0.0/team', unversionedId: 'team', isDocsHomePage: false, @@ -577,8 +598,7 @@ describe('versioned website (community)', () => { slug: '/team', source: path.join( '@site', - path.relative(siteDir, versionedDir), - 'version-1.0.0', + path.relative(siteDir, version100.docsDirPath), 'team.md', ), title: 'team', @@ -587,38 +607,18 @@ describe('versioned website (community)', () => { sidebar: 'version-1.0.0/community', }); - expect(docsSidebars).toMatchSnapshot('all sidebars'); - expect(versionToSidebars).toMatchSnapshot( - 'sidebars needed for each version', - ); + expect(currentVersion.sidebars).toMatchSnapshot('current version sidebars'); + expect(version100.sidebars).toMatchSnapshot('100 version sidebars'); const {actions, utils} = createFakeActions(pluginContentDir); - await plugin.contentLoaded({ + await plugin.contentLoaded!({ content, actions, + allContent: {}, }); - // The created base metadata for each nested docs route is smartly chunked/ splitted across version - const latestVersionBaseMetadata = utils.getCreatedDataByPrefix( - 'community-route-', - ); - expect(latestVersionBaseMetadata).toMatchSnapshot( - 'base metadata for latest version', - ); - expect(latestVersionBaseMetadata.docsSidebars).not.toEqual(docsSidebars); - expect(latestVersionBaseMetadata.permalinkToSidebar).not.toEqual( - permalinkToSidebar, - ); - const nextVersionBaseMetadata = utils.getCreatedDataByPrefix( - 'community-next-route-', - ); - expect(nextVersionBaseMetadata).toMatchSnapshot( - 'base metadata for next version', - ); - expect(nextVersionBaseMetadata.docsSidebars).not.toEqual(docsSidebars); - expect(nextVersionBaseMetadata.permalinkToSidebar).not.toEqual( - permalinkToSidebar, - ); + utils.checkVersionMetadataPropCreated(currentVersion); + utils.checkVersionMetadataPropCreated(version100); utils.expectSnapshot(); }); diff --git a/packages/docusaurus-plugin-content-docs/src/cli.ts b/packages/docusaurus-plugin-content-docs/src/cli.ts index c0ea5546615b..75b734c04b0d 100644 --- a/packages/docusaurus-plugin-content-docs/src/cli.ts +++ b/packages/docusaurus-plugin-content-docs/src/cli.ts @@ -18,7 +18,7 @@ import {DEFAULT_PLUGIN_ID} from '@docusaurus/core/lib/constants'; // Tests depend on non-default export for mocking. // eslint-disable-next-line import/prefer-default-export -export function cliDocsVersion( +export function cliDocsVersionCommand( version: string | null | undefined, siteDir: string, pluginId: string, diff --git a/packages/docusaurus-plugin-content-docs/src/index.ts b/packages/docusaurus-plugin-content-docs/src/index.ts index 2dd69a227d1e..92f5d5dcba48 100644 --- a/packages/docusaurus-plugin-content-docs/src/index.ts +++ b/packages/docusaurus-plugin-content-docs/src/index.ts @@ -32,7 +32,7 @@ import { DocFile, } from './types'; import {RuleSetRule} from 'webpack'; -import {cliDocsVersion} from './cli'; +import {cliDocsVersionCommand} from './cli'; import {VERSIONS_JSON_FILE} from './constants'; import {OptionsSchema} from './options'; import {flatten, keyBy, compact} from 'lodash'; @@ -88,7 +88,7 @@ export default function pluginContentDocs( .arguments('') .description(commandDescription) .action((version) => { - cliDocsVersion(version, siteDir, pluginId, { + cliDocsVersionCommand(version, siteDir, pluginId, { path: options.path, sidebarPath: options.sidebarPath, }); @@ -123,11 +123,7 @@ export default function pluginContentDocs( const docFiles = await readVersionDocs(versionMetadata, options); if (docFiles.length === 0) { throw new Error( - `Docs version has no docs! At least once document is required!\n For version=${JSON.stringify( - versionMetadata, - null, - 2, - )}`, + `Docs version ${versionMetadata.versionName} has no docs! At least one doc should exist at ${versionMetadata.docsDirPath}`, ); } async function processVersionDoc(docFile: DocFile) { @@ -155,6 +151,9 @@ export default function pluginContentDocs( (doc) => doc.id, ); + const validDocIds = Object.keys(docsBaseById); + sidebarsUtils.checkSidebarsDocIds(validDocIds); + // Add sidebar/next/previous to the docs function addNavData(doc: DocMetadataBase): DocMetadata { const { diff --git a/packages/docusaurus-plugin-content-docs/src/props.ts b/packages/docusaurus-plugin-content-docs/src/props.ts index cdc1d94ba27c..f194648db697 100644 --- a/packages/docusaurus-plugin-content-docs/src/props.ts +++ b/packages/docusaurus-plugin-content-docs/src/props.ts @@ -16,7 +16,7 @@ import { } from './types'; import {keyBy, mapValues} from 'lodash'; -function toSidebarsProp(loadedVersion: LoadedVersion): PropSidebars { +export function toSidebarsProp(loadedVersion: LoadedVersion): PropSidebars { const docsById = keyBy(loadedVersion.docs, (doc) => doc.id); const convertDocLink = (item: SidebarItemDoc): SidebarItemLink => { diff --git a/packages/docusaurus-plugin-content-docs/src/sidebars.ts b/packages/docusaurus-plugin-content-docs/src/sidebars.ts index 9fed7529764d..e2cbd02f6d6f 100644 --- a/packages/docusaurus-plugin-content-docs/src/sidebars.ts +++ b/packages/docusaurus-plugin-content-docs/src/sidebars.ts @@ -15,7 +15,7 @@ import { SidebarItemDoc, Sidebar, } from './types'; -import {mapValues, flatten} from 'lodash'; +import {mapValues, flatten, difference} from 'lodash'; import {getElementsAround} from '@docusaurus/utils'; type SidebarItemCategoryJSON = { @@ -277,5 +277,25 @@ export function createSidebarsUtils(sidebars: Sidebars) { } } - return {getFirstDocIdOfFirstSidebar, getSidebarNameByDocId, getDocNavigation}; + function checkSidebarsDocIds(validDocIds: string[]) { + const allSidebarDocIds = flatten(Object.values(sidebarNameToDocIds)); + const invalidSidebarDocIds = difference(allSidebarDocIds, validDocIds); + if (invalidSidebarDocIds.length > 0) { + throw new Error( + `Bad sidebars file. +These sidebar document ids do not exist: +- ${invalidSidebarDocIds.sort().join('\n- ')}\`, + +Available document ids= +- ${validDocIds.sort().join('\n- ')}`, + ); + } + } + + return { + getFirstDocIdOfFirstSidebar, + getSidebarNameByDocId, + getDocNavigation, + checkSidebarsDocIds, + }; } From 62b90d934a61885fcb4c14b1bc4618bb3fb0d3ea Mon Sep 17 00:00:00 2001 From: slorber Date: Fri, 14 Aug 2020 16:56:30 +0200 Subject: [PATCH 26/30] fix test due to absolute path --- .../src/__tests__/index.test.ts | 2 +- packages/docusaurus-plugin-content-docs/src/index.ts | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/index.test.ts b/packages/docusaurus-plugin-content-docs/src/__tests__/index.test.ts index 0aadf6ade96d..fa1b1b71fa2b 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/index.test.ts +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/index.test.ts @@ -130,7 +130,7 @@ describe('empty/no docs website', () => { await expect( plugin.loadContent!(), ).rejects.toThrowErrorMatchingInlineSnapshot( - `"Docs version current has no docs! At least one doc should exist at /Users/sebastienlorber/Desktop/projects/docusaurus/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/empty-site/docs"`, + `"Docs version current has no docs! At least one doc should exist at path=[docs]"`, ); }); diff --git a/packages/docusaurus-plugin-content-docs/src/index.ts b/packages/docusaurus-plugin-content-docs/src/index.ts index 92f5d5dcba48..579cafc92345 100644 --- a/packages/docusaurus-plugin-content-docs/src/index.ts +++ b/packages/docusaurus-plugin-content-docs/src/index.ts @@ -123,7 +123,12 @@ export default function pluginContentDocs( const docFiles = await readVersionDocs(versionMetadata, options); if (docFiles.length === 0) { throw new Error( - `Docs version ${versionMetadata.versionName} has no docs! At least one doc should exist at ${versionMetadata.docsDirPath}`, + `Docs version ${ + versionMetadata.versionName + } has no docs! At least one doc should exist at path=[${path.relative( + siteDir, + versionMetadata.docsDirPath, + )}]`, ); } async function processVersionDoc(docFile: DocFile) { @@ -184,7 +189,7 @@ export default function pluginContentDocs( sourceToPermalink[source] = permalink; }); - // TODO useful? replace with global state logic? + // TODO really useful? replace with global state logic? const permalinkToSidebar: PermalinkToSidebar = {}; Object.values(docs).forEach((doc) => { if (doc.sidebar) { From 96eee71611a62204b9d28b7bf153b891ed55f15f Mon Sep 17 00:00:00 2001 From: slorber Date: Fri, 14 Aug 2020 16:59:35 +0200 Subject: [PATCH 27/30] fix webpack tests --- .../src/server/__tests__/__fixtures__/custom-site/sidebars.json | 1 + .../src/server/__tests__/__fixtures__/simple-site/sidebars.json | 1 + 2 files changed, 2 insertions(+) create mode 100644 packages/docusaurus/src/server/__tests__/__fixtures__/custom-site/sidebars.json create mode 100644 packages/docusaurus/src/server/__tests__/__fixtures__/simple-site/sidebars.json diff --git a/packages/docusaurus/src/server/__tests__/__fixtures__/custom-site/sidebars.json b/packages/docusaurus/src/server/__tests__/__fixtures__/custom-site/sidebars.json new file mode 100644 index 000000000000..0967ef424bce --- /dev/null +++ b/packages/docusaurus/src/server/__tests__/__fixtures__/custom-site/sidebars.json @@ -0,0 +1 @@ +{} diff --git a/packages/docusaurus/src/server/__tests__/__fixtures__/simple-site/sidebars.json b/packages/docusaurus/src/server/__tests__/__fixtures__/simple-site/sidebars.json new file mode 100644 index 000000000000..0967ef424bce --- /dev/null +++ b/packages/docusaurus/src/server/__tests__/__fixtures__/simple-site/sidebars.json @@ -0,0 +1 @@ +{} From 8c0a606fcf0cc277140645b7a06c711d8126bd29 Mon Sep 17 00:00:00 2001 From: slorber Date: Mon, 17 Aug 2020 17:18:09 +0200 Subject: [PATCH 28/30] refactor linkify + add broken markdown links warning --- .../src/index.ts | 35 ++--- .../__tests__/__fixtures__/docs/doc5.md | 6 + .../src/markdown/__tests__/linkify.test.ts | 105 ++++++++++++--- .../src/markdown/index.ts | 19 +-- .../src/markdown/linkify.ts | 123 ++++++++++-------- .../src/types.ts | 13 ++ website/docs/using-themes.md | 2 +- .../version-2.0.0-alpha.54/using-themes.md | 2 +- .../version-2.0.0-alpha.55/using-themes.md | 2 +- .../version-2.0.0-alpha.56/using-themes.md | 2 +- .../version-2.0.0-alpha.58/using-themes.md | 2 +- .../version-2.0.0-alpha.60/using-themes.md | 2 +- .../version-2.0.0-alpha.61/using-themes.md | 2 +- 13 files changed, 202 insertions(+), 113 deletions(-) create mode 100644 packages/docusaurus-plugin-content-docs/src/markdown/__tests__/__fixtures__/docs/doc5.md diff --git a/packages/docusaurus-plugin-content-docs/src/index.ts b/packages/docusaurus-plugin-content-docs/src/index.ts index 579cafc92345..6d451b2b0306 100644 --- a/packages/docusaurus-plugin-content-docs/src/index.ts +++ b/packages/docusaurus-plugin-content-docs/src/index.ts @@ -16,7 +16,7 @@ import {LoadContext, Plugin, RouteConfig} from '@docusaurus/types'; import {loadSidebars, createSidebarsUtils} from './sidebars'; import {readVersionDocs, processDocMetadata} from './docs'; -import {readVersionsMetadata, getVersionedDocsDirPath} from './versions'; +import {readVersionsMetadata} from './versions'; import { PluginOptions, @@ -30,6 +30,7 @@ import { DocNavLink, LoadedVersion, DocFile, + DocsMarkdownOption, } from './types'; import {RuleSetRule} from 'webpack'; import {cliDocsVersionCommand} from './cli'; @@ -38,6 +39,7 @@ import {OptionsSchema} from './options'; import {flatten, keyBy, compact} from 'lodash'; import {toGlobalDataVersion} from './globalData'; import {toVersionMetadataProp} from './props'; +import chalk from 'chalk'; export default function pluginContentDocs( context: LoadContext, @@ -47,8 +49,6 @@ export default function pluginContentDocs( const versionsMetadata = readVersionsMetadata({context, options}); - const docsDir = path.resolve(siteDir, options.path); - const sourceToPermalink: SourceToPermalink = {}; const pluginId = options.id ?? DEFAULT_PLUGIN_ID; @@ -199,7 +199,7 @@ export default function pluginContentDocs( // The "main doc" is the "version entry point" // We browse this doc by clicking on a version: - // - the doc at / + // - the "home" doc (at '/docs/') // - the first doc of the first sidebar // - a random doc (if no docs are in any sidebar... edge case) function getMainDoc(): DocMetadata { @@ -297,10 +297,20 @@ export default function pluginContentDocs( const {getBabelLoader, getCacheLoader} = utils; const {rehypePlugins, remarkPlugins} = options; - // TODO instead of creating one mdx loader rule for all versions - // it may be simpler to create one mdx loader per version - // particularly to handle the markdown/linkify process - // (docsDir/versionedDir are a bit annoying here...) + const docsMarkdownOptions: DocsMarkdownOption = { + siteDir, + sourceToPermalink, + versionsMetadata, + onBrokenMarkdownLink: (brokenMarkdownLink) => { + // TODO make this warning configurable? + console.warn( + chalk.yellow( + `Docs markdown link couldn't be resolved: (${brokenMarkdownLink.link}) in ${brokenMarkdownLink.filePath} for version ${brokenMarkdownLink.version.versionName}`, + ), + ); + }, + }; + function createMDXLoaderRule(): RuleSetRule { return { test: /(\.mdx?)$/, @@ -324,14 +334,7 @@ export default function pluginContentDocs( }, { loader: path.resolve(__dirname, './markdown/index.js'), - options: { - siteDir, - - // TODO legacy attributes, need refactor - docsDir, - sourceToPermalink, - versionedDir: getVersionedDocsDirPath(siteDir, options.id), - }, + options: docsMarkdownOptions, }, ]), }; diff --git a/packages/docusaurus-plugin-content-docs/src/markdown/__tests__/__fixtures__/docs/doc5.md b/packages/docusaurus-plugin-content-docs/src/markdown/__tests__/__fixtures__/docs/doc5.md new file mode 100644 index 000000000000..cea1e3ade8f9 --- /dev/null +++ b/packages/docusaurus-plugin-content-docs/src/markdown/__tests__/__fixtures__/docs/doc5.md @@ -0,0 +1,6 @@ +### Not Existing Docs + +- [docNotExist1](docNotExist1.md) +- [docNotExist2](./docNotExist2.mdx) +- [docNotExist3](../docNotExist3.mdx) +- [docNotExist4](./subdir/docNotExist4.md) diff --git a/packages/docusaurus-plugin-content-docs/src/markdown/__tests__/linkify.test.ts b/packages/docusaurus-plugin-content-docs/src/markdown/__tests__/linkify.test.ts index 9147a683c572..1e12fc36dfba 100644 --- a/packages/docusaurus-plugin-content-docs/src/markdown/__tests__/linkify.test.ts +++ b/packages/docusaurus-plugin-content-docs/src/markdown/__tests__/linkify.test.ts @@ -7,13 +7,41 @@ import fs from 'fs-extra'; import path from 'path'; -import linkify from '../linkify'; -import {SourceToPermalink} from '../../types'; -import {VERSIONED_DOCS_DIR} from '../../constants'; +import {linkify} from '../linkify'; +import { + DocsMarkdownOption, + SourceToPermalink, + VersionMetadata, + BrokenMarkdownLink, +} from '../../types'; +import {VERSIONED_DOCS_DIR, CURRENT_VERSION_NAME} from '../../constants'; + +function createFakeVersion( + versionName: string, + docsDirPath: string, +): VersionMetadata { + return { + versionName, + versionLabel: 'Any', + versionPath: 'any', + docsDirPath, + sidebarFilePath: 'any', + routePriority: undefined, + isLast: false, + }; +} const siteDir = path.join(__dirname, '__fixtures__'); -const docsDir = path.join(siteDir, 'docs'); -const versionedDir = path.join(siteDir, VERSIONED_DOCS_DIR); + +const versionCurrent = createFakeVersion( + CURRENT_VERSION_NAME, + path.join(siteDir, 'docs'), +); +const version100 = createFakeVersion( + CURRENT_VERSION_NAME, + path.join(siteDir, VERSIONED_DOCS_DIR, 'version-1.0.0'), +); + const sourceToPermalink: SourceToPermalink = { '@site/docs/doc1.md': '/docs/doc1', '@site/docs/doc2.md': '/docs/doc2', @@ -24,28 +52,34 @@ const sourceToPermalink: SourceToPermalink = { '/docs/1.0.0/subdir/doc1', }; -const transform = (filepath) => { - const content = fs.readFileSync(filepath, 'utf-8'); - const transformedContent = linkify( - content, - filepath, - docsDir, - siteDir, +function createMarkdownOptions( + options?: Partial, +): DocsMarkdownOption { + return { sourceToPermalink, - versionedDir, - ); + onBrokenMarkdownLink: () => {}, + versionsMetadata: [versionCurrent, version100], + siteDir, + ...options, + }; +} + +const transform = (filepath: string, options?: Partial) => { + const markdownOptions = createMarkdownOptions(options); + const content = fs.readFileSync(filepath, 'utf-8'); + const transformedContent = linkify(content, filepath, markdownOptions); return [content, transformedContent]; }; test('transform nothing', () => { - const doc1 = path.join(docsDir, 'doc1.md'); + const doc1 = path.join(versionCurrent.docsDirPath, 'doc1.md'); const [content, transformedContent] = transform(doc1); expect(transformedContent).toMatchSnapshot(); expect(content).toEqual(transformedContent); }); test('transform to correct links', () => { - const doc2 = path.join(docsDir, 'doc2.md'); + const doc2 = path.join(versionCurrent.docsDirPath, 'doc2.md'); const [content, transformedContent] = transform(doc2); expect(transformedContent).toMatchSnapshot(); expect(transformedContent).toContain('](/docs/doc1'); @@ -58,7 +92,8 @@ test('transform to correct links', () => { }); test('transform relative links', () => { - const doc3 = path.join(docsDir, 'subdir', 'doc3.md'); + const doc3 = path.join(versionCurrent.docsDirPath, 'subdir', 'doc3.md'); + const [content, transformedContent] = transform(doc3); expect(transformedContent).toMatchSnapshot(); expect(transformedContent).toContain('](/docs/doc2'); @@ -67,7 +102,7 @@ test('transform relative links', () => { }); test('transforms reference links', () => { - const doc4 = path.join(docsDir, 'doc4.md'); + const doc4 = path.join(versionCurrent.docsDirPath, 'doc4.md'); const [content, transformedContent] = transform(doc4); expect(transformedContent).toMatchSnapshot(); expect(transformedContent).toContain('[doc1]: /docs/doc1'); @@ -77,8 +112,38 @@ test('transforms reference links', () => { expect(content).not.toEqual(transformedContent); }); +test('report broken markdown links', () => { + const doc5 = path.join(versionCurrent.docsDirPath, 'doc5.md'); + const onBrokenMarkdownLink = jest.fn(); + const [content, transformedContent] = transform(doc5, { + onBrokenMarkdownLink, + }); + expect(transformedContent).toEqual(content); + expect(onBrokenMarkdownLink).toHaveBeenCalledTimes(4); + expect(onBrokenMarkdownLink).toHaveBeenNthCalledWith(1, { + filePath: doc5, + link: 'docNotExist1.md', + version: versionCurrent, + } as BrokenMarkdownLink); + expect(onBrokenMarkdownLink).toHaveBeenNthCalledWith(2, { + filePath: doc5, + link: './docNotExist2.mdx', + version: versionCurrent, + } as BrokenMarkdownLink); + expect(onBrokenMarkdownLink).toHaveBeenNthCalledWith(3, { + filePath: doc5, + link: '../docNotExist3.mdx', + version: versionCurrent, + } as BrokenMarkdownLink); + expect(onBrokenMarkdownLink).toHaveBeenNthCalledWith(4, { + filePath: doc5, + link: './subdir/docNotExist4.md', + version: versionCurrent, + } as BrokenMarkdownLink); +}); + test('transforms absolute links in versioned docs', () => { - const doc2 = path.join(versionedDir, 'version-1.0.0', 'doc2.md'); + const doc2 = path.join(version100.docsDirPath, 'doc2.md'); const [content, transformedContent] = transform(doc2); expect(transformedContent).toMatchSnapshot(); expect(transformedContent).toContain('](/docs/1.0.0/subdir/doc1'); @@ -89,7 +154,7 @@ test('transforms absolute links in versioned docs', () => { }); test('transforms relative links in versioned docs', () => { - const doc1 = path.join(versionedDir, 'version-1.0.0', 'subdir', 'doc1.md'); + const doc1 = path.join(version100.docsDirPath, 'subdir', 'doc1.md'); const [content, transformedContent] = transform(doc1); expect(transformedContent).toMatchSnapshot(); expect(transformedContent).toContain('](/docs/1.0.0/doc2'); diff --git a/packages/docusaurus-plugin-content-docs/src/markdown/index.ts b/packages/docusaurus-plugin-content-docs/src/markdown/index.ts index 6c54199a8eb8..d02c03f6bb9a 100644 --- a/packages/docusaurus-plugin-content-docs/src/markdown/index.ts +++ b/packages/docusaurus-plugin-content-docs/src/markdown/index.ts @@ -7,26 +7,15 @@ import {getOptions} from 'loader-utils'; import {loader} from 'webpack'; -import linkify from './linkify'; +import {linkify} from './linkify'; +import {DocsMarkdownOption} from '../types'; const markdownLoader: loader.Loader = function (source) { const fileString = source as string; const callback = this.async(); - const {docsDir, siteDir, versionedDir, sourceToPermalink} = getOptions(this); - + const options = getOptions(this) as DocsMarkdownOption; return ( - callback && - callback( - null, - linkify( - fileString, - this.resourcePath, - docsDir, - siteDir, - sourceToPermalink, - versionedDir, - ), - ) + callback && callback(null, linkify(fileString, this.resourcePath, options)) ); }; diff --git a/packages/docusaurus-plugin-content-docs/src/markdown/linkify.ts b/packages/docusaurus-plugin-content-docs/src/markdown/linkify.ts index 66250aac26a8..5d48335ba86e 100644 --- a/packages/docusaurus-plugin-content-docs/src/markdown/linkify.ts +++ b/packages/docusaurus-plugin-content-docs/src/markdown/linkify.ts @@ -7,68 +7,81 @@ import path from 'path'; import {resolve} from 'url'; -import {getSubFolder} from '@docusaurus/utils'; -import {SourceToPermalink} from '../types'; +import { + DocsMarkdownOption, + VersionMetadata, + BrokenMarkdownLink, +} from '../types'; -export default function ( - fileString: string, - filePath: string, - docsDir: string, - siteDir: string, - sourceToPermalink: SourceToPermalink, - versionedDir?: string, -): string { - // Determine the source dir. e.g: /website/docs, /website/versioned_docs/version-1.0.0 - let sourceDir: string | undefined; - const thisSource = filePath; - if (thisSource.startsWith(docsDir)) { - sourceDir = docsDir; - } else if (versionedDir && thisSource.startsWith(versionedDir)) { - const specificVersionDir = getSubFolder(thisSource, versionedDir); - // e.g: specificVersionDir = version-1.0.0 - if (specificVersionDir) { - sourceDir = path.join(versionedDir, specificVersionDir); - } +function getVersion(filePath: string, options: DocsMarkdownOption) { + const versionFound = options.versionsMetadata.find((version) => + filePath.startsWith(version.docsDirPath), + ); + if (!versionFound) { + throw new Error( + `Unexpected, markdown file does not belong to any docs version! file=${filePath}`, + ); } + return versionFound; +} - let content = fileString; +function replaceMarkdownLinks( + fileString: string, + filePath: string, + version: VersionMetadata, + options: DocsMarkdownOption, +) { + const {siteDir, sourceToPermalink, onBrokenMarkdownLink} = options; + const {docsDirPath} = version; // Replace internal markdown linking (except in fenced blocks). - if (sourceDir) { - let fencedBlock = false; - const lines = content.split('\n').map((line) => { - if (line.trim().startsWith('```')) { - fencedBlock = !fencedBlock; - } - if (fencedBlock) { - return line; - } + let fencedBlock = false; + const lines = fileString.split('\n').map((line) => { + if (line.trim().startsWith('```')) { + fencedBlock = !fencedBlock; + } + if (fencedBlock) { + return line; + } - let modifiedLine = line; - // Replace inline-style links or reference-style links e.g: - // This is [Document 1](doc1.md) -> we replace this doc1.md with correct link - // [doc1]: doc1.md -> we replace this doc1.md with correct link - const mdRegex = /(?:(?:\]\()|(?:\]:\s?))(?!https)([^'")\]\s>]+\.mdx?)/g; - let mdMatch = mdRegex.exec(modifiedLine); - while (mdMatch !== null) { - // Replace it to correct html link. - const mdLink = mdMatch[1]; - const targetSource = `${sourceDir}/${mdLink}`; - const aliasedSource = (source: string) => - `@site/${path.relative(siteDir, source)}`; - const permalink = - sourceToPermalink[aliasedSource(resolve(thisSource, mdLink))] || - sourceToPermalink[aliasedSource(targetSource)]; - if (permalink) { - modifiedLine = modifiedLine.replace(mdLink, permalink); - } - mdMatch = mdRegex.exec(modifiedLine); + let modifiedLine = line; + // Replace inline-style links or reference-style links e.g: + // This is [Document 1](doc1.md) -> we replace this doc1.md with correct link + // [doc1]: doc1.md -> we replace this doc1.md with correct link + const mdRegex = /(?:(?:\]\()|(?:\]:\s?))(?!https)([^'")\]\s>]+\.mdx?)/g; + let mdMatch = mdRegex.exec(modifiedLine); + while (mdMatch !== null) { + // Replace it to correct html link. + const mdLink = mdMatch[1]; + const targetSource = `${docsDirPath}/${mdLink}`; + const aliasedSource = (source: string) => + `@site/${path.relative(siteDir, source)}`; + const permalink = + sourceToPermalink[aliasedSource(resolve(filePath, mdLink))] || + sourceToPermalink[aliasedSource(targetSource)]; + if (permalink) { + modifiedLine = modifiedLine.replace(mdLink, permalink); + } else { + const brokenMarkdownLink: BrokenMarkdownLink = { + version, + filePath, + link: mdLink, + }; + onBrokenMarkdownLink(brokenMarkdownLink); } - return modifiedLine; - }); + mdMatch = mdRegex.exec(modifiedLine); + } + return modifiedLine; + }); - content = lines.join('\n'); - } + return lines.join('\n'); +} - return content; +export function linkify( + fileString: string, + filePath: string, + options: DocsMarkdownOption, +): string { + const version = getVersion(filePath, options); + return replaceMarkdownLinks(fileString, filePath, version, options); } diff --git a/packages/docusaurus-plugin-content-docs/src/types.ts b/packages/docusaurus-plugin-content-docs/src/types.ts index 889dab70ffce..9528f5758b51 100644 --- a/packages/docusaurus-plugin-content-docs/src/types.ts +++ b/packages/docusaurus-plugin-content-docs/src/types.ts @@ -175,3 +175,16 @@ export type PropSidebarItem = PropSidebarItemLink | PropSidebarItemCategory; export type PropSidebars = { [sidebarId: string]: PropSidebarItem[]; }; + +export type BrokenMarkdownLink = { + filePath: string; + version: VersionMetadata; + link: string; +}; + +export type DocsMarkdownOption = { + versionsMetadata: VersionMetadata[]; + siteDir: string; + sourceToPermalink: SourceToPermalink; + onBrokenMarkdownLink: (brokenMarkdownLink: BrokenMarkdownLink) => void; +}; diff --git a/website/docs/using-themes.md b/website/docs/using-themes.md index 5f66df46cad1..ad542fd028f2 100644 --- a/website/docs/using-themes.md +++ b/website/docs/using-themes.md @@ -189,7 +189,7 @@ High-level overview about themes: Related pieces --- -- [Advanced Guides – Themes](advanced-themes.md) +- [Advanced Guides – Themes](using-themes.md) - [Lifecycle APIs](lifecycle-apis.md) References diff --git a/website/versioned_docs/version-2.0.0-alpha.54/using-themes.md b/website/versioned_docs/version-2.0.0-alpha.54/using-themes.md index 544f93293a66..0aded7b80931 100644 --- a/website/versioned_docs/version-2.0.0-alpha.54/using-themes.md +++ b/website/versioned_docs/version-2.0.0-alpha.54/using-themes.md @@ -182,7 +182,7 @@ High-level overview about themes: Related pieces --- -- [Advanced Guides – Themes](advanced-themes.md) +- [Advanced Guides – Themes](using-themes.md) - [Lifecycle APIs](lifecycle-apis.md) References diff --git a/website/versioned_docs/version-2.0.0-alpha.55/using-themes.md b/website/versioned_docs/version-2.0.0-alpha.55/using-themes.md index 56e706ab9f84..f6701b9a3585 100644 --- a/website/versioned_docs/version-2.0.0-alpha.55/using-themes.md +++ b/website/versioned_docs/version-2.0.0-alpha.55/using-themes.md @@ -184,7 +184,7 @@ High-level overview about themes: Related pieces --- -- [Advanced Guides – Themes](advanced-themes.md) +- [Advanced Guides – Themes](using-themes.md) - [Lifecycle APIs](lifecycle-apis.md) References diff --git a/website/versioned_docs/version-2.0.0-alpha.56/using-themes.md b/website/versioned_docs/version-2.0.0-alpha.56/using-themes.md index e09dd1e6f384..f330a9a35b7d 100644 --- a/website/versioned_docs/version-2.0.0-alpha.56/using-themes.md +++ b/website/versioned_docs/version-2.0.0-alpha.56/using-themes.md @@ -184,7 +184,7 @@ High-level overview about themes: Related pieces --- -- [Advanced Guides – Themes](advanced-themes.md) +- [Advanced Guides – Themes](using-themes.md) - [Lifecycle APIs](lifecycle-apis.md) References diff --git a/website/versioned_docs/version-2.0.0-alpha.58/using-themes.md b/website/versioned_docs/version-2.0.0-alpha.58/using-themes.md index 56e706ab9f84..f6701b9a3585 100644 --- a/website/versioned_docs/version-2.0.0-alpha.58/using-themes.md +++ b/website/versioned_docs/version-2.0.0-alpha.58/using-themes.md @@ -184,7 +184,7 @@ High-level overview about themes: Related pieces --- -- [Advanced Guides – Themes](advanced-themes.md) +- [Advanced Guides – Themes](using-themes.md) - [Lifecycle APIs](lifecycle-apis.md) References diff --git a/website/versioned_docs/version-2.0.0-alpha.60/using-themes.md b/website/versioned_docs/version-2.0.0-alpha.60/using-themes.md index 5f66df46cad1..ad542fd028f2 100644 --- a/website/versioned_docs/version-2.0.0-alpha.60/using-themes.md +++ b/website/versioned_docs/version-2.0.0-alpha.60/using-themes.md @@ -189,7 +189,7 @@ High-level overview about themes: Related pieces --- -- [Advanced Guides – Themes](advanced-themes.md) +- [Advanced Guides – Themes](using-themes.md) - [Lifecycle APIs](lifecycle-apis.md) References diff --git a/website/versioned_docs/version-2.0.0-alpha.61/using-themes.md b/website/versioned_docs/version-2.0.0-alpha.61/using-themes.md index 5f66df46cad1..ad542fd028f2 100644 --- a/website/versioned_docs/version-2.0.0-alpha.61/using-themes.md +++ b/website/versioned_docs/version-2.0.0-alpha.61/using-themes.md @@ -189,7 +189,7 @@ High-level overview about themes: Related pieces --- -- [Advanced Guides – Themes](advanced-themes.md) +- [Advanced Guides – Themes](using-themes.md) - [Lifecycle APIs](lifecycle-apis.md) References From 0a48fd4e1981ce670ee909fd5f917cf6cda2f963 Mon Sep 17 00:00:00 2001 From: slorber Date: Mon, 17 Aug 2020 17:22:06 +0200 Subject: [PATCH 29/30] fix DOM warning due to forwarding legacy prop to div element --- .../src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx | 1 + .../src/theme/NavbarItem/DocsVersionNavbarItem.tsx | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx b/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx index e9fd19496347..a9a6efd56e00 100644 --- a/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx +++ b/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx @@ -19,6 +19,7 @@ const getVersionMainDoc = (version) => export default function DocsVersionDropdownNavbarItem({ mobile, docsPluginId, + nextVersionLabel: _unused, // TODO legacy, remove asap ...props }) { const activeDocContext = useActiveDocContext(docsPluginId); diff --git a/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionNavbarItem.tsx b/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionNavbarItem.tsx index 58eb426febac..5d6a0f33bf95 100644 --- a/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionNavbarItem.tsx +++ b/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionNavbarItem.tsx @@ -16,6 +16,7 @@ export default function DocsVersionNavbarItem({ label: staticLabel, to: staticTo, docsPluginId, + nextVersionLabel: _unused, // TODO legacy, remove asap ...props }) { const activeVersion = useActiveVersion(docsPluginId); From a362c088ba87cfd1d7eda900fbe8992043375955 Mon Sep 17 00:00:00 2001 From: slorber Date: Mon, 17 Aug 2020 17:32:33 +0200 Subject: [PATCH 30/30] add todo --- packages/docusaurus-theme-classic/src/validateThemeConfig.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/docusaurus-theme-classic/src/validateThemeConfig.js b/packages/docusaurus-theme-classic/src/validateThemeConfig.js index 38153e3b59ab..5d6e919dc977 100644 --- a/packages/docusaurus-theme-classic/src/validateThemeConfig.js +++ b/packages/docusaurus-theme-classic/src/validateThemeConfig.js @@ -56,7 +56,7 @@ const DocsVersionDropdownNavbarItemSchema = Joi.object({ type: Joi.string().equal('docsVersionDropdown').required(), position: NavbarItemPosition, docsPluginId: Joi.string(), - nextVersionLabel: Joi.string().default('Next'), + nextVersionLabel: Joi.string().default('Next'), // TODO remove soon }); // Can this be made easier? :/