@docusaurus/plugin-content-docs 2.0.0-beta.2 → 2.0.0-beta.22

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 (230) 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 -72
  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 +34 -7
  12. package/lib/docs.js +202 -79
  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} +23 -4
  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 +124 -154
  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 +56 -23
  29. package/lib/props.d.ts +7 -2
  30. package/lib/props.js +84 -14
  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 +22 -0
  35. package/lib/{sidebarItemsGenerator.d.ts → sidebars/generator.d.ts} +1 -6
  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 -185
  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 -114
  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 +281 -83
  71. package/src/frontMatter.ts +63 -0
  72. package/src/globalData.ts +57 -7
  73. package/src/index.ts +183 -224
  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 +61 -29
  79. package/src/plugin-content-docs.d.ts +569 -93
  80. package/src/props.ts +121 -21
  81. package/src/routes.ts +159 -0
  82. package/src/server-export.ts +22 -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 -250
  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 -211
  101. package/lib/sidebars.d.ts +0 -43
  102. package/lib/sidebars.js +0 -320
  103. package/lib/theme/hooks/useDocs.d.ts +0 -20
  104. package/lib/theme/hooks/useDocs.js +0 -72
  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/foo/bar.md +0 -69
  125. package/src/__tests__/__fixtures__/simple-site/docs/foo/baz.md +0 -70
  126. package/src/__tests__/__fixtures__/simple-site/docs/headingAsTitle.md +0 -1
  127. package/src/__tests__/__fixtures__/simple-site/docs/hello.md +0 -53
  128. package/src/__tests__/__fixtures__/simple-site/docs/ipsum.md +0 -5
  129. package/src/__tests__/__fixtures__/simple-site/docs/lorem.md +0 -6
  130. package/src/__tests__/__fixtures__/simple-site/docs/rootAbsoluteSlug.md +0 -5
  131. package/src/__tests__/__fixtures__/simple-site/docs/rootRelativeSlug.md +0 -5
  132. package/src/__tests__/__fixtures__/simple-site/docs/rootResolvedSlug.md +0 -5
  133. package/src/__tests__/__fixtures__/simple-site/docs/rootTryToEscapeSlug.md +0 -5
  134. package/src/__tests__/__fixtures__/simple-site/docs/slugs/absoluteSlug.md +0 -5
  135. package/src/__tests__/__fixtures__/simple-site/docs/slugs/relativeSlug.md +0 -5
  136. package/src/__tests__/__fixtures__/simple-site/docs/slugs/resolvedSlug.md +0 -5
  137. package/src/__tests__/__fixtures__/simple-site/docs/slugs/tryToEscapeSlug.md +0 -5
  138. package/src/__tests__/__fixtures__/simple-site/docusaurus.config.js +0 -14
  139. package/src/__tests__/__fixtures__/simple-site/sidebars.json +0 -23
  140. package/src/__tests__/__fixtures__/simple-site/wrong-sidebars.json +0 -7
  141. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/0-getting-started.md +0 -3
  142. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/1-installation.md +0 -3
  143. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/3-API/00_api-overview.md +0 -3
  144. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/3-API/01_Core APIs/0 --- Client API.md +0 -1
  145. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/3-API/01_Core APIs/1 --- Server API.md +0 -1
  146. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/3-API/02_Extension APIs/0. Plugin API.md +0 -1
  147. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/3-API/02_Extension APIs/1. Theme API.md +0 -1
  148. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/3-API/02_Extension APIs/_category_.yml +0 -1
  149. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/3-API/03_api-end.md +0 -3
  150. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/3-API/_category_.json +0 -3
  151. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/Guides/0-guide2.5.md +0 -8
  152. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/Guides/02-guide2.md +0 -7
  153. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/Guides/_category_.json +0 -3
  154. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/Guides/a-guide4.md +0 -7
  155. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/Guides/b-guide5.md +0 -7
  156. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/Guides/guide3.md +0 -8
  157. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/Guides/z-guide1.md +0 -8
  158. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/partialAutogeneratedSidebars.js +0 -23
  159. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/partialAutogeneratedSidebars2.js +0 -16
  160. package/src/__tests__/__fixtures__/site-with-doc-label/docs/hello-1.md +0 -7
  161. package/src/__tests__/__fixtures__/site-with-doc-label/docs/hello-2.md +0 -8
  162. package/src/__tests__/__fixtures__/site-with-doc-label/docusaurus.config.js +0 -14
  163. package/src/__tests__/__fixtures__/site-with-doc-label/sidebars.json +0 -14
  164. package/src/__tests__/__fixtures__/versioned-site/community/team.md +0 -1
  165. package/src/__tests__/__fixtures__/versioned-site/community_sidebars.json +0 -3
  166. package/src/__tests__/__fixtures__/versioned-site/community_versioned_docs/version-1.0.0/team.md +0 -1
  167. package/src/__tests__/__fixtures__/versioned-site/community_versioned_sidebars/version-1.0.0-sidebars.json +0 -3
  168. package/src/__tests__/__fixtures__/versioned-site/community_versions.json +0 -1
  169. package/src/__tests__/__fixtures__/versioned-site/docs/foo/bar.md +0 -4
  170. package/src/__tests__/__fixtures__/versioned-site/docs/hello.md +0 -1
  171. package/src/__tests__/__fixtures__/versioned-site/docs/slugs/absoluteSlug.md +0 -5
  172. package/src/__tests__/__fixtures__/versioned-site/docs/slugs/relativeSlug.md +0 -5
  173. package/src/__tests__/__fixtures__/versioned-site/docs/slugs/resolvedSlug.md +0 -5
  174. package/src/__tests__/__fixtures__/versioned-site/docs/slugs/tryToEscapeSlug.md +0 -5
  175. package/src/__tests__/__fixtures__/versioned-site/docusaurus.config.js +0 -18
  176. package/src/__tests__/__fixtures__/versioned-site/i18n/en/docusaurus-plugin-content-docs/version-1.0.0/hello.md +0 -1
  177. package/src/__tests__/__fixtures__/versioned-site/i18n/en/docusaurus-plugin-content-docs-community/current/team.md +0 -5
  178. package/src/__tests__/__fixtures__/versioned-site/i18n/fr/docusaurus-plugin-content-docs/version-1.0.0/hello.md +0 -1
  179. package/src/__tests__/__fixtures__/versioned-site/sidebars.json +0 -10
  180. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-1.0.0/foo/bar.md +0 -4
  181. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-1.0.0/foo/baz.md +0 -1
  182. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-1.0.0/hello.md +0 -1
  183. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-1.0.1/foo/bar.md +0 -1
  184. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-1.0.1/hello.md +0 -1
  185. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-withSlugs/rootAbsoluteSlug.md +0 -5
  186. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-withSlugs/rootRelativeSlug.md +0 -5
  187. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-withSlugs/rootResolvedSlug.md +0 -5
  188. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-withSlugs/rootTryToEscapeSlug.md +0 -5
  189. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-withSlugs/slugs/absoluteSlug.md +0 -5
  190. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-withSlugs/slugs/relativeSlug.md +0 -5
  191. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-withSlugs/slugs/resolvedSlug.md +0 -5
  192. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-withSlugs/slugs/tryToEscapeSlug.md +0 -5
  193. package/src/__tests__/__fixtures__/versioned-site/versioned_sidebars/version-1.0.0-sidebars.json +0 -11
  194. package/src/__tests__/__fixtures__/versioned-site/versioned_sidebars/version-1.0.1-sidebars.json +0 -10
  195. package/src/__tests__/__fixtures__/versioned-site/versioned_sidebars/version-withSlugs-sidebars.json +0 -5
  196. package/src/__tests__/__fixtures__/versioned-site/versions.json +0 -5
  197. package/src/__tests__/__snapshots__/cli.test.ts.snap +0 -90
  198. package/src/__tests__/__snapshots__/index.test.ts.snap +0 -1916
  199. package/src/__tests__/__snapshots__/sidebars.test.ts.snap +0 -218
  200. package/src/__tests__/__snapshots__/translations.test.ts.snap +0 -487
  201. package/src/__tests__/cli.test.ts +0 -333
  202. package/src/__tests__/docFrontMatter.test.ts +0 -244
  203. package/src/__tests__/docs.test.ts +0 -878
  204. package/src/__tests__/index.test.ts +0 -1871
  205. package/src/__tests__/lastUpdate.test.ts +0 -69
  206. package/src/__tests__/numberPrefix.test.ts +0 -199
  207. package/src/__tests__/options.test.ts +0 -231
  208. package/src/__tests__/sidebarItemsGenerator.test.ts +0 -336
  209. package/src/__tests__/sidebars.test.ts +0 -639
  210. package/src/__tests__/slug.test.ts +0 -109
  211. package/src/__tests__/translations.test.ts +0 -159
  212. package/src/__tests__/versions.test.ts +0 -741
  213. package/src/client/__tests__/docsClientUtils.test.ts +0 -371
  214. package/src/docFrontMatter.ts +0 -41
  215. package/src/markdown/__tests__/__fixtures__/docs/doc-localized.md +0 -1
  216. package/src/markdown/__tests__/__fixtures__/docs/doc1.md +0 -13
  217. package/src/markdown/__tests__/__fixtures__/docs/doc2.md +0 -12
  218. package/src/markdown/__tests__/__fixtures__/docs/doc4.md +0 -19
  219. package/src/markdown/__tests__/__fixtures__/docs/doc5.md +0 -6
  220. package/src/markdown/__tests__/__fixtures__/docs/subdir/doc3.md +0 -3
  221. package/src/markdown/__tests__/__fixtures__/versioned_docs/version-1.0.0/doc2.md +0 -7
  222. package/src/markdown/__tests__/__fixtures__/versioned_docs/version-1.0.0/subdir/doc1.md +0 -3
  223. package/src/markdown/__tests__/__snapshots__/linkify.test.ts.snap +0 -82
  224. package/src/markdown/__tests__/linkify.test.ts +0 -190
  225. package/src/sidebarItemsGenerator.ts +0 -307
  226. package/src/sidebars.ts +0 -522
  227. package/src/theme/hooks/useDocs.ts +0 -99
  228. package/src/versions.ts +0 -572
  229. package/tsconfig.json +0 -9
  230. 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,
19
+ Globby,
20
+ normalizeFrontMatterTags,
17
21
  } from '@docusaurus/utils';
18
- import {LoadContext} from '@docusaurus/types';
19
22
 
20
23
  import {getFileLastUpdate} from './lastUpdate';
21
- import {
22
- DocFile,
23
- DocMetadataBase,
24
- LastUpdateData,
25
- MetadataOptions,
26
- PluginOptions,
27
- VersionMetadata,
28
- } from './types';
29
24
  import getSlug from './slug';
30
25
  import {CURRENT_VERSION_NAME} from './constants';
31
- import globby from 'globby';
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,50 +95,61 @@ 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(
92
111
  versionMetadata: VersionMetadata,
93
112
  options: Pick<
94
113
  PluginOptions,
95
- 'include' | 'showLastUpdateAuthor' | 'showLastUpdateTime'
114
+ 'include' | 'exclude' | 'showLastUpdateAuthor' | 'showLastUpdateTime'
96
115
  >,
97
116
  ): Promise<DocFile[]> {
98
- const sources = await globby(options.include, {
117
+ const sources = await Globby(options.include, {
99
118
  cwd: versionMetadata.contentPath,
119
+ ignore: options.exclude,
100
120
  });
101
121
  return Promise.all(
102
- sources.map((source) => readDocFile(versionMetadata, source, options)),
122
+ sources.map((source) => readDocFile(versionMetadata, source)),
103
123
  );
104
124
  }
105
125
 
106
- 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({
107
140
  docFile,
108
141
  versionMetadata,
109
142
  context,
110
143
  options,
144
+ env,
111
145
  }: {
112
146
  docFile: DocFile;
113
147
  versionMetadata: VersionMetadata;
114
148
  context: LoadContext;
115
149
  options: MetadataOptions;
116
- }): DocMetadataBase {
117
- const {source, content, lastUpdate, contentPath, filePath} = docFile;
118
- const {homePageId} = options;
150
+ env: DocEnv;
151
+ }): Promise<DocMetadataBase> {
152
+ const {source, content, contentPath, filePath} = docFile;
119
153
  const {siteDir, i18n} = context;
120
154
 
121
155
  const {
@@ -128,25 +162,29 @@ function doProcessDocMetadata({
128
162
  const {
129
163
  custom_edit_url: customEditURL,
130
164
 
131
- // Strip number prefixes by default (01-MyFolder/01-MyDoc.md => MyFolder/MyDoc) by default,
132
- // but allow to disable this behavior with frontmatterr
133
- // eslint-disable-next-line camelcase
134
- 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,
135
170
  } = frontMatter;
136
171
 
137
- // ex: api/plugins/myDoc -> myDoc
138
- // 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
139
179
  const sourceFileNameWithoutExtension = path.basename(
140
180
  source,
141
181
  path.extname(source),
142
182
  );
143
183
 
144
- // ex: api/plugins/myDoc -> api/plugins
145
- // ex: myDoc -> .
184
+ // E.g. api/plugins/myDoc -> api/plugins; myDoc -> .
146
185
  const sourceDirName = path.dirname(source);
147
186
 
148
- // eslint-disable-next-line camelcase
149
- const {filename: unprefixedFileName, numberPrefix} = parse_number_prefixes
187
+ const {filename: unprefixedFileName, numberPrefix} = parseNumberPrefixes
150
188
  ? options.numberPrefixParser(sourceFileNameWithoutExtension)
151
189
  : {filename: sourceFileNameWithoutExtension, numberPrefix: undefined};
152
190
 
@@ -155,7 +193,8 @@ function doProcessDocMetadata({
155
193
  throw new Error(`Document id "${baseID}" cannot include slash.`);
156
194
  }
157
195
 
158
- // 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
159
198
  const sidebarPosition: number | undefined =
160
199
  frontMatter.sidebar_position ?? numberPrefix;
161
200
 
@@ -168,14 +207,13 @@ function doProcessDocMetadata({
168
207
  : `version-${versionMetadata.versionName}`;
169
208
 
170
209
  // TODO legacy retrocompatibility
171
- // 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?
172
211
  function computeDirNameIdPrefix() {
173
212
  if (sourceDirName === '.') {
174
213
  return undefined;
175
214
  }
176
215
  // Eventually remove the number prefixes from intermediate directories
177
- // eslint-disable-next-line camelcase
178
- return parse_number_prefixes
216
+ return parseNumberPrefixes
179
217
  ? stripPathNumberPrefixes(sourceDirName, options.numberPrefixParser)
180
218
  : sourceDirName;
181
219
  }
@@ -188,31 +226,23 @@ function doProcessDocMetadata({
188
226
  // legacy versioned id, requires a breaking change to modify this
189
227
  const id = [versionIdPrefix, unversionedId].filter(Boolean).join('/');
190
228
 
191
- // TODO remove soon, deprecated homePageId
192
- const isDocsHomePage = unversionedId === (homePageId ?? '_index');
193
- if (frontMatter.slug && isDocsHomePage) {
194
- throw new Error(
195
- `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`,
196
- );
197
- }
198
-
199
- const docSlug = isDocsHomePage
200
- ? '/'
201
- : getSlug({
202
- baseID,
203
- dirName: sourceDirName,
204
- frontmatterSlug: frontMatter.slug,
205
- stripDirNumberPrefixes: parse_number_prefixes,
206
- numberPrefixParser: options.numberPrefixParser,
207
- });
229
+ const docSlug = getSlug({
230
+ baseID,
231
+ source,
232
+ sourceDirName,
233
+ frontMatterSlug: frontMatter.slug,
234
+ stripDirNumberPrefixes: parseNumberPrefixes,
235
+ numberPrefixParser: options.numberPrefixParser,
236
+ });
208
237
 
209
- // Note: the title is used by default for page title, sidebar label, pagination buttons...
210
- // 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)
211
241
  const title: string = frontMatter.title ?? contentTitle ?? baseID;
212
242
 
213
243
  const description: string = frontMatter.description ?? excerpt ?? '';
214
244
 
215
- const permalink = normalizeUrl([versionMetadata.versionPath, docSlug]);
245
+ const permalink = normalizeUrl([versionMetadata.path, docSlug]);
216
246
 
217
247
  function getDocEditUrl() {
218
248
  const relativeFilePath = path.relative(contentPath, filePath);
@@ -231,14 +261,30 @@ function doProcessDocMetadata({
231
261
  const isLocalized = contentPath === versionMetadata.contentPathLocalized;
232
262
  const baseVersionEditUrl =
233
263
  isLocalized && options.editLocalizedFiles
234
- ? versionMetadata.versionEditUrlLocalized
235
- : versionMetadata.versionEditUrl;
264
+ ? versionMetadata.editUrlLocalized
265
+ : versionMetadata.editUrl;
236
266
  return getEditUrl(relativeFilePath, baseVersionEditUrl);
237
- } else {
238
- return undefined;
239
267
  }
268
+ return undefined;
240
269
  }
241
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
+
242
288
  // Assign all of object properties during instantiation (if possible) for
243
289
  // NodeJS optimization.
244
290
  // Adding properties to object after instantiation will cause hidden
@@ -246,20 +292,23 @@ function doProcessDocMetadata({
246
292
  return {
247
293
  unversionedId,
248
294
  id,
249
- isDocsHomePage,
250
295
  title,
251
296
  description,
252
297
  source: aliasedSitePath(filePath, siteDir),
253
298
  sourceDirName,
254
299
  slug: docSlug,
255
300
  permalink,
301
+ draft,
256
302
  editUrl: customEditURL !== undefined ? customEditURL : getDocEditUrl(),
303
+ tags: normalizeFrontMatterTags(versionMetadata.tagsPath, frontMatter.tags),
257
304
  version: versionMetadata.versionName,
258
305
  lastUpdatedBy: lastUpdate.lastUpdatedBy,
259
306
  lastUpdatedAt: lastUpdate.lastUpdatedAt,
260
307
  formattedLastUpdatedAt: lastUpdate.lastUpdatedAt
261
- ? new Intl.DateTimeFormat(i18n.currentLocale).format(
262
- lastUpdate.lastUpdatedAt * 1000,
308
+ ? formatDate(
309
+ i18n.currentLocale,
310
+ new Date(lastUpdate.lastUpdatedAt * 1000),
311
+ i18n.localeConfigs[i18n.currentLocale]!.calendar,
263
312
  )
264
313
  : undefined,
265
314
  sidebarPosition,
@@ -272,15 +321,164 @@ export function processDocMetadata(args: {
272
321
  versionMetadata: VersionMetadata;
273
322
  context: LoadContext;
274
323
  options: MetadataOptions;
275
- }): DocMetadataBase {
324
+ env: DocEnv;
325
+ }): Promise<DocMetadataBase> {
276
326
  try {
277
327
  return doProcessDocMetadata(args);
278
- } catch (e) {
279
- console.error(
280
- chalk.red(
281
- `Can't process doc metadatas for doc at path "${args.docFile.filePath}" in version "${args.versionMetadata.versionName}"`,
282
- ),
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,
283
352
  );
284
- 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};
285
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
+ );
286
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
  }