@docusaurus/plugin-content-docs 0.0.0-4071 → 0.0.0-4075

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 (64) hide show
  1. package/lib/.tsbuildinfo +1 -1
  2. package/lib/cli.d.ts +1 -1
  3. package/lib/cli.js +12 -16
  4. package/lib/index.js +12 -16
  5. package/lib/options.js +2 -2
  6. package/lib/{sidebarItemsGenerator.d.ts → sidebars/generator.d.ts} +1 -1
  7. package/lib/sidebars/generator.js +174 -0
  8. package/lib/sidebars/index.d.ts +14 -0
  9. package/lib/sidebars/index.js +64 -0
  10. package/lib/sidebars/normalization.d.ts +9 -0
  11. package/lib/sidebars/normalization.js +58 -0
  12. package/lib/sidebars/processor.d.ts +16 -0
  13. package/lib/sidebars/processor.js +70 -0
  14. package/lib/sidebars/types.d.ts +87 -0
  15. package/lib/sidebars/types.js +13 -0
  16. package/lib/sidebars/utils.d.ts +22 -0
  17. package/lib/sidebars/utils.js +101 -0
  18. package/lib/sidebars/validation.d.ts +8 -0
  19. package/lib/sidebars/validation.js +102 -0
  20. package/lib/translations.d.ts +1 -1
  21. package/lib/translations.js +9 -9
  22. package/lib/types.d.ts +1 -58
  23. package/package.json +10 -9
  24. package/src/__tests__/index.test.ts +4 -5
  25. package/src/__tests__/options.test.ts +1 -1
  26. package/src/cli.ts +23 -29
  27. package/src/index.ts +19 -27
  28. package/src/options.ts +1 -1
  29. package/src/plugin-content-docs.d.ts +6 -24
  30. package/src/props.ts +3 -5
  31. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-category-shorthand.js +0 -0
  32. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-category-wrong-items.json +0 -0
  33. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-category-wrong-label.json +0 -0
  34. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-category.js +0 -0
  35. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-collapsed-first-level.json +0 -0
  36. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-collapsed.json +0 -0
  37. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-doc-id-not-string.json +0 -0
  38. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-first-level-not-category.js +0 -0
  39. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-link-wrong-href.json +0 -0
  40. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-link-wrong-label.json +0 -0
  41. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-link.json +0 -0
  42. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-unknown-type.json +0 -0
  43. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-wrong-field.json +0 -0
  44. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars.json +0 -0
  45. package/src/{__tests__/__snapshots__/sidebars.test.ts.snap → sidebars/__tests__/__snapshots__/index.test.ts.snap} +6 -6
  46. package/src/{__tests__/sidebarItemsGenerator.test.ts → sidebars/__tests__/generator.test.ts} +2 -2
  47. package/src/sidebars/__tests__/index.test.ts +202 -0
  48. package/src/sidebars/__tests__/processor.test.ts +148 -0
  49. package/src/sidebars/__tests__/utils.test.ts +395 -0
  50. package/src/sidebars/generator.ts +253 -0
  51. package/src/sidebars/index.ts +84 -0
  52. package/src/sidebars/normalization.ts +88 -0
  53. package/src/sidebars/processor.ts +124 -0
  54. package/src/sidebars/types.ts +156 -0
  55. package/src/sidebars/utils.ts +146 -0
  56. package/src/sidebars/validation.ts +124 -0
  57. package/src/translations.ts +7 -12
  58. package/src/types.ts +1 -94
  59. package/lib/sidebarItemsGenerator.js +0 -222
  60. package/lib/sidebars.d.ts +0 -45
  61. package/lib/sidebars.js +0 -365
  62. package/src/__tests__/sidebars.test.ts +0 -741
  63. package/src/sidebarItemsGenerator.ts +0 -322
  64. package/src/sidebars.ts +0 -609
@@ -1,322 +0,0 @@
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 {
9
- SidebarItem,
10
- SidebarItemDoc,
11
- SidebarItemCategory,
12
- SidebarItemsGenerator,
13
- SidebarItemsGeneratorDoc,
14
- } from './types';
15
- import {sortBy, take, last, orderBy} from 'lodash';
16
- import {addTrailingSlash, posixPath} from '@docusaurus/utils';
17
- import {Joi} from '@docusaurus/utils-validation';
18
- import chalk from 'chalk';
19
- import path from 'path';
20
- import fs from 'fs-extra';
21
- import Yaml from 'js-yaml';
22
-
23
- const BreadcrumbSeparator = '/';
24
-
25
- export const CategoryMetadataFilenameBase = '_category_';
26
- export const CategoryMetadataFilenamePattern = '_category_.{json,yml,yaml}';
27
-
28
- export type CategoryMetadatasFile = {
29
- label?: string;
30
- position?: number;
31
- collapsed?: boolean;
32
- collapsible?: boolean;
33
- className?: string;
34
-
35
- // TODO should we allow "items" here? how would this work? would an "autogenerated" type be allowed?
36
- // This mkdocs plugin do something like that: https://github.com/lukasgeiter/mkdocs-awesome-pages-plugin/
37
- // cf comment: https://github.com/facebook/docusaurus/issues/3464#issuecomment-784765199
38
- };
39
-
40
- type WithPosition = {position?: number};
41
- type SidebarItemWithPosition = SidebarItem & WithPosition;
42
-
43
- const CategoryMetadatasFileSchema = Joi.object<CategoryMetadatasFile>({
44
- label: Joi.string(),
45
- position: Joi.number(),
46
- collapsed: Joi.boolean(),
47
- collapsible: Joi.boolean(),
48
- className: Joi.string(),
49
- });
50
-
51
- // TODO I now believe we should read all the category metadata files ahead of time: we may need this metadata to customize docs metadata
52
- // Example use-case being able to disable number prefix parsing at the folder level, or customize the default route path segment for an intermediate directory...
53
- // TODO later if there is `CategoryFolder/index.md`, we may want to read the metadata as yaml on it
54
- // see https://github.com/facebook/docusaurus/issues/3464#issuecomment-818670449
55
- async function readCategoryMetadatasFile(
56
- categoryDirPath: string,
57
- ): Promise<CategoryMetadatasFile | null> {
58
- function validateCategoryMetadataFile(
59
- content: unknown,
60
- ): CategoryMetadatasFile {
61
- return Joi.attempt(content, CategoryMetadatasFileSchema);
62
- }
63
-
64
- async function tryReadFile(
65
- fileNameWithExtension: string,
66
- parse: (content: string) => unknown,
67
- ): Promise<CategoryMetadatasFile | null> {
68
- // Simpler to use only posix paths for mocking file metadatas in tests
69
- const filePath = posixPath(
70
- path.join(categoryDirPath, fileNameWithExtension),
71
- );
72
- if (await fs.pathExists(filePath)) {
73
- const contentString = await fs.readFile(filePath, {encoding: 'utf8'});
74
- const unsafeContent: unknown = parse(contentString);
75
- try {
76
- return validateCategoryMetadataFile(unsafeContent);
77
- } catch (e) {
78
- console.error(
79
- chalk.red(
80
- `The docs sidebar category metadata file looks invalid!\nPath: ${filePath}`,
81
- ),
82
- );
83
- throw e;
84
- }
85
- }
86
- return null;
87
- }
88
-
89
- return (
90
- (await tryReadFile(`${CategoryMetadataFilenameBase}.json`, JSON.parse)) ??
91
- (await tryReadFile(`${CategoryMetadataFilenameBase}.yml`, Yaml.load)) ??
92
- // eslint-disable-next-line no-return-await
93
- (await tryReadFile(`${CategoryMetadataFilenameBase}.yaml`, Yaml.load))
94
- );
95
- }
96
-
97
- // [...parents, tail]
98
- function parseBreadcrumb(breadcrumb: string[]): {
99
- parents: string[];
100
- tail: string;
101
- } {
102
- return {
103
- parents: take(breadcrumb, breadcrumb.length - 1),
104
- tail: last(breadcrumb)!,
105
- };
106
- }
107
-
108
- // Comment for this feature: https://github.com/facebook/docusaurus/issues/3464#issuecomment-818670449
109
- export const DefaultSidebarItemsGenerator: SidebarItemsGenerator = async ({
110
- item,
111
- docs: allDocs,
112
- version,
113
- numberPrefixParser,
114
- options,
115
- }) => {
116
- // Doc at the root of the autogenerated sidebar dir
117
- function isRootDoc(doc: SidebarItemsGeneratorDoc) {
118
- return doc.sourceDirName === item.dirName;
119
- }
120
-
121
- // Doc inside a subfolder of the autogenerated sidebar dir
122
- function isCategoryDoc(doc: SidebarItemsGeneratorDoc) {
123
- if (isRootDoc(doc)) {
124
- return false;
125
- }
126
-
127
- return (
128
- // autogen dir is . and doc is in subfolder
129
- item.dirName === '.' ||
130
- // autogen dir is not . and doc is in subfolder
131
- // "api/myDoc" startsWith "api/" (note "api2/myDoc" is not included)
132
- doc.sourceDirName.startsWith(addTrailingSlash(item.dirName))
133
- );
134
- }
135
-
136
- function isInAutogeneratedDir(doc: SidebarItemsGeneratorDoc) {
137
- return isRootDoc(doc) || isCategoryDoc(doc);
138
- }
139
-
140
- // autogenDir=a/b and docDir=a/b/c/d => returns c/d
141
- // autogenDir=a/b and docDir=a/b => returns .
142
- function getDocDirRelativeToAutogenDir(
143
- doc: SidebarItemsGeneratorDoc,
144
- ): string {
145
- if (!isInAutogeneratedDir(doc)) {
146
- throw new Error(
147
- 'getDocDirRelativeToAutogenDir() can only be called for subdocs of the sidebar autogen dir.',
148
- );
149
- }
150
- // Is there a node API to compare 2 relative paths more easily?
151
- // path.relative() does not give good results
152
- if (item.dirName === '.') {
153
- return doc.sourceDirName;
154
- } else if (item.dirName === doc.sourceDirName) {
155
- return '.';
156
- } else {
157
- return doc.sourceDirName.replace(addTrailingSlash(item.dirName), '');
158
- }
159
- }
160
-
161
- // Get only docs in the autogen dir
162
- // Sort by folder+filename at once
163
- const docs = sortBy(allDocs.filter(isInAutogeneratedDir), (d) => d.source);
164
-
165
- if (docs.length === 0) {
166
- console.warn(
167
- chalk.yellow(
168
- `No docs found in dir ${item.dirName}: can't auto-generate a sidebar.`,
169
- ),
170
- );
171
- }
172
-
173
- function createDocSidebarItem(
174
- doc: SidebarItemsGeneratorDoc,
175
- ): SidebarItemDoc & WithPosition {
176
- return {
177
- type: 'doc',
178
- id: doc.id,
179
- ...(doc.frontMatter.sidebar_label && {
180
- label: doc.frontMatter.sidebar_label,
181
- }),
182
- ...(doc.frontMatter.sidebar_class_name && {
183
- className: doc.frontMatter.sidebar_class_name,
184
- }),
185
- ...(typeof doc.sidebarPosition !== 'undefined' && {
186
- position: doc.sidebarPosition,
187
- }),
188
- };
189
- }
190
-
191
- async function createCategorySidebarItem({
192
- breadcrumb,
193
- }: {
194
- breadcrumb: string[];
195
- }): Promise<SidebarItemCategory & WithPosition> {
196
- const categoryDirPath = path.join(
197
- version.contentPath,
198
- item.dirName, // fix https://github.com/facebook/docusaurus/issues/4638
199
- breadcrumb.join(BreadcrumbSeparator),
200
- );
201
-
202
- const categoryMetadatas = await readCategoryMetadatasFile(categoryDirPath);
203
-
204
- const {tail} = parseBreadcrumb(breadcrumb);
205
-
206
- const {filename, numberPrefix} = numberPrefixParser(tail);
207
-
208
- const position = categoryMetadatas?.position ?? numberPrefix;
209
-
210
- const collapsible =
211
- categoryMetadatas?.collapsible ?? options.sidebarCollapsible;
212
- const collapsed = categoryMetadatas?.collapsed ?? options.sidebarCollapsed;
213
- const className = categoryMetadatas?.className;
214
-
215
- return {
216
- type: 'category',
217
- label: categoryMetadatas?.label ?? filename,
218
- items: [],
219
- collapsed,
220
- collapsible,
221
- ...(typeof position !== 'undefined' && {position}),
222
- ...(typeof className !== 'undefined' && {className}),
223
- };
224
- }
225
-
226
- // Not sure how to simplify this algorithm :/
227
- async function autogenerateSidebarItems(): Promise<
228
- SidebarItemWithPosition[]
229
- > {
230
- const sidebarItems: SidebarItem[] = []; // mutable result
231
-
232
- const categoriesByBreadcrumb: Record<string, SidebarItemCategory> = {}; // mutable cache of categories already created
233
-
234
- async function getOrCreateCategoriesForBreadcrumb(
235
- breadcrumb: string[],
236
- ): Promise<SidebarItemCategory | null> {
237
- if (breadcrumb.length === 0) {
238
- return null;
239
- }
240
- const {parents} = parseBreadcrumb(breadcrumb);
241
- const parentCategory = await getOrCreateCategoriesForBreadcrumb(parents);
242
- const existingCategory =
243
- categoriesByBreadcrumb[breadcrumb.join(BreadcrumbSeparator)];
244
-
245
- if (existingCategory) {
246
- return existingCategory;
247
- } else {
248
- const newCategory = await createCategorySidebarItem({
249
- breadcrumb,
250
- });
251
- if (parentCategory) {
252
- parentCategory.items.push(newCategory);
253
- } else {
254
- sidebarItems.push(newCategory);
255
- }
256
- categoriesByBreadcrumb[breadcrumb.join(BreadcrumbSeparator)] =
257
- newCategory;
258
- return newCategory;
259
- }
260
- }
261
-
262
- // Get the category breadcrumb of a doc (relative to the dir of the autogenerated sidebar item)
263
- function getRelativeBreadcrumb(doc: SidebarItemsGeneratorDoc): string[] {
264
- const relativeDirPath = getDocDirRelativeToAutogenDir(doc);
265
- if (relativeDirPath === '.') {
266
- return [];
267
- } else {
268
- return relativeDirPath.split(BreadcrumbSeparator);
269
- }
270
- }
271
-
272
- async function handleDocItem(doc: SidebarItemsGeneratorDoc): Promise<void> {
273
- const breadcrumb = getRelativeBreadcrumb(doc);
274
- const category = await getOrCreateCategoriesForBreadcrumb(breadcrumb);
275
-
276
- const docSidebarItem = createDocSidebarItem(doc);
277
- if (category) {
278
- category.items.push(docSidebarItem);
279
- } else {
280
- sidebarItems.push(docSidebarItem);
281
- }
282
- }
283
-
284
- // async process made sequential on purpose! order matters
285
- // eslint-disable-next-line no-restricted-syntax
286
- for (const doc of docs) {
287
- // eslint-disable-next-line no-await-in-loop
288
- await handleDocItem(doc);
289
- }
290
-
291
- return sidebarItems;
292
- }
293
-
294
- const sidebarItems = await autogenerateSidebarItems();
295
-
296
- return sortSidebarItems(sidebarItems);
297
- };
298
-
299
- // Recursively sort the categories/docs + remove the "position" attribute from final output
300
- // Note: the "position" is only used to sort "inside" a sidebar slice
301
- // It is not used to sort across multiple consecutive sidebar slices (ie a whole Category composed of multiple autogenerated items)
302
- function sortSidebarItems(
303
- sidebarItems: SidebarItemWithPosition[],
304
- ): SidebarItem[] {
305
- const processedSidebarItems = sidebarItems.map((item) => {
306
- if (item.type === 'category') {
307
- return {
308
- ...item,
309
- items: sortSidebarItems(item.items),
310
- };
311
- }
312
- return item;
313
- });
314
-
315
- const sortedSidebarItems = orderBy(
316
- processedSidebarItems,
317
- (item) => item.position,
318
- ['asc'],
319
- );
320
-
321
- return sortedSidebarItems.map(({position, ...item}) => item);
322
- }