@docusaurus/plugin-content-docs 2.0.0-beta.fc64c12e4 → 2.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 (236) hide show
  1. package/lib/categoryGeneratedIndex.d.ts +12 -0
  2. package/lib/categoryGeneratedIndex.js +37 -0
  3. package/lib/cli.d.ts +3 -2
  4. package/lib/cli.js +57 -73
  5. package/lib/client/docsClientUtils.d.ts +9 -28
  6. package/lib/client/docsClientUtils.js +34 -43
  7. package/lib/client/index.d.ts +81 -0
  8. package/lib/client/index.js +67 -0
  9. package/lib/constants.d.ts +4 -0
  10. package/lib/constants.js +4 -1
  11. package/lib/docs.d.ts +33 -6
  12. package/lib/docs.js +201 -78
  13. package/{src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docusaurus.config.js → lib/frontMatter.d.ts} +4 -8
  14. package/lib/{docFrontMatter.js → frontMatter.js} +22 -3
  15. package/lib/globalData.d.ts +3 -3
  16. package/lib/globalData.js +35 -6
  17. package/lib/index.d.ts +3 -3
  18. package/lib/index.js +120 -153
  19. package/lib/lastUpdate.d.ts +4 -6
  20. package/lib/lastUpdate.js +22 -26
  21. package/lib/markdown/index.d.ts +3 -6
  22. package/lib/markdown/index.js +3 -3
  23. package/lib/markdown/linkify.d.ts +1 -1
  24. package/lib/markdown/linkify.js +6 -3
  25. package/lib/numberPrefix.d.ts +1 -1
  26. package/lib/numberPrefix.js +16 -21
  27. package/lib/options.d.ts +3 -5
  28. package/lib/options.js +34 -26
  29. package/lib/props.d.ts +7 -2
  30. package/lib/props.js +84 -13
  31. package/lib/routes.d.ts +29 -0
  32. package/lib/routes.js +96 -0
  33. package/lib/server-export.d.ts +9 -0
  34. package/lib/server-export.js +25 -0
  35. package/lib/{sidebarItemsGenerator.d.ts → sidebars/generator.d.ts} +1 -7
  36. package/lib/sidebars/generator.js +209 -0
  37. package/lib/sidebars/index.d.ts +13 -0
  38. package/lib/sidebars/index.js +92 -0
  39. package/lib/sidebars/normalization.d.ts +13 -0
  40. package/lib/sidebars/normalization.js +59 -0
  41. package/lib/sidebars/postProcessor.d.ts +11 -0
  42. package/lib/sidebars/postProcessor.js +81 -0
  43. package/lib/sidebars/processor.d.ts +10 -0
  44. package/lib/sidebars/processor.js +79 -0
  45. package/lib/sidebars/types.d.ts +183 -0
  46. package/lib/{docFrontMatter.d.ts → sidebars/types.js} +2 -2
  47. package/lib/sidebars/utils.d.ts +55 -0
  48. package/lib/sidebars/utils.js +259 -0
  49. package/lib/sidebars/validation.d.ts +11 -0
  50. package/lib/sidebars/validation.js +143 -0
  51. package/lib/slug.d.ts +5 -4
  52. package/lib/slug.js +28 -18
  53. package/{src/__tests__/__fixtures__/sidebars/sidebars-first-level-not-category.js → lib/tags.d.ts} +3 -14
  54. package/lib/tags.js +21 -0
  55. package/lib/translations.d.ts +3 -3
  56. package/lib/translations.js +100 -93
  57. package/lib/types.d.ts +13 -192
  58. package/lib/versions/files.d.ts +50 -0
  59. package/lib/versions/files.js +141 -0
  60. package/lib/versions/index.d.ts +36 -0
  61. package/lib/versions/index.js +154 -0
  62. package/lib/versions/validation.d.ts +17 -0
  63. package/lib/versions/validation.js +71 -0
  64. package/package.json +44 -30
  65. package/src/categoryGeneratedIndex.ts +60 -0
  66. package/src/cli.ts +88 -118
  67. package/src/client/docsClientUtils.ts +44 -71
  68. package/src/client/index.ts +158 -0
  69. package/src/constants.ts +4 -2
  70. package/src/docs.ts +277 -80
  71. package/src/frontMatter.ts +63 -0
  72. package/src/globalData.ts +57 -7
  73. package/src/index.ts +174 -221
  74. package/src/lastUpdate.ts +27 -38
  75. package/src/markdown/index.ts +10 -16
  76. package/src/markdown/linkify.ts +7 -4
  77. package/src/numberPrefix.ts +19 -26
  78. package/src/options.ts +38 -38
  79. package/src/plugin-content-docs.d.ts +570 -91
  80. package/src/props.ts +121 -20
  81. package/src/routes.ts +159 -0
  82. package/src/server-export.ts +26 -0
  83. package/src/sidebars/README.md +10 -0
  84. package/src/sidebars/generator.ts +292 -0
  85. package/src/sidebars/index.ts +118 -0
  86. package/src/sidebars/normalization.ts +91 -0
  87. package/src/sidebars/postProcessor.ts +112 -0
  88. package/src/sidebars/processor.ts +123 -0
  89. package/src/sidebars/types.ts +280 -0
  90. package/src/sidebars/utils.ts +393 -0
  91. package/src/sidebars/validation.ts +179 -0
  92. package/src/slug.ts +41 -22
  93. package/src/tags.ts +20 -0
  94. package/src/translations.ts +155 -124
  95. package/src/types.ts +17 -259
  96. package/src/versions/files.ts +216 -0
  97. package/src/versions/index.ts +246 -0
  98. package/src/versions/validation.ts +115 -0
  99. package/lib/.tsbuildinfo +0 -1
  100. package/lib/sidebarItemsGenerator.js +0 -215
  101. package/lib/sidebars.d.ts +0 -45
  102. package/lib/sidebars.js +0 -354
  103. package/lib/theme/hooks/useDocs.d.ts +0 -20
  104. package/lib/theme/hooks/useDocs.js +0 -75
  105. package/lib/versions.d.ts +0 -16
  106. package/lib/versions.js +0 -319
  107. package/src/__tests__/__fixtures__/bad-id-site/docs/invalid-id.md +0 -5
  108. package/src/__tests__/__fixtures__/bad-slug-on-doc-home-site/docs/docWithSlug.md +0 -5
  109. package/src/__tests__/__fixtures__/empty-site/docusaurus.config.js +0 -16
  110. package/src/__tests__/__fixtures__/empty-site/sidebars.json +0 -1
  111. package/src/__tests__/__fixtures__/sidebars/sidebars-category-shorthand.js +0 -34
  112. package/src/__tests__/__fixtures__/sidebars/sidebars-category-wrong-items.json +0 -11
  113. package/src/__tests__/__fixtures__/sidebars/sidebars-category-wrong-label.json +0 -11
  114. package/src/__tests__/__fixtures__/sidebars/sidebars-category.js +0 -44
  115. package/src/__tests__/__fixtures__/sidebars/sidebars-collapsed-first-level.json +0 -20
  116. package/src/__tests__/__fixtures__/sidebars/sidebars-collapsed.json +0 -21
  117. package/src/__tests__/__fixtures__/sidebars/sidebars-doc-id-not-string.json +0 -10
  118. package/src/__tests__/__fixtures__/sidebars/sidebars-link-wrong-href.json +0 -11
  119. package/src/__tests__/__fixtures__/sidebars/sidebars-link-wrong-label.json +0 -11
  120. package/src/__tests__/__fixtures__/sidebars/sidebars-link.json +0 -11
  121. package/src/__tests__/__fixtures__/sidebars/sidebars-unknown-type.json +0 -14
  122. package/src/__tests__/__fixtures__/sidebars/sidebars-wrong-field.json +0 -20
  123. package/src/__tests__/__fixtures__/sidebars/sidebars.json +0 -20
  124. package/src/__tests__/__fixtures__/simple-site/docs/_partials/somePartial.md +0 -3
  125. package/src/__tests__/__fixtures__/simple-site/docs/_partials/subfolder/somePartial.md +0 -3
  126. package/src/__tests__/__fixtures__/simple-site/docs/_somePartial.md +0 -3
  127. package/src/__tests__/__fixtures__/simple-site/docs/foo/bar.md +0 -69
  128. package/src/__tests__/__fixtures__/simple-site/docs/foo/baz.md +0 -70
  129. package/src/__tests__/__fixtures__/simple-site/docs/headingAsTitle.md +0 -1
  130. package/src/__tests__/__fixtures__/simple-site/docs/hello.md +0 -53
  131. package/src/__tests__/__fixtures__/simple-site/docs/ipsum.md +0 -5
  132. package/src/__tests__/__fixtures__/simple-site/docs/lorem.md +0 -6
  133. package/src/__tests__/__fixtures__/simple-site/docs/rootAbsoluteSlug.md +0 -5
  134. package/src/__tests__/__fixtures__/simple-site/docs/rootRelativeSlug.md +0 -5
  135. package/src/__tests__/__fixtures__/simple-site/docs/rootResolvedSlug.md +0 -5
  136. package/src/__tests__/__fixtures__/simple-site/docs/rootTryToEscapeSlug.md +0 -5
  137. package/src/__tests__/__fixtures__/simple-site/docs/slugs/absoluteSlug.md +0 -5
  138. package/src/__tests__/__fixtures__/simple-site/docs/slugs/relativeSlug.md +0 -5
  139. package/src/__tests__/__fixtures__/simple-site/docs/slugs/resolvedSlug.md +0 -5
  140. package/src/__tests__/__fixtures__/simple-site/docs/slugs/tryToEscapeSlug.md +0 -5
  141. package/src/__tests__/__fixtures__/simple-site/docusaurus.config.js +0 -14
  142. package/src/__tests__/__fixtures__/simple-site/sidebars.json +0 -23
  143. package/src/__tests__/__fixtures__/simple-site/wrong-sidebars.json +0 -7
  144. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/0-getting-started.md +0 -3
  145. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/1-installation.md +0 -3
  146. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/3-API/00_api-overview.md +0 -3
  147. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/3-API/01_Core APIs/0 --- Client API.md +0 -1
  148. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/3-API/01_Core APIs/1 --- Server API.md +0 -1
  149. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/3-API/02_Extension APIs/0. Plugin API.md +0 -1
  150. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/3-API/02_Extension APIs/1. Theme API.md +0 -1
  151. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/3-API/02_Extension APIs/_category_.yml +0 -1
  152. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/3-API/03_api-end.md +0 -3
  153. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/3-API/_category_.json +0 -3
  154. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/Guides/0-guide2.5.md +0 -8
  155. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/Guides/02-guide2.md +0 -7
  156. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/Guides/_category_.json +0 -3
  157. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/Guides/a-guide4.md +0 -7
  158. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/Guides/b-guide5.md +0 -7
  159. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/Guides/guide3.md +0 -8
  160. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/Guides/z-guide1.md +0 -8
  161. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/partialAutogeneratedSidebars.js +0 -23
  162. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/partialAutogeneratedSidebars2.js +0 -16
  163. package/src/__tests__/__fixtures__/site-with-doc-label/docs/hello-1.md +0 -7
  164. package/src/__tests__/__fixtures__/site-with-doc-label/docs/hello-2.md +0 -8
  165. package/src/__tests__/__fixtures__/site-with-doc-label/docusaurus.config.js +0 -14
  166. package/src/__tests__/__fixtures__/site-with-doc-label/sidebars.json +0 -14
  167. package/src/__tests__/__fixtures__/versioned-site/community/team.md +0 -1
  168. package/src/__tests__/__fixtures__/versioned-site/community_sidebars.json +0 -3
  169. package/src/__tests__/__fixtures__/versioned-site/community_versioned_docs/version-1.0.0/team.md +0 -1
  170. package/src/__tests__/__fixtures__/versioned-site/community_versioned_sidebars/version-1.0.0-sidebars.json +0 -3
  171. package/src/__tests__/__fixtures__/versioned-site/community_versions.json +0 -1
  172. package/src/__tests__/__fixtures__/versioned-site/docs/foo/bar.md +0 -4
  173. package/src/__tests__/__fixtures__/versioned-site/docs/hello.md +0 -1
  174. package/src/__tests__/__fixtures__/versioned-site/docs/slugs/absoluteSlug.md +0 -5
  175. package/src/__tests__/__fixtures__/versioned-site/docs/slugs/relativeSlug.md +0 -5
  176. package/src/__tests__/__fixtures__/versioned-site/docs/slugs/resolvedSlug.md +0 -5
  177. package/src/__tests__/__fixtures__/versioned-site/docs/slugs/tryToEscapeSlug.md +0 -5
  178. package/src/__tests__/__fixtures__/versioned-site/docusaurus.config.js +0 -18
  179. package/src/__tests__/__fixtures__/versioned-site/i18n/en/docusaurus-plugin-content-docs/version-1.0.0/hello.md +0 -1
  180. package/src/__tests__/__fixtures__/versioned-site/i18n/en/docusaurus-plugin-content-docs-community/current/team.md +0 -5
  181. package/src/__tests__/__fixtures__/versioned-site/i18n/fr/docusaurus-plugin-content-docs/version-1.0.0/hello.md +0 -1
  182. package/src/__tests__/__fixtures__/versioned-site/sidebars.json +0 -10
  183. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-1.0.0/foo/bar.md +0 -4
  184. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-1.0.0/foo/baz.md +0 -1
  185. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-1.0.0/hello.md +0 -1
  186. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-1.0.1/_partials/somePartial.md +0 -3
  187. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-1.0.1/_partials/subfolder/somePartial.md +0 -3
  188. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-1.0.1/_somePartial.md +0 -3
  189. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-1.0.1/foo/bar.md +0 -1
  190. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-1.0.1/hello.md +0 -1
  191. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-withSlugs/rootAbsoluteSlug.md +0 -5
  192. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-withSlugs/rootRelativeSlug.md +0 -5
  193. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-withSlugs/rootResolvedSlug.md +0 -5
  194. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-withSlugs/rootTryToEscapeSlug.md +0 -5
  195. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-withSlugs/slugs/absoluteSlug.md +0 -5
  196. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-withSlugs/slugs/relativeSlug.md +0 -5
  197. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-withSlugs/slugs/resolvedSlug.md +0 -5
  198. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-withSlugs/slugs/tryToEscapeSlug.md +0 -5
  199. package/src/__tests__/__fixtures__/versioned-site/versioned_sidebars/version-1.0.0-sidebars.json +0 -11
  200. package/src/__tests__/__fixtures__/versioned-site/versioned_sidebars/version-1.0.1-sidebars.json +0 -10
  201. package/src/__tests__/__fixtures__/versioned-site/versioned_sidebars/version-withSlugs-sidebars.json +0 -5
  202. package/src/__tests__/__fixtures__/versioned-site/versions.json +0 -5
  203. package/src/__tests__/__snapshots__/cli.test.ts.snap +0 -95
  204. package/src/__tests__/__snapshots__/index.test.ts.snap +0 -1926
  205. package/src/__tests__/__snapshots__/sidebars.test.ts.snap +0 -233
  206. package/src/__tests__/__snapshots__/translations.test.ts.snap +0 -484
  207. package/src/__tests__/cli.test.ts +0 -337
  208. package/src/__tests__/docFrontMatter.test.ts +0 -244
  209. package/src/__tests__/docs.test.ts +0 -878
  210. package/src/__tests__/index.test.ts +0 -1885
  211. package/src/__tests__/lastUpdate.test.ts +0 -69
  212. package/src/__tests__/numberPrefix.test.ts +0 -199
  213. package/src/__tests__/options.test.ts +0 -272
  214. package/src/__tests__/sidebarItemsGenerator.test.ts +0 -358
  215. package/src/__tests__/sidebars.test.ts +0 -746
  216. package/src/__tests__/slug.test.ts +0 -109
  217. package/src/__tests__/translations.test.ts +0 -158
  218. package/src/__tests__/versions.test.ts +0 -741
  219. package/src/client/__tests__/docsClientUtils.test.ts +0 -371
  220. package/src/docFrontMatter.ts +0 -41
  221. package/src/markdown/__tests__/__fixtures__/docs/doc-localized.md +0 -1
  222. package/src/markdown/__tests__/__fixtures__/docs/doc1.md +0 -13
  223. package/src/markdown/__tests__/__fixtures__/docs/doc2.md +0 -12
  224. package/src/markdown/__tests__/__fixtures__/docs/doc4.md +0 -19
  225. package/src/markdown/__tests__/__fixtures__/docs/doc5.md +0 -6
  226. package/src/markdown/__tests__/__fixtures__/docs/subdir/doc3.md +0 -3
  227. package/src/markdown/__tests__/__fixtures__/versioned_docs/version-1.0.0/doc2.md +0 -7
  228. package/src/markdown/__tests__/__fixtures__/versioned_docs/version-1.0.0/subdir/doc1.md +0 -3
  229. package/src/markdown/__tests__/__snapshots__/linkify.test.ts.snap +0 -82
  230. package/src/markdown/__tests__/linkify.test.ts +0 -190
  231. package/src/sidebarItemsGenerator.ts +0 -315
  232. package/src/sidebars.ts +0 -589
  233. package/src/theme/hooks/useDocs.ts +0 -103
  234. package/src/versions.ts +0 -572
  235. package/tsconfig.json +0 -9
  236. package/types.d.ts +0 -13
package/src/docs.ts CHANGED
@@ -7,32 +7,41 @@
7
7
 
8
8
  import path from 'path';
9
9
  import fs from 'fs-extra';
10
+ import logger from '@docusaurus/logger';
10
11
  import {
11
12
  aliasedSitePath,
12
13
  getEditUrl,
13
14
  getFolderContainingFile,
15
+ getContentPathList,
14
16
  normalizeUrl,
15
17
  parseMarkdownString,
16
18
  posixPath,
17
19
  Globby,
20
+ normalizeFrontMatterTags,
18
21
  } from '@docusaurus/utils';
19
- import {LoadContext} from '@docusaurus/types';
20
22
 
21
23
  import {getFileLastUpdate} from './lastUpdate';
22
- import {
23
- DocFile,
24
- DocMetadataBase,
25
- LastUpdateData,
26
- MetadataOptions,
27
- PluginOptions,
28
- VersionMetadata,
29
- } from './types';
30
24
  import getSlug from './slug';
31
25
  import {CURRENT_VERSION_NAME} from './constants';
32
- import {getDocsDirPaths} from './versions';
33
26
  import {stripPathNumberPrefixes} from './numberPrefix';
34
- import {validateDocFrontMatter} from './docFrontMatter';
35
- import chalk from 'chalk';
27
+ import {validateDocFrontMatter} from './frontMatter';
28
+ import {toDocNavigationLink, toNavigationLink} from './sidebars/utils';
29
+ import type {
30
+ MetadataOptions,
31
+ PluginOptions,
32
+ CategoryIndexMatcher,
33
+ DocMetadataBase,
34
+ DocMetadata,
35
+ PropNavigationLink,
36
+ LastUpdateData,
37
+ VersionMetadata,
38
+ DocFrontMatter,
39
+ LoadedVersion,
40
+ FileChange,
41
+ } from '@docusaurus/plugin-content-docs';
42
+ import type {LoadContext} from '@docusaurus/types';
43
+ import type {SidebarsUtils} from './sidebars/utils';
44
+ import type {DocFile} from './types';
36
45
 
37
46
  type LastUpdateOptions = Pick<
38
47
  PluginOptions,
@@ -42,9 +51,21 @@ type LastUpdateOptions = Pick<
42
51
  async function readLastUpdateData(
43
52
  filePath: string,
44
53
  options: LastUpdateOptions,
54
+ lastUpdateFrontMatter: FileChange | undefined,
45
55
  ): Promise<LastUpdateData> {
46
56
  const {showLastUpdateAuthor, showLastUpdateTime} = options;
47
57
  if (showLastUpdateAuthor || showLastUpdateTime) {
58
+ const frontMatterTimestamp = lastUpdateFrontMatter?.date
59
+ ? new Date(lastUpdateFrontMatter.date).getTime() / 1000
60
+ : undefined;
61
+
62
+ if (lastUpdateFrontMatter?.author && lastUpdateFrontMatter.date) {
63
+ return {
64
+ lastUpdatedAt: frontMatterTimestamp,
65
+ lastUpdatedBy: lastUpdateFrontMatter.author,
66
+ };
67
+ }
68
+
48
69
  // Use fake data in dev for faster development.
49
70
  const fileLastUpdateData =
50
71
  process.env.NODE_ENV === 'production'
@@ -53,14 +74,16 @@ async function readLastUpdateData(
53
74
  author: 'Author',
54
75
  timestamp: 1539502055,
55
76
  };
56
-
57
- if (fileLastUpdateData) {
58
- const {author, timestamp} = fileLastUpdateData;
59
- return {
60
- lastUpdatedAt: showLastUpdateTime ? timestamp : undefined,
61
- lastUpdatedBy: showLastUpdateAuthor ? author : undefined,
62
- };
63
- }
77
+ const {author, timestamp} = fileLastUpdateData ?? {};
78
+
79
+ return {
80
+ lastUpdatedBy: showLastUpdateAuthor
81
+ ? lastUpdateFrontMatter?.author ?? author
82
+ : undefined,
83
+ lastUpdatedAt: showLastUpdateTime
84
+ ? frontMatterTimestamp ?? timestamp
85
+ : undefined,
86
+ };
64
87
  }
65
88
 
66
89
  return {};
@@ -72,20 +95,16 @@ export async function readDocFile(
72
95
  'contentPath' | 'contentPathLocalized'
73
96
  >,
74
97
  source: string,
75
- options: LastUpdateOptions,
76
98
  ): Promise<DocFile> {
77
99
  const contentPath = await getFolderContainingFile(
78
- getDocsDirPaths(versionMetadata),
100
+ getContentPathList(versionMetadata),
79
101
  source,
80
102
  );
81
103
 
82
104
  const filePath = path.join(contentPath, source);
83
105
 
84
- const [content, lastUpdate] = await Promise.all([
85
- fs.readFile(filePath, 'utf-8'),
86
- readLastUpdateData(filePath, options),
87
- ]);
88
- return {source, content, lastUpdate, contentPath, filePath};
106
+ const content = await fs.readFile(filePath, 'utf-8');
107
+ return {source, content, contentPath, filePath};
89
108
  }
90
109
 
91
110
  export async function readVersionDocs(
@@ -100,23 +119,37 @@ export async function readVersionDocs(
100
119
  ignore: options.exclude,
101
120
  });
102
121
  return Promise.all(
103
- sources.map((source) => readDocFile(versionMetadata, source, options)),
122
+ sources.map((source) => readDocFile(versionMetadata, source)),
104
123
  );
105
124
  }
106
125
 
107
- function doProcessDocMetadata({
126
+ export type DocEnv = 'production' | 'development';
127
+
128
+ /** Docs with draft front matter are only considered draft in production. */
129
+ function isDraftForEnvironment({
130
+ env,
131
+ frontMatter,
132
+ }: {
133
+ frontMatter: DocFrontMatter;
134
+ env: DocEnv;
135
+ }): boolean {
136
+ return (env === 'production' && frontMatter.draft) ?? false;
137
+ }
138
+
139
+ async function doProcessDocMetadata({
108
140
  docFile,
109
141
  versionMetadata,
110
142
  context,
111
143
  options,
144
+ env,
112
145
  }: {
113
146
  docFile: DocFile;
114
147
  versionMetadata: VersionMetadata;
115
148
  context: LoadContext;
116
149
  options: MetadataOptions;
117
- }): DocMetadataBase {
118
- const {source, content, lastUpdate, contentPath, filePath} = docFile;
119
- const {homePageId} = options;
150
+ env: DocEnv;
151
+ }): Promise<DocMetadataBase> {
152
+ const {source, content, contentPath, filePath} = docFile;
120
153
  const {siteDir, i18n} = context;
121
154
 
122
155
  const {
@@ -129,25 +162,29 @@ function doProcessDocMetadata({
129
162
  const {
130
163
  custom_edit_url: customEditURL,
131
164
 
132
- // Strip number prefixes by default (01-MyFolder/01-MyDoc.md => MyFolder/MyDoc) by default,
133
- // but allow to disable this behavior with frontmatterr
134
- // eslint-disable-next-line camelcase
135
- parse_number_prefixes = true,
165
+ // Strip number prefixes by default
166
+ // (01-MyFolder/01-MyDoc.md => MyFolder/MyDoc)
167
+ // but allow to disable this behavior with front matter
168
+ parse_number_prefixes: parseNumberPrefixes = true,
169
+ last_update: lastUpdateFrontMatter,
136
170
  } = frontMatter;
137
171
 
138
- // ex: api/plugins/myDoc -> myDoc
139
- // ex: myDoc -> myDoc
172
+ const lastUpdate = await readLastUpdateData(
173
+ filePath,
174
+ options,
175
+ lastUpdateFrontMatter,
176
+ );
177
+
178
+ // E.g. api/plugins/myDoc -> myDoc; myDoc -> myDoc
140
179
  const sourceFileNameWithoutExtension = path.basename(
141
180
  source,
142
181
  path.extname(source),
143
182
  );
144
183
 
145
- // ex: api/plugins/myDoc -> api/plugins
146
- // ex: myDoc -> .
184
+ // E.g. api/plugins/myDoc -> api/plugins; myDoc -> .
147
185
  const sourceDirName = path.dirname(source);
148
186
 
149
- // eslint-disable-next-line camelcase
150
- const {filename: unprefixedFileName, numberPrefix} = parse_number_prefixes
187
+ const {filename: unprefixedFileName, numberPrefix} = parseNumberPrefixes
151
188
  ? options.numberPrefixParser(sourceFileNameWithoutExtension)
152
189
  : {filename: sourceFileNameWithoutExtension, numberPrefix: undefined};
153
190
 
@@ -156,7 +193,8 @@ function doProcessDocMetadata({
156
193
  throw new Error(`Document id "${baseID}" cannot include slash.`);
157
194
  }
158
195
 
159
- // For autogenerated sidebars, sidebar position can come from filename number prefix or frontmatter
196
+ // For autogenerated sidebars, sidebar position can come from filename number
197
+ // prefix or front matter
160
198
  const sidebarPosition: number | undefined =
161
199
  frontMatter.sidebar_position ?? numberPrefix;
162
200
 
@@ -169,14 +207,13 @@ function doProcessDocMetadata({
169
207
  : `version-${versionMetadata.versionName}`;
170
208
 
171
209
  // TODO legacy retrocompatibility
172
- // I think it's bad to affect the frontmatter id with the dirname?
210
+ // I think it's bad to affect the front matter id with the dirname?
173
211
  function computeDirNameIdPrefix() {
174
212
  if (sourceDirName === '.') {
175
213
  return undefined;
176
214
  }
177
215
  // Eventually remove the number prefixes from intermediate directories
178
- // eslint-disable-next-line camelcase
179
- return parse_number_prefixes
216
+ return parseNumberPrefixes
180
217
  ? stripPathNumberPrefixes(sourceDirName, options.numberPrefixParser)
181
218
  : sourceDirName;
182
219
  }
@@ -189,31 +226,23 @@ function doProcessDocMetadata({
189
226
  // legacy versioned id, requires a breaking change to modify this
190
227
  const id = [versionIdPrefix, unversionedId].filter(Boolean).join('/');
191
228
 
192
- // TODO remove soon, deprecated homePageId
193
- const isDocsHomePage = unversionedId === (homePageId ?? '_index');
194
- if (frontMatter.slug && isDocsHomePage) {
195
- throw new Error(
196
- `The docs homepage (homePageId=${homePageId}) is not allowed to have a frontmatter slug=${frontMatter.slug} => you have to choose either homePageId or slug, not both`,
197
- );
198
- }
199
-
200
- const docSlug = isDocsHomePage
201
- ? '/'
202
- : getSlug({
203
- baseID,
204
- dirName: sourceDirName,
205
- frontmatterSlug: frontMatter.slug,
206
- stripDirNumberPrefixes: parse_number_prefixes,
207
- numberPrefixParser: options.numberPrefixParser,
208
- });
229
+ const docSlug = getSlug({
230
+ baseID,
231
+ source,
232
+ sourceDirName,
233
+ frontMatterSlug: frontMatter.slug,
234
+ stripDirNumberPrefixes: parseNumberPrefixes,
235
+ numberPrefixParser: options.numberPrefixParser,
236
+ });
209
237
 
210
- // Note: the title is used by default for page title, sidebar label, pagination buttons...
211
- // frontMatter.title should be used in priority over contentTitle (because it can contain markdown/JSX syntax)
238
+ // Note: the title is used by default for page title, sidebar label,
239
+ // pagination buttons... frontMatter.title should be used in priority over
240
+ // contentTitle (because it can contain markdown/JSX syntax)
212
241
  const title: string = frontMatter.title ?? contentTitle ?? baseID;
213
242
 
214
243
  const description: string = frontMatter.description ?? excerpt ?? '';
215
244
 
216
- const permalink = normalizeUrl([versionMetadata.versionPath, docSlug]);
245
+ const permalink = normalizeUrl([versionMetadata.path, docSlug]);
217
246
 
218
247
  function getDocEditUrl() {
219
248
  const relativeFilePath = path.relative(contentPath, filePath);
@@ -232,14 +261,30 @@ function doProcessDocMetadata({
232
261
  const isLocalized = contentPath === versionMetadata.contentPathLocalized;
233
262
  const baseVersionEditUrl =
234
263
  isLocalized && options.editLocalizedFiles
235
- ? versionMetadata.versionEditUrlLocalized
236
- : versionMetadata.versionEditUrl;
264
+ ? versionMetadata.editUrlLocalized
265
+ : versionMetadata.editUrl;
237
266
  return getEditUrl(relativeFilePath, baseVersionEditUrl);
238
- } else {
239
- return undefined;
240
267
  }
268
+ return undefined;
241
269
  }
242
270
 
271
+ const draft = isDraftForEnvironment({env, frontMatter});
272
+
273
+ const formatDate = (locale: string, date: Date, calendar: string): string => {
274
+ try {
275
+ return new Intl.DateTimeFormat(locale, {
276
+ day: 'numeric',
277
+ month: 'short',
278
+ year: 'numeric',
279
+ timeZone: 'UTC',
280
+ calendar,
281
+ }).format(date);
282
+ } catch (err) {
283
+ logger.error`Can't format docs lastUpdatedAt date "${String(date)}"`;
284
+ throw err;
285
+ }
286
+ };
287
+
243
288
  // Assign all of object properties during instantiation (if possible) for
244
289
  // NodeJS optimization.
245
290
  // Adding properties to object after instantiation will cause hidden
@@ -247,20 +292,23 @@ function doProcessDocMetadata({
247
292
  return {
248
293
  unversionedId,
249
294
  id,
250
- isDocsHomePage,
251
295
  title,
252
296
  description,
253
297
  source: aliasedSitePath(filePath, siteDir),
254
298
  sourceDirName,
255
299
  slug: docSlug,
256
300
  permalink,
301
+ draft,
257
302
  editUrl: customEditURL !== undefined ? customEditURL : getDocEditUrl(),
303
+ tags: normalizeFrontMatterTags(versionMetadata.tagsPath, frontMatter.tags),
258
304
  version: versionMetadata.versionName,
259
305
  lastUpdatedBy: lastUpdate.lastUpdatedBy,
260
306
  lastUpdatedAt: lastUpdate.lastUpdatedAt,
261
307
  formattedLastUpdatedAt: lastUpdate.lastUpdatedAt
262
- ? new Intl.DateTimeFormat(i18n.currentLocale).format(
263
- lastUpdate.lastUpdatedAt * 1000,
308
+ ? formatDate(
309
+ i18n.currentLocale,
310
+ new Date(lastUpdate.lastUpdatedAt * 1000),
311
+ i18n.localeConfigs[i18n.currentLocale]!.calendar,
264
312
  )
265
313
  : undefined,
266
314
  sidebarPosition,
@@ -273,15 +321,164 @@ export function processDocMetadata(args: {
273
321
  versionMetadata: VersionMetadata;
274
322
  context: LoadContext;
275
323
  options: MetadataOptions;
276
- }): DocMetadataBase {
324
+ env: DocEnv;
325
+ }): Promise<DocMetadataBase> {
277
326
  try {
278
327
  return doProcessDocMetadata(args);
279
- } catch (e) {
280
- console.error(
281
- chalk.red(
282
- `Can't process doc metadatas for doc at path "${args.docFile.filePath}" in version "${args.versionMetadata.versionName}"`,
283
- ),
328
+ } catch (err) {
329
+ logger.error`Can't process doc metadata for doc at path path=${args.docFile.filePath} in version name=${args.versionMetadata.versionName}`;
330
+ throw err;
331
+ }
332
+ }
333
+
334
+ export function addDocNavigation(
335
+ docsBase: DocMetadataBase[],
336
+ sidebarsUtils: SidebarsUtils,
337
+ sidebarFilePath: string,
338
+ ): LoadedVersion['docs'] {
339
+ const docsById = createDocsByIdIndex(docsBase);
340
+
341
+ sidebarsUtils.checkSidebarsDocIds(
342
+ docsBase.flatMap(getDocIds),
343
+ sidebarFilePath,
344
+ );
345
+
346
+ // Add sidebar/next/previous to the docs
347
+ function addNavData(doc: DocMetadataBase): DocMetadata {
348
+ const navigation = sidebarsUtils.getDocNavigation(
349
+ doc.unversionedId,
350
+ doc.id,
351
+ doc.frontMatter.displayed_sidebar,
284
352
  );
285
- throw e;
353
+
354
+ const toNavigationLinkByDocId = (
355
+ docId: string | null | undefined,
356
+ type: 'prev' | 'next',
357
+ ): PropNavigationLink | undefined => {
358
+ if (!docId) {
359
+ return undefined;
360
+ }
361
+ const navDoc = docsById[docId];
362
+ if (!navDoc) {
363
+ // This could only happen if user provided the ID through front matter
364
+ throw new Error(
365
+ `Error when loading ${doc.id} in ${doc.sourceDirName}: the pagination_${type} front matter points to a non-existent ID ${docId}.`,
366
+ );
367
+ }
368
+ return toDocNavigationLink(navDoc);
369
+ };
370
+
371
+ const previous =
372
+ doc.frontMatter.pagination_prev !== undefined
373
+ ? toNavigationLinkByDocId(doc.frontMatter.pagination_prev, 'prev')
374
+ : toNavigationLink(navigation.previous, docsById);
375
+ const next =
376
+ doc.frontMatter.pagination_next !== undefined
377
+ ? toNavigationLinkByDocId(doc.frontMatter.pagination_next, 'next')
378
+ : toNavigationLink(navigation.next, docsById);
379
+
380
+ return {...doc, sidebar: navigation.sidebarName, previous, next};
286
381
  }
382
+
383
+ const docsWithNavigation = docsBase.map(addNavData);
384
+ // Sort to ensure consistent output for tests
385
+ docsWithNavigation.sort((a, b) => a.id.localeCompare(b.id));
386
+ return docsWithNavigation;
387
+ }
388
+
389
+ /**
390
+ * The "main doc" is the "version entry point"
391
+ * We browse this doc by clicking on a version:
392
+ * - the "home" doc (at '/docs/')
393
+ * - the first doc of the first sidebar
394
+ * - a random doc (if no docs are in any sidebar... edge case)
395
+ */
396
+ export function getMainDocId({
397
+ docs,
398
+ sidebarsUtils,
399
+ }: {
400
+ docs: DocMetadataBase[];
401
+ sidebarsUtils: SidebarsUtils;
402
+ }): string {
403
+ function getMainDoc(): DocMetadata {
404
+ const versionHomeDoc = docs.find((doc) => doc.slug === '/');
405
+ const firstDocIdOfFirstSidebar =
406
+ sidebarsUtils.getFirstDocIdOfFirstSidebar();
407
+ if (versionHomeDoc) {
408
+ return versionHomeDoc;
409
+ } else if (firstDocIdOfFirstSidebar) {
410
+ return docs.find(
411
+ (doc) =>
412
+ doc.id === firstDocIdOfFirstSidebar ||
413
+ doc.unversionedId === firstDocIdOfFirstSidebar,
414
+ )!;
415
+ }
416
+ return docs[0]!;
417
+ }
418
+
419
+ return getMainDoc().unversionedId;
420
+ }
421
+
422
+ // By convention, Docusaurus considers some docs are "indexes":
423
+ // - index.md
424
+ // - readme.md
425
+ // - <folder>/<folder>.md
426
+ //
427
+ // This function is the default implementation of this convention
428
+ //
429
+ // Those index docs produce a different behavior
430
+ // - Slugs do not end with a weird "/index" suffix
431
+ // - Auto-generated sidebar categories link to them as intro
432
+ export const isCategoryIndex: CategoryIndexMatcher = ({
433
+ fileName,
434
+ directories,
435
+ }): boolean => {
436
+ const eligibleDocIndexNames = [
437
+ 'index',
438
+ 'readme',
439
+ directories[0]?.toLowerCase(),
440
+ ];
441
+ return eligibleDocIndexNames.includes(fileName.toLowerCase());
442
+ };
443
+
444
+ /**
445
+ * `guides/sidebar/autogenerated.md` ->
446
+ * `'autogenerated', '.md', ['sidebar', 'guides']`
447
+ */
448
+ export function toCategoryIndexMatcherParam({
449
+ source,
450
+ sourceDirName,
451
+ }: Pick<
452
+ DocMetadataBase,
453
+ 'source' | 'sourceDirName'
454
+ >): Parameters<CategoryIndexMatcher>[0] {
455
+ // source + sourceDirName are always posix-style
456
+ return {
457
+ fileName: path.posix.parse(source).name,
458
+ extension: path.posix.parse(source).ext,
459
+ directories: sourceDirName.split(path.posix.sep).reverse(),
460
+ };
461
+ }
462
+
463
+ // Return both doc ids
464
+ // TODO legacy retro-compatibility due to old versioned sidebars using
465
+ // versioned doc ids ("id" should be removed & "versionedId" should be renamed
466
+ // to "id")
467
+ export function getDocIds(doc: DocMetadataBase): [string, string] {
468
+ return [doc.unversionedId, doc.id];
469
+ }
470
+
471
+ // Docs are indexed by both versioned and unversioned ids at the same time
472
+ // TODO legacy retro-compatibility due to old versioned sidebars using
473
+ // versioned doc ids ("id" should be removed & "versionedId" should be renamed
474
+ // to "id")
475
+ export function createDocsByIdIndex<
476
+ Doc extends {id: string; unversionedId: string},
477
+ >(docs: Doc[]): {[docId: string]: Doc} {
478
+ return Object.fromEntries(
479
+ docs.flatMap((doc) => [
480
+ [doc.unversionedId, doc],
481
+ [doc.id, doc],
482
+ ]),
483
+ );
287
484
  }
@@ -0,0 +1,63 @@
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
+ JoiFrontMatter as Joi, // Custom instance for front matter
10
+ URISchema,
11
+ FrontMatterTagsSchema,
12
+ FrontMatterTOCHeadingLevels,
13
+ validateFrontMatter,
14
+ } from '@docusaurus/utils-validation';
15
+ import type {DocFrontMatter} from '@docusaurus/plugin-content-docs';
16
+
17
+ const FrontMatterLastUpdateErrorMessage =
18
+ '{{#label}} does not look like a valid front matter FileChange object. Please use a FileChange object (with an author and/or date).';
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_label: Joi.string(),
36
+ sidebar_position: Joi.number(),
37
+ sidebar_class_name: Joi.string(),
38
+ sidebar_custom_props: Joi.object().unknown(),
39
+ displayed_sidebar: Joi.string().allow(null),
40
+ tags: FrontMatterTagsSchema,
41
+ pagination_label: Joi.string(),
42
+ custom_edit_url: URISchema.allow('', null),
43
+ parse_number_prefixes: Joi.boolean(),
44
+ pagination_next: Joi.string().allow(null),
45
+ pagination_prev: Joi.string().allow(null),
46
+ draft: Joi.boolean(),
47
+ ...FrontMatterTOCHeadingLevels,
48
+ last_update: Joi.object({
49
+ author: Joi.string(),
50
+ date: Joi.date().raw(),
51
+ })
52
+ .or('author', 'date')
53
+ .messages({
54
+ 'object.missing': FrontMatterLastUpdateErrorMessage,
55
+ 'object.base': FrontMatterLastUpdateErrorMessage,
56
+ }),
57
+ }).unknown();
58
+
59
+ export function validateDocFrontMatter(frontMatter: {
60
+ [key: string]: unknown;
61
+ }): DocFrontMatter {
62
+ return validateFrontMatter(frontMatter, DocFrontMatterSchema);
63
+ }
package/src/globalData.ts CHANGED
@@ -5,9 +5,21 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
 
8
- import {DocMetadata, GlobalDoc, LoadedVersion, GlobalVersion} from './types';
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';
9
21
 
10
- export function toGlobalDataDoc(doc: DocMetadata): GlobalDoc {
22
+ function toGlobalDataDoc(doc: DocMetadata): GlobalDoc {
11
23
  return {
12
24
  id: doc.unversionedId,
13
25
  path: doc.permalink,
@@ -15,13 +27,51 @@ export function toGlobalDataDoc(doc: DocMetadata): GlobalDoc {
15
27
  };
16
28
  }
17
29
 
18
- export function toGlobalDataVersion(version: LoadedVersion): GlobalVersion {
30
+ function toGlobalDataGeneratedIndex(
31
+ doc: CategoryGeneratedIndexMetadata,
32
+ ): GlobalDoc {
33
+ return {
34
+ id: doc.slug,
35
+ path: doc.permalink,
36
+ sidebar: doc.sidebar,
37
+ };
38
+ }
39
+
40
+ function toGlobalSidebars(
41
+ sidebars: Sidebars,
42
+ version: FullVersion,
43
+ ): {[sidebarId: string]: GlobalSidebar} {
44
+ return _.mapValues(sidebars, (sidebar, sidebarId) => {
45
+ const firstLink = version.sidebarsUtils.getFirstLink(sidebarId);
46
+ if (!firstLink) {
47
+ return {};
48
+ }
49
+ return {
50
+ link: {
51
+ path:
52
+ firstLink.type === 'generated-index'
53
+ ? firstLink.permalink
54
+ : version.docs.find(
55
+ (doc) =>
56
+ doc.id === firstLink.id || doc.unversionedId === firstLink.id,
57
+ )!.permalink,
58
+ label: firstLink.label,
59
+ },
60
+ };
61
+ });
62
+ }
63
+
64
+ export function toGlobalDataVersion(version: FullVersion): GlobalVersion {
19
65
  return {
20
66
  name: version.versionName,
21
- label: version.versionLabel,
67
+ label: version.label,
22
68
  isLast: version.isLast,
23
- path: version.versionPath,
24
- mainDocId: version.mainDocId,
25
- docs: version.docs.map(toGlobalDataDoc),
69
+ path: version.path,
70
+ mainDocId: getMainDocId(version),
71
+ docs: version.docs
72
+ .map(toGlobalDataDoc)
73
+ .concat(version.categoryGeneratedIndices.map(toGlobalDataGeneratedIndex)),
74
+ draftIds: version.drafts.map((doc) => doc.unversionedId),
75
+ sidebars: toGlobalSidebars(version.sidebars, version),
26
76
  };
27
77
  }