@docusaurus/plugin-content-docs 2.0.0-beta.ff31de0ff → 2.0.1

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 (231) 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 +62 -72
  5. package/lib/client/docsClientUtils.d.ts +9 -28
  6. package/lib/client/docsClientUtils.js +35 -51
  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 +35 -8
  12. package/lib/docs.js +211 -78
  13. package/{src/__tests__/__fixtures__/site-with-autogenerated-sidebar/partialAutogeneratedSidebars2.js → lib/frontMatter.d.ts} +4 -10
  14. package/lib/frontMatter.js +53 -0
  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 +137 -146
  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 +7 -4
  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 +57 -29
  29. package/lib/props.d.ts +7 -2
  30. package/lib/props.js +87 -16
  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 -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/{types.d.ts → lib/sidebars/types.js} +2 -7
  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 +29 -19
  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 -172
  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 -29
  65. package/src/categoryGeneratedIndex.ts +60 -0
  66. package/src/cli.ts +94 -114
  67. package/src/client/docsClientUtils.ts +47 -84
  68. package/src/client/index.ts +158 -0
  69. package/src/constants.ts +4 -2
  70. package/src/docs.ts +294 -78
  71. package/src/frontMatter.ts +63 -0
  72. package/src/globalData.ts +57 -7
  73. package/src/index.ts +200 -204
  74. package/src/lastUpdate.ts +27 -34
  75. package/src/markdown/index.ts +10 -16
  76. package/src/markdown/linkify.ts +8 -5
  77. package/src/numberPrefix.ts +19 -26
  78. package/src/options.ts +61 -43
  79. package/src/plugin-content-docs.d.ts +575 -78
  80. package/src/props.ts +128 -23
  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 +42 -23
  93. package/src/tags.ts +20 -0
  94. package/src/translations.ts +155 -124
  95. package/src/types.ts +17 -234
  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 -4673
  100. package/lib/docFrontMatter.d.ts +0 -21
  101. package/lib/docFrontMatter.js +0 -33
  102. package/lib/sidebarItemsGenerator.js +0 -211
  103. package/lib/sidebars.d.ts +0 -42
  104. package/lib/sidebars.js +0 -309
  105. package/lib/theme/hooks/useDocs.d.ts +0 -20
  106. package/lib/theme/hooks/useDocs.js +0 -72
  107. package/lib/versions.d.ts +0 -16
  108. package/lib/versions.js +0 -287
  109. package/src/__tests__/__fixtures__/bad-id-site/docs/invalid-id.md +0 -5
  110. package/src/__tests__/__fixtures__/bad-slug-on-doc-home-site/docs/docWithSlug.md +0 -5
  111. package/src/__tests__/__fixtures__/empty-site/docusaurus.config.js +0 -16
  112. package/src/__tests__/__fixtures__/empty-site/sidebars.json +0 -1
  113. package/src/__tests__/__fixtures__/sidebars/sidebars-category-shorthand.js +0 -34
  114. package/src/__tests__/__fixtures__/sidebars/sidebars-category-wrong-items.json +0 -11
  115. package/src/__tests__/__fixtures__/sidebars/sidebars-category-wrong-label.json +0 -11
  116. package/src/__tests__/__fixtures__/sidebars/sidebars-category.js +0 -44
  117. package/src/__tests__/__fixtures__/sidebars/sidebars-collapsed-first-level.json +0 -20
  118. package/src/__tests__/__fixtures__/sidebars/sidebars-collapsed.json +0 -21
  119. package/src/__tests__/__fixtures__/sidebars/sidebars-doc-id-not-string.json +0 -10
  120. package/src/__tests__/__fixtures__/sidebars/sidebars-link-wrong-href.json +0 -11
  121. package/src/__tests__/__fixtures__/sidebars/sidebars-link-wrong-label.json +0 -11
  122. package/src/__tests__/__fixtures__/sidebars/sidebars-link.json +0 -11
  123. package/src/__tests__/__fixtures__/sidebars/sidebars-unknown-type.json +0 -14
  124. package/src/__tests__/__fixtures__/sidebars/sidebars-wrong-field.json +0 -20
  125. package/src/__tests__/__fixtures__/sidebars/sidebars.json +0 -20
  126. package/src/__tests__/__fixtures__/simple-site/docs/foo/bar.md +0 -69
  127. package/src/__tests__/__fixtures__/simple-site/docs/foo/baz.md +0 -67
  128. package/src/__tests__/__fixtures__/simple-site/docs/headingAsTitle.md +0 -1
  129. package/src/__tests__/__fixtures__/simple-site/docs/hello.md +0 -52
  130. package/src/__tests__/__fixtures__/simple-site/docs/ipsum.md +0 -5
  131. package/src/__tests__/__fixtures__/simple-site/docs/lorem.md +0 -6
  132. package/src/__tests__/__fixtures__/simple-site/docs/rootAbsoluteSlug.md +0 -5
  133. package/src/__tests__/__fixtures__/simple-site/docs/rootRelativeSlug.md +0 -5
  134. package/src/__tests__/__fixtures__/simple-site/docs/rootResolvedSlug.md +0 -5
  135. package/src/__tests__/__fixtures__/simple-site/docs/rootTryToEscapeSlug.md +0 -5
  136. package/src/__tests__/__fixtures__/simple-site/docs/slugs/absoluteSlug.md +0 -5
  137. package/src/__tests__/__fixtures__/simple-site/docs/slugs/relativeSlug.md +0 -5
  138. package/src/__tests__/__fixtures__/simple-site/docs/slugs/resolvedSlug.md +0 -5
  139. package/src/__tests__/__fixtures__/simple-site/docs/slugs/tryToEscapeSlug.md +0 -5
  140. package/src/__tests__/__fixtures__/simple-site/docusaurus.config.js +0 -14
  141. package/src/__tests__/__fixtures__/simple-site/sidebars.json +0 -23
  142. package/src/__tests__/__fixtures__/simple-site/wrong-sidebars.json +0 -7
  143. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/0-getting-started.md +0 -3
  144. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/1-installation.md +0 -3
  145. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/3-API/00_api-overview.md +0 -3
  146. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/3-API/01_Core APIs/0 --- Client API.md +0 -1
  147. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/3-API/01_Core APIs/1 --- Server API.md +0 -1
  148. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/3-API/02_Extension APIs/0. Plugin API.md +0 -1
  149. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/3-API/02_Extension APIs/1. Theme API.md +0 -1
  150. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/3-API/02_Extension APIs/_category_.yml +0 -1
  151. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/3-API/03_api-end.md +0 -3
  152. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/3-API/_category_.json +0 -3
  153. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/Guides/0-guide2.5.md +0 -8
  154. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/Guides/02-guide2.md +0 -7
  155. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/Guides/_category_.json +0 -3
  156. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/Guides/a-guide4.md +0 -7
  157. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/Guides/b-guide5.md +0 -7
  158. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/Guides/guide3.md +0 -8
  159. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/Guides/z-guide1.md +0 -8
  160. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docusaurus.config.js +0 -14
  161. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/partialAutogeneratedSidebars.js +0 -23
  162. package/src/__tests__/__fixtures__/site-with-doc-label/docs/hello-1.md +0 -7
  163. package/src/__tests__/__fixtures__/site-with-doc-label/docs/hello-2.md +0 -8
  164. package/src/__tests__/__fixtures__/site-with-doc-label/docusaurus.config.js +0 -14
  165. package/src/__tests__/__fixtures__/site-with-doc-label/sidebars.json +0 -14
  166. package/src/__tests__/__fixtures__/versioned-site/community/team.md +0 -1
  167. package/src/__tests__/__fixtures__/versioned-site/community_sidebars.json +0 -3
  168. package/src/__tests__/__fixtures__/versioned-site/community_versioned_docs/version-1.0.0/team.md +0 -1
  169. package/src/__tests__/__fixtures__/versioned-site/community_versioned_sidebars/version-1.0.0-sidebars.json +0 -3
  170. package/src/__tests__/__fixtures__/versioned-site/community_versions.json +0 -1
  171. package/src/__tests__/__fixtures__/versioned-site/docs/foo/bar.md +0 -4
  172. package/src/__tests__/__fixtures__/versioned-site/docs/hello.md +0 -1
  173. package/src/__tests__/__fixtures__/versioned-site/docs/slugs/absoluteSlug.md +0 -5
  174. package/src/__tests__/__fixtures__/versioned-site/docs/slugs/relativeSlug.md +0 -5
  175. package/src/__tests__/__fixtures__/versioned-site/docs/slugs/resolvedSlug.md +0 -5
  176. package/src/__tests__/__fixtures__/versioned-site/docs/slugs/tryToEscapeSlug.md +0 -5
  177. package/src/__tests__/__fixtures__/versioned-site/docusaurus.config.js +0 -18
  178. package/src/__tests__/__fixtures__/versioned-site/i18n/en/docusaurus-plugin-content-docs/version-1.0.0/hello.md +0 -1
  179. package/src/__tests__/__fixtures__/versioned-site/i18n/en/docusaurus-plugin-content-docs-community/current/team.md +0 -5
  180. package/src/__tests__/__fixtures__/versioned-site/i18n/fr/docusaurus-plugin-content-docs/version-1.0.0/hello.md +0 -1
  181. package/src/__tests__/__fixtures__/versioned-site/sidebars.json +0 -10
  182. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-1.0.0/foo/bar.md +0 -4
  183. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-1.0.0/foo/baz.md +0 -1
  184. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-1.0.0/hello.md +0 -1
  185. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-1.0.1/foo/bar.md +0 -1
  186. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-1.0.1/hello.md +0 -1
  187. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-withSlugs/rootAbsoluteSlug.md +0 -5
  188. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-withSlugs/rootRelativeSlug.md +0 -5
  189. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-withSlugs/rootResolvedSlug.md +0 -5
  190. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-withSlugs/rootTryToEscapeSlug.md +0 -5
  191. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-withSlugs/slugs/absoluteSlug.md +0 -5
  192. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-withSlugs/slugs/relativeSlug.md +0 -5
  193. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-withSlugs/slugs/resolvedSlug.md +0 -5
  194. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-withSlugs/slugs/tryToEscapeSlug.md +0 -5
  195. package/src/__tests__/__fixtures__/versioned-site/versioned_sidebars/version-1.0.0-sidebars.json +0 -11
  196. package/src/__tests__/__fixtures__/versioned-site/versioned_sidebars/version-1.0.1-sidebars.json +0 -10
  197. package/src/__tests__/__fixtures__/versioned-site/versioned_sidebars/version-withSlugs-sidebars.json +0 -5
  198. package/src/__tests__/__fixtures__/versioned-site/versions.json +0 -5
  199. package/src/__tests__/__snapshots__/cli.test.ts.snap +0 -90
  200. package/src/__tests__/__snapshots__/index.test.ts.snap +0 -1907
  201. package/src/__tests__/__snapshots__/sidebars.test.ts.snap +0 -218
  202. package/src/__tests__/__snapshots__/translations.test.ts.snap +0 -487
  203. package/src/__tests__/cli.test.ts +0 -333
  204. package/src/__tests__/docFrontMatter.test.ts +0 -204
  205. package/src/__tests__/docs.test.ts +0 -875
  206. package/src/__tests__/index.test.ts +0 -1831
  207. package/src/__tests__/lastUpdate.test.ts +0 -68
  208. package/src/__tests__/numberPrefix.test.ts +0 -199
  209. package/src/__tests__/options.test.ts +0 -232
  210. package/src/__tests__/sidebarItemsGenerator.test.ts +0 -336
  211. package/src/__tests__/sidebars.test.ts +0 -638
  212. package/src/__tests__/slug.test.ts +0 -109
  213. package/src/__tests__/translations.test.ts +0 -159
  214. package/src/__tests__/versions.test.ts +0 -718
  215. package/src/client/__tests__/docsClientUtils.test.ts +0 -372
  216. package/src/docFrontMatter.ts +0 -53
  217. package/src/markdown/__tests__/__fixtures__/docs/doc-localized.md +0 -1
  218. package/src/markdown/__tests__/__fixtures__/docs/doc1.md +0 -13
  219. package/src/markdown/__tests__/__fixtures__/docs/doc2.md +0 -12
  220. package/src/markdown/__tests__/__fixtures__/docs/doc4.md +0 -19
  221. package/src/markdown/__tests__/__fixtures__/docs/doc5.md +0 -6
  222. package/src/markdown/__tests__/__fixtures__/docs/subdir/doc3.md +0 -3
  223. package/src/markdown/__tests__/__fixtures__/versioned_docs/version-1.0.0/doc2.md +0 -7
  224. package/src/markdown/__tests__/__fixtures__/versioned_docs/version-1.0.0/subdir/doc1.md +0 -3
  225. package/src/markdown/__tests__/__snapshots__/linkify.test.ts.snap +0 -82
  226. package/src/markdown/__tests__/linkify.test.ts +0 -190
  227. package/src/sidebarItemsGenerator.ts +0 -307
  228. package/src/sidebars.ts +0 -489
  229. package/src/theme/hooks/useDocs.ts +0 -99
  230. package/src/versions.ts +0 -511
  231. package/tsconfig.json +0 -9
package/src/docs.ts CHANGED
@@ -7,31 +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';
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';
35
45
 
36
46
  type LastUpdateOptions = Pick<
37
47
  PluginOptions,
@@ -41,9 +51,21 @@ type LastUpdateOptions = Pick<
41
51
  async function readLastUpdateData(
42
52
  filePath: string,
43
53
  options: LastUpdateOptions,
54
+ lastUpdateFrontMatter: FileChange | undefined,
44
55
  ): Promise<LastUpdateData> {
45
56
  const {showLastUpdateAuthor, showLastUpdateTime} = options;
46
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
+
47
69
  // Use fake data in dev for faster development.
48
70
  const fileLastUpdateData =
49
71
  process.env.NODE_ENV === 'production'
@@ -52,14 +74,16 @@ async function readLastUpdateData(
52
74
  author: 'Author',
53
75
  timestamp: 1539502055,
54
76
  };
77
+ const {author, timestamp} = fileLastUpdateData ?? {};
55
78
 
56
- if (fileLastUpdateData) {
57
- const {author, timestamp} = fileLastUpdateData;
58
- return {
59
- lastUpdatedAt: showLastUpdateTime ? timestamp : undefined,
60
- lastUpdatedBy: showLastUpdateAuthor ? author : undefined,
61
- };
62
- }
79
+ return {
80
+ lastUpdatedBy: showLastUpdateAuthor
81
+ ? lastUpdateFrontMatter?.author ?? author
82
+ : undefined,
83
+ lastUpdatedAt: showLastUpdateTime
84
+ ? frontMatterTimestamp ?? timestamp
85
+ : undefined,
86
+ };
63
87
  }
64
88
 
65
89
  return {};
@@ -71,50 +95,61 @@ export async function readDocFile(
71
95
  'contentPath' | 'contentPathLocalized'
72
96
  >,
73
97
  source: string,
74
- options: LastUpdateOptions,
75
98
  ): Promise<DocFile> {
76
99
  const contentPath = await getFolderContainingFile(
77
- getDocsDirPaths(versionMetadata),
100
+ getContentPathList(versionMetadata),
78
101
  source,
79
102
  );
80
103
 
81
104
  const filePath = path.join(contentPath, source);
82
105
 
83
- const [content, lastUpdate] = await Promise.all([
84
- fs.readFile(filePath, 'utf-8'),
85
- readLastUpdateData(filePath, options),
86
- ]);
87
- return {source, content, lastUpdate, contentPath, filePath};
106
+ const content = await fs.readFile(filePath, 'utf-8');
107
+ return {source, content, contentPath, filePath};
88
108
  }
89
109
 
90
110
  export async function readVersionDocs(
91
111
  versionMetadata: VersionMetadata,
92
112
  options: Pick<
93
113
  PluginOptions,
94
- 'include' | 'showLastUpdateAuthor' | 'showLastUpdateTime'
114
+ 'include' | 'exclude' | 'showLastUpdateAuthor' | 'showLastUpdateTime'
95
115
  >,
96
116
  ): Promise<DocFile[]> {
97
- const sources = await globby(options.include, {
117
+ const sources = await Globby(options.include, {
98
118
  cwd: versionMetadata.contentPath,
119
+ ignore: options.exclude,
99
120
  });
100
121
  return Promise.all(
101
- sources.map((source) => readDocFile(versionMetadata, source, options)),
122
+ sources.map((source) => readDocFile(versionMetadata, source)),
102
123
  );
103
124
  }
104
125
 
105
- export function processDocMetadata({
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({
106
140
  docFile,
107
141
  versionMetadata,
108
142
  context,
109
143
  options,
144
+ env,
110
145
  }: {
111
146
  docFile: DocFile;
112
147
  versionMetadata: VersionMetadata;
113
148
  context: LoadContext;
114
149
  options: MetadataOptions;
115
- }): DocMetadataBase {
116
- const {source, content, lastUpdate, contentPath, filePath} = docFile;
117
- const {homePageId} = options;
150
+ env: DocEnv;
151
+ }): Promise<DocMetadataBase> {
152
+ const {source, content, contentPath, filePath} = docFile;
118
153
  const {siteDir, i18n} = context;
119
154
 
120
155
  const {
@@ -125,35 +160,41 @@ export function processDocMetadata({
125
160
  const frontMatter = validateDocFrontMatter(unsafeFrontMatter);
126
161
 
127
162
  const {
128
- sidebar_label: sidebarLabel,
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
- 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,
134
170
  } = frontMatter;
135
171
 
136
- // ex: api/plugins/myDoc -> myDoc
137
- // 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
138
179
  const sourceFileNameWithoutExtension = path.basename(
139
180
  source,
140
181
  path.extname(source),
141
182
  );
142
183
 
143
- // ex: api/plugins/myDoc -> api/plugins
144
- // ex: myDoc -> .
184
+ // E.g. api/plugins/myDoc -> api/plugins; myDoc -> .
145
185
  const sourceDirName = path.dirname(source);
146
186
 
147
- const {filename: unprefixedFileName, numberPrefix} = parse_number_prefixes
187
+ const {filename: unprefixedFileName, numberPrefix} = parseNumberPrefixes
148
188
  ? options.numberPrefixParser(sourceFileNameWithoutExtension)
149
189
  : {filename: sourceFileNameWithoutExtension, numberPrefix: undefined};
150
190
 
151
191
  const baseID: string = frontMatter.id ?? unprefixedFileName;
152
192
  if (baseID.includes('/')) {
153
- throw new Error(`Document id [${baseID}] cannot include "/".`);
193
+ throw new Error(`Document id "${baseID}" cannot include slash.`);
154
194
  }
155
195
 
156
- // 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
157
198
  const sidebarPosition: number | undefined =
158
199
  frontMatter.sidebar_position ?? numberPrefix;
159
200
 
@@ -166,13 +207,13 @@ export function processDocMetadata({
166
207
  : `version-${versionMetadata.versionName}`;
167
208
 
168
209
  // TODO legacy retrocompatibility
169
- // 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?
170
211
  function computeDirNameIdPrefix() {
171
212
  if (sourceDirName === '.') {
172
213
  return undefined;
173
214
  }
174
215
  // Eventually remove the number prefixes from intermediate directories
175
- return parse_number_prefixes
216
+ return parseNumberPrefixes
176
217
  ? stripPathNumberPrefixes(sourceDirName, options.numberPrefixParser)
177
218
  : sourceDirName;
178
219
  }
@@ -185,33 +226,23 @@ export function processDocMetadata({
185
226
  // legacy versioned id, requires a breaking change to modify this
186
227
  const id = [versionIdPrefix, unversionedId].filter(Boolean).join('/');
187
228
 
188
- // TODO remove soon, deprecated homePageId
189
- const isDocsHomePage = unversionedId === (homePageId ?? '_index');
190
- if (frontMatter.slug && isDocsHomePage) {
191
- throw new Error(
192
- `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`,
193
- );
194
- }
195
-
196
- const docSlug = isDocsHomePage
197
- ? '/'
198
- : getSlug({
199
- baseID,
200
- dirName: sourceDirName,
201
- frontmatterSlug: frontMatter.slug,
202
- stripDirNumberPrefixes: parse_number_prefixes,
203
- numberPrefixParser: options.numberPrefixParser,
204
- });
229
+ const docSlug = getSlug({
230
+ baseID,
231
+ source,
232
+ sourceDirName,
233
+ frontMatterSlug: frontMatter.slug,
234
+ stripDirNumberPrefixes: parseNumberPrefixes,
235
+ numberPrefixParser: options.numberPrefixParser,
236
+ });
205
237
 
206
- // TODO expose both headingTitle+metaTitle to theme?
207
- // Different fallbacks order on purpose!
208
- // See https://github.com/facebook/docusaurus/issues/4665#issuecomment-825831367
209
- const headingTitle: string = contentTitle ?? frontMatter.title ?? baseID;
210
- // const metaTitle: string = frontMatter.title ?? contentTitle ?? baseID;
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)
241
+ const title: string = frontMatter.title ?? contentTitle ?? baseID;
211
242
 
212
243
  const description: string = frontMatter.description ?? excerpt ?? '';
213
244
 
214
- const permalink = normalizeUrl([versionMetadata.versionPath, docSlug]);
245
+ const permalink = normalizeUrl([versionMetadata.path, docSlug]);
215
246
 
216
247
  function getDocEditUrl() {
217
248
  const relativeFilePath = path.relative(contentPath, filePath);
@@ -230,14 +261,30 @@ export function processDocMetadata({
230
261
  const isLocalized = contentPath === versionMetadata.contentPathLocalized;
231
262
  const baseVersionEditUrl =
232
263
  isLocalized && options.editLocalizedFiles
233
- ? versionMetadata.versionEditUrlLocalized
234
- : versionMetadata.versionEditUrl;
264
+ ? versionMetadata.editUrlLocalized
265
+ : versionMetadata.editUrl;
235
266
  return getEditUrl(relativeFilePath, baseVersionEditUrl);
236
- } else {
237
- return undefined;
238
267
  }
268
+ return undefined;
239
269
  }
240
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
+
241
288
  // Assign all of object properties during instantiation (if possible) for
242
289
  // NodeJS optimization.
243
290
  // Adding properties to object after instantiation will cause hidden
@@ -245,24 +292,193 @@ export function processDocMetadata({
245
292
  return {
246
293
  unversionedId,
247
294
  id,
248
- isDocsHomePage,
249
- title: headingTitle,
295
+ title,
250
296
  description,
251
297
  source: aliasedSitePath(filePath, siteDir),
252
298
  sourceDirName,
253
299
  slug: docSlug,
254
300
  permalink,
301
+ draft,
255
302
  editUrl: customEditURL !== undefined ? customEditURL : getDocEditUrl(),
303
+ tags: normalizeFrontMatterTags(versionMetadata.tagsPath, frontMatter.tags),
256
304
  version: versionMetadata.versionName,
257
305
  lastUpdatedBy: lastUpdate.lastUpdatedBy,
258
306
  lastUpdatedAt: lastUpdate.lastUpdatedAt,
259
307
  formattedLastUpdatedAt: lastUpdate.lastUpdatedAt
260
- ? new Intl.DateTimeFormat(i18n.currentLocale).format(
261
- lastUpdate.lastUpdatedAt * 1000,
308
+ ? formatDate(
309
+ i18n.currentLocale,
310
+ new Date(lastUpdate.lastUpdatedAt * 1000),
311
+ i18n.localeConfigs[i18n.currentLocale]!.calendar,
262
312
  )
263
313
  : undefined,
264
- sidebar_label: sidebarLabel,
265
314
  sidebarPosition,
266
315
  frontMatter,
267
316
  };
268
317
  }
318
+
319
+ export function processDocMetadata(args: {
320
+ docFile: DocFile;
321
+ versionMetadata: VersionMetadata;
322
+ context: LoadContext;
323
+ options: MetadataOptions;
324
+ env: DocEnv;
325
+ }): Promise<DocMetadataBase> {
326
+ try {
327
+ return doProcessDocMetadata(args);
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,
352
+ );
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};
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
+ );
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
  }