@docusaurus/plugin-content-docs 2.0.0-beta.ff31de0ff → 2.0.0-rc.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/cli.ts CHANGED
@@ -5,22 +5,24 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
 
8
- import {
9
- getVersionsFilePath,
10
- getVersionedDocsDirPath,
11
- getVersionedSidebarsDirPath,
12
- } from './versions';
13
8
  import fs from 'fs-extra';
14
9
  import path from 'path';
10
+ import logger from '@docusaurus/logger';
11
+ import {DEFAULT_PLUGIN_ID} from '@docusaurus/utils';
15
12
  import {
16
- PathOptions,
17
- UnprocessedSidebarItem,
18
- UnprocessedSidebars,
19
- } from './types';
20
- import {loadSidebars} from './sidebars';
21
- import {DEFAULT_PLUGIN_ID} from '@docusaurus/core/lib/constants';
22
-
23
- function createVersionedSidebarFile({
13
+ getVersionsFilePath,
14
+ getVersionDocsDirPath,
15
+ getVersionSidebarsPath,
16
+ getDocsDirPathLocalized,
17
+ readVersionsFile,
18
+ } from './versions/files';
19
+ import {validateVersionName} from './versions/validation';
20
+ import {loadSidebarsFileUnsafe} from './sidebars';
21
+ import {CURRENT_VERSION_NAME} from './constants';
22
+ import type {PluginOptions} from '@docusaurus/plugin-content-docs';
23
+ import type {LoadContext} from '@docusaurus/types';
24
+
25
+ async function createVersionedSidebarFile({
24
26
  siteDir,
25
27
  pluginId,
26
28
  sidebarPath,
@@ -32,133 +34,111 @@ function createVersionedSidebarFile({
32
34
  version: string;
33
35
  }) {
34
36
  // Load current sidebar and create a new versioned sidebars file (if needed).
35
- const loadedSidebars = loadSidebars(sidebarPath);
37
+ // Note: we don't need the sidebars file to be normalized: it's ok to let
38
+ // plugin option changes to impact older, versioned sidebars
39
+ // We don't validate here, assuming the user has already built the version
40
+ const sidebars = await loadSidebarsFileUnsafe(sidebarPath);
36
41
 
37
- // Do not create a useless versioned sidebars file if sidebars file is empty or sidebars are disabled/false)
38
- const shouldCreateVersionedSidebarFile =
39
- Object.keys(loadedSidebars).length > 0;
42
+ // Do not create a useless versioned sidebars file if sidebars file is empty
43
+ // or sidebars are disabled/false)
44
+ const shouldCreateVersionedSidebarFile = Object.keys(sidebars).length > 0;
40
45
 
41
46
  if (shouldCreateVersionedSidebarFile) {
42
- // TODO @slorber: this "version prefix" in versioned sidebars looks like a bad idea to me
43
- // TODO try to get rid of it
44
- // Transform id in original sidebar to versioned id.
45
- const normalizeItem = (
46
- item: UnprocessedSidebarItem,
47
- ): UnprocessedSidebarItem => {
48
- switch (item.type) {
49
- case 'category':
50
- return {...item, items: item.items.map(normalizeItem)};
51
- case 'ref':
52
- case 'doc':
53
- return {
54
- type: item.type,
55
- id: `version-${version}/${item.id}`,
56
- };
57
- default:
58
- return item;
59
- }
60
- };
61
-
62
- const versionedSidebar: UnprocessedSidebars = Object.entries(
63
- loadedSidebars,
64
- ).reduce((acc: UnprocessedSidebars, [sidebarId, sidebarItems]) => {
65
- const newVersionedSidebarId = `version-${version}/${sidebarId}`;
66
- acc[newVersionedSidebarId] = sidebarItems.map(normalizeItem);
67
- return acc;
68
- }, {});
69
-
70
- const versionedSidebarsDir = getVersionedSidebarsDirPath(siteDir, pluginId);
71
- const newSidebarFile = path.join(
72
- versionedSidebarsDir,
73
- `version-${version}-sidebars.json`,
74
- );
75
- fs.ensureDirSync(path.dirname(newSidebarFile));
76
- fs.writeFileSync(
77
- newSidebarFile,
78
- `${JSON.stringify(versionedSidebar, null, 2)}\n`,
47
+ await fs.outputFile(
48
+ getVersionSidebarsPath(siteDir, pluginId, version),
49
+ `${JSON.stringify(sidebars, null, 2)}\n`,
79
50
  'utf8',
80
51
  );
81
52
  }
82
53
  }
83
54
 
84
55
  // Tests depend on non-default export for mocking.
85
- // eslint-disable-next-line import/prefer-default-export
86
- export function cliDocsVersionCommand(
87
- version: string | null | undefined,
88
- siteDir: string,
89
- pluginId: string,
90
- options: PathOptions,
91
- ): void {
56
+ export async function cliDocsVersionCommand(
57
+ version: unknown,
58
+ {id: pluginId, path: docsPath, sidebarPath}: PluginOptions,
59
+ {siteDir, i18n}: LoadContext,
60
+ ): Promise<void> {
92
61
  // It wouldn't be very user-friendly to show a [default] log prefix,
93
62
  // so we use [docs] instead of [default]
94
63
  const pluginIdLogPrefix =
95
- pluginId === DEFAULT_PLUGIN_ID ? '[docs] ' : `[${pluginId}] `;
96
-
97
- if (!version) {
98
- throw new Error(
99
- `${pluginIdLogPrefix}No version tag specified!. Pass the version you wish to create as an argument. Ex: 1.0.0`,
100
- );
101
- }
102
-
103
- if (version.includes('/') || version.includes('\\')) {
104
- throw new Error(
105
- `${pluginIdLogPrefix}Invalid version tag specified! Do not include slash (/) or (\\). Try something like: 1.0.0`,
106
- );
107
- }
64
+ pluginId === DEFAULT_PLUGIN_ID ? '[docs]' : `[${pluginId}]`;
108
65
 
109
- if (version.length > 32) {
110
- throw new Error(
111
- `${pluginIdLogPrefix}Invalid version tag specified! Length must <= 32 characters. Try something like: 1.0.0`,
112
- );
66
+ try {
67
+ validateVersionName(version);
68
+ } catch (err) {
69
+ logger.info`${pluginIdLogPrefix}: Invalid version name provided. Try something like: 1.0.0`;
70
+ throw err;
113
71
  }
114
72
 
115
- // Since we are going to create `version-${version}` folder, we need to make
116
- // sure it's a valid pathname.
117
- // eslint-disable-next-line no-control-regex
118
- if (/[<>:"|?*\x00-\x1F]/g.test(version)) {
119
- throw new Error(
120
- `${pluginIdLogPrefix}Invalid version tag specified! Please ensure its a valid pathname too. Try something like: 1.0.0`,
121
- );
122
- }
123
-
124
- if (/^\.\.?$/.test(version)) {
125
- throw new Error(
126
- `${pluginIdLogPrefix}Invalid version tag specified! Do not name your version "." or "..". Try something like: 1.0.0`,
127
- );
128
- }
129
-
130
- // Load existing versions.
131
- let versions = [];
132
- const versionsJSONFile = getVersionsFilePath(siteDir, pluginId);
133
- if (fs.existsSync(versionsJSONFile)) {
134
- versions = JSON.parse(fs.readFileSync(versionsJSONFile, 'utf8'));
135
- }
73
+ const versions = (await readVersionsFile(siteDir, pluginId)) ?? [];
136
74
 
137
75
  // Check if version already exists.
138
76
  if (versions.includes(version)) {
139
77
  throw new Error(
140
- `${pluginIdLogPrefix}This version already exists!. Use a version tag that does not already exist.`,
78
+ `${pluginIdLogPrefix}: this version already exists! Use a version tag that does not already exist.`,
141
79
  );
142
80
  }
143
81
 
144
- const {path: docsPath, sidebarPath} = options;
145
-
146
- // Copy docs files.
147
- const docsDir = path.join(siteDir, docsPath);
148
- if (fs.existsSync(docsDir) && fs.readdirSync(docsDir).length > 0) {
149
- const versionedDir = getVersionedDocsDirPath(siteDir, pluginId);
150
- const newVersionDir = path.join(versionedDir, `version-${version}`);
151
- fs.copySync(docsDir, newVersionDir);
152
- } else {
153
- throw new Error(`${pluginIdLogPrefix}There is no docs to version !`);
82
+ if (i18n.locales.length > 1) {
83
+ logger.info`Versioned docs will be created for the following locales: name=${i18n.locales}`;
154
84
  }
155
85
 
156
- createVersionedSidebarFile({siteDir, pluginId, version, sidebarPath});
86
+ await Promise.all(
87
+ i18n.locales.map(async (locale) => {
88
+ const localizationDir = path.resolve(
89
+ siteDir,
90
+ i18n.path,
91
+ i18n.localeConfigs[locale]!.path,
92
+ );
93
+ // Copy docs files.
94
+ const docsDir =
95
+ locale === i18n.defaultLocale
96
+ ? path.resolve(siteDir, docsPath)
97
+ : getDocsDirPathLocalized({
98
+ localizationDir,
99
+ pluginId,
100
+ versionName: CURRENT_VERSION_NAME,
101
+ });
102
+
103
+ if (
104
+ !(await fs.pathExists(docsDir)) ||
105
+ (await fs.readdir(docsDir)).length === 0
106
+ ) {
107
+ if (locale === i18n.defaultLocale) {
108
+ throw new Error(
109
+ logger.interpolate`${pluginIdLogPrefix}: no docs found in path=${docsDir}.`,
110
+ );
111
+ } else {
112
+ logger.warn`${pluginIdLogPrefix}: no docs found in path=${docsDir}. Skipping.`;
113
+ return;
114
+ }
115
+ }
116
+
117
+ const newVersionDir =
118
+ locale === i18n.defaultLocale
119
+ ? getVersionDocsDirPath(siteDir, pluginId, version)
120
+ : getDocsDirPathLocalized({
121
+ localizationDir,
122
+ pluginId,
123
+ versionName: version,
124
+ });
125
+ await fs.copy(docsDir, newVersionDir);
126
+ }),
127
+ );
128
+
129
+ await createVersionedSidebarFile({
130
+ siteDir,
131
+ pluginId,
132
+ version,
133
+ sidebarPath,
134
+ });
157
135
 
158
136
  // Update versions.json file.
159
137
  versions.unshift(version);
160
- fs.ensureDirSync(path.dirname(versionsJSONFile));
161
- fs.writeFileSync(versionsJSONFile, `${JSON.stringify(versions, null, 2)}\n`);
138
+ await fs.outputFile(
139
+ getVersionsFilePath(siteDir, pluginId),
140
+ `${JSON.stringify(versions, null, 2)}\n`,
141
+ );
162
142
 
163
- console.log(`${pluginIdLogPrefix}Version ${version} created!`);
143
+ logger.success`name=${pluginIdLogPrefix}: version name=${version} created!`;
164
144
  }
@@ -7,49 +7,37 @@
7
7
 
8
8
  import {matchPath} from '@docusaurus/router';
9
9
 
10
- import {GlobalPluginData, GlobalVersion, GlobalDoc} from '../types';
10
+ import type {
11
+ GlobalPluginData,
12
+ GlobalVersion,
13
+ GlobalDoc,
14
+ ActivePlugin,
15
+ ActiveDocContext,
16
+ DocVersionSuggestions,
17
+ } from '@docusaurus/plugin-content-docs/client';
18
+ import type {UseDataOptions} from '@docusaurus/types';
11
19
 
12
20
  // This code is not part of the api surface, not in ./theme on purpose
13
21
 
14
- // Short/convenient type aliases
15
- type Version = GlobalVersion;
16
- type Doc = GlobalDoc;
17
-
18
- export type ActivePlugin = {
19
- pluginId: string;
20
- pluginData: GlobalPluginData;
21
- };
22
-
23
- export type GetActivePluginOptions = {failfast?: boolean};
24
-
25
22
  // get the data of the plugin that is currently "active"
26
23
  // ie the docs of that plugin are currently browsed
27
24
  // it is useful to support multiple docs plugin instances
28
25
  export function getActivePlugin(
29
- allPluginDatas: Record<string, GlobalPluginData>,
30
- pathname: string,
31
- options: {failfast: true}, // use fail-fast option if you know for sure one plugin instance is active
32
- ): ActivePlugin;
33
- export function getActivePlugin(
34
- allPluginDatas: Record<string, GlobalPluginData>,
35
- pathname: string,
36
- options?: GetActivePluginOptions,
37
- ): ActivePlugin | undefined;
38
-
39
- export function getActivePlugin(
40
- allPluginDatas: Record<string, GlobalPluginData>,
26
+ allPluginData: {[pluginId: string]: GlobalPluginData},
41
27
  pathname: string,
42
- options: GetActivePluginOptions = {},
28
+ options: UseDataOptions = {},
43
29
  ): ActivePlugin | undefined {
44
- const activeEntry = Object.entries(allPluginDatas).find(
45
- ([_id, pluginData]) => {
46
- return !!matchPath(pathname, {
47
- path: pluginData.path,
48
- exact: false,
49
- strict: false,
50
- });
51
- },
52
- );
30
+ const activeEntry = Object.entries(allPluginData)
31
+ // Route sorting: '/android/foo' should match '/android' instead of '/'
32
+ .sort((a, b) => b[1].path.localeCompare(a[1].path))
33
+ .find(
34
+ ([, pluginData]) =>
35
+ !!matchPath(pathname, {
36
+ path: pluginData.path,
37
+ exact: false,
38
+ strict: false,
39
+ }),
40
+ );
53
41
 
54
42
  const activePlugin: ActivePlugin | undefined = activeEntry
55
43
  ? {pluginId: activeEntry[0], pluginData: activeEntry[1]}
@@ -57,8 +45,8 @@ export function getActivePlugin(
57
45
 
58
46
  if (!activePlugin && options.failfast) {
59
47
  throw new Error(
60
- `Can't find active docs plugin for pathname=${pathname}, while it was expected to be found. Maybe you tried to use a docs feature that can only be used on a docs-related page? Existing docs plugin paths are: ${Object.values(
61
- allPluginDatas,
48
+ `Can't find active docs plugin for "${pathname}" pathname, while it was expected to be found. Maybe you tried to use a docs feature that can only be used on a docs-related page? Existing docs plugin paths are: ${Object.values(
49
+ allPluginData,
62
50
  )
63
51
  .map((plugin) => plugin.path)
64
52
  .join(', ')}`,
@@ -68,42 +56,34 @@ export function getActivePlugin(
68
56
  return activePlugin;
69
57
  }
70
58
 
71
- export type ActiveDocContext = {
72
- activeVersion?: Version;
73
- activeDoc?: Doc;
74
- alternateDocVersions: Record<string, Doc>;
75
- };
76
-
77
- export const getLatestVersion = (data: GlobalPluginData): Version => {
78
- return data.versions.find((version) => version.isLast)!;
79
- };
59
+ export const getLatestVersion = (data: GlobalPluginData): GlobalVersion =>
60
+ data.versions.find((version) => version.isLast)!;
80
61
 
81
- // Note: return undefined on doc-unrelated pages,
82
- // because there's no version currently considered as active
83
- export const getActiveVersion = (
62
+ export function getActiveVersion(
84
63
  data: GlobalPluginData,
85
64
  pathname: string,
86
- ): Version | undefined => {
65
+ ): GlobalVersion | undefined {
87
66
  const lastVersion = getLatestVersion(data);
88
67
  // Last version is a route like /docs/*,
89
- // we need to try to match it last or it would match /docs/version-1.0/* as well
68
+ // we need to match it last or it would match /docs/version-1.0/* as well
90
69
  const orderedVersionsMetadata = [
91
70
  ...data.versions.filter((version) => version !== lastVersion),
92
71
  lastVersion,
93
72
  ];
94
- return orderedVersionsMetadata.find((version) => {
95
- return !!matchPath(pathname, {
96
- path: version.path,
97
- exact: false,
98
- strict: false,
99
- });
100
- });
101
- };
73
+ return orderedVersionsMetadata.find(
74
+ (version) =>
75
+ !!matchPath(pathname, {
76
+ path: version.path,
77
+ exact: false,
78
+ strict: false,
79
+ }),
80
+ );
81
+ }
102
82
 
103
- export const getActiveDocContext = (
83
+ export function getActiveDocContext(
104
84
  data: GlobalPluginData,
105
85
  pathname: string,
106
- ): ActiveDocContext => {
86
+ ): ActiveDocContext {
107
87
  const activeVersion = getActiveVersion(data, pathname);
108
88
  const activeDoc = activeVersion?.docs.find(
109
89
  (doc) =>
@@ -137,32 +117,15 @@ export const getActiveDocContext = (
137
117
  activeDoc,
138
118
  alternateDocVersions: alternateVersionDocs,
139
119
  };
140
- };
141
-
142
- export type DocVersionSuggestions = {
143
- // suggest the same doc, in latest version (if exist)
144
- latestDocSuggestion?: GlobalDoc;
145
- // suggest the latest version
146
- latestVersionSuggestion?: GlobalVersion;
147
- };
120
+ }
148
121
 
149
- export const getDocVersionSuggestions = (
122
+ export function getDocVersionSuggestions(
150
123
  data: GlobalPluginData,
151
124
  pathname: string,
152
- ): DocVersionSuggestions => {
125
+ ): DocVersionSuggestions {
153
126
  const latestVersion = getLatestVersion(data);
154
127
  const activeDocContext = getActiveDocContext(data, pathname);
155
-
156
- // We only suggest another doc/version if user is not using the latest version
157
- const isNotOnLatestVersion = activeDocContext.activeVersion !== latestVersion;
158
-
159
- const latestDocSuggestion: GlobalDoc | undefined = isNotOnLatestVersion
160
- ? activeDocContext?.alternateDocVersions[latestVersion.name]
161
- : undefined;
162
-
163
- const latestVersionSuggestion = isNotOnLatestVersion
164
- ? latestVersion
165
- : undefined;
166
-
167
- return {latestDocSuggestion, latestVersionSuggestion};
168
- };
128
+ const latestDocSuggestion: GlobalDoc | undefined =
129
+ activeDocContext.alternateDocVersions[latestVersion.name];
130
+ return {latestDocSuggestion, latestVersionSuggestion: latestVersion};
131
+ }
@@ -0,0 +1,158 @@
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 {useLocation} from '@docusaurus/router';
9
+ import {
10
+ useAllPluginInstancesData,
11
+ usePluginData,
12
+ } from '@docusaurus/useGlobalData';
13
+
14
+ import {
15
+ getActivePlugin,
16
+ getLatestVersion,
17
+ getActiveVersion,
18
+ getActiveDocContext,
19
+ getDocVersionSuggestions,
20
+ } from './docsClientUtils';
21
+ import type {UseDataOptions} from '@docusaurus/types';
22
+
23
+ export type ActivePlugin = {
24
+ pluginId: string;
25
+ pluginData: GlobalPluginData;
26
+ };
27
+ export type ActiveDocContext = {
28
+ activeVersion?: GlobalVersion;
29
+ activeDoc?: GlobalDoc;
30
+ alternateDocVersions: {[versionName: string]: GlobalDoc};
31
+ };
32
+ export type GlobalDoc = {
33
+ /**
34
+ * For generated index pages, this is the `slug`, **not** `permalink`
35
+ * (without base URL). Because slugs have leading slashes but IDs don't,
36
+ * there won't be clashes.
37
+ */
38
+ id: string;
39
+ path: string;
40
+ sidebar: string | undefined;
41
+ };
42
+
43
+ export type GlobalVersion = {
44
+ name: string;
45
+ label: string;
46
+ isLast: boolean;
47
+ path: string;
48
+ /** The doc with `slug: /`, or first doc in first sidebar */
49
+ mainDocId: string;
50
+ docs: GlobalDoc[];
51
+ /** Unversioned IDs. In development, this list is empty. */
52
+ draftIds: string[];
53
+ sidebars?: {[sidebarId: string]: GlobalSidebar};
54
+ };
55
+
56
+ export type GlobalSidebar = {
57
+ link?: {
58
+ label: string;
59
+ path: string;
60
+ };
61
+ // ... we may add other things here later
62
+ };
63
+ export type GlobalPluginData = {
64
+ path: string;
65
+ versions: GlobalVersion[];
66
+ breadcrumbs: boolean;
67
+ };
68
+ export type DocVersionSuggestions = {
69
+ /** Suggest the latest version */
70
+ latestVersionSuggestion: GlobalVersion;
71
+ /** Suggest the same doc, in latest version (if one exists) */
72
+ latestDocSuggestion?: GlobalDoc;
73
+ };
74
+
75
+ // Important to use a constant object to avoid React useEffect executions etc.
76
+ // see https://github.com/facebook/docusaurus/issues/5089
77
+ const StableEmptyObject = {};
78
+
79
+ // In blog-only mode, docs hooks are still used by the theme. We need a fail-
80
+ // safe fallback when the docs plugin is not in use
81
+ export const useAllDocsData = (): {[pluginId: string]: GlobalPluginData} =>
82
+ (useAllPluginInstancesData('docusaurus-plugin-content-docs') as
83
+ | {
84
+ [pluginId: string]: GlobalPluginData;
85
+ }
86
+ | undefined) ?? StableEmptyObject;
87
+
88
+ export const useDocsData = (pluginId: string | undefined): GlobalPluginData =>
89
+ usePluginData('docusaurus-plugin-content-docs', pluginId, {
90
+ failfast: true,
91
+ }) as GlobalPluginData;
92
+
93
+ // TODO this feature should be provided by docusaurus core
94
+ export function useActivePlugin(
95
+ options: UseDataOptions = {},
96
+ ): ActivePlugin | undefined {
97
+ const data = useAllDocsData();
98
+ const {pathname} = useLocation();
99
+ return getActivePlugin(data, pathname, options);
100
+ }
101
+
102
+ export function useActivePluginAndVersion(
103
+ options: UseDataOptions = {},
104
+ ):
105
+ | {activePlugin: ActivePlugin; activeVersion: GlobalVersion | undefined}
106
+ | undefined {
107
+ const activePlugin = useActivePlugin(options);
108
+ const {pathname} = useLocation();
109
+ if (!activePlugin) {
110
+ return undefined;
111
+ }
112
+ const activeVersion = getActiveVersion(activePlugin.pluginData, pathname);
113
+ return {
114
+ activePlugin,
115
+ activeVersion,
116
+ };
117
+ }
118
+
119
+ /** Versions are returned ordered (most recent first). */
120
+ export function useVersions(pluginId: string | undefined): GlobalVersion[] {
121
+ const data = useDocsData(pluginId);
122
+ return data.versions;
123
+ }
124
+
125
+ export function useLatestVersion(pluginId: string | undefined): GlobalVersion {
126
+ const data = useDocsData(pluginId);
127
+ return getLatestVersion(data);
128
+ }
129
+
130
+ /**
131
+ * Returns `undefined` on doc-unrelated pages, because there's no version
132
+ * currently considered as active.
133
+ */
134
+ export function useActiveVersion(
135
+ pluginId: string | undefined,
136
+ ): GlobalVersion | undefined {
137
+ const data = useDocsData(pluginId);
138
+ const {pathname} = useLocation();
139
+ return getActiveVersion(data, pathname);
140
+ }
141
+
142
+ export function useActiveDocContext(
143
+ pluginId: string | undefined,
144
+ ): ActiveDocContext {
145
+ const data = useDocsData(pluginId);
146
+ const {pathname} = useLocation();
147
+ return getActiveDocContext(data, pathname);
148
+ }
149
+ /**
150
+ * Useful to say "hey, you are not on the latest docs version, please switch"
151
+ */
152
+ export function useDocVersionSuggestions(
153
+ pluginId: string | undefined,
154
+ ): DocVersionSuggestions {
155
+ const data = useDocsData(pluginId);
156
+ const {pathname} = useLocation();
157
+ return getDocVersionSuggestions(data, pathname);
158
+ }
package/src/constants.ts CHANGED
@@ -5,9 +5,11 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
 
8
- // The name of the version at the root of your site (website/docs)
8
+ /** The name of the version that's actively worked on (e.g. `website/docs`) */
9
9
  export const CURRENT_VERSION_NAME = 'current';
10
-
10
+ /** All doc versions are stored here by version names */
11
11
  export const VERSIONED_DOCS_DIR = 'versioned_docs';
12
+ /** All doc versioned sidebars are stored here by version names */
12
13
  export const VERSIONED_SIDEBARS_DIR = 'versioned_sidebars';
14
+ /** The version names. Should 1-1 map to the content of versioned docs dir. */
13
15
  export const VERSIONS_JSON_FILE = 'versions.json';