@docusaurus/plugin-content-docs 2.0.0-beta.1ec2c95e3 → 2.0.0-beta.21

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 (229) 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 +58 -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 +22 -0
  8. package/lib/client/index.js +59 -0
  9. package/lib/constants.d.ts +4 -0
  10. package/lib/constants.js +4 -1
  11. package/lib/docs.d.ts +32 -3
  12. package/lib/docs.js +164 -63
  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} +12 -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 +123 -149
  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 +54 -13
  29. package/lib/props.d.ts +7 -2
  30. package/lib/props.js +70 -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 +55 -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 +178 -0
  46. package/lib/{docFrontMatter.d.ts → sidebars/types.js} +2 -2
  47. package/lib/sidebars/utils.d.ts +54 -0
  48. package/lib/sidebars/utils.js +255 -0
  49. package/lib/sidebars/validation.d.ts +11 -0
  50. package/lib/sidebars/validation.js +142 -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 +83 -93
  57. package/lib/types.d.ts +13 -184
  58. package/lib/versions/files.d.ts +51 -0
  59. package/lib/versions/files.js +143 -0
  60. package/lib/versions/index.d.ts +36 -0
  61. package/lib/versions/index.js +155 -0
  62. package/lib/versions/validation.d.ts +17 -0
  63. package/lib/versions/validation.js +71 -0
  64. package/package.json +34 -29
  65. package/src/categoryGeneratedIndex.ts +60 -0
  66. package/src/cli.ts +85 -114
  67. package/src/client/docsClientUtils.ts +44 -71
  68. package/src/client/index.ts +106 -0
  69. package/src/constants.ts +4 -2
  70. package/{types.d.ts → src/deps.d.ts} +1 -1
  71. package/src/docs.ts +228 -65
  72. package/src/{docFrontMatter.ts → frontMatter.ts} +20 -10
  73. package/src/globalData.ts +57 -7
  74. package/src/index.ts +179 -216
  75. package/src/lastUpdate.ts +26 -37
  76. package/src/markdown/index.ts +10 -16
  77. package/src/markdown/linkify.ts +7 -4
  78. package/src/numberPrefix.ts +19 -26
  79. package/src/options.ts +59 -18
  80. package/src/plugin-content-docs.d.ts +642 -89
  81. package/src/props.ts +103 -21
  82. package/src/routes.ts +159 -0
  83. package/src/server-export.ts +22 -0
  84. package/src/sidebars/README.md +10 -0
  85. package/src/sidebars/generator.ts +292 -0
  86. package/src/sidebars/index.ts +118 -0
  87. package/src/sidebars/normalization.ts +85 -0
  88. package/src/sidebars/postProcessor.ts +112 -0
  89. package/src/sidebars/processor.ts +123 -0
  90. package/src/sidebars/types.ts +275 -0
  91. package/src/sidebars/utils.ts +390 -0
  92. package/src/sidebars/validation.ts +178 -0
  93. package/src/slug.ts +41 -22
  94. package/src/tags.ts +20 -0
  95. package/src/translations.ts +129 -123
  96. package/src/types.ts +18 -249
  97. package/src/versions/files.ts +220 -0
  98. package/src/versions/index.ts +247 -0
  99. package/src/versions/validation.ts +115 -0
  100. package/lib/.tsbuildinfo +0 -1
  101. package/lib/sidebarItemsGenerator.js +0 -211
  102. package/lib/sidebars.d.ts +0 -43
  103. package/lib/sidebars.js +0 -320
  104. package/lib/theme/hooks/useDocs.d.ts +0 -20
  105. package/lib/theme/hooks/useDocs.js +0 -75
  106. package/lib/versions.d.ts +0 -16
  107. package/lib/versions.js +0 -319
  108. package/src/__tests__/__fixtures__/bad-id-site/docs/invalid-id.md +0 -5
  109. package/src/__tests__/__fixtures__/bad-slug-on-doc-home-site/docs/docWithSlug.md +0 -5
  110. package/src/__tests__/__fixtures__/empty-site/docusaurus.config.js +0 -16
  111. package/src/__tests__/__fixtures__/empty-site/sidebars.json +0 -1
  112. package/src/__tests__/__fixtures__/sidebars/sidebars-category-shorthand.js +0 -34
  113. package/src/__tests__/__fixtures__/sidebars/sidebars-category-wrong-items.json +0 -11
  114. package/src/__tests__/__fixtures__/sidebars/sidebars-category-wrong-label.json +0 -11
  115. package/src/__tests__/__fixtures__/sidebars/sidebars-category.js +0 -44
  116. package/src/__tests__/__fixtures__/sidebars/sidebars-collapsed-first-level.json +0 -20
  117. package/src/__tests__/__fixtures__/sidebars/sidebars-collapsed.json +0 -21
  118. package/src/__tests__/__fixtures__/sidebars/sidebars-doc-id-not-string.json +0 -10
  119. package/src/__tests__/__fixtures__/sidebars/sidebars-link-wrong-href.json +0 -11
  120. package/src/__tests__/__fixtures__/sidebars/sidebars-link-wrong-label.json +0 -11
  121. package/src/__tests__/__fixtures__/sidebars/sidebars-link.json +0 -11
  122. package/src/__tests__/__fixtures__/sidebars/sidebars-unknown-type.json +0 -14
  123. package/src/__tests__/__fixtures__/sidebars/sidebars-wrong-field.json +0 -20
  124. package/src/__tests__/__fixtures__/sidebars/sidebars.json +0 -20
  125. package/src/__tests__/__fixtures__/simple-site/docs/foo/bar.md +0 -69
  126. package/src/__tests__/__fixtures__/simple-site/docs/foo/baz.md +0 -70
  127. package/src/__tests__/__fixtures__/simple-site/docs/headingAsTitle.md +0 -1
  128. package/src/__tests__/__fixtures__/simple-site/docs/hello.md +0 -53
  129. package/src/__tests__/__fixtures__/simple-site/docs/ipsum.md +0 -5
  130. package/src/__tests__/__fixtures__/simple-site/docs/lorem.md +0 -6
  131. package/src/__tests__/__fixtures__/simple-site/docs/rootAbsoluteSlug.md +0 -5
  132. package/src/__tests__/__fixtures__/simple-site/docs/rootRelativeSlug.md +0 -5
  133. package/src/__tests__/__fixtures__/simple-site/docs/rootResolvedSlug.md +0 -5
  134. package/src/__tests__/__fixtures__/simple-site/docs/rootTryToEscapeSlug.md +0 -5
  135. package/src/__tests__/__fixtures__/simple-site/docs/slugs/absoluteSlug.md +0 -5
  136. package/src/__tests__/__fixtures__/simple-site/docs/slugs/relativeSlug.md +0 -5
  137. package/src/__tests__/__fixtures__/simple-site/docs/slugs/resolvedSlug.md +0 -5
  138. package/src/__tests__/__fixtures__/simple-site/docs/slugs/tryToEscapeSlug.md +0 -5
  139. package/src/__tests__/__fixtures__/simple-site/docusaurus.config.js +0 -14
  140. package/src/__tests__/__fixtures__/simple-site/sidebars.json +0 -23
  141. package/src/__tests__/__fixtures__/simple-site/wrong-sidebars.json +0 -7
  142. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/0-getting-started.md +0 -3
  143. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/1-installation.md +0 -3
  144. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/3-API/00_api-overview.md +0 -3
  145. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/3-API/01_Core APIs/0 --- Client API.md +0 -1
  146. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/3-API/01_Core APIs/1 --- Server API.md +0 -1
  147. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/3-API/02_Extension APIs/0. Plugin API.md +0 -1
  148. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/3-API/02_Extension APIs/1. Theme API.md +0 -1
  149. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/3-API/02_Extension APIs/_category_.yml +0 -1
  150. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/3-API/03_api-end.md +0 -3
  151. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/3-API/_category_.json +0 -3
  152. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/Guides/0-guide2.5.md +0 -8
  153. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/Guides/02-guide2.md +0 -7
  154. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/Guides/_category_.json +0 -3
  155. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/Guides/a-guide4.md +0 -7
  156. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/Guides/b-guide5.md +0 -7
  157. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/Guides/guide3.md +0 -8
  158. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/docs/Guides/z-guide1.md +0 -8
  159. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/partialAutogeneratedSidebars.js +0 -23
  160. package/src/__tests__/__fixtures__/site-with-autogenerated-sidebar/partialAutogeneratedSidebars2.js +0 -16
  161. package/src/__tests__/__fixtures__/site-with-doc-label/docs/hello-1.md +0 -7
  162. package/src/__tests__/__fixtures__/site-with-doc-label/docs/hello-2.md +0 -8
  163. package/src/__tests__/__fixtures__/site-with-doc-label/docusaurus.config.js +0 -14
  164. package/src/__tests__/__fixtures__/site-with-doc-label/sidebars.json +0 -14
  165. package/src/__tests__/__fixtures__/versioned-site/community/team.md +0 -1
  166. package/src/__tests__/__fixtures__/versioned-site/community_sidebars.json +0 -3
  167. package/src/__tests__/__fixtures__/versioned-site/community_versioned_docs/version-1.0.0/team.md +0 -1
  168. package/src/__tests__/__fixtures__/versioned-site/community_versioned_sidebars/version-1.0.0-sidebars.json +0 -3
  169. package/src/__tests__/__fixtures__/versioned-site/community_versions.json +0 -1
  170. package/src/__tests__/__fixtures__/versioned-site/docs/foo/bar.md +0 -4
  171. package/src/__tests__/__fixtures__/versioned-site/docs/hello.md +0 -1
  172. package/src/__tests__/__fixtures__/versioned-site/docs/slugs/absoluteSlug.md +0 -5
  173. package/src/__tests__/__fixtures__/versioned-site/docs/slugs/relativeSlug.md +0 -5
  174. package/src/__tests__/__fixtures__/versioned-site/docs/slugs/resolvedSlug.md +0 -5
  175. package/src/__tests__/__fixtures__/versioned-site/docs/slugs/tryToEscapeSlug.md +0 -5
  176. package/src/__tests__/__fixtures__/versioned-site/docusaurus.config.js +0 -18
  177. package/src/__tests__/__fixtures__/versioned-site/i18n/en/docusaurus-plugin-content-docs/version-1.0.0/hello.md +0 -1
  178. package/src/__tests__/__fixtures__/versioned-site/i18n/en/docusaurus-plugin-content-docs-community/current/team.md +0 -5
  179. package/src/__tests__/__fixtures__/versioned-site/i18n/fr/docusaurus-plugin-content-docs/version-1.0.0/hello.md +0 -1
  180. package/src/__tests__/__fixtures__/versioned-site/sidebars.json +0 -10
  181. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-1.0.0/foo/bar.md +0 -4
  182. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-1.0.0/foo/baz.md +0 -1
  183. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-1.0.0/hello.md +0 -1
  184. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-1.0.1/foo/bar.md +0 -1
  185. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-1.0.1/hello.md +0 -1
  186. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-withSlugs/rootAbsoluteSlug.md +0 -5
  187. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-withSlugs/rootRelativeSlug.md +0 -5
  188. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-withSlugs/rootResolvedSlug.md +0 -5
  189. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-withSlugs/rootTryToEscapeSlug.md +0 -5
  190. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-withSlugs/slugs/absoluteSlug.md +0 -5
  191. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-withSlugs/slugs/relativeSlug.md +0 -5
  192. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-withSlugs/slugs/resolvedSlug.md +0 -5
  193. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-withSlugs/slugs/tryToEscapeSlug.md +0 -5
  194. package/src/__tests__/__fixtures__/versioned-site/versioned_sidebars/version-1.0.0-sidebars.json +0 -11
  195. package/src/__tests__/__fixtures__/versioned-site/versioned_sidebars/version-1.0.1-sidebars.json +0 -10
  196. package/src/__tests__/__fixtures__/versioned-site/versioned_sidebars/version-withSlugs-sidebars.json +0 -5
  197. package/src/__tests__/__fixtures__/versioned-site/versions.json +0 -5
  198. package/src/__tests__/__snapshots__/cli.test.ts.snap +0 -90
  199. package/src/__tests__/__snapshots__/index.test.ts.snap +0 -1916
  200. package/src/__tests__/__snapshots__/sidebars.test.ts.snap +0 -218
  201. package/src/__tests__/__snapshots__/translations.test.ts.snap +0 -487
  202. package/src/__tests__/cli.test.ts +0 -333
  203. package/src/__tests__/docFrontMatter.test.ts +0 -244
  204. package/src/__tests__/docs.test.ts +0 -878
  205. package/src/__tests__/index.test.ts +0 -1871
  206. package/src/__tests__/lastUpdate.test.ts +0 -69
  207. package/src/__tests__/numberPrefix.test.ts +0 -199
  208. package/src/__tests__/options.test.ts +0 -231
  209. package/src/__tests__/sidebarItemsGenerator.test.ts +0 -336
  210. package/src/__tests__/sidebars.test.ts +0 -639
  211. package/src/__tests__/slug.test.ts +0 -109
  212. package/src/__tests__/translations.test.ts +0 -159
  213. package/src/__tests__/versions.test.ts +0 -741
  214. package/src/client/__tests__/docsClientUtils.test.ts +0 -371
  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 -103
  228. package/src/versions.ts +0 -572
  229. package/tsconfig.json +0 -9
@@ -0,0 +1,220 @@
1
+ /**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ import path from 'path';
9
+ import fs from 'fs-extra';
10
+ import {getPluginI18nPath, DEFAULT_PLUGIN_ID} from '@docusaurus/utils';
11
+ import {
12
+ VERSIONS_JSON_FILE,
13
+ VERSIONED_DOCS_DIR,
14
+ VERSIONED_SIDEBARS_DIR,
15
+ CURRENT_VERSION_NAME,
16
+ } from '../constants';
17
+ import {validateVersionNames} from './validation';
18
+ import type {
19
+ PluginOptions,
20
+ VersionMetadata,
21
+ } from '@docusaurus/plugin-content-docs';
22
+ import type {VersionContext} from './index';
23
+
24
+ /** Add a prefix like `community_version-1.0.0`. No-op for default instance. */
25
+ function addPluginIdPrefix(fileOrDir: string, pluginId: string): string {
26
+ return pluginId === DEFAULT_PLUGIN_ID
27
+ ? fileOrDir
28
+ : `${pluginId}_${fileOrDir}`;
29
+ }
30
+
31
+ /** `[siteDir]/community_versioned_docs/version-1.0.0` */
32
+ export function getVersionDocsDirPath(
33
+ siteDir: string,
34
+ pluginId: string,
35
+ versionName: string,
36
+ ): string {
37
+ return path.join(
38
+ siteDir,
39
+ addPluginIdPrefix(VERSIONED_DOCS_DIR, pluginId),
40
+ `version-${versionName}`,
41
+ );
42
+ }
43
+
44
+ /** `[siteDir]/community_versioned_sidebars/version-1.0.0-sidebars.json` */
45
+ export function getVersionSidebarsPath(
46
+ siteDir: string,
47
+ pluginId: string,
48
+ versionName: string,
49
+ ): string {
50
+ return path.join(
51
+ siteDir,
52
+ addPluginIdPrefix(VERSIONED_SIDEBARS_DIR, pluginId),
53
+ `version-${versionName}-sidebars.json`,
54
+ );
55
+ }
56
+
57
+ export function getDocsDirPathLocalized({
58
+ siteDir,
59
+ locale,
60
+ pluginId,
61
+ versionName,
62
+ }: {
63
+ siteDir: string;
64
+ locale: string;
65
+ pluginId: string;
66
+ versionName: string;
67
+ }): string {
68
+ return getPluginI18nPath({
69
+ siteDir,
70
+ locale,
71
+ pluginName: 'docusaurus-plugin-content-docs',
72
+ pluginId,
73
+ subPaths: [
74
+ versionName === CURRENT_VERSION_NAME
75
+ ? CURRENT_VERSION_NAME
76
+ : `version-${versionName}`,
77
+ ],
78
+ });
79
+ }
80
+
81
+ /** `community` => `[siteDir]/community_versions.json` */
82
+ export function getVersionsFilePath(siteDir: string, pluginId: string): string {
83
+ return path.join(siteDir, addPluginIdPrefix(VERSIONS_JSON_FILE, pluginId));
84
+ }
85
+
86
+ /**
87
+ * Reads the plugin's respective `versions.json` file, and returns its content.
88
+ *
89
+ * @throws Throws if validation fails, i.e. `versions.json` doesn't contain an
90
+ * array of valid version names.
91
+ */
92
+ export async function readVersionsFile(
93
+ siteDir: string,
94
+ pluginId: string,
95
+ ): Promise<string[] | null> {
96
+ const versionsFilePath = getVersionsFilePath(siteDir, pluginId);
97
+ if (await fs.pathExists(versionsFilePath)) {
98
+ const content: unknown = await fs.readJSON(versionsFilePath);
99
+ validateVersionNames(content);
100
+ return content;
101
+ }
102
+ return null;
103
+ }
104
+
105
+ /**
106
+ * Reads the `versions.json` file, and returns an ordered list of version names.
107
+ *
108
+ * - If `disableVersioning` is turned on, it will return `["current"]` (requires
109
+ * `includeCurrentVersion` to be true);
110
+ * - If `includeCurrentVersion` is turned on, "current" will be inserted at the
111
+ * beginning, if not already there.
112
+ *
113
+ * You need to use {@link filterVersions} after this.
114
+ *
115
+ * @throws Throws an error if `disableVersioning: true` but `versions.json`
116
+ * doesn't exist (i.e. site is not versioned)
117
+ * @throws Throws an error if versions list is empty (empty `versions.json` or
118
+ * `disableVersioning` is true, and not including current version)
119
+ */
120
+ export async function readVersionNames(
121
+ siteDir: string,
122
+ options: PluginOptions,
123
+ ): Promise<string[]> {
124
+ const versionFileContent = await readVersionsFile(siteDir, options.id);
125
+
126
+ if (!versionFileContent && options.disableVersioning) {
127
+ throw new Error(
128
+ `Docs: using "disableVersioning: true" option on a non-versioned site does not make sense.`,
129
+ );
130
+ }
131
+
132
+ const versions = options.disableVersioning ? [] : versionFileContent ?? [];
133
+
134
+ // We add the current version at the beginning, unless:
135
+ // - user don't want to; or
136
+ // - it's already been explicitly added to versions.json
137
+ if (
138
+ options.includeCurrentVersion &&
139
+ !versions.includes(CURRENT_VERSION_NAME)
140
+ ) {
141
+ versions.unshift(CURRENT_VERSION_NAME);
142
+ }
143
+
144
+ if (versions.length === 0) {
145
+ throw new Error(
146
+ `It is not possible to use docs without any version. No version is included because you have requested to not include ${path.resolve(
147
+ options.path,
148
+ )} through "includeCurrentVersion: false", while ${
149
+ options.disableVersioning
150
+ ? 'versioning is disabled with "disableVersioning: true"'
151
+ : `the versions file is empty/non-existent`
152
+ }.`,
153
+ );
154
+ }
155
+
156
+ return versions;
157
+ }
158
+
159
+ /**
160
+ * Gets the path-related version metadata.
161
+ *
162
+ * @throws Throws if the resolved docs folder or sidebars file doesn't exist.
163
+ * Does not throw if a versioned sidebar is missing (since we don't create empty
164
+ * files).
165
+ */
166
+ export async function getVersionMetadataPaths({
167
+ versionName,
168
+ context,
169
+ options,
170
+ }: VersionContext): Promise<
171
+ Pick<
172
+ VersionMetadata,
173
+ 'contentPath' | 'contentPathLocalized' | 'sidebarFilePath'
174
+ >
175
+ > {
176
+ const isCurrent = versionName === CURRENT_VERSION_NAME;
177
+ const contentPathLocalized = getDocsDirPathLocalized({
178
+ siteDir: context.siteDir,
179
+ locale: context.i18n.currentLocale,
180
+ pluginId: options.id,
181
+ versionName,
182
+ });
183
+ const contentPath = isCurrent
184
+ ? path.resolve(context.siteDir, options.path)
185
+ : getVersionDocsDirPath(context.siteDir, options.id, versionName);
186
+ const sidebarFilePath = isCurrent
187
+ ? options.sidebarPath
188
+ : getVersionSidebarsPath(context.siteDir, options.id, versionName);
189
+
190
+ if (!(await fs.pathExists(contentPath))) {
191
+ throw new Error(
192
+ `The docs folder does not exist for version "${versionName}". A docs folder is expected to be found at ${path.relative(
193
+ context.siteDir,
194
+ contentPath,
195
+ )}.`,
196
+ );
197
+ }
198
+
199
+ // If the current version defines a path to a sidebar file that does not
200
+ // exist, we throw! Note: for versioned sidebars, the file may not exist (as
201
+ // we prefer to not create it rather than to create an empty file)
202
+ // See https://github.com/facebook/docusaurus/issues/3366
203
+ // See https://github.com/facebook/docusaurus/pull/4775
204
+ if (
205
+ versionName === CURRENT_VERSION_NAME &&
206
+ typeof sidebarFilePath === 'string' &&
207
+ !(await fs.pathExists(sidebarFilePath))
208
+ ) {
209
+ throw new Error(`The path to the sidebar file does not exist at "${path.relative(
210
+ context.siteDir,
211
+ sidebarFilePath,
212
+ )}".
213
+ Please set the docs "sidebarPath" field in your config file to:
214
+ - a sidebars path that exists
215
+ - false: to disable the sidebar
216
+ - undefined: for Docusaurus to generate it automatically`);
217
+ }
218
+
219
+ return {contentPath, contentPathLocalized, sidebarFilePath};
220
+ }
@@ -0,0 +1,247 @@
1
+ /**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ import path from 'path';
9
+ import {normalizeUrl, posixPath} from '@docusaurus/utils';
10
+ import {CURRENT_VERSION_NAME} from '../constants';
11
+ import {validateVersionsOptions} from './validation';
12
+ import {
13
+ getDocsDirPathLocalized,
14
+ getVersionMetadataPaths,
15
+ readVersionNames,
16
+ } from './files';
17
+ import type {
18
+ PluginOptions,
19
+ VersionBanner,
20
+ VersionMetadata,
21
+ } from '@docusaurus/plugin-content-docs';
22
+ import type {LoadContext} from '@docusaurus/types';
23
+
24
+ export type VersionContext = {
25
+ /** The version name to get banner of. */
26
+ versionName: string;
27
+ /** All versions, ordered from newest to oldest. */
28
+ versionNames: string[];
29
+ lastVersionName: string;
30
+ context: LoadContext;
31
+ options: PluginOptions;
32
+ };
33
+
34
+ function getVersionEditUrls({
35
+ contentPath,
36
+ contentPathLocalized,
37
+ context,
38
+ options,
39
+ }: Pick<VersionMetadata, 'contentPath' | 'contentPathLocalized'> & {
40
+ context: LoadContext;
41
+ options: PluginOptions;
42
+ }): Pick<VersionMetadata, 'editUrl' | 'editUrlLocalized'> {
43
+ // If the user is using the functional form of editUrl,
44
+ // she has total freedom and we can't compute a "version edit url"
45
+ if (!options.editUrl || typeof options.editUrl === 'function') {
46
+ return {editUrl: undefined, editUrlLocalized: undefined};
47
+ }
48
+
49
+ const editDirPath = options.editCurrentVersion ? options.path : contentPath;
50
+ const editDirPathLocalized = options.editCurrentVersion
51
+ ? getDocsDirPathLocalized({
52
+ siteDir: context.siteDir,
53
+ locale: context.i18n.currentLocale,
54
+ versionName: CURRENT_VERSION_NAME,
55
+ pluginId: options.id,
56
+ })
57
+ : contentPathLocalized;
58
+
59
+ const versionPathSegment = posixPath(
60
+ path.relative(context.siteDir, path.resolve(context.siteDir, editDirPath)),
61
+ );
62
+ const versionPathSegmentLocalized = posixPath(
63
+ path.relative(
64
+ context.siteDir,
65
+ path.resolve(context.siteDir, editDirPathLocalized),
66
+ ),
67
+ );
68
+
69
+ const editUrl = normalizeUrl([options.editUrl, versionPathSegment]);
70
+
71
+ const editUrlLocalized = normalizeUrl([
72
+ options.editUrl,
73
+ versionPathSegmentLocalized,
74
+ ]);
75
+
76
+ return {editUrl, editUrlLocalized};
77
+ }
78
+
79
+ /**
80
+ * The default version banner depends on the version's relative position to the
81
+ * latest version. More recent ones are "unreleased", and older ones are
82
+ * "unmaintained".
83
+ */
84
+ export function getDefaultVersionBanner({
85
+ versionName,
86
+ versionNames,
87
+ lastVersionName,
88
+ }: VersionContext): VersionBanner | null {
89
+ // Current version: good, no banner
90
+ if (versionName === lastVersionName) {
91
+ return null;
92
+ }
93
+ // Upcoming versions: unreleased banner
94
+ if (
95
+ versionNames.indexOf(versionName) < versionNames.indexOf(lastVersionName)
96
+ ) {
97
+ return 'unreleased';
98
+ }
99
+ // Older versions: display unmaintained banner
100
+ return 'unmaintained';
101
+ }
102
+
103
+ export function getVersionBanner(
104
+ context: VersionContext,
105
+ ): VersionMetadata['banner'] {
106
+ const {versionName, options} = context;
107
+ const versionBannerOption = options.versions[versionName]?.banner;
108
+ if (versionBannerOption) {
109
+ return versionBannerOption === 'none' ? null : versionBannerOption;
110
+ }
111
+ return getDefaultVersionBanner(context);
112
+ }
113
+
114
+ export function getVersionBadge({
115
+ versionName,
116
+ versionNames,
117
+ options,
118
+ }: VersionContext): VersionMetadata['badge'] {
119
+ // If site is not versioned or only one version is included
120
+ // we don't show the version badge by default
121
+ // See https://github.com/facebook/docusaurus/issues/3362
122
+ const defaultVersionBadge = versionNames.length !== 1;
123
+ return options.versions[versionName]?.badge ?? defaultVersionBadge;
124
+ }
125
+
126
+ function getVersionClassName({
127
+ versionName,
128
+ options,
129
+ }: VersionContext): VersionMetadata['className'] {
130
+ const defaultVersionClassName = `docs-version-${versionName}`;
131
+ return options.versions[versionName]?.className ?? defaultVersionClassName;
132
+ }
133
+
134
+ function getVersionLabel({
135
+ versionName,
136
+ options,
137
+ }: VersionContext): VersionMetadata['label'] {
138
+ const defaultVersionLabel =
139
+ versionName === CURRENT_VERSION_NAME ? 'Next' : versionName;
140
+ return options.versions[versionName]?.label ?? defaultVersionLabel;
141
+ }
142
+
143
+ function getVersionPathPart({
144
+ versionName,
145
+ options,
146
+ lastVersionName,
147
+ }: VersionContext): string {
148
+ function getDefaultVersionPathPart() {
149
+ if (versionName === lastVersionName) {
150
+ return '';
151
+ }
152
+ return versionName === CURRENT_VERSION_NAME ? 'next' : versionName;
153
+ }
154
+ return options.versions[versionName]?.path ?? getDefaultVersionPathPart();
155
+ }
156
+
157
+ async function createVersionMetadata(
158
+ context: VersionContext,
159
+ ): Promise<VersionMetadata> {
160
+ const {versionName, lastVersionName, options, context: loadContext} = context;
161
+ const {sidebarFilePath, contentPath, contentPathLocalized} =
162
+ await getVersionMetadataPaths(context);
163
+ const versionPathPart = getVersionPathPart(context);
164
+
165
+ const routePath = normalizeUrl([
166
+ loadContext.baseUrl,
167
+ options.routeBasePath,
168
+ versionPathPart,
169
+ ]);
170
+
171
+ const versionEditUrls = getVersionEditUrls({
172
+ contentPath,
173
+ contentPathLocalized,
174
+ context: loadContext,
175
+ options,
176
+ });
177
+
178
+ return {
179
+ versionName,
180
+ label: getVersionLabel(context),
181
+ banner: getVersionBanner(context),
182
+ badge: getVersionBadge(context),
183
+ className: getVersionClassName(context),
184
+ path: routePath,
185
+ tagsPath: normalizeUrl([routePath, options.tagsBasePath]),
186
+ ...versionEditUrls,
187
+ isLast: versionName === lastVersionName,
188
+ routePriority: versionPathPart === '' ? -1 : undefined,
189
+ sidebarFilePath,
190
+ contentPath,
191
+ contentPathLocalized,
192
+ };
193
+ }
194
+
195
+ /**
196
+ * Filter versions according to provided options (i.e. `onlyIncludeVersions`).
197
+ *
198
+ * Note: we preserve the order in which versions are provided; the order of the
199
+ * `onlyIncludeVersions` array does not matter
200
+ */
201
+ export function filterVersions(
202
+ versionNamesUnfiltered: string[],
203
+ options: PluginOptions,
204
+ ): string[] {
205
+ if (options.onlyIncludeVersions) {
206
+ return versionNamesUnfiltered.filter((name) =>
207
+ options.onlyIncludeVersions!.includes(name),
208
+ );
209
+ }
210
+ return versionNamesUnfiltered;
211
+ }
212
+
213
+ function getLastVersionName({
214
+ versionNames,
215
+ options,
216
+ }: Pick<VersionContext, 'versionNames' | 'options'>) {
217
+ return (
218
+ options.lastVersion ??
219
+ versionNames.find((name) => name !== CURRENT_VERSION_NAME) ??
220
+ CURRENT_VERSION_NAME
221
+ );
222
+ }
223
+
224
+ export async function readVersionsMetadata({
225
+ context,
226
+ options,
227
+ }: {
228
+ context: LoadContext;
229
+ options: PluginOptions;
230
+ }): Promise<VersionMetadata[]> {
231
+ const allVersionNames = await readVersionNames(context.siteDir, options);
232
+ validateVersionsOptions(allVersionNames, options);
233
+ const versionNames = filterVersions(allVersionNames, options);
234
+ const lastVersionName = getLastVersionName({versionNames, options});
235
+ const versionsMetadata = await Promise.all(
236
+ versionNames.map((versionName) =>
237
+ createVersionMetadata({
238
+ versionName,
239
+ versionNames,
240
+ lastVersionName,
241
+ context,
242
+ options,
243
+ }),
244
+ ),
245
+ );
246
+ return versionsMetadata;
247
+ }
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ import _ from 'lodash';
9
+ import type {VersionsOptions} from '@docusaurus/plugin-content-docs';
10
+
11
+ export function validateVersionName(name: unknown): asserts name is string {
12
+ if (typeof name !== 'string') {
13
+ throw new Error(
14
+ `Versions should be strings. Found type "${typeof name}" for version ${JSON.stringify(
15
+ name,
16
+ )}.`,
17
+ );
18
+ }
19
+ if (!name.trim()) {
20
+ throw new Error(
21
+ `Invalid version name "${name}": version name must contain at least one non-whitespace character.`,
22
+ );
23
+ }
24
+ const errors: [RegExp, string][] = [
25
+ [/[/\\]/, 'should not include slash (/) or backslash (\\)'],
26
+ [/.{33,}/, 'cannot be longer than 32 characters'],
27
+ // eslint-disable-next-line no-control-regex
28
+ [/[<>:"|?*\x00-\x1F]/, 'should be a valid file path'],
29
+ [/^\.\.?$/, 'should not be "." or ".."'],
30
+ ];
31
+
32
+ errors.forEach(([pattern, message]) => {
33
+ if (pattern.test(name)) {
34
+ throw new Error(
35
+ `Invalid version name "${name}": version name ${message}.`,
36
+ );
37
+ }
38
+ });
39
+ }
40
+
41
+ export function validateVersionNames(
42
+ names: unknown,
43
+ ): asserts names is string[] {
44
+ if (!Array.isArray(names)) {
45
+ throw new Error(
46
+ `The versions file should contain an array of version names! Found content: ${JSON.stringify(
47
+ names,
48
+ )}`,
49
+ );
50
+ }
51
+
52
+ names.forEach(validateVersionName);
53
+ }
54
+
55
+ /**
56
+ * @throws Throws for one of the following invalid options:
57
+ * - `lastVersion` is non-existent
58
+ * - `versions` includes unknown keys
59
+ * - `onlyIncludeVersions` is empty, contains unknown names, or doesn't include
60
+ * `latestVersion` (if provided)
61
+ */
62
+ export function validateVersionsOptions(
63
+ availableVersionNames: string[],
64
+ options: VersionsOptions,
65
+ ): void {
66
+ const availableVersionNamesMsg = `Available version names are: ${availableVersionNames.join(
67
+ ', ',
68
+ )}`;
69
+ if (
70
+ options.lastVersion &&
71
+ !availableVersionNames.includes(options.lastVersion)
72
+ ) {
73
+ throw new Error(
74
+ `Docs option lastVersion: ${options.lastVersion} is invalid. ${availableVersionNamesMsg}`,
75
+ );
76
+ }
77
+ const unknownVersionConfigNames = _.difference(
78
+ Object.keys(options.versions),
79
+ availableVersionNames,
80
+ );
81
+ if (unknownVersionConfigNames.length > 0) {
82
+ throw new Error(
83
+ `Invalid docs option "versions": unknown versions (${unknownVersionConfigNames.join(
84
+ ',',
85
+ )}) found. ${availableVersionNamesMsg}`,
86
+ );
87
+ }
88
+
89
+ if (options.onlyIncludeVersions) {
90
+ if (options.onlyIncludeVersions.length === 0) {
91
+ throw new Error(
92
+ `Invalid docs option "onlyIncludeVersions": an empty array is not allowed, at least one version is needed.`,
93
+ );
94
+ }
95
+ const unknownOnlyIncludeVersionNames = _.difference(
96
+ options.onlyIncludeVersions,
97
+ availableVersionNames,
98
+ );
99
+ if (unknownOnlyIncludeVersionNames.length > 0) {
100
+ throw new Error(
101
+ `Invalid docs option "onlyIncludeVersions": unknown versions (${unknownOnlyIncludeVersionNames.join(
102
+ ',',
103
+ )}) found. ${availableVersionNamesMsg}`,
104
+ );
105
+ }
106
+ if (
107
+ options.lastVersion &&
108
+ !options.onlyIncludeVersions.includes(options.lastVersion)
109
+ ) {
110
+ throw new Error(
111
+ `Invalid docs option "lastVersion": if you use both the "onlyIncludeVersions" and "lastVersion" options, then "lastVersion" must be present in the provided "onlyIncludeVersions" array.`,
112
+ );
113
+ }
114
+ }
115
+ }