@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.
Files changed (119) hide show
  1. package/README.md +6 -2
  2. package/lib/categoryGeneratedIndex.d.ts +12 -0
  3. package/lib/categoryGeneratedIndex.js +36 -0
  4. package/lib/cli.d.ts +13 -0
  5. package/lib/cli.js +124 -0
  6. package/lib/client/doc.d.ts +29 -0
  7. package/lib/client/doc.js +47 -0
  8. package/lib/client/docSidebarItemsExpandedState.d.ts +30 -0
  9. package/lib/client/docSidebarItemsExpandedState.js +27 -0
  10. package/lib/client/docsClientUtils.d.ts +15 -0
  11. package/lib/client/docsClientUtils.js +86 -0
  12. package/lib/client/docsPreferredVersion.d.ts +29 -0
  13. package/lib/client/docsPreferredVersion.js +124 -0
  14. package/lib/client/docsSearch.d.ts +19 -0
  15. package/lib/client/docsSearch.js +39 -0
  16. package/lib/client/docsSidebar.d.ts +25 -0
  17. package/lib/client/docsSidebar.js +29 -0
  18. package/lib/client/docsUtils.d.ts +112 -0
  19. package/lib/client/docsUtils.js +299 -0
  20. package/lib/client/docsVersion.d.ts +19 -0
  21. package/lib/client/docsVersion.js +25 -0
  22. package/lib/client/index.d.ts +91 -0
  23. package/lib/client/index.js +83 -0
  24. package/lib/client/structuredDataUtils.d.ts +11 -0
  25. package/lib/client/structuredDataUtils.js +35 -0
  26. package/lib/constants.d.ts +14 -0
  27. package/lib/constants.js +17 -0
  28. package/lib/contentHelpers.d.ts +12 -0
  29. package/lib/contentHelpers.js +31 -0
  30. package/lib/docs.d.ts +48 -0
  31. package/lib/docs.js +252 -0
  32. package/lib/frontMatter.d.ts +10 -0
  33. package/lib/frontMatter.js +46 -0
  34. package/lib/globalData.d.ts +9 -0
  35. package/lib/globalData.js +59 -0
  36. package/lib/index.d.ts +10 -0
  37. package/lib/index.js +198 -0
  38. package/lib/numberPrefix.d.ts +11 -0
  39. package/lib/numberPrefix.js +50 -0
  40. package/lib/options.d.ts +10 -0
  41. package/lib/options.js +138 -0
  42. package/lib/props.d.ts +21 -0
  43. package/lib/props.js +156 -0
  44. package/lib/routes.d.ts +22 -0
  45. package/lib/routes.js +184 -0
  46. package/lib/server-export.d.ts +9 -0
  47. package/lib/server-export.js +25 -0
  48. package/lib/sidebars/generator.d.ts +10 -0
  49. package/lib/sidebars/generator.js +217 -0
  50. package/lib/sidebars/index.d.ts +13 -0
  51. package/lib/sidebars/index.js +99 -0
  52. package/lib/sidebars/normalization.d.ts +13 -0
  53. package/lib/sidebars/normalization.js +58 -0
  54. package/lib/sidebars/postProcessor.d.ts +11 -0
  55. package/lib/sidebars/postProcessor.js +82 -0
  56. package/lib/sidebars/processor.d.ts +10 -0
  57. package/lib/sidebars/processor.js +77 -0
  58. package/lib/sidebars/types.d.ts +192 -0
  59. package/lib/sidebars/types.js +8 -0
  60. package/lib/sidebars/utils.d.ts +69 -0
  61. package/lib/sidebars/utils.js +327 -0
  62. package/lib/sidebars/validation.d.ts +11 -0
  63. package/lib/sidebars/validation.js +152 -0
  64. package/lib/slug.d.ts +15 -0
  65. package/lib/slug.js +51 -0
  66. package/lib/translations.d.ts +10 -0
  67. package/lib/translations.js +243 -0
  68. package/lib/types.d.ts +27 -0
  69. package/lib/types.js +8 -0
  70. package/lib/versions/files.d.ts +54 -0
  71. package/lib/versions/files.js +152 -0
  72. package/lib/versions/loadVersion.d.ts +17 -0
  73. package/lib/versions/loadVersion.js +126 -0
  74. package/lib/versions/validation.d.ts +17 -0
  75. package/lib/versions/validation.js +70 -0
  76. package/lib/versions/version.d.ts +40 -0
  77. package/lib/versions/version.js +189 -0
  78. package/package.json +70 -4
  79. package/src/categoryGeneratedIndex.ts +60 -0
  80. package/src/cli.ts +180 -0
  81. package/src/client/doc.tsx +71 -0
  82. package/src/client/docSidebarItemsExpandedState.tsx +55 -0
  83. package/src/client/docsClientUtils.ts +140 -0
  84. package/src/client/docsPreferredVersion.tsx +245 -0
  85. package/src/client/docsSearch.test.ts +15 -0
  86. package/src/client/docsSearch.ts +57 -0
  87. package/src/client/docsSidebar.tsx +50 -0
  88. package/src/client/docsUtils.tsx +442 -0
  89. package/src/client/docsVersion.tsx +36 -0
  90. package/src/client/index.ts +211 -0
  91. package/src/client/structuredDataUtils.ts +54 -0
  92. package/src/constants.ts +15 -0
  93. package/src/contentHelpers.ts +34 -0
  94. package/src/docs.ts +402 -0
  95. package/src/frontMatter.ts +57 -0
  96. package/src/globalData.ts +79 -0
  97. package/src/index.ts +289 -0
  98. package/src/numberPrefix.ts +62 -0
  99. package/src/options.ts +185 -0
  100. package/src/plugin-content-docs.d.ts +667 -0
  101. package/src/props.ts +223 -0
  102. package/src/routes.ts +263 -0
  103. package/src/server-export.ts +26 -0
  104. package/src/sidebars/README.md +10 -0
  105. package/src/sidebars/generator.ts +307 -0
  106. package/src/sidebars/index.ts +130 -0
  107. package/src/sidebars/normalization.ts +91 -0
  108. package/src/sidebars/postProcessor.ts +115 -0
  109. package/src/sidebars/processor.ts +122 -0
  110. package/src/sidebars/types.ts +293 -0
  111. package/src/sidebars/utils.ts +529 -0
  112. package/src/sidebars/validation.ts +189 -0
  113. package/src/slug.ts +79 -0
  114. package/src/translations.ts +365 -0
  115. package/src/types.ts +34 -0
  116. package/src/versions/files.ts +240 -0
  117. package/src/versions/loadVersion.ts +185 -0
  118. package/src/versions/validation.ts +115 -0
  119. 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
+ }
@@ -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
+ }