@azerion/bunosaurus-plugin-content-docs 0.0.0-stage → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -2
- package/lib/categoryGeneratedIndex.d.ts +12 -0
- package/lib/categoryGeneratedIndex.js +36 -0
- package/lib/cli.d.ts +13 -0
- package/lib/cli.js +124 -0
- package/lib/client/doc.d.ts +29 -0
- package/lib/client/doc.js +47 -0
- package/lib/client/docSidebarItemsExpandedState.d.ts +30 -0
- package/lib/client/docSidebarItemsExpandedState.js +27 -0
- package/lib/client/docsClientUtils.d.ts +15 -0
- package/lib/client/docsClientUtils.js +86 -0
- package/lib/client/docsPreferredVersion.d.ts +29 -0
- package/lib/client/docsPreferredVersion.js +124 -0
- package/lib/client/docsSearch.d.ts +19 -0
- package/lib/client/docsSearch.js +39 -0
- package/lib/client/docsSidebar.d.ts +25 -0
- package/lib/client/docsSidebar.js +29 -0
- package/lib/client/docsUtils.d.ts +112 -0
- package/lib/client/docsUtils.js +299 -0
- package/lib/client/docsVersion.d.ts +19 -0
- package/lib/client/docsVersion.js +25 -0
- package/lib/client/index.d.ts +91 -0
- package/lib/client/index.js +83 -0
- package/lib/client/structuredDataUtils.d.ts +11 -0
- package/lib/client/structuredDataUtils.js +35 -0
- package/lib/constants.d.ts +14 -0
- package/lib/constants.js +17 -0
- package/lib/contentHelpers.d.ts +12 -0
- package/lib/contentHelpers.js +31 -0
- package/lib/docs.d.ts +48 -0
- package/lib/docs.js +252 -0
- package/lib/frontMatter.d.ts +10 -0
- package/lib/frontMatter.js +46 -0
- package/lib/globalData.d.ts +9 -0
- package/lib/globalData.js +59 -0
- package/lib/index.d.ts +10 -0
- package/lib/index.js +198 -0
- package/lib/numberPrefix.d.ts +11 -0
- package/lib/numberPrefix.js +50 -0
- package/lib/options.d.ts +10 -0
- package/lib/options.js +138 -0
- package/lib/props.d.ts +21 -0
- package/lib/props.js +156 -0
- package/lib/routes.d.ts +22 -0
- package/lib/routes.js +184 -0
- package/lib/server-export.d.ts +9 -0
- package/lib/server-export.js +25 -0
- package/lib/sidebars/generator.d.ts +10 -0
- package/lib/sidebars/generator.js +217 -0
- package/lib/sidebars/index.d.ts +13 -0
- package/lib/sidebars/index.js +99 -0
- package/lib/sidebars/normalization.d.ts +13 -0
- package/lib/sidebars/normalization.js +58 -0
- package/lib/sidebars/postProcessor.d.ts +11 -0
- package/lib/sidebars/postProcessor.js +82 -0
- package/lib/sidebars/processor.d.ts +10 -0
- package/lib/sidebars/processor.js +77 -0
- package/lib/sidebars/types.d.ts +192 -0
- package/lib/sidebars/types.js +8 -0
- package/lib/sidebars/utils.d.ts +69 -0
- package/lib/sidebars/utils.js +327 -0
- package/lib/sidebars/validation.d.ts +11 -0
- package/lib/sidebars/validation.js +152 -0
- package/lib/slug.d.ts +15 -0
- package/lib/slug.js +51 -0
- package/lib/translations.d.ts +10 -0
- package/lib/translations.js +243 -0
- package/lib/types.d.ts +27 -0
- package/lib/types.js +8 -0
- package/lib/versions/files.d.ts +54 -0
- package/lib/versions/files.js +152 -0
- package/lib/versions/loadVersion.d.ts +17 -0
- package/lib/versions/loadVersion.js +126 -0
- package/lib/versions/validation.d.ts +17 -0
- package/lib/versions/validation.js +70 -0
- package/lib/versions/version.d.ts +40 -0
- package/lib/versions/version.js +189 -0
- package/package.json +70 -4
- package/src/categoryGeneratedIndex.ts +60 -0
- package/src/cli.ts +180 -0
- package/src/client/doc.tsx +71 -0
- package/src/client/docSidebarItemsExpandedState.tsx +55 -0
- package/src/client/docsClientUtils.ts +140 -0
- package/src/client/docsPreferredVersion.tsx +245 -0
- package/src/client/docsSearch.test.ts +15 -0
- package/src/client/docsSearch.ts +57 -0
- package/src/client/docsSidebar.tsx +50 -0
- package/src/client/docsUtils.tsx +442 -0
- package/src/client/docsVersion.tsx +36 -0
- package/src/client/index.ts +211 -0
- package/src/client/structuredDataUtils.ts +54 -0
- package/src/constants.ts +15 -0
- package/src/contentHelpers.ts +34 -0
- package/src/docs.ts +402 -0
- package/src/frontMatter.ts +57 -0
- package/src/globalData.ts +79 -0
- package/src/index.ts +289 -0
- package/src/numberPrefix.ts +62 -0
- package/src/options.ts +185 -0
- package/src/plugin-content-docs.d.ts +667 -0
- package/src/props.ts +223 -0
- package/src/routes.ts +263 -0
- package/src/server-export.ts +26 -0
- package/src/sidebars/README.md +10 -0
- package/src/sidebars/generator.ts +307 -0
- package/src/sidebars/index.ts +130 -0
- package/src/sidebars/normalization.ts +91 -0
- package/src/sidebars/postProcessor.ts +115 -0
- package/src/sidebars/processor.ts +122 -0
- package/src/sidebars/types.ts +293 -0
- package/src/sidebars/utils.ts +529 -0
- package/src/sidebars/validation.ts +189 -0
- package/src/slug.ts +79 -0
- package/src/translations.ts +365 -0
- package/src/types.ts +34 -0
- package/src/versions/files.ts +240 -0
- package/src/versions/loadVersion.ts +185 -0
- package/src/versions/validation.ts +115 -0
- package/src/versions/version.ts +300 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
|
9
|
+
import {applyTrailingSlash} from '@docusaurus/utils-common';
|
|
10
|
+
import type {PropSidebarBreadcrumbsItem} from '@docusaurus/plugin-content-docs';
|
|
11
|
+
import type {WithContext, BreadcrumbList} from 'schema-dts';
|
|
12
|
+
import type {DocusaurusConfig} from '@docusaurus/types';
|
|
13
|
+
|
|
14
|
+
type Params = Pick<DocusaurusConfig, 'url' | 'baseUrl' | 'trailingSlash'>;
|
|
15
|
+
|
|
16
|
+
function getAbsoluteUrl(permalink: string, params: Params): string {
|
|
17
|
+
const absoluteUrl = `${params.url}${permalink}`;
|
|
18
|
+
return applyTrailingSlash(absoluteUrl, {
|
|
19
|
+
trailingSlash: params.trailingSlash,
|
|
20
|
+
baseUrl: params.baseUrl,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function getBreadcrumbsStructuredData({
|
|
25
|
+
breadcrumbs,
|
|
26
|
+
params,
|
|
27
|
+
}: {
|
|
28
|
+
breadcrumbs: PropSidebarBreadcrumbsItem[];
|
|
29
|
+
params: Params;
|
|
30
|
+
}): WithContext<BreadcrumbList> {
|
|
31
|
+
return {
|
|
32
|
+
'@context': 'https://schema.org',
|
|
33
|
+
'@type': 'BreadcrumbList',
|
|
34
|
+
itemListElement: breadcrumbs
|
|
35
|
+
// We filter breadcrumb items without links, they are not allowed
|
|
36
|
+
// See also https://github.com/facebook/docusaurus/issues/9319#issuecomment-2643560845
|
|
37
|
+
.filter((breadcrumb) => breadcrumb.href)
|
|
38
|
+
.map((breadcrumb, index) => ({
|
|
39
|
+
'@type': 'ListItem',
|
|
40
|
+
position: index + 1,
|
|
41
|
+
name: breadcrumb.label,
|
|
42
|
+
item: getAbsoluteUrl(breadcrumb.href!, params),
|
|
43
|
+
})),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function useBreadcrumbsStructuredData({
|
|
48
|
+
breadcrumbs,
|
|
49
|
+
}: {
|
|
50
|
+
breadcrumbs: PropSidebarBreadcrumbsItem[];
|
|
51
|
+
}): WithContext<BreadcrumbList> {
|
|
52
|
+
const {siteConfig} = useDocusaurusContext();
|
|
53
|
+
return getBreadcrumbsStructuredData({breadcrumbs, params: siteConfig});
|
|
54
|
+
}
|
package/src/constants.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/** The name of the version that's actively worked on (e.g. `website/docs`) */
|
|
9
|
+
export const CURRENT_VERSION_NAME = 'current';
|
|
10
|
+
/** All doc versions are stored here by version names */
|
|
11
|
+
export const VERSIONED_DOCS_DIR = 'versioned_docs';
|
|
12
|
+
/** All doc versioned sidebars are stored here by version names */
|
|
13
|
+
export const VERSIONED_SIDEBARS_DIR = 'versioned_sidebars';
|
|
14
|
+
/** The version names. Should 1-1 map to the content of versioned docs dir. */
|
|
15
|
+
export const VERSIONS_JSON_FILE = 'versions.json';
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type {DocMetadata, LoadedContent} from '@docusaurus/plugin-content-docs';
|
|
9
|
+
|
|
10
|
+
function indexDocsBySource(content: LoadedContent): Map<string, DocMetadata> {
|
|
11
|
+
const allDocs = content.loadedVersions.flatMap((v) => v.docs);
|
|
12
|
+
return new Map(allDocs.map((doc) => [doc.source, doc]));
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// TODO this is bad, we should have a better way to do this (new lifecycle?)
|
|
16
|
+
// The source to doc/permalink is a mutable map passed to the mdx loader
|
|
17
|
+
// See https://github.com/facebook/docusaurus/pull/10457
|
|
18
|
+
// See https://github.com/facebook/docusaurus/pull/10185
|
|
19
|
+
export function createContentHelpers() {
|
|
20
|
+
const sourceToDoc = new Map<string, DocMetadata>();
|
|
21
|
+
const sourceToPermalink = new Map<string, string>();
|
|
22
|
+
|
|
23
|
+
// Mutable map update :/
|
|
24
|
+
function updateContent(content: LoadedContent): void {
|
|
25
|
+
sourceToDoc.clear();
|
|
26
|
+
sourceToPermalink.clear();
|
|
27
|
+
indexDocsBySource(content).forEach((value, key) => {
|
|
28
|
+
sourceToDoc.set(key, value);
|
|
29
|
+
sourceToPermalink.set(key, value.permalink);
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return {updateContent, sourceToDoc, sourceToPermalink};
|
|
34
|
+
}
|
package/src/docs.ts
ADDED
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import path from 'node:path';
|
|
9
|
+
import _ from 'lodash';
|
|
10
|
+
import {
|
|
11
|
+
aliasedSitePath,
|
|
12
|
+
getEditUrl,
|
|
13
|
+
getFolderContainingFile,
|
|
14
|
+
getContentPathList,
|
|
15
|
+
normalizeUrl,
|
|
16
|
+
parseMarkdownFile,
|
|
17
|
+
posixPath,
|
|
18
|
+
isUnlisted,
|
|
19
|
+
isDraft,
|
|
20
|
+
readLastUpdateData,
|
|
21
|
+
normalizeTags,
|
|
22
|
+
} from '@docusaurus/utils';
|
|
23
|
+
import {glob} from '@docusaurus/glob';
|
|
24
|
+
import {fs} from '@docusaurus/fs';
|
|
25
|
+
import {validateDocFrontMatter} from './frontMatter';
|
|
26
|
+
import getSlug from './slug';
|
|
27
|
+
import {stripPathNumberPrefixes} from './numberPrefix';
|
|
28
|
+
import {toDocNavigationLink, toNavigationLink} from './sidebars/utils';
|
|
29
|
+
import type {TagsFile} from '@docusaurus/utils';
|
|
30
|
+
import type {
|
|
31
|
+
MetadataOptions,
|
|
32
|
+
PluginOptions,
|
|
33
|
+
CategoryIndexMatcher,
|
|
34
|
+
DocMetadataBase,
|
|
35
|
+
DocMetadata,
|
|
36
|
+
PropNavigationLink,
|
|
37
|
+
VersionMetadata,
|
|
38
|
+
LoadedVersion,
|
|
39
|
+
} from '@docusaurus/plugin-content-docs';
|
|
40
|
+
import type {LoadContext} from '@docusaurus/types';
|
|
41
|
+
import type {SidebarsUtils} from './sidebars/utils';
|
|
42
|
+
import type {DocFile} from './types';
|
|
43
|
+
|
|
44
|
+
export async function readDocFile(
|
|
45
|
+
versionMetadata: Pick<
|
|
46
|
+
VersionMetadata,
|
|
47
|
+
'contentPath' | 'contentPathLocalized'
|
|
48
|
+
>,
|
|
49
|
+
source: string,
|
|
50
|
+
): Promise<DocFile> {
|
|
51
|
+
const contentPath = await getFolderContainingFile(
|
|
52
|
+
getContentPathList(versionMetadata),
|
|
53
|
+
source,
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
const filePath = path.join(contentPath, source);
|
|
57
|
+
|
|
58
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
59
|
+
return {source, content, contentPath, filePath};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export async function readVersionDocs(
|
|
63
|
+
versionMetadata: VersionMetadata,
|
|
64
|
+
options: Pick<
|
|
65
|
+
PluginOptions,
|
|
66
|
+
'include' | 'exclude' | 'showLastUpdateAuthor' | 'showLastUpdateTime'
|
|
67
|
+
>,
|
|
68
|
+
): Promise<DocFile[]> {
|
|
69
|
+
const sources = await glob(options.include, {
|
|
70
|
+
cwd: versionMetadata.contentPath,
|
|
71
|
+
ignore: options.exclude,
|
|
72
|
+
});
|
|
73
|
+
return Promise.all(
|
|
74
|
+
sources.map((source) => readDocFile(versionMetadata, source)),
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export type DocEnv = 'production' | 'development';
|
|
79
|
+
|
|
80
|
+
async function doProcessDocMetadata({
|
|
81
|
+
docFile,
|
|
82
|
+
versionMetadata,
|
|
83
|
+
context,
|
|
84
|
+
options,
|
|
85
|
+
env,
|
|
86
|
+
tagsFile,
|
|
87
|
+
}: {
|
|
88
|
+
docFile: DocFile;
|
|
89
|
+
versionMetadata: VersionMetadata;
|
|
90
|
+
context: LoadContext;
|
|
91
|
+
options: MetadataOptions;
|
|
92
|
+
env: DocEnv;
|
|
93
|
+
tagsFile: TagsFile | null;
|
|
94
|
+
}): Promise<DocMetadataBase> {
|
|
95
|
+
const {source, content, contentPath, filePath} = docFile;
|
|
96
|
+
const {
|
|
97
|
+
siteDir,
|
|
98
|
+
siteConfig: {
|
|
99
|
+
markdown: {parseFrontMatter},
|
|
100
|
+
vcs,
|
|
101
|
+
},
|
|
102
|
+
} = context;
|
|
103
|
+
|
|
104
|
+
const {
|
|
105
|
+
frontMatter: unsafeFrontMatter,
|
|
106
|
+
contentTitle,
|
|
107
|
+
excerpt,
|
|
108
|
+
} = await parseMarkdownFile({
|
|
109
|
+
filePath,
|
|
110
|
+
fileContent: content,
|
|
111
|
+
parseFrontMatter,
|
|
112
|
+
});
|
|
113
|
+
const frontMatter = validateDocFrontMatter(unsafeFrontMatter);
|
|
114
|
+
|
|
115
|
+
const {
|
|
116
|
+
custom_edit_url: customEditURL,
|
|
117
|
+
|
|
118
|
+
// Strip number prefixes by default
|
|
119
|
+
// (01-MyFolder/01-MyDoc.md => MyFolder/MyDoc)
|
|
120
|
+
// but allow to disable this behavior with front matter
|
|
121
|
+
parse_number_prefixes: parseNumberPrefixes = true,
|
|
122
|
+
last_update: lastUpdateFrontMatter,
|
|
123
|
+
} = frontMatter;
|
|
124
|
+
|
|
125
|
+
const lastUpdate = await readLastUpdateData(
|
|
126
|
+
filePath,
|
|
127
|
+
options,
|
|
128
|
+
lastUpdateFrontMatter,
|
|
129
|
+
vcs,
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
// E.g. api/plugins/myDoc -> myDoc; myDoc -> myDoc
|
|
133
|
+
const sourceFileNameWithoutExtension = path.basename(
|
|
134
|
+
source,
|
|
135
|
+
path.extname(source),
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
// E.g. api/plugins/myDoc -> api/plugins; myDoc -> .
|
|
139
|
+
const sourceDirName = path.dirname(source);
|
|
140
|
+
|
|
141
|
+
const {filename: unprefixedFileName, numberPrefix} = parseNumberPrefixes
|
|
142
|
+
? options.numberPrefixParser(sourceFileNameWithoutExtension)
|
|
143
|
+
: {filename: sourceFileNameWithoutExtension, numberPrefix: undefined};
|
|
144
|
+
|
|
145
|
+
const baseID: string = frontMatter.id ?? unprefixedFileName;
|
|
146
|
+
if (baseID.includes('/')) {
|
|
147
|
+
throw new Error(`Document id "${baseID}" cannot include slash.`);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// For autogenerated sidebars, sidebar position can come from filename number
|
|
151
|
+
// prefix or front matter
|
|
152
|
+
const sidebarPosition: number | undefined =
|
|
153
|
+
frontMatter.sidebar_position ?? numberPrefix;
|
|
154
|
+
|
|
155
|
+
// TODO legacy retrocompatibility
|
|
156
|
+
// I think it's bad to affect the front matter id with the dirname?
|
|
157
|
+
function computeDirNameIdPrefix() {
|
|
158
|
+
if (sourceDirName === '.') {
|
|
159
|
+
return undefined;
|
|
160
|
+
}
|
|
161
|
+
// Eventually remove the number prefixes from intermediate directories
|
|
162
|
+
return parseNumberPrefixes
|
|
163
|
+
? stripPathNumberPrefixes(sourceDirName, options.numberPrefixParser)
|
|
164
|
+
: sourceDirName;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const id = [computeDirNameIdPrefix(), baseID].filter(Boolean).join('/');
|
|
168
|
+
|
|
169
|
+
const docSlug = getSlug({
|
|
170
|
+
baseID,
|
|
171
|
+
source,
|
|
172
|
+
sourceDirName,
|
|
173
|
+
frontMatterSlug: frontMatter.slug,
|
|
174
|
+
stripDirNumberPrefixes: parseNumberPrefixes,
|
|
175
|
+
numberPrefixParser: options.numberPrefixParser,
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
// Note: the title is used by default for page title, sidebar label,
|
|
179
|
+
// pagination buttons... frontMatter.title should be used in priority over
|
|
180
|
+
// contentTitle (because it can contain markdown/JSX syntax)
|
|
181
|
+
const title: string = frontMatter.title ?? contentTitle ?? baseID;
|
|
182
|
+
|
|
183
|
+
const description: string = frontMatter.description ?? excerpt ?? '';
|
|
184
|
+
|
|
185
|
+
const permalink = normalizeUrl([versionMetadata.path, docSlug]);
|
|
186
|
+
|
|
187
|
+
function getDocEditUrl() {
|
|
188
|
+
const relativeFilePath = path.relative(contentPath, filePath);
|
|
189
|
+
|
|
190
|
+
if (typeof options.editUrl === 'function') {
|
|
191
|
+
return options.editUrl({
|
|
192
|
+
version: versionMetadata.versionName,
|
|
193
|
+
versionDocsDirPath: posixPath(
|
|
194
|
+
path.relative(siteDir, versionMetadata.contentPath),
|
|
195
|
+
),
|
|
196
|
+
docPath: posixPath(relativeFilePath),
|
|
197
|
+
permalink,
|
|
198
|
+
locale: context.i18n.currentLocale,
|
|
199
|
+
});
|
|
200
|
+
} else if (typeof options.editUrl === 'string') {
|
|
201
|
+
const isLocalized =
|
|
202
|
+
typeof versionMetadata.contentPathLocalized !== 'undefined' &&
|
|
203
|
+
contentPath === versionMetadata.contentPathLocalized;
|
|
204
|
+
const baseVersionEditUrl =
|
|
205
|
+
isLocalized && options.editLocalizedFiles
|
|
206
|
+
? versionMetadata.editUrlLocalized
|
|
207
|
+
: versionMetadata.editUrl;
|
|
208
|
+
return getEditUrl(relativeFilePath, baseVersionEditUrl);
|
|
209
|
+
}
|
|
210
|
+
return undefined;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const draft = isDraft({env, frontMatter});
|
|
214
|
+
const unlisted = isUnlisted({env, frontMatter});
|
|
215
|
+
|
|
216
|
+
const tags = normalizeTags({
|
|
217
|
+
options,
|
|
218
|
+
source,
|
|
219
|
+
frontMatterTags: frontMatter.tags,
|
|
220
|
+
tagsBaseRoutePath: versionMetadata.tagsPath,
|
|
221
|
+
tagsFile,
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
// Assign all of object properties during instantiation (if possible) for
|
|
225
|
+
// NodeJS optimization.
|
|
226
|
+
// Adding properties to object after instantiation will cause hidden
|
|
227
|
+
// class transitions.
|
|
228
|
+
return {
|
|
229
|
+
id,
|
|
230
|
+
title,
|
|
231
|
+
description,
|
|
232
|
+
source: aliasedSitePath(filePath, siteDir),
|
|
233
|
+
sourceDirName,
|
|
234
|
+
slug: docSlug,
|
|
235
|
+
permalink,
|
|
236
|
+
draft,
|
|
237
|
+
unlisted,
|
|
238
|
+
editUrl: customEditURL !== undefined ? customEditURL : getDocEditUrl(),
|
|
239
|
+
tags,
|
|
240
|
+
version: versionMetadata.versionName,
|
|
241
|
+
lastUpdatedBy: lastUpdate.lastUpdatedBy,
|
|
242
|
+
lastUpdatedAt: lastUpdate.lastUpdatedAt,
|
|
243
|
+
sidebarPosition,
|
|
244
|
+
frontMatter,
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
export async function processDocMetadata(args: {
|
|
249
|
+
docFile: DocFile;
|
|
250
|
+
versionMetadata: VersionMetadata;
|
|
251
|
+
context: LoadContext;
|
|
252
|
+
options: MetadataOptions;
|
|
253
|
+
env: DocEnv;
|
|
254
|
+
tagsFile: TagsFile | null;
|
|
255
|
+
}): Promise<DocMetadataBase> {
|
|
256
|
+
try {
|
|
257
|
+
return await doProcessDocMetadata(args);
|
|
258
|
+
} catch (err) {
|
|
259
|
+
throw new Error(
|
|
260
|
+
`Can't process doc metadata for doc at path path=${args.docFile.filePath} in version name=${args.versionMetadata.versionName}`,
|
|
261
|
+
{cause: err},
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function getUnlistedIds(docs: DocMetadataBase[]): Set<string> {
|
|
267
|
+
return new Set(docs.filter((doc) => doc.unlisted).map((doc) => doc.id));
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
export function addDocNavigation({
|
|
271
|
+
docs,
|
|
272
|
+
sidebarsUtils,
|
|
273
|
+
}: {
|
|
274
|
+
docs: DocMetadataBase[];
|
|
275
|
+
sidebarsUtils: SidebarsUtils;
|
|
276
|
+
}): LoadedVersion['docs'] {
|
|
277
|
+
const docsById = createDocsByIdIndex(docs);
|
|
278
|
+
const unlistedIds = getUnlistedIds(docs);
|
|
279
|
+
|
|
280
|
+
// Add sidebar/next/previous to the docs
|
|
281
|
+
function addNavData(doc: DocMetadataBase): DocMetadata {
|
|
282
|
+
const navigation = sidebarsUtils.getDocNavigation({
|
|
283
|
+
docId: doc.id,
|
|
284
|
+
displayedSidebar: doc.frontMatter.displayed_sidebar,
|
|
285
|
+
unlistedIds,
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
const toNavigationLinkByDocId = (
|
|
289
|
+
docId: string | null | undefined,
|
|
290
|
+
type: 'prev' | 'next',
|
|
291
|
+
): PropNavigationLink | undefined => {
|
|
292
|
+
if (!docId) {
|
|
293
|
+
return undefined;
|
|
294
|
+
}
|
|
295
|
+
const navDoc = docsById[docId];
|
|
296
|
+
if (!navDoc) {
|
|
297
|
+
// This could only happen if user provided the ID through front matter
|
|
298
|
+
throw new Error(
|
|
299
|
+
`Error when loading ${doc.id} in ${doc.sourceDirName}: the pagination_${type} front matter points to a non-existent ID ${docId}.`,
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
// Gracefully handle explicitly providing an unlisted doc ID in production
|
|
303
|
+
if (navDoc.unlisted) {
|
|
304
|
+
return undefined;
|
|
305
|
+
}
|
|
306
|
+
return toDocNavigationLink(navDoc);
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
const previous =
|
|
310
|
+
doc.frontMatter.pagination_prev !== undefined
|
|
311
|
+
? toNavigationLinkByDocId(doc.frontMatter.pagination_prev, 'prev')
|
|
312
|
+
: toNavigationLink(navigation.previous, docsById);
|
|
313
|
+
const next =
|
|
314
|
+
doc.frontMatter.pagination_next !== undefined
|
|
315
|
+
? toNavigationLinkByDocId(doc.frontMatter.pagination_next, 'next')
|
|
316
|
+
: toNavigationLink(navigation.next, docsById);
|
|
317
|
+
|
|
318
|
+
return {...doc, sidebar: navigation.sidebarName, previous, next};
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
const docsWithNavigation = docs.map(addNavData);
|
|
322
|
+
// Sort to ensure consistent output for tests
|
|
323
|
+
docsWithNavigation.sort((a, b) => a.id.localeCompare(b.id));
|
|
324
|
+
return docsWithNavigation;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* The "main doc" is the "version entry point"
|
|
329
|
+
* We browse this doc by clicking on a version:
|
|
330
|
+
* - the "home" doc (at '/docs/')
|
|
331
|
+
* - the first doc of the first sidebar
|
|
332
|
+
* - a random doc (if no docs are in any sidebar... edge case)
|
|
333
|
+
*/
|
|
334
|
+
export function getMainDocId({
|
|
335
|
+
docs,
|
|
336
|
+
sidebarsUtils,
|
|
337
|
+
}: {
|
|
338
|
+
docs: DocMetadataBase[];
|
|
339
|
+
sidebarsUtils: SidebarsUtils;
|
|
340
|
+
}): string {
|
|
341
|
+
function getMainDoc(): DocMetadata {
|
|
342
|
+
const versionHomeDoc = docs.find((doc) => doc.slug === '/');
|
|
343
|
+
const firstDocIdOfFirstSidebar =
|
|
344
|
+
sidebarsUtils.getFirstDocIdOfFirstSidebar();
|
|
345
|
+
if (versionHomeDoc) {
|
|
346
|
+
return versionHomeDoc;
|
|
347
|
+
} else if (firstDocIdOfFirstSidebar) {
|
|
348
|
+
return docs.find((doc) => doc.id === firstDocIdOfFirstSidebar)!;
|
|
349
|
+
}
|
|
350
|
+
return docs[0]!;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
return getMainDoc().id;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// By convention, Docusaurus considers some docs are "indexes":
|
|
357
|
+
// - index.md
|
|
358
|
+
// - readme.md
|
|
359
|
+
// - <folder>/<folder>.md
|
|
360
|
+
//
|
|
361
|
+
// This function is the default implementation of this convention
|
|
362
|
+
//
|
|
363
|
+
// Those index docs produce a different behavior
|
|
364
|
+
// - Slugs do not end with a weird "/index" suffix
|
|
365
|
+
// - Auto-generated sidebar categories link to them as intro
|
|
366
|
+
export const isCategoryIndex: CategoryIndexMatcher = ({
|
|
367
|
+
fileName,
|
|
368
|
+
directories,
|
|
369
|
+
}): boolean => {
|
|
370
|
+
const eligibleDocIndexNames = [
|
|
371
|
+
'index',
|
|
372
|
+
'readme',
|
|
373
|
+
directories[0]?.toLowerCase(),
|
|
374
|
+
];
|
|
375
|
+
return eligibleDocIndexNames.includes(fileName.toLowerCase());
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* `guides/sidebar/autogenerated.md` ->
|
|
380
|
+
* `'autogenerated', '.md', ['sidebar', 'guides']`
|
|
381
|
+
*/
|
|
382
|
+
export function toCategoryIndexMatcherParam({
|
|
383
|
+
source,
|
|
384
|
+
sourceDirName,
|
|
385
|
+
}: Pick<
|
|
386
|
+
DocMetadataBase,
|
|
387
|
+
'source' | 'sourceDirName'
|
|
388
|
+
>): Parameters<CategoryIndexMatcher>[0] {
|
|
389
|
+
// source + sourceDirName are always posix-style
|
|
390
|
+
return {
|
|
391
|
+
fileName: path.posix.parse(source).name,
|
|
392
|
+
extension: path.posix.parse(source).ext,
|
|
393
|
+
directories: sourceDirName.split(path.posix.sep).reverse(),
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// Docs are indexed by their id
|
|
398
|
+
export function createDocsByIdIndex<Doc extends {id: string}>(
|
|
399
|
+
docs: Doc[],
|
|
400
|
+
): {[docId: string]: Doc} {
|
|
401
|
+
return _.keyBy(docs, (d) => d.id);
|
|
402
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
/* eslint-disable camelcase */
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
JoiFrontMatter as Joi, // Custom instance for front matter
|
|
11
|
+
URISchema,
|
|
12
|
+
FrontMatterTagsSchema,
|
|
13
|
+
FrontMatterTOCHeadingLevels,
|
|
14
|
+
validateFrontMatter,
|
|
15
|
+
ContentVisibilitySchema,
|
|
16
|
+
FrontMatterLastUpdateSchema,
|
|
17
|
+
} from '@docusaurus/utils-validation';
|
|
18
|
+
import type {DocFrontMatter} from '@docusaurus/plugin-content-docs';
|
|
19
|
+
|
|
20
|
+
// NOTE: we don't add any default value on purpose here
|
|
21
|
+
// We don't want default values to magically appear in doc metadata and props
|
|
22
|
+
// While the user did not provide those values explicitly
|
|
23
|
+
// We use default values in code instead
|
|
24
|
+
const DocFrontMatterSchema = Joi.object<DocFrontMatter>({
|
|
25
|
+
id: Joi.string(),
|
|
26
|
+
// See https://github.com/facebook/docusaurus/issues/4591#issuecomment-822372398
|
|
27
|
+
title: Joi.string().allow(''),
|
|
28
|
+
hide_title: Joi.boolean(),
|
|
29
|
+
hide_table_of_contents: Joi.boolean(),
|
|
30
|
+
keywords: Joi.array().items(Joi.string().required()),
|
|
31
|
+
image: URISchema,
|
|
32
|
+
// See https://github.com/facebook/docusaurus/issues/4591#issuecomment-822372398
|
|
33
|
+
description: Joi.string().allow(''),
|
|
34
|
+
slug: Joi.string(),
|
|
35
|
+
sidebar_key: Joi.string(),
|
|
36
|
+
sidebar_label: Joi.string(),
|
|
37
|
+
sidebar_position: Joi.number(),
|
|
38
|
+
sidebar_class_name: Joi.string(),
|
|
39
|
+
sidebar_custom_props: Joi.object().unknown(),
|
|
40
|
+
displayed_sidebar: Joi.string().allow(null),
|
|
41
|
+
tags: FrontMatterTagsSchema,
|
|
42
|
+
pagination_label: Joi.string(),
|
|
43
|
+
custom_edit_url: URISchema.allow('', null),
|
|
44
|
+
parse_number_prefixes: Joi.boolean(),
|
|
45
|
+
pagination_next: Joi.string().allow(null),
|
|
46
|
+
pagination_prev: Joi.string().allow(null),
|
|
47
|
+
...FrontMatterTOCHeadingLevels,
|
|
48
|
+
last_update: FrontMatterLastUpdateSchema,
|
|
49
|
+
})
|
|
50
|
+
.unknown()
|
|
51
|
+
.concat(ContentVisibilitySchema);
|
|
52
|
+
|
|
53
|
+
export function validateDocFrontMatter(frontMatter: {
|
|
54
|
+
[key: string]: unknown;
|
|
55
|
+
}): DocFrontMatter {
|
|
56
|
+
return validateFrontMatter(frontMatter, DocFrontMatterSchema);
|
|
57
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import _ from 'lodash';
|
|
9
|
+
import {getMainDocId} from './docs';
|
|
10
|
+
import type {FullVersion} from './types';
|
|
11
|
+
import type {
|
|
12
|
+
CategoryGeneratedIndexMetadata,
|
|
13
|
+
DocMetadata,
|
|
14
|
+
} from '@docusaurus/plugin-content-docs';
|
|
15
|
+
import type {
|
|
16
|
+
GlobalVersion,
|
|
17
|
+
GlobalSidebar,
|
|
18
|
+
GlobalDoc,
|
|
19
|
+
} from '@docusaurus/plugin-content-docs/client';
|
|
20
|
+
import type {Sidebars} from './sidebars/types';
|
|
21
|
+
|
|
22
|
+
function toGlobalDataDoc(doc: DocMetadata): GlobalDoc {
|
|
23
|
+
return {
|
|
24
|
+
id: doc.id,
|
|
25
|
+
path: doc.permalink,
|
|
26
|
+
|
|
27
|
+
// optimize global data size: do not add unlisted: false/undefined
|
|
28
|
+
...(doc.unlisted && {unlisted: doc.unlisted}),
|
|
29
|
+
|
|
30
|
+
// TODO optimize size? remove attribute when no sidebar (breaking change?)
|
|
31
|
+
sidebar: doc.sidebar,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function toGlobalDataGeneratedIndex(
|
|
36
|
+
doc: CategoryGeneratedIndexMetadata,
|
|
37
|
+
): GlobalDoc {
|
|
38
|
+
return {
|
|
39
|
+
id: doc.slug,
|
|
40
|
+
path: doc.permalink,
|
|
41
|
+
sidebar: doc.sidebar,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function toGlobalSidebars(
|
|
46
|
+
sidebars: Sidebars,
|
|
47
|
+
version: FullVersion,
|
|
48
|
+
): {[sidebarId: string]: GlobalSidebar} {
|
|
49
|
+
return _.mapValues(sidebars, (sidebar, sidebarId) => {
|
|
50
|
+
const firstLink = version.sidebarsUtils.getFirstLink(sidebarId);
|
|
51
|
+
if (!firstLink) {
|
|
52
|
+
return {};
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
link: {
|
|
56
|
+
path:
|
|
57
|
+
firstLink.type === 'generated-index'
|
|
58
|
+
? firstLink.permalink
|
|
59
|
+
: version.docs.find((doc) => doc.id === firstLink.id)!.permalink,
|
|
60
|
+
label: firstLink.label,
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function toGlobalDataVersion(version: FullVersion): GlobalVersion {
|
|
67
|
+
return {
|
|
68
|
+
name: version.versionName,
|
|
69
|
+
label: version.label,
|
|
70
|
+
isLast: version.isLast,
|
|
71
|
+
path: version.path,
|
|
72
|
+
mainDocId: getMainDocId(version),
|
|
73
|
+
docs: version.docs
|
|
74
|
+
.map(toGlobalDataDoc)
|
|
75
|
+
.concat(version.categoryGeneratedIndices.map(toGlobalDataGeneratedIndex)),
|
|
76
|
+
draftIds: version.drafts.map((doc) => doc.id),
|
|
77
|
+
sidebars: toGlobalSidebars(version.sidebars, version),
|
|
78
|
+
};
|
|
79
|
+
}
|