@docusaurus/plugin-content-docs 2.0.0-beta.8bda3b2db → 2.0.0-beta.9

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 (117) hide show
  1. package/lib/.tsbuildinfo +1 -1
  2. package/lib/cli.d.ts +2 -2
  3. package/lib/cli.js +20 -24
  4. package/lib/client/docsClientUtils.d.ts +1 -4
  5. package/lib/client/docsClientUtils.js +12 -16
  6. package/lib/docFrontMatter.js +7 -3
  7. package/lib/docs.d.ts +4 -2
  8. package/lib/docs.js +77 -23
  9. package/lib/index.js +88 -94
  10. package/lib/lastUpdate.js +8 -8
  11. package/lib/markdown/index.d.ts +3 -6
  12. package/lib/markdown/index.js +3 -3
  13. package/lib/markdown/linkify.js +2 -2
  14. package/lib/options.d.ts +1 -1
  15. package/lib/options.js +39 -11
  16. package/lib/props.d.ts +7 -2
  17. package/lib/props.js +27 -4
  18. package/lib/{sidebarItemsGenerator.d.ts → sidebars/generator.d.ts} +3 -1
  19. package/lib/sidebars/generator.js +174 -0
  20. package/lib/sidebars/index.d.ts +14 -0
  21. package/lib/sidebars/index.js +64 -0
  22. package/lib/sidebars/normalization.d.ts +9 -0
  23. package/lib/sidebars/normalization.js +58 -0
  24. package/lib/sidebars/processor.d.ts +16 -0
  25. package/lib/sidebars/processor.js +70 -0
  26. package/lib/sidebars/types.d.ts +87 -0
  27. package/lib/sidebars/types.js +13 -0
  28. package/lib/sidebars/utils.d.ts +22 -0
  29. package/lib/sidebars/utils.js +101 -0
  30. package/lib/sidebars/validation.d.ts +8 -0
  31. package/lib/sidebars/validation.js +102 -0
  32. package/lib/slug.js +4 -4
  33. package/lib/tags.d.ts +8 -0
  34. package/lib/tags.js +22 -0
  35. package/lib/theme/hooks/useDocs.js +24 -21
  36. package/lib/translations.d.ts +1 -1
  37. package/lib/translations.js +13 -13
  38. package/lib/types.d.ts +35 -58
  39. package/lib/versions.d.ts +1 -1
  40. package/lib/versions.js +75 -22
  41. package/package.json +15 -14
  42. package/src/__tests__/__fixtures__/simple-site/docs/_partials/somePartial.md +3 -0
  43. package/src/__tests__/__fixtures__/simple-site/docs/_partials/subfolder/somePartial.md +3 -0
  44. package/src/__tests__/__fixtures__/simple-site/docs/_somePartial.md +3 -0
  45. package/src/__tests__/__fixtures__/simple-site/docs/foo/baz.md +5 -0
  46. package/src/__tests__/__fixtures__/simple-site/docs/hello.md +1 -0
  47. package/src/__tests__/__fixtures__/simple-site/docs/rootAbsoluteSlug.md +2 -0
  48. package/src/__tests__/__fixtures__/simple-site/docs/rootRelativeSlug.md +2 -0
  49. package/src/__tests__/__fixtures__/simple-site/docs/rootResolvedSlug.md +2 -0
  50. package/src/__tests__/__fixtures__/simple-site/docs/rootTryToEscapeSlug.md +2 -0
  51. package/src/__tests__/__fixtures__/simple-site/sidebars.json +15 -1
  52. package/src/__tests__/__fixtures__/versioned-site/docs/foo/bar.md +6 -0
  53. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-1.0.1/_partials/somePartial.md +3 -0
  54. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-1.0.1/_partials/subfolder/somePartial.md +3 -0
  55. package/src/__tests__/__fixtures__/versioned-site/versioned_docs/version-1.0.1/_somePartial.md +3 -0
  56. package/src/__tests__/__snapshots__/cli.test.ts.snap +33 -0
  57. package/src/__tests__/__snapshots__/docs.test.ts.snap +140 -0
  58. package/src/__tests__/__snapshots__/index.test.ts.snap +478 -60
  59. package/src/__tests__/__snapshots__/translations.test.ts.snap +0 -3
  60. package/src/__tests__/cli.test.ts +14 -10
  61. package/src/__tests__/docFrontMatter.test.ts +163 -48
  62. package/src/__tests__/docs.test.ts +167 -21
  63. package/src/__tests__/index.test.ts +74 -30
  64. package/src/__tests__/lastUpdate.test.ts +3 -2
  65. package/src/__tests__/options.test.ts +46 -3
  66. package/src/__tests__/props.test.ts +62 -0
  67. package/src/__tests__/translations.test.ts +0 -1
  68. package/src/__tests__/versions.test.ts +88 -60
  69. package/src/cli.ts +27 -30
  70. package/src/client/__tests__/docsClientUtils.test.ts +4 -5
  71. package/src/client/docsClientUtils.ts +6 -27
  72. package/src/docFrontMatter.ts +8 -3
  73. package/src/docs.ts +92 -9
  74. package/src/index.ts +114 -121
  75. package/src/lastUpdate.ts +10 -6
  76. package/src/markdown/index.ts +8 -12
  77. package/src/numberPrefix.ts +4 -2
  78. package/src/options.ts +47 -17
  79. package/src/plugin-content-docs.d.ts +121 -34
  80. package/src/props.ts +42 -6
  81. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-category-shorthand.js +0 -0
  82. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-category-wrong-items.json +0 -0
  83. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-category-wrong-label.json +0 -0
  84. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-category.js +0 -0
  85. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-collapsed-first-level.json +0 -0
  86. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-collapsed.json +0 -0
  87. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-doc-id-not-string.json +0 -0
  88. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-first-level-not-category.js +0 -0
  89. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-link-wrong-href.json +0 -0
  90. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-link-wrong-label.json +0 -0
  91. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-link.json +0 -0
  92. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-unknown-type.json +0 -0
  93. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars-wrong-field.json +0 -0
  94. package/src/{__tests__ → sidebars/__tests__}/__fixtures__/sidebars/sidebars.json +0 -0
  95. package/src/{__tests__/__snapshots__/sidebars.test.ts.snap → sidebars/__tests__/__snapshots__/index.test.ts.snap} +21 -6
  96. package/src/{__tests__/sidebarItemsGenerator.test.ts → sidebars/__tests__/generator.test.ts} +29 -7
  97. package/src/sidebars/__tests__/index.test.ts +202 -0
  98. package/src/sidebars/__tests__/processor.test.ts +148 -0
  99. package/src/sidebars/__tests__/utils.test.ts +395 -0
  100. package/src/sidebars/generator.ts +253 -0
  101. package/src/sidebars/index.ts +84 -0
  102. package/src/sidebars/normalization.ts +88 -0
  103. package/src/sidebars/processor.ts +124 -0
  104. package/src/sidebars/types.ts +156 -0
  105. package/src/sidebars/utils.ts +146 -0
  106. package/src/sidebars/validation.ts +124 -0
  107. package/src/tags.ts +21 -0
  108. package/src/theme/hooks/useDocs.ts +5 -1
  109. package/src/translations.ts +26 -36
  110. package/src/types.ts +48 -99
  111. package/src/versions.ts +109 -17
  112. package/lib/sidebarItemsGenerator.js +0 -211
  113. package/lib/sidebars.d.ts +0 -43
  114. package/lib/sidebars.js +0 -319
  115. package/src/__tests__/sidebars.test.ts +0 -639
  116. package/src/sidebarItemsGenerator.ts +0 -307
  117. package/src/sidebars.ts +0 -506
package/src/docs.ts CHANGED
@@ -7,6 +7,8 @@
7
7
 
8
8
  import path from 'path';
9
9
  import fs from 'fs-extra';
10
+ import chalk from 'chalk';
11
+ import {keyBy} from 'lodash';
10
12
  import {
11
13
  aliasedSitePath,
12
14
  getEditUrl,
@@ -14,6 +16,8 @@ import {
14
16
  normalizeUrl,
15
17
  parseMarkdownString,
16
18
  posixPath,
19
+ Globby,
20
+ normalizeFrontMatterTags,
17
21
  } from '@docusaurus/utils';
18
22
  import {LoadContext} from '@docusaurus/types';
19
23
 
@@ -21,18 +25,21 @@ import {getFileLastUpdate} from './lastUpdate';
21
25
  import {
22
26
  DocFile,
23
27
  DocMetadataBase,
28
+ DocMetadata,
29
+ DocNavLink,
24
30
  LastUpdateData,
25
31
  MetadataOptions,
26
32
  PluginOptions,
27
33
  VersionMetadata,
34
+ LoadedVersion,
28
35
  } from './types';
29
36
  import getSlug from './slug';
30
37
  import {CURRENT_VERSION_NAME} from './constants';
31
- import globby from 'globby';
32
38
  import {getDocsDirPaths} from './versions';
33
39
  import {stripPathNumberPrefixes} from './numberPrefix';
34
40
  import {validateDocFrontMatter} from './docFrontMatter';
35
- import chalk from 'chalk';
41
+ import type {Sidebars} from './sidebars/types';
42
+ import {createSidebarsUtils} from './sidebars/utils';
36
43
 
37
44
  type LastUpdateOptions = Pick<
38
45
  PluginOptions,
@@ -92,11 +99,12 @@ export async function readVersionDocs(
92
99
  versionMetadata: VersionMetadata,
93
100
  options: Pick<
94
101
  PluginOptions,
95
- 'include' | 'showLastUpdateAuthor' | 'showLastUpdateTime'
102
+ 'include' | 'exclude' | 'showLastUpdateAuthor' | 'showLastUpdateTime'
96
103
  >,
97
104
  ): Promise<DocFile[]> {
98
- const sources = await globby(options.include, {
105
+ const sources = await Globby(options.include, {
99
106
  cwd: versionMetadata.contentPath,
107
+ ignore: options.exclude,
100
108
  });
101
109
  return Promise.all(
102
110
  sources.map((source) => readDocFile(versionMetadata, source, options)),
@@ -129,8 +137,8 @@ function doProcessDocMetadata({
129
137
  custom_edit_url: customEditURL,
130
138
 
131
139
  // 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,
140
+ // but allow to disable this behavior with frontmatter
141
+ parse_number_prefixes: parseNumberPrefixes = true,
134
142
  } = frontMatter;
135
143
 
136
144
  // ex: api/plugins/myDoc -> myDoc
@@ -144,7 +152,7 @@ function doProcessDocMetadata({
144
152
  // ex: myDoc -> .
145
153
  const sourceDirName = path.dirname(source);
146
154
 
147
- const {filename: unprefixedFileName, numberPrefix} = parse_number_prefixes
155
+ const {filename: unprefixedFileName, numberPrefix} = parseNumberPrefixes
148
156
  ? options.numberPrefixParser(sourceFileNameWithoutExtension)
149
157
  : {filename: sourceFileNameWithoutExtension, numberPrefix: undefined};
150
158
 
@@ -172,7 +180,7 @@ function doProcessDocMetadata({
172
180
  return undefined;
173
181
  }
174
182
  // Eventually remove the number prefixes from intermediate directories
175
- return parse_number_prefixes
183
+ return parseNumberPrefixes
176
184
  ? stripPathNumberPrefixes(sourceDirName, options.numberPrefixParser)
177
185
  : sourceDirName;
178
186
  }
@@ -199,7 +207,7 @@ function doProcessDocMetadata({
199
207
  baseID,
200
208
  dirName: sourceDirName,
201
209
  frontmatterSlug: frontMatter.slug,
202
- stripDirNumberPrefixes: parse_number_prefixes,
210
+ stripDirNumberPrefixes: parseNumberPrefixes,
203
211
  numberPrefixParser: options.numberPrefixParser,
204
212
  });
205
213
 
@@ -251,6 +259,7 @@ function doProcessDocMetadata({
251
259
  slug: docSlug,
252
260
  permalink,
253
261
  editUrl: customEditURL !== undefined ? customEditURL : getDocEditUrl(),
262
+ tags: normalizeFrontMatterTags(versionMetadata.tagsPath, frontMatter.tags),
254
263
  version: versionMetadata.versionName,
255
264
  lastUpdatedBy: lastUpdate.lastUpdatedBy,
256
265
  lastUpdatedAt: lastUpdate.lastUpdatedAt,
@@ -281,3 +290,77 @@ export function processDocMetadata(args: {
281
290
  throw e;
282
291
  }
283
292
  }
293
+
294
+ export function handleNavigation(
295
+ docsBase: DocMetadataBase[],
296
+ sidebars: Sidebars,
297
+ sidebarFilePath: string,
298
+ ): Pick<LoadedVersion, 'mainDocId' | 'docs'> {
299
+ const docsBaseById = keyBy(docsBase, (doc) => doc.id);
300
+ const {checkSidebarsDocIds, getDocNavigation, getFirstDocIdOfFirstSidebar} =
301
+ createSidebarsUtils(sidebars);
302
+
303
+ const validDocIds = Object.keys(docsBaseById);
304
+ checkSidebarsDocIds(validDocIds, sidebarFilePath);
305
+
306
+ // Add sidebar/next/previous to the docs
307
+ function addNavData(doc: DocMetadataBase): DocMetadata {
308
+ const {sidebarName, previousId, nextId} = getDocNavigation(doc.id);
309
+ const toDocNavLink = (
310
+ docId: string | null | undefined,
311
+ type: 'prev' | 'next',
312
+ ): DocNavLink | undefined => {
313
+ if (!docId) {
314
+ return undefined;
315
+ }
316
+ if (!docsBaseById[docId]) {
317
+ // This could only happen if user provided the ID through front matter
318
+ throw new Error(
319
+ `Error when loading ${doc.id} in ${doc.sourceDirName}: the pagination_${type} front matter points to a non-existent ID ${docId}.`,
320
+ );
321
+ }
322
+ const {
323
+ title,
324
+ permalink,
325
+ frontMatter: {
326
+ pagination_label: paginationLabel,
327
+ sidebar_label: sidebarLabel,
328
+ },
329
+ } = docsBaseById[docId];
330
+ return {title: paginationLabel ?? sidebarLabel ?? title, permalink};
331
+ };
332
+ const {
333
+ frontMatter: {
334
+ pagination_next: paginationNext = nextId,
335
+ pagination_prev: paginationPrev = previousId,
336
+ },
337
+ } = doc;
338
+ const previous = toDocNavLink(paginationPrev, 'prev');
339
+ const next = toDocNavLink(paginationNext, 'next');
340
+ return {...doc, sidebar: sidebarName, previous, next};
341
+ }
342
+ const docs = docsBase.map(addNavData);
343
+ // sort to ensure consistent output for tests
344
+ docs.sort((a, b) => a.id.localeCompare(b.id));
345
+
346
+ /**
347
+ * The "main doc" is the "version entry point"
348
+ * We browse this doc by clicking on a version:
349
+ * - the "home" doc (at '/docs/')
350
+ * - the first doc of the first sidebar
351
+ * - a random doc (if no docs are in any sidebar... edge case)
352
+ */
353
+ function getMainDoc(): DocMetadata {
354
+ const versionHomeDoc = docs.find((doc) => doc.slug === '/');
355
+ const firstDocIdOfFirstSidebar = getFirstDocIdOfFirstSidebar();
356
+ if (versionHomeDoc) {
357
+ return versionHomeDoc;
358
+ } else if (firstDocIdOfFirstSidebar) {
359
+ return docs.find((doc) => doc.id === firstDocIdOfFirstSidebar)!;
360
+ } else {
361
+ return docs[0];
362
+ }
363
+ }
364
+
365
+ return {mainDocId: getMainDoc().unversionedId, docs};
366
+ }
package/src/index.ts CHANGED
@@ -18,10 +18,12 @@ import {
18
18
  reportMessage,
19
19
  posixPath,
20
20
  addTrailingPathSeparator,
21
+ createAbsoluteFilePathMatcher,
21
22
  } from '@docusaurus/utils';
22
23
  import {LoadContext, Plugin, RouteConfig} from '@docusaurus/types';
23
- import {loadSidebars, createSidebarsUtils, processSidebars} from './sidebars';
24
- import {readVersionDocs, processDocMetadata} from './docs';
24
+ import {loadSidebars} from './sidebars';
25
+ import {CategoryMetadataFilenamePattern} from './sidebars/generator';
26
+ import {readVersionDocs, processDocMetadata, handleNavigation} from './docs';
25
27
  import {getDocsDirPaths, readVersionsMetadata} from './versions';
26
28
 
27
29
  import {
@@ -32,24 +34,24 @@ import {
32
34
  DocMetadata,
33
35
  GlobalPluginData,
34
36
  VersionMetadata,
35
- DocNavLink,
36
37
  LoadedVersion,
37
38
  DocFile,
38
39
  DocsMarkdownOption,
40
+ VersionTag,
39
41
  } from './types';
40
- import {PermalinkToSidebar} from '@docusaurus/plugin-content-docs-types';
41
42
  import {RuleSetRule} from 'webpack';
42
43
  import {cliDocsVersionCommand} from './cli';
43
44
  import {VERSIONS_JSON_FILE} from './constants';
44
- import {flatten, keyBy, compact} from 'lodash';
45
+ import {keyBy, mapValues} from 'lodash';
45
46
  import {toGlobalDataVersion} from './globalData';
46
- import {toVersionMetadataProp} from './props';
47
+ import {toTagDocListProp, toVersionMetadataProp} from './props';
47
48
  import {
48
49
  translateLoadedContent,
49
50
  getLoadedContentTranslationFiles,
50
51
  } from './translations';
51
- import {CategoryMetadataFilenamePattern} from './sidebarItemsGenerator';
52
52
  import chalk from 'chalk';
53
+ import {getVersionTags} from './tags';
54
+ import {PropTagsListPage} from '@docusaurus/plugin-content-docs-types';
53
55
 
54
56
  export default function pluginContentDocs(
55
57
  context: LoadContext,
@@ -59,7 +61,6 @@ export default function pluginContentDocs(
59
61
 
60
62
  const versionsMetadata = readVersionsMetadata({context, options});
61
63
 
62
- const sourceToPermalink: SourceToPermalink = {};
63
64
  const pluginId = options.id ?? DEFAULT_PLUGIN_ID;
64
65
 
65
66
  const pluginDataDirRoot = path.join(
@@ -101,6 +102,8 @@ export default function pluginContentDocs(
101
102
  cliDocsVersionCommand(version, siteDir, pluginId, {
102
103
  path: options.path,
103
104
  sidebarPath: options.sidebarPath,
105
+ sidebarCollapsed: options.sidebarCollapsed,
106
+ sidebarCollapsible: options.sidebarCollapsible,
104
107
  });
105
108
  });
106
109
  },
@@ -109,22 +112,12 @@ export default function pluginContentDocs(
109
112
  return getLoadedContentTranslationFiles(content);
110
113
  },
111
114
 
112
- getClientModules() {
113
- const modules = [];
114
- if (options.admonitions) {
115
- modules.push(require.resolve('remark-admonitions/styles/infima.css'));
116
- }
117
- return modules;
118
- },
119
-
120
115
  getPathsToWatch() {
121
116
  function getVersionPathsToWatch(version: VersionMetadata): string[] {
122
117
  const result = [
123
- ...flatten(
124
- options.include.map((pattern) =>
125
- getDocsDirPaths(version).map(
126
- (docsDirPath) => `${docsDirPath}/${pattern}`,
127
- ),
118
+ ...options.include.flatMap((pattern) =>
119
+ getDocsDirPaths(version).map(
120
+ (docsDirPath) => `${docsDirPath}/${pattern}`,
128
121
  ),
129
122
  ),
130
123
  `${version.contentPath}/**/${CategoryMetadataFilenamePattern}`,
@@ -135,7 +128,7 @@ export default function pluginContentDocs(
135
128
  return result;
136
129
  }
137
130
 
138
- return flatten(versionsMetadata.map(getVersionPathsToWatch));
131
+ return versionsMetadata.flatMap(getVersionPathsToWatch);
139
132
  },
140
133
 
141
134
  async loadContent() {
@@ -167,104 +160,28 @@ export default function pluginContentDocs(
167
160
  async function doLoadVersion(
168
161
  versionMetadata: VersionMetadata,
169
162
  ): Promise<LoadedVersion> {
170
- const unprocessedSidebars = loadSidebars(
171
- versionMetadata.sidebarFilePath,
172
- );
173
-
174
163
  const docsBase: DocMetadataBase[] = await loadVersionDocsBase(
175
164
  versionMetadata,
176
165
  );
177
- const docsBaseById: Record<string, DocMetadataBase> = keyBy(
178
- docsBase,
179
- (doc) => doc.id,
180
- );
181
166
 
182
- const sidebars = await processSidebars({
167
+ const sidebars = await loadSidebars(versionMetadata.sidebarFilePath, {
183
168
  sidebarItemsGenerator: options.sidebarItemsGenerator,
184
169
  numberPrefixParser: options.numberPrefixParser,
185
- unprocessedSidebars,
186
170
  docs: docsBase,
187
171
  version: versionMetadata,
172
+ options: {
173
+ sidebarCollapsed: options.sidebarCollapsed,
174
+ sidebarCollapsible: options.sidebarCollapsible,
175
+ },
188
176
  });
189
-
190
- const sidebarsUtils = createSidebarsUtils(sidebars);
191
-
192
- const validDocIds = Object.keys(docsBaseById);
193
- sidebarsUtils.checkSidebarsDocIds(
194
- validDocIds,
195
- versionMetadata.sidebarFilePath as string,
196
- );
197
-
198
- // Add sidebar/next/previous to the docs
199
- function addNavData(doc: DocMetadataBase): DocMetadata {
200
- const {
201
- sidebarName,
202
- previousId,
203
- nextId,
204
- } = sidebarsUtils.getDocNavigation(doc.id);
205
- const toDocNavLink = (navDocId: string): DocNavLink => {
206
- const {title, permalink, frontMatter} = docsBaseById[navDocId];
207
- return {
208
- title:
209
- frontMatter.pagination_label ??
210
- frontMatter.sidebar_label ??
211
- title,
212
- permalink,
213
- };
214
- };
215
- return {
216
- ...doc,
217
- sidebar: sidebarName,
218
- previous: previousId ? toDocNavLink(previousId) : undefined,
219
- next: nextId ? toDocNavLink(nextId) : undefined,
220
- };
221
- }
222
-
223
- const docs = docsBase.map(addNavData);
224
-
225
- // sort to ensure consistent output for tests
226
- docs.sort((a, b) => a.id.localeCompare(b.id));
227
-
228
- // TODO annoying side effect!
229
- Object.values(docs).forEach((loadedDoc) => {
230
- const {source, permalink} = loadedDoc;
231
- sourceToPermalink[source] = permalink;
232
- });
233
-
234
- // TODO really useful? replace with global state logic?
235
- const permalinkToSidebar: PermalinkToSidebar = {};
236
- Object.values(docs).forEach((doc) => {
237
- if (doc.sidebar) {
238
- permalinkToSidebar[doc.permalink] = doc.sidebar;
239
- }
240
- });
241
-
242
- // The "main doc" is the "version entry point"
243
- // We browse this doc by clicking on a version:
244
- // - the "home" doc (at '/docs/')
245
- // - the first doc of the first sidebar
246
- // - a random doc (if no docs are in any sidebar... edge case)
247
- function getMainDoc(): DocMetadata {
248
- const versionHomeDoc = docs.find(
249
- (doc) =>
250
- doc.unversionedId === options.homePageId || doc.slug === '/',
251
- );
252
- const firstDocIdOfFirstSidebar = sidebarsUtils.getFirstDocIdOfFirstSidebar();
253
- if (versionHomeDoc) {
254
- return versionHomeDoc;
255
- } else if (firstDocIdOfFirstSidebar) {
256
- return docs.find((doc) => doc.id === firstDocIdOfFirstSidebar)!;
257
- } else {
258
- return docs[0];
259
- }
260
- }
261
-
262
177
  return {
263
178
  ...versionMetadata,
264
- mainDocId: getMainDoc().unversionedId,
179
+ ...handleNavigation(
180
+ docsBase,
181
+ sidebars,
182
+ versionMetadata.sidebarFilePath as string,
183
+ ),
265
184
  sidebars,
266
- permalinkToSidebar,
267
- docs: docs.map(addNavData),
268
185
  };
269
186
  }
270
187
 
@@ -307,30 +224,91 @@ export default function pluginContentDocs(
307
224
  JSON.stringify(metadataItem, null, 2),
308
225
  );
309
226
 
310
- return {
227
+ const docRoute: RouteConfig = {
311
228
  path: metadataItem.permalink,
312
229
  component: docItemComponent,
313
230
  exact: true,
314
231
  modules: {
315
232
  content: metadataItem.source,
316
233
  },
234
+ // Because the parent (DocPage) comp need to access it easily
235
+ // This permits to render the sidebar once without unmount/remount when navigating (and preserve sidebar state)
236
+ ...(metadataItem.sidebar && {
237
+ sidebar: metadataItem.sidebar,
238
+ }),
317
239
  };
240
+
241
+ return docRoute;
318
242
  }),
319
243
  );
320
244
 
321
245
  return routes.sort((a, b) => a.path.localeCompare(b.path));
322
246
  };
323
247
 
324
- async function doCreateVersionRoutes(loadedVersion: LoadedVersion) {
248
+ async function createVersionTagsRoutes(loadedVersion: LoadedVersion) {
249
+ const versionTags = getVersionTags(loadedVersion.docs);
250
+
251
+ async function createTagsListPage() {
252
+ const tagsProp: PropTagsListPage['tags'] = Object.values(
253
+ versionTags,
254
+ ).map((tagValue) => ({
255
+ name: tagValue.name,
256
+ permalink: tagValue.permalink,
257
+ count: tagValue.docIds.length,
258
+ }));
259
+
260
+ // Only create /tags page if there are tags.
261
+ if (Object.keys(tagsProp).length > 0) {
262
+ const tagsPropPath = await createData(
263
+ `${docuHash(`tags-list-${loadedVersion.versionName}-prop`)}.json`,
264
+ JSON.stringify(tagsProp, null, 2),
265
+ );
266
+ addRoute({
267
+ path: loadedVersion.tagsPath,
268
+ exact: true,
269
+ component: options.docTagsListComponent,
270
+ modules: {
271
+ tags: aliasedSource(tagsPropPath),
272
+ },
273
+ });
274
+ }
275
+ }
276
+
277
+ async function createTagDocListPage(tag: VersionTag) {
278
+ const tagProps = toTagDocListProp({
279
+ allTagsPath: loadedVersion.tagsPath,
280
+ tag,
281
+ docs: loadedVersion.docs,
282
+ });
283
+ const tagPropPath = await createData(
284
+ `${docuHash(`tag-${tag.permalink}`)}.json`,
285
+ JSON.stringify(tagProps, null, 2),
286
+ );
287
+ addRoute({
288
+ path: tag.permalink,
289
+ component: options.docTagDocListComponent,
290
+ exact: true,
291
+ modules: {
292
+ tag: aliasedSource(tagPropPath),
293
+ },
294
+ });
295
+ }
296
+
297
+ await createTagsListPage();
298
+ await Promise.all(Object.values(versionTags).map(createTagDocListPage));
299
+ }
300
+
301
+ async function doCreateVersionRoutes(
302
+ loadedVersion: LoadedVersion,
303
+ ): Promise<void> {
304
+ await createVersionTagsRoutes(loadedVersion);
305
+
306
+ const versionMetadata = toVersionMetadataProp(pluginId, loadedVersion);
325
307
  const versionMetadataPropPath = await createData(
326
308
  `${docuHash(
327
309
  `version-${loadedVersion.versionName}-metadata-prop`,
328
310
  )}.json`,
329
- JSON.stringify(
330
- toVersionMetadataProp(pluginId, loadedVersion),
331
- null,
332
- 2,
333
- ),
311
+ JSON.stringify(versionMetadata, null, 2),
334
312
  );
335
313
 
336
314
  addRoute({
@@ -348,7 +326,9 @@ export default function pluginContentDocs(
348
326
  });
349
327
  }
350
328
 
351
- async function createVersionRoutes(loadedVersion: LoadedVersion) {
329
+ async function createVersionRoutes(
330
+ loadedVersion: LoadedVersion,
331
+ ): Promise<void> {
352
332
  try {
353
333
  return await doCreateVersionRoutes(loadedVersion);
354
334
  } catch (e) {
@@ -369,7 +349,7 @@ export default function pluginContentDocs(
369
349
  });
370
350
  },
371
351
 
372
- configureWebpack(_config, isServer, utils) {
352
+ configureWebpack(_config, isServer, utils, content) {
373
353
  const {getJSLoader} = utils;
374
354
  const {
375
355
  rehypePlugins,
@@ -378,9 +358,17 @@ export default function pluginContentDocs(
378
358
  beforeDefaultRemarkPlugins,
379
359
  } = options;
380
360
 
361
+ function getSourceToPermalink(): SourceToPermalink {
362
+ const allDocs = content.loadedVersions.flatMap((v) => v.docs);
363
+ return mapValues(
364
+ keyBy(allDocs, (d) => d.source),
365
+ (d) => d.permalink,
366
+ );
367
+ }
368
+
381
369
  const docsMarkdownOptions: DocsMarkdownOption = {
382
370
  siteDir,
383
- sourceToPermalink,
371
+ sourceToPermalink: getSourceToPermalink(),
384
372
  versionsMetadata,
385
373
  onBrokenMarkdownLink: (brokenMarkdownLink) => {
386
374
  if (siteConfig.onBrokenMarkdownLinks === 'ignore') {
@@ -394,12 +382,13 @@ export default function pluginContentDocs(
394
382
  };
395
383
 
396
384
  function createMDXLoaderRule(): RuleSetRule {
385
+ const contentDirs = versionsMetadata.flatMap(getDocsDirPaths);
397
386
  return {
398
387
  test: /(\.mdx?)$/,
399
- include: flatten(versionsMetadata.map(getDocsDirPaths))
388
+ include: contentDirs
400
389
  // Trailing slash is important, see https://github.com/facebook/docusaurus/pull/3970
401
390
  .map(addTrailingPathSeparator),
402
- use: compact([
391
+ use: [
403
392
  getJSLoader({isServer}),
404
393
  {
405
394
  loader: require.resolve('@docusaurus/mdx-loader'),
@@ -409,6 +398,10 @@ export default function pluginContentDocs(
409
398
  beforeDefaultRehypePlugins,
410
399
  beforeDefaultRemarkPlugins,
411
400
  staticDir: path.join(siteDir, STATIC_DIR_NAME),
401
+ isMDXPartial: createAbsoluteFilePathMatcher(
402
+ options.exclude,
403
+ contentDirs,
404
+ ),
412
405
  metadataPath: (mdxPath: string) => {
413
406
  // Note that metadataPath must be the same/in-sync as
414
407
  // the path from createData for each MDX.
@@ -421,7 +414,7 @@ export default function pluginContentDocs(
421
414
  loader: path.resolve(__dirname, './markdown/index.js'),
422
415
  options: docsMarkdownOptions,
423
416
  },
424
- ]),
417
+ ].filter(Boolean),
425
418
  };
426
419
  }
427
420
 
package/src/lastUpdate.ts CHANGED
@@ -7,6 +7,7 @@
7
7
 
8
8
  import shell from 'shelljs';
9
9
  import execa from 'execa';
10
+ import path from 'path';
10
11
 
11
12
  type FileLastUpdateData = {timestamp?: number; author?: string};
12
13
 
@@ -43,12 +44,15 @@ export async function getFileLastUpdate(
43
44
  return null;
44
45
  }
45
46
 
46
- const {stdout} = await execa('git', [
47
- 'log',
48
- '-1',
49
- '--format=%ct, %an',
50
- filePath,
51
- ]);
47
+ const fileBasename = path.basename(filePath);
48
+ const fileDirname = path.dirname(filePath);
49
+ const {stdout} = await execa(
50
+ 'git',
51
+ ['log', '-1', '--format=%ct, %an', fileBasename],
52
+ {
53
+ cwd: fileDirname,
54
+ },
55
+ );
52
56
  return getTimestampAndAuthor(stdout);
53
57
  } catch (error) {
54
58
  console.error(error);
@@ -7,20 +7,16 @@
7
7
 
8
8
  import {linkify} from './linkify';
9
9
  import {DocsMarkdownOption} from '../types';
10
+ import type {LoaderContext} from 'webpack';
10
11
 
11
- // TODO temporary until Webpack5 export this type
12
- // see https://github.com/webpack/webpack/issues/11630
13
- interface Loader extends Function {
14
- (this: any, source: string): string | Buffer | void | undefined;
15
- }
16
-
17
- const markdownLoader: Loader = function (source) {
18
- const fileString = source as string;
12
+ export default function markdownLoader(
13
+ this: LoaderContext<DocsMarkdownOption>,
14
+ source: string,
15
+ ): void {
16
+ const fileString = source;
19
17
  const callback = this.async();
20
- const options = this.getOptions() as DocsMarkdownOption;
18
+ const options = this.getOptions();
21
19
  return (
22
20
  callback && callback(null, linkify(fileString, this.resourcePath, options))
23
21
  );
24
- };
25
-
26
- export default markdownLoader;
22
+ }
@@ -10,7 +10,8 @@ import {NumberPrefixParser} from './types';
10
10
  // Best-effort to avoid parsing some patterns as number prefix
11
11
  const IgnoredPrefixPatterns = (function () {
12
12
  // ignore common date-like patterns: https://github.com/facebook/docusaurus/issues/4640
13
- const DateLikePrefixRegex = /^((\d{2}|\d{4})[-_.]\d{2}([-_.](\d{2}|\d{4}))?)(.*)$/;
13
+ const DateLikePrefixRegex =
14
+ /^((\d{2}|\d{4})[-_.]\d{2}([-_.](\d{2}|\d{4}))?)(.*)$/;
14
15
 
15
16
  // ignore common versioning patterns: https://github.com/facebook/docusaurus/issues/4653
16
17
  // note: we could try to parse float numbers in filenames but that is probably not worth it
@@ -23,7 +24,8 @@ const IgnoredPrefixPatterns = (function () {
23
24
  );
24
25
  })();
25
26
 
26
- const NumberPrefixRegex = /^(?<numberPrefix>\d+)(?<separator>\s*[-_.]+\s*)(?<suffix>.*)$/;
27
+ const NumberPrefixRegex =
28
+ /^(?<numberPrefix>\d+)(?<separator>\s*[-_.]+\s*)(?<suffix>.*)$/;
27
29
 
28
30
  // 0-myDoc => {filename: myDoc, numberPrefix: 0}
29
31
  // 003 - myDoc => {filename: myDoc, numberPrefix: 3}