mirror of
https://github.com/facebook/docusaurus.git
synced 2025-07-19 17:49:19 +02:00
Merge branch 'main' into slorber/fix-docs-category-index-translation-key-conflict
This commit is contained in:
commit
13828934b4
252 changed files with 10021 additions and 8162 deletions
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/plugin-content-docs",
|
||||
"version": "3.8.0",
|
||||
"version": "3.8.1",
|
||||
"description": "Docs plugin for Docusaurus.",
|
||||
"main": "lib/index.js",
|
||||
"sideEffects": false,
|
||||
|
@ -35,15 +35,15 @@
|
|||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "3.8.0",
|
||||
"@docusaurus/logger": "3.8.0",
|
||||
"@docusaurus/mdx-loader": "3.8.0",
|
||||
"@docusaurus/module-type-aliases": "3.8.0",
|
||||
"@docusaurus/theme-common": "3.8.0",
|
||||
"@docusaurus/types": "3.8.0",
|
||||
"@docusaurus/utils": "3.8.0",
|
||||
"@docusaurus/utils-common": "3.8.0",
|
||||
"@docusaurus/utils-validation": "3.8.0",
|
||||
"@docusaurus/core": "3.8.1",
|
||||
"@docusaurus/logger": "3.8.1",
|
||||
"@docusaurus/mdx-loader": "3.8.1",
|
||||
"@docusaurus/module-type-aliases": "3.8.1",
|
||||
"@docusaurus/theme-common": "3.8.1",
|
||||
"@docusaurus/types": "3.8.1",
|
||||
"@docusaurus/utils": "3.8.1",
|
||||
"@docusaurus/utils-common": "3.8.1",
|
||||
"@docusaurus/utils-validation": "3.8.1",
|
||||
"@types/react-router-config": "^5.0.7",
|
||||
"combine-promises": "^1.1.0",
|
||||
"fs-extra": "^11.1.1",
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
id: hello-2
|
||||
title: Hello 2
|
||||
sidebar_label: Hello 2 From Doc
|
||||
sidebar_class_name: front-matter-class-name
|
||||
sidebar_custom_props: {custom: "from front matter"}
|
||||
---
|
||||
|
||||
Hello World 2!
|
||||
|
|
|
@ -8,7 +8,9 @@
|
|||
{
|
||||
"id": "hello-2",
|
||||
"type": "doc",
|
||||
"label": "Hello Two"
|
||||
"label": "Hello Two",
|
||||
"className": "class-name-from-sidebars.json",
|
||||
"customProps": {"test": "from sidebars.json"}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -8,6 +8,10 @@ exports[`sidebar site with undefined sidebar 1`] = `
|
|||
"type": "doc",
|
||||
},
|
||||
{
|
||||
"className": "front-matter-class-name",
|
||||
"customProps": {
|
||||
"custom": "from front matter",
|
||||
},
|
||||
"id": "hello-2",
|
||||
"label": "Hello 2 From Doc",
|
||||
"type": "doc",
|
||||
|
|
|
@ -25,7 +25,7 @@ import {
|
|||
type DocEnv,
|
||||
} from '../docs';
|
||||
import {loadSidebars} from '../sidebars';
|
||||
import {readVersionsMetadata} from '../versions';
|
||||
import {readVersionsMetadata} from '../versions/version';
|
||||
import {DEFAULT_OPTIONS} from '../options';
|
||||
import type {Sidebars} from '../sidebars/types';
|
||||
import type {DocFile} from '../types';
|
||||
|
|
|
@ -582,14 +582,16 @@ describe('site with doc label', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('sidebar_label in doc has higher precedence over label in sidebar.json', async () => {
|
||||
it('frontMatter.sidebar_* data in doc has higher precedence over sidebar.json data', async () => {
|
||||
const {content} = await loadSite();
|
||||
const loadedVersion = content.loadedVersions[0]!;
|
||||
const sidebarProps = toSidebarsProp(loadedVersion);
|
||||
|
||||
expect((sidebarProps.docs![1] as PropSidebarItemLink).label).toBe(
|
||||
'Hello 2 From Doc',
|
||||
);
|
||||
const item = sidebarProps.docs![1] as PropSidebarItemLink;
|
||||
|
||||
expect(item.label).toBe('Hello 2 From Doc');
|
||||
expect(item.className).toBe('front-matter-class-name');
|
||||
expect(item.customProps).toStrictEqual({custom: 'from front matter'});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -7,8 +7,6 @@
|
|||
|
||||
import path from 'path';
|
||||
import fs from 'fs-extra';
|
||||
import _ from 'lodash';
|
||||
import logger from '@docusaurus/logger';
|
||||
import {
|
||||
normalizeUrl,
|
||||
docuHash,
|
||||
|
@ -17,30 +15,19 @@ import {
|
|||
posixPath,
|
||||
addTrailingPathSeparator,
|
||||
createAbsoluteFilePathMatcher,
|
||||
createSlugger,
|
||||
resolveMarkdownLinkPathname,
|
||||
DEFAULT_PLUGIN_ID,
|
||||
type TagsFile,
|
||||
} from '@docusaurus/utils';
|
||||
import {
|
||||
getTagsFile,
|
||||
getTagsFilePathsToWatch,
|
||||
} from '@docusaurus/utils-validation';
|
||||
import {getTagsFilePathsToWatch} from '@docusaurus/utils-validation';
|
||||
import {createMDXLoaderRule} from '@docusaurus/mdx-loader';
|
||||
import {loadSidebars, resolveSidebarPathOption} from './sidebars';
|
||||
import {resolveSidebarPathOption} from './sidebars';
|
||||
import {CategoryMetadataFilenamePattern} from './sidebars/generator';
|
||||
import {
|
||||
readVersionDocs,
|
||||
processDocMetadata,
|
||||
addDocNavigation,
|
||||
type DocEnv,
|
||||
createDocsByIdIndex,
|
||||
} from './docs';
|
||||
import {type DocEnv} from './docs';
|
||||
import {
|
||||
getVersionFromSourceFilePath,
|
||||
readVersionsMetadata,
|
||||
toFullVersion,
|
||||
} from './versions';
|
||||
} from './versions/version';
|
||||
import cliDocs from './cli';
|
||||
import {VERSIONS_JSON_FILE} from './constants';
|
||||
import {toGlobalDataVersion} from './globalData';
|
||||
|
@ -49,19 +36,17 @@ import {
|
|||
getLoadedContentTranslationFiles,
|
||||
} from './translations';
|
||||
import {createAllRoutes} from './routes';
|
||||
import {createSidebarsUtils} from './sidebars/utils';
|
||||
|
||||
import {createContentHelpers} from './contentHelpers';
|
||||
import {loadVersion} from './versions/loadVersion';
|
||||
import type {
|
||||
PluginOptions,
|
||||
DocMetadataBase,
|
||||
VersionMetadata,
|
||||
DocFrontMatter,
|
||||
LoadedContent,
|
||||
LoadedVersion,
|
||||
} from '@docusaurus/plugin-content-docs';
|
||||
import type {LoadContext, Plugin} from '@docusaurus/types';
|
||||
import type {DocFile, FullVersion} from './types';
|
||||
import type {FullVersion} from './types';
|
||||
import type {RuleSetRule} from 'webpack';
|
||||
|
||||
// MDX loader is not 100% deterministic, leading to cache invalidation issue
|
||||
|
@ -172,18 +157,12 @@ export default async function pluginContentDocs(
|
|||
sourceFilePath,
|
||||
versionsMetadata,
|
||||
);
|
||||
const permalink = resolveMarkdownLinkPathname(linkPathname, {
|
||||
return resolveMarkdownLinkPathname(linkPathname, {
|
||||
sourceFilePath,
|
||||
sourceToPermalink: contentHelpers.sourceToPermalink,
|
||||
siteDir,
|
||||
contentPaths: version,
|
||||
});
|
||||
if (permalink === null) {
|
||||
logger.report(
|
||||
siteConfig.onBrokenMarkdownLinks,
|
||||
)`Docs markdown link couldn't be resolved: (url=${linkPathname}) in source file path=${sourceFilePath} for version number=${version.versionName}`;
|
||||
}
|
||||
return permalink;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -243,102 +222,17 @@ export default async function pluginContentDocs(
|
|||
},
|
||||
|
||||
async loadContent() {
|
||||
async function loadVersionDocsBase(
|
||||
versionMetadata: VersionMetadata,
|
||||
tagsFile: TagsFile | null,
|
||||
): Promise<DocMetadataBase[]> {
|
||||
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 "${path.relative(
|
||||
siteDir,
|
||||
versionMetadata.contentPath,
|
||||
)}".`,
|
||||
);
|
||||
}
|
||||
function processVersionDoc(docFile: DocFile) {
|
||||
return processDocMetadata({
|
||||
docFile,
|
||||
versionMetadata,
|
||||
context,
|
||||
options,
|
||||
env,
|
||||
tagsFile,
|
||||
});
|
||||
}
|
||||
return Promise.all(docFiles.map(processVersionDoc));
|
||||
}
|
||||
|
||||
async function doLoadVersion(
|
||||
versionMetadata: VersionMetadata,
|
||||
): Promise<LoadedVersion> {
|
||||
const tagsFile = await getTagsFile({
|
||||
contentPaths: versionMetadata,
|
||||
tags: options.tags,
|
||||
});
|
||||
|
||||
const docsBase: DocMetadataBase[] = await loadVersionDocsBase(
|
||||
versionMetadata,
|
||||
tagsFile,
|
||||
);
|
||||
|
||||
// TODO we only ever need draftIds in further code, not full draft items
|
||||
// To simplify and prevent mistakes, avoid exposing draft
|
||||
// replace draft=>draftIds in content loaded
|
||||
const [drafts, docs] = _.partition(docsBase, (doc) => doc.draft);
|
||||
|
||||
const sidebars = await loadSidebars(versionMetadata.sidebarFilePath, {
|
||||
sidebarItemsGenerator: options.sidebarItemsGenerator,
|
||||
numberPrefixParser: options.numberPrefixParser,
|
||||
docs,
|
||||
drafts,
|
||||
version: versionMetadata,
|
||||
sidebarOptions: {
|
||||
sidebarCollapsed: options.sidebarCollapsed,
|
||||
sidebarCollapsible: options.sidebarCollapsible,
|
||||
},
|
||||
categoryLabelSlugger: createSlugger(),
|
||||
});
|
||||
|
||||
const sidebarsUtils = createSidebarsUtils(sidebars);
|
||||
|
||||
const docsById = createDocsByIdIndex(docs);
|
||||
const allDocIds = Object.keys(docsById);
|
||||
|
||||
sidebarsUtils.checkLegacyVersionedSidebarNames({
|
||||
sidebarFilePath: versionMetadata.sidebarFilePath as string,
|
||||
versionMetadata,
|
||||
});
|
||||
sidebarsUtils.checkSidebarsDocIds({
|
||||
allDocIds,
|
||||
sidebarFilePath: versionMetadata.sidebarFilePath as string,
|
||||
versionMetadata,
|
||||
});
|
||||
|
||||
return {
|
||||
...versionMetadata,
|
||||
docs: addDocNavigation({
|
||||
docs,
|
||||
sidebarsUtils,
|
||||
}),
|
||||
drafts,
|
||||
sidebars,
|
||||
};
|
||||
}
|
||||
|
||||
async function loadVersion(versionMetadata: VersionMetadata) {
|
||||
try {
|
||||
return await doLoadVersion(versionMetadata);
|
||||
} catch (err) {
|
||||
logger.error`Loading of version failed for version name=${versionMetadata.versionName}`;
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
loadedVersions: await Promise.all(versionsMetadata.map(loadVersion)),
|
||||
loadedVersions: await Promise.all(
|
||||
versionsMetadata.map((versionMetadata) =>
|
||||
loadVersion({
|
||||
context,
|
||||
options,
|
||||
env,
|
||||
versionMetadata,
|
||||
}),
|
||||
),
|
||||
),
|
||||
};
|
||||
},
|
||||
|
||||
|
|
|
@ -38,22 +38,14 @@ export function toSidebarDocItemLinkProp({
|
|||
'id' | 'title' | 'permalink' | 'unlisted' | 'frontMatter'
|
||||
>;
|
||||
}): PropSidebarItemLink {
|
||||
const {
|
||||
id,
|
||||
title,
|
||||
permalink,
|
||||
frontMatter: {
|
||||
sidebar_label: sidebarLabel,
|
||||
sidebar_custom_props: customProps,
|
||||
},
|
||||
unlisted,
|
||||
} = doc;
|
||||
const {id, title, permalink, frontMatter, unlisted} = doc;
|
||||
return {
|
||||
type: 'link',
|
||||
label: sidebarLabel ?? item.label ?? title,
|
||||
href: permalink,
|
||||
className: item.className,
|
||||
customProps: item.customProps ?? customProps,
|
||||
// Front Matter data takes precedence over sidebars.json
|
||||
label: frontMatter.sidebar_label ?? item.label ?? title,
|
||||
className: frontMatter.sidebar_class_name ?? item.className,
|
||||
customProps: frontMatter.sidebar_custom_props ?? item.customProps,
|
||||
docId: id,
|
||||
unlisted,
|
||||
};
|
||||
|
|
|
@ -22,5 +22,5 @@ export {
|
|||
getDefaultVersionBanner,
|
||||
getVersionBadge,
|
||||
getVersionBanner,
|
||||
} from './versions';
|
||||
} from './versions/version';
|
||||
export {readVersionNames} from './versions/files';
|
||||
|
|
|
@ -76,6 +76,10 @@ exports[`postProcess transforms category without subitems 1`] = `
|
|||
{
|
||||
"sidebar": [
|
||||
{
|
||||
"className": "category-className",
|
||||
"customProps": {
|
||||
"custom": true,
|
||||
},
|
||||
"id": "doc ID",
|
||||
"label": "Category 2",
|
||||
"type": "doc",
|
||||
|
|
|
@ -31,6 +31,8 @@ describe('postProcess', () => {
|
|||
type: 'doc',
|
||||
id: 'doc ID',
|
||||
},
|
||||
className: 'category-className',
|
||||
customProps: {custom: true},
|
||||
items: [],
|
||||
},
|
||||
],
|
||||
|
|
|
@ -77,10 +77,13 @@ function postProcessSidebarItem(
|
|||
) {
|
||||
return null;
|
||||
}
|
||||
const {label, className, customProps} = category;
|
||||
return {
|
||||
type: 'doc',
|
||||
label: category.label,
|
||||
id: category.link.id,
|
||||
label,
|
||||
...(className && {className}),
|
||||
...(customProps && {customProps}),
|
||||
};
|
||||
}
|
||||
// A non-collapsible category can't be collapsed!
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
|
||||
# Hello
|
||||
|
||||
World
|
|
@ -0,0 +1,4 @@
|
|||
|
||||
# Hello
|
||||
|
||||
World
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
id: doc
|
||||
---
|
||||
|
||||
# Hello
|
||||
|
||||
World
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
# no id but should conflict due to the name anyway
|
||||
---
|
||||
|
||||
# Hello
|
||||
|
||||
World
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
id: doc
|
||||
---
|
||||
|
||||
# Hello
|
||||
|
||||
World
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
id: doc
|
||||
---
|
||||
|
||||
# Hello
|
||||
|
||||
World
|
|
@ -0,0 +1,3 @@
|
|||
# Hello
|
||||
|
||||
World
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
id: doc
|
||||
---
|
||||
|
||||
# Hello
|
||||
|
||||
World
|
|
@ -0,0 +1,3 @@
|
|||
# Hello
|
||||
|
||||
World
|
|
@ -0,0 +1,3 @@
|
|||
# Hello
|
||||
|
||||
World
|
|
@ -0,0 +1,3 @@
|
|||
# Hello
|
||||
|
||||
World
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
id: doc-1
|
||||
---
|
||||
|
||||
# Hello
|
||||
|
||||
World
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
id: doc-2
|
||||
---
|
||||
|
||||
# Hello
|
||||
|
||||
World
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
id: doc-3
|
||||
---
|
||||
|
||||
# Hello
|
||||
|
||||
World
|
|
@ -0,0 +1,3 @@
|
|||
[
|
||||
"with-id-conflicts"
|
||||
]
|
|
@ -0,0 +1,4 @@
|
|||
|
||||
# Hello
|
||||
|
||||
World
|
|
@ -0,0 +1,53 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`loadVersion minimal site can load current version 1`] = `
|
||||
{
|
||||
"badge": false,
|
||||
"banner": null,
|
||||
"className": "docs-version-current",
|
||||
"contentPath": "<PROJECT_ROOT>/packages/docusaurus-plugin-content-docs/src/versions/__tests__/__fixtures__/site-minimal/docs",
|
||||
"contentPathLocalized": "<PROJECT_ROOT>/packages/docusaurus-plugin-content-docs/src/versions/__tests__/__fixtures__/site-minimal/i18n/en/docusaurus-plugin-content-docs/current",
|
||||
"docs": [
|
||||
{
|
||||
"description": "World",
|
||||
"draft": false,
|
||||
"editUrl": undefined,
|
||||
"frontMatter": {},
|
||||
"id": "hello",
|
||||
"lastUpdatedAt": undefined,
|
||||
"lastUpdatedBy": undefined,
|
||||
"next": undefined,
|
||||
"permalink": "/docs/hello",
|
||||
"previous": undefined,
|
||||
"sidebar": "defaultSidebar",
|
||||
"sidebarPosition": undefined,
|
||||
"slug": "/hello",
|
||||
"source": "@site/docs/hello.md",
|
||||
"sourceDirName": ".",
|
||||
"tags": [],
|
||||
"title": "Hello",
|
||||
"unlisted": false,
|
||||
"version": "current",
|
||||
},
|
||||
],
|
||||
"drafts": [],
|
||||
"editUrl": undefined,
|
||||
"editUrlLocalized": undefined,
|
||||
"isLast": true,
|
||||
"label": "Next",
|
||||
"noIndex": false,
|
||||
"path": "/docs",
|
||||
"routePriority": -1,
|
||||
"sidebarFilePath": undefined,
|
||||
"sidebars": {
|
||||
"defaultSidebar": [
|
||||
{
|
||||
"id": "hello",
|
||||
"type": "doc",
|
||||
},
|
||||
],
|
||||
},
|
||||
"tagsPath": "/docs/tags",
|
||||
"versionName": "current",
|
||||
}
|
||||
`;
|
|
@ -0,0 +1,117 @@
|
|||
/**
|
||||
* 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 {fromPartial} from '@total-typescript/shoehorn';
|
||||
import {DEFAULT_PARSE_FRONT_MATTER} from '@docusaurus/utils/src';
|
||||
import {readVersionsMetadata} from '../version';
|
||||
import {DEFAULT_OPTIONS} from '../../options';
|
||||
import {loadVersion} from '../loadVersion';
|
||||
import type {I18n, LoadContext} from '@docusaurus/types';
|
||||
import type {PluginOptions} from '@docusaurus/plugin-content-docs';
|
||||
|
||||
const DefaultI18N: I18n = {
|
||||
path: 'i18n',
|
||||
currentLocale: 'en',
|
||||
locales: ['en'],
|
||||
defaultLocale: 'en',
|
||||
localeConfigs: {},
|
||||
};
|
||||
|
||||
async function siteFixture(fixture: string) {
|
||||
const siteDir = path.resolve(path.join(__dirname, './__fixtures__', fixture));
|
||||
const options: PluginOptions = fromPartial<PluginOptions>({
|
||||
id: 'default',
|
||||
...DEFAULT_OPTIONS,
|
||||
});
|
||||
const context = fromPartial<LoadContext>({
|
||||
siteDir,
|
||||
baseUrl: '/',
|
||||
i18n: DefaultI18N,
|
||||
localizationDir: path.join(siteDir, 'i18n/en'),
|
||||
siteConfig: {
|
||||
markdown: {
|
||||
parseFrontMatter: DEFAULT_PARSE_FRONT_MATTER,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const versions = await readVersionsMetadata({
|
||||
options,
|
||||
context,
|
||||
});
|
||||
|
||||
return {
|
||||
siteDir,
|
||||
options,
|
||||
context,
|
||||
versions,
|
||||
};
|
||||
}
|
||||
|
||||
describe('loadVersion', () => {
|
||||
describe('minimal site', () => {
|
||||
it('can load current version', async () => {
|
||||
const {options, context, versions} = await siteFixture('site-minimal');
|
||||
|
||||
const version = versions[0];
|
||||
expect(version).toBeDefined();
|
||||
expect(version.versionName).toBe('current');
|
||||
|
||||
const loadedVersion = loadVersion({
|
||||
context,
|
||||
options,
|
||||
versionMetadata: version,
|
||||
env: 'production',
|
||||
});
|
||||
|
||||
await expect(loadedVersion).resolves.toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('site with broken versions', () => {
|
||||
async function loadTestVersion(versionName: string) {
|
||||
const {options, context, versions} = await siteFixture(
|
||||
'site-broken-versions',
|
||||
);
|
||||
const version = versions.find((v) => v.versionName === versionName);
|
||||
if (!version) {
|
||||
throw new Error(`Version '${versionName}' should exist`);
|
||||
}
|
||||
return loadVersion({
|
||||
context,
|
||||
options,
|
||||
versionMetadata: version,
|
||||
env: 'production',
|
||||
});
|
||||
}
|
||||
|
||||
it('rejects version with doc id conflict', async () => {
|
||||
await expect(() => loadTestVersion('with-id-conflicts')).rejects
|
||||
.toThrowErrorMatchingInlineSnapshot(`
|
||||
"The docs plugin found docs sharing the same id:
|
||||
|
||||
- \`frontMatter/doc\` found in 3 docs:
|
||||
- versioned_docs/version-with-id-conflicts/frontMatter/doc.md
|
||||
- versioned_docs/version-with-id-conflicts/frontMatter/doc1.md
|
||||
- versioned_docs/version-with-id-conflicts/frontMatter/doc2.md
|
||||
|
||||
- \`number-prefix/doc\` found in 2 docs:
|
||||
- versioned_docs/version-with-id-conflicts/number-prefix/1-doc.md
|
||||
- versioned_docs/version-with-id-conflicts/number-prefix/2-doc.md
|
||||
|
||||
- \`number-prefix/deeply/nested/doc\` found in 2 docs:
|
||||
- versioned_docs/version-with-id-conflicts/number-prefix/deeply/nested/2-doc.md
|
||||
- versioned_docs/version-with-id-conflicts/number-prefix/deeply/nested/3-doc.md
|
||||
|
||||
Docs should have distinct ids.
|
||||
In case of conflict, you can rename the docs file, or use the \`id\` front matter to assign an explicit distinct id to each doc.
|
||||
"
|
||||
`);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -8,7 +8,7 @@
|
|||
import {jest} from '@jest/globals';
|
||||
import path from 'path';
|
||||
import {DEFAULT_PLUGIN_ID} from '@docusaurus/utils';
|
||||
import {readVersionsMetadata} from '../index';
|
||||
import {readVersionsMetadata} from '../version';
|
||||
import {DEFAULT_OPTIONS} from '../../options';
|
||||
import type {I18n, LoadContext} from '@docusaurus/types';
|
||||
import type {
|
|
@ -19,7 +19,7 @@ import type {
|
|||
PluginOptions,
|
||||
VersionMetadata,
|
||||
} from '@docusaurus/plugin-content-docs';
|
||||
import type {VersionContext} from './index';
|
||||
import type {VersionContext} from './version';
|
||||
|
||||
/** Add a prefix like `community_version-1.0.0`. No-op for default instance. */
|
||||
function addPluginIdPrefix(fileOrDir: string, pluginId: string): string {
|
||||
|
|
|
@ -0,0 +1,179 @@
|
|||
/**
|
||||
* 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 _ from 'lodash';
|
||||
import {aliasedSitePathToRelativePath, createSlugger} from '@docusaurus/utils';
|
||||
import {getTagsFile} from '@docusaurus/utils-validation';
|
||||
import logger from '@docusaurus/logger';
|
||||
import {
|
||||
addDocNavigation,
|
||||
createDocsByIdIndex,
|
||||
type DocEnv,
|
||||
processDocMetadata,
|
||||
readVersionDocs,
|
||||
} from '../docs';
|
||||
import {loadSidebars} from '../sidebars';
|
||||
import {createSidebarsUtils} from '../sidebars/utils';
|
||||
import type {TagsFile} from '@docusaurus/utils';
|
||||
import type {
|
||||
DocMetadataBase,
|
||||
LoadedVersion,
|
||||
PluginOptions,
|
||||
VersionMetadata,
|
||||
} from '@docusaurus/plugin-content-docs';
|
||||
import type {DocFile} from '../types';
|
||||
import type {LoadContext} from '@docusaurus/types';
|
||||
|
||||
type LoadVersionParams = {
|
||||
context: LoadContext;
|
||||
options: PluginOptions;
|
||||
versionMetadata: VersionMetadata;
|
||||
env: DocEnv;
|
||||
};
|
||||
|
||||
function ensureNoDuplicateDocId(docs: DocMetadataBase[]): void {
|
||||
const duplicatesById = _.chain(docs)
|
||||
.groupBy((d) => d.id)
|
||||
.pickBy((group) => group.length > 1)
|
||||
.value();
|
||||
|
||||
const duplicateIdEntries = Object.entries(duplicatesById);
|
||||
|
||||
if (duplicateIdEntries.length) {
|
||||
const idMessages = duplicateIdEntries
|
||||
.map(([id, duplicateDocs]) => {
|
||||
return logger.interpolate`- code=${id} found in number=${
|
||||
duplicateDocs.length
|
||||
} docs:
|
||||
- ${duplicateDocs
|
||||
.map((d) => aliasedSitePathToRelativePath(d.source))
|
||||
.join('\n - ')}`;
|
||||
})
|
||||
.join('\n\n');
|
||||
|
||||
const message = `The docs plugin found docs sharing the same id:
|
||||
\n${idMessages}\n
|
||||
Docs should have distinct ids.
|
||||
In case of conflict, you can rename the docs file, or use the ${logger.code(
|
||||
'id',
|
||||
)} front matter to assign an explicit distinct id to each doc.
|
||||
`;
|
||||
|
||||
throw new Error(message);
|
||||
}
|
||||
}
|
||||
|
||||
async function loadVersionDocsBase({
|
||||
tagsFile,
|
||||
context,
|
||||
options,
|
||||
versionMetadata,
|
||||
env,
|
||||
}: LoadVersionParams & {
|
||||
tagsFile: TagsFile | null;
|
||||
}): Promise<DocMetadataBase[]> {
|
||||
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 "${path.relative(
|
||||
context.siteDir,
|
||||
versionMetadata.contentPath,
|
||||
)}".`,
|
||||
);
|
||||
}
|
||||
function processVersionDoc(docFile: DocFile) {
|
||||
return processDocMetadata({
|
||||
docFile,
|
||||
versionMetadata,
|
||||
context,
|
||||
options,
|
||||
env,
|
||||
tagsFile,
|
||||
});
|
||||
}
|
||||
const docs = await Promise.all(docFiles.map(processVersionDoc));
|
||||
ensureNoDuplicateDocId(docs);
|
||||
return docs;
|
||||
}
|
||||
|
||||
async function doLoadVersion({
|
||||
context,
|
||||
options,
|
||||
versionMetadata,
|
||||
env,
|
||||
}: LoadVersionParams): Promise<LoadedVersion> {
|
||||
const tagsFile = await getTagsFile({
|
||||
contentPaths: versionMetadata,
|
||||
tags: options.tags,
|
||||
});
|
||||
|
||||
const docsBase: DocMetadataBase[] = await loadVersionDocsBase({
|
||||
tagsFile,
|
||||
context,
|
||||
options,
|
||||
versionMetadata,
|
||||
env,
|
||||
});
|
||||
|
||||
// TODO we only ever need draftIds in further code, not full draft items
|
||||
// To simplify and prevent mistakes, avoid exposing draft
|
||||
// replace draft=>draftIds in content loaded
|
||||
const [drafts, docs] = _.partition(docsBase, (doc) => doc.draft);
|
||||
|
||||
const sidebars = await loadSidebars(versionMetadata.sidebarFilePath, {
|
||||
sidebarItemsGenerator: options.sidebarItemsGenerator,
|
||||
numberPrefixParser: options.numberPrefixParser,
|
||||
docs,
|
||||
drafts,
|
||||
version: versionMetadata,
|
||||
sidebarOptions: {
|
||||
sidebarCollapsed: options.sidebarCollapsed,
|
||||
sidebarCollapsible: options.sidebarCollapsible,
|
||||
},
|
||||
categoryLabelSlugger: createSlugger(),
|
||||
});
|
||||
|
||||
const sidebarsUtils = createSidebarsUtils(sidebars);
|
||||
|
||||
const docsById = createDocsByIdIndex(docs);
|
||||
const allDocIds = Object.keys(docsById);
|
||||
|
||||
sidebarsUtils.checkLegacyVersionedSidebarNames({
|
||||
sidebarFilePath: versionMetadata.sidebarFilePath as string,
|
||||
versionMetadata,
|
||||
});
|
||||
sidebarsUtils.checkSidebarsDocIds({
|
||||
allDocIds,
|
||||
sidebarFilePath: versionMetadata.sidebarFilePath as string,
|
||||
versionMetadata,
|
||||
});
|
||||
|
||||
return {
|
||||
...versionMetadata,
|
||||
docs: addDocNavigation({
|
||||
docs,
|
||||
sidebarsUtils,
|
||||
}),
|
||||
drafts,
|
||||
sidebars,
|
||||
};
|
||||
}
|
||||
|
||||
export async function loadVersion(
|
||||
params: LoadVersionParams,
|
||||
): Promise<LoadedVersion> {
|
||||
try {
|
||||
return await doLoadVersion(params);
|
||||
} catch (err) {
|
||||
// TODO use error cause (but need to refactor many tests)
|
||||
logger.error`Loading of version failed for version name=${params.versionMetadata.versionName}`;
|
||||
throw err;
|
||||
}
|
||||
}
|
|
@ -243,7 +243,7 @@ export async function readVersionsMetadata({
|
|||
validateVersionsOptions(allVersionNames, options);
|
||||
const versionNames = filterVersions(allVersionNames, options);
|
||||
const lastVersionName = getLastVersionName({versionNames, options});
|
||||
const versionsMetadata = await Promise.all(
|
||||
return Promise.all(
|
||||
versionNames.map((versionName) =>
|
||||
createVersionMetadata({
|
||||
versionName,
|
||||
|
@ -254,7 +254,6 @@ export async function readVersionsMetadata({
|
|||
}),
|
||||
),
|
||||
);
|
||||
return versionsMetadata;
|
||||
}
|
||||
|
||||
export function toFullVersion(version: LoadedVersion): FullVersion {
|
Loading…
Add table
Add a link
Reference in a new issue