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

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 (47) hide show
  1. package/lib/.tsbuildinfo +1 -4661
  2. package/lib/cli.js +15 -10
  3. package/lib/client/docsClientUtils.d.ts +1 -1
  4. package/lib/client/docsClientUtils.js +3 -10
  5. package/lib/docFrontMatter.d.ts +1 -14
  6. package/lib/docFrontMatter.js +3 -2
  7. package/lib/docs.d.ts +1 -1
  8. package/lib/docs.js +22 -12
  9. package/lib/index.js +38 -17
  10. package/lib/lastUpdate.js +6 -6
  11. package/lib/markdown/linkify.js +1 -1
  12. package/lib/options.js +1 -6
  13. package/lib/props.js +4 -3
  14. package/lib/sidebarItemsGenerator.js +3 -3
  15. package/lib/sidebars.d.ts +3 -2
  16. package/lib/sidebars.js +28 -17
  17. package/lib/slug.js +2 -2
  18. package/lib/types.d.ts +18 -5
  19. package/lib/versions.js +54 -22
  20. package/package.json +13 -12
  21. package/src/__tests__/__fixtures__/simple-site/docs/foo/baz.md +4 -1
  22. package/src/__tests__/__fixtures__/simple-site/docs/hello.md +1 -0
  23. package/src/__tests__/__snapshots__/index.test.ts.snap +22 -13
  24. package/src/__tests__/cli.test.ts +16 -16
  25. package/src/__tests__/docFrontMatter.test.ts +47 -7
  26. package/src/__tests__/docs.test.ts +8 -5
  27. package/src/__tests__/index.test.ts +52 -12
  28. package/src/__tests__/lastUpdate.test.ts +3 -2
  29. package/src/__tests__/options.test.ts +0 -1
  30. package/src/__tests__/sidebars.test.ts +9 -8
  31. package/src/__tests__/versions.test.ts +34 -11
  32. package/src/cli.ts +17 -11
  33. package/src/client/__tests__/docsClientUtils.test.ts +6 -7
  34. package/src/client/docsClientUtils.ts +6 -16
  35. package/src/docFrontMatter.ts +5 -17
  36. package/src/docs.ts +28 -10
  37. package/src/index.ts +58 -21
  38. package/src/lastUpdate.ts +10 -6
  39. package/src/markdown/linkify.ts +1 -1
  40. package/src/options.ts +1 -15
  41. package/src/plugin-content-docs.d.ts +21 -0
  42. package/src/props.ts +8 -3
  43. package/src/sidebarItemsGenerator.ts +3 -3
  44. package/src/sidebars.ts +52 -19
  45. package/src/slug.ts +2 -2
  46. package/src/types.ts +23 -7
  47. package/src/versions.ts +88 -27
package/src/cli.ts CHANGED
@@ -17,7 +17,7 @@ import {
17
17
  UnprocessedSidebarItem,
18
18
  UnprocessedSidebars,
19
19
  } from './types';
20
- import {loadSidebars} from './sidebars';
20
+ import {loadSidebars, resolveSidebarPathOption} from './sidebars';
21
21
  import {DEFAULT_PLUGIN_ID} from '@docusaurus/core/lib/constants';
22
22
 
23
23
  function createVersionedSidebarFile({
@@ -92,23 +92,23 @@ export function cliDocsVersionCommand(
92
92
  // It wouldn't be very user-friendly to show a [default] log prefix,
93
93
  // so we use [docs] instead of [default]
94
94
  const pluginIdLogPrefix =
95
- pluginId === DEFAULT_PLUGIN_ID ? '[docs] ' : `[${pluginId}] `;
95
+ pluginId === DEFAULT_PLUGIN_ID ? '[docs]' : `[${pluginId}]`;
96
96
 
97
97
  if (!version) {
98
98
  throw new Error(
99
- `${pluginIdLogPrefix}No version tag specified!. Pass the version you wish to create as an argument. Ex: 1.0.0`,
99
+ `${pluginIdLogPrefix}: no version tag specified! Pass the version you wish to create as an argument, for example: 1.0.0.`,
100
100
  );
101
101
  }
102
102
 
103
103
  if (version.includes('/') || version.includes('\\')) {
104
104
  throw new Error(
105
- `${pluginIdLogPrefix}Invalid version tag specified! Do not include slash (/) or (\\). Try something like: 1.0.0`,
105
+ `${pluginIdLogPrefix}: invalid version tag specified! Do not include slash (/) or backslash (\\). Try something like: 1.0.0.`,
106
106
  );
107
107
  }
108
108
 
109
109
  if (version.length > 32) {
110
110
  throw new Error(
111
- `${pluginIdLogPrefix}Invalid version tag specified! Length must <= 32 characters. Try something like: 1.0.0`,
111
+ `${pluginIdLogPrefix}: invalid version tag specified! Length cannot exceed 32 characters. Try something like: 1.0.0.`,
112
112
  );
113
113
  }
114
114
 
@@ -117,13 +117,13 @@ export function cliDocsVersionCommand(
117
117
  // eslint-disable-next-line no-control-regex
118
118
  if (/[<>:"|?*\x00-\x1F]/g.test(version)) {
119
119
  throw new Error(
120
- `${pluginIdLogPrefix}Invalid version tag specified! Please ensure its a valid pathname too. Try something like: 1.0.0`,
120
+ `${pluginIdLogPrefix}: invalid version tag specified! Please ensure its a valid pathname too. Try something like: 1.0.0.`,
121
121
  );
122
122
  }
123
123
 
124
124
  if (/^\.\.?$/.test(version)) {
125
125
  throw new Error(
126
- `${pluginIdLogPrefix}Invalid version tag specified! Do not name your version "." or "..". Try something like: 1.0.0`,
126
+ `${pluginIdLogPrefix}: invalid version tag specified! Do not name your version "." or "..". Try something like: 1.0.0.`,
127
127
  );
128
128
  }
129
129
 
@@ -137,7 +137,7 @@ export function cliDocsVersionCommand(
137
137
  // Check if version already exists.
138
138
  if (versions.includes(version)) {
139
139
  throw new Error(
140
- `${pluginIdLogPrefix}This version already exists!. Use a version tag that does not already exist.`,
140
+ `${pluginIdLogPrefix}: this version already exists! Use a version tag that does not already exist.`,
141
141
  );
142
142
  }
143
143
 
@@ -145,20 +145,26 @@ export function cliDocsVersionCommand(
145
145
 
146
146
  // Copy docs files.
147
147
  const docsDir = path.join(siteDir, docsPath);
148
+
148
149
  if (fs.existsSync(docsDir) && fs.readdirSync(docsDir).length > 0) {
149
150
  const versionedDir = getVersionedDocsDirPath(siteDir, pluginId);
150
151
  const newVersionDir = path.join(versionedDir, `version-${version}`);
151
152
  fs.copySync(docsDir, newVersionDir);
152
153
  } else {
153
- throw new Error(`${pluginIdLogPrefix}There is no docs to version !`);
154
+ throw new Error(`${pluginIdLogPrefix}: there is no docs to version!`);
154
155
  }
155
156
 
156
- createVersionedSidebarFile({siteDir, pluginId, version, sidebarPath});
157
+ createVersionedSidebarFile({
158
+ siteDir,
159
+ pluginId,
160
+ version,
161
+ sidebarPath: resolveSidebarPathOption(siteDir, sidebarPath),
162
+ });
157
163
 
158
164
  // Update versions.json file.
159
165
  versions.unshift(version);
160
166
  fs.ensureDirSync(path.dirname(versionsJSONFile));
161
167
  fs.writeFileSync(versionsJSONFile, `${JSON.stringify(versions, null, 2)}\n`);
162
168
 
163
- console.log(`${pluginIdLogPrefix}Version ${version} created!`);
169
+ console.log(`${pluginIdLogPrefix}: version ${version} created!`);
164
170
  }
@@ -35,12 +35,12 @@ describe('docsClientUtils', () => {
35
35
  expect(() =>
36
36
  getActivePlugin(data, '/', {failfast: true}),
37
37
  ).toThrowErrorMatchingInlineSnapshot(
38
- `"Can't find active docs plugin for 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: /ios, /android"`,
38
+ `"Can't find active docs plugin for \\"/\\" 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: /ios, /android"`,
39
39
  );
40
40
  expect(() =>
41
41
  getActivePlugin(data, '/xyz', {failfast: true}),
42
42
  ).toThrowErrorMatchingInlineSnapshot(
43
- `"Can't find active docs plugin for pathname=/xyz, 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: /ios, /android"`,
43
+ `"Can't find active docs plugin for \\"/xyz\\" 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: /ios, /android"`,
44
44
  );
45
45
 
46
46
  const activePluginIos: ActivePlugin = {
@@ -350,14 +350,13 @@ describe('docsClientUtils', () => {
350
350
  latestVersionSuggestion: version2,
351
351
  });
352
352
 
353
- // nothing to suggest, we are already on latest version
354
353
  expect(getDocVersionSuggestions(data, '/docs/')).toEqual({
355
- latestDocSuggestion: undefined,
356
- latestVersionSuggestion: undefined,
354
+ latestDocSuggestion: version2.docs[0],
355
+ latestVersionSuggestion: version2,
357
356
  });
358
357
  expect(getDocVersionSuggestions(data, '/docs/doc2')).toEqual({
359
- latestDocSuggestion: undefined,
360
- latestVersionSuggestion: undefined,
358
+ latestDocSuggestion: version2.docs[1],
359
+ latestVersionSuggestion: version2,
361
360
  });
362
361
 
363
362
  expect(getDocVersionSuggestions(data, '/docs/version1/')).toEqual({
@@ -57,7 +57,7 @@ export function getActivePlugin(
57
57
 
58
58
  if (!activePlugin && options.failfast) {
59
59
  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(
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
61
  allPluginDatas,
62
62
  )
63
63
  .map((plugin) => plugin.path)
@@ -140,10 +140,10 @@ export const getActiveDocContext = (
140
140
  };
141
141
 
142
142
  export type DocVersionSuggestions = {
143
+ // suggest the latest version
144
+ latestVersionSuggestion: GlobalVersion;
143
145
  // suggest the same doc, in latest version (if exist)
144
146
  latestDocSuggestion?: GlobalDoc;
145
- // suggest the latest version
146
- latestVersionSuggestion?: GlobalVersion;
147
147
  };
148
148
 
149
149
  export const getDocVersionSuggestions = (
@@ -152,17 +152,7 @@ export const getDocVersionSuggestions = (
152
152
  ): DocVersionSuggestions => {
153
153
  const latestVersion = getLatestVersion(data);
154
154
  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};
155
+ const latestDocSuggestion: GlobalDoc | undefined =
156
+ activeDocContext?.alternateDocVersions[latestVersion.name];
157
+ return {latestDocSuggestion, latestVersionSuggestion: latestVersion};
168
158
  };
@@ -9,23 +9,10 @@
9
9
 
10
10
  import {
11
11
  JoiFrontMatter as Joi, // Custom instance for frontmatter
12
+ URISchema,
12
13
  validateFrontMatter,
13
14
  } from '@docusaurus/utils-validation';
14
-
15
- export type DocFrontMatter = {
16
- id?: string;
17
- title?: string;
18
- hide_title?: boolean;
19
- hide_table_of_contents?: boolean;
20
- keywords?: string[];
21
- image?: string;
22
- description?: string;
23
- slug?: string;
24
- sidebar_label?: string;
25
- sidebar_position?: number;
26
- custom_edit_url?: string | null;
27
- parse_number_prefixes?: boolean;
28
- };
15
+ import {DocFrontMatter} from './types';
29
16
 
30
17
  // NOTE: we don't add any default value on purpose here
31
18
  // We don't want default values to magically appear in doc metadatas and props
@@ -37,12 +24,13 @@ const DocFrontMatterSchema = Joi.object<DocFrontMatter>({
37
24
  hide_title: Joi.boolean(),
38
25
  hide_table_of_contents: Joi.boolean(),
39
26
  keywords: Joi.array().items(Joi.string().required()),
40
- image: Joi.string().uri({allowRelative: false}),
27
+ image: URISchema,
41
28
  description: Joi.string().allow(''), // see https://github.com/facebook/docusaurus/issues/4591#issuecomment-822372398
42
29
  slug: Joi.string(),
43
30
  sidebar_label: Joi.string(),
44
31
  sidebar_position: Joi.number().min(0),
45
- custom_edit_url: Joi.string().uri({allowRelative: true}).allow('', null),
32
+ pagination_label: Joi.string(),
33
+ custom_edit_url: URISchema.allow('', null),
46
34
  parse_number_prefixes: Joi.boolean(),
47
35
  }).unknown();
48
36
 
package/src/docs.ts CHANGED
@@ -32,6 +32,7 @@ import globby from 'globby';
32
32
  import {getDocsDirPaths} from './versions';
33
33
  import {stripPathNumberPrefixes} from './numberPrefix';
34
34
  import {validateDocFrontMatter} from './docFrontMatter';
35
+ import chalk from 'chalk';
35
36
 
36
37
  type LastUpdateOptions = Pick<
37
38
  PluginOptions,
@@ -102,7 +103,7 @@ export async function readVersionDocs(
102
103
  );
103
104
  }
104
105
 
105
- export function processDocMetadata({
106
+ function doProcessDocMetadata({
106
107
  docFile,
107
108
  versionMetadata,
108
109
  context,
@@ -125,11 +126,11 @@ export function processDocMetadata({
125
126
  const frontMatter = validateDocFrontMatter(unsafeFrontMatter);
126
127
 
127
128
  const {
128
- sidebar_label: sidebarLabel,
129
129
  custom_edit_url: customEditURL,
130
130
 
131
131
  // Strip number prefixes by default (01-MyFolder/01-MyDoc.md => MyFolder/MyDoc) by default,
132
132
  // but allow to disable this behavior with frontmatterr
133
+ // eslint-disable-next-line camelcase
133
134
  parse_number_prefixes = true,
134
135
  } = frontMatter;
135
136
 
@@ -144,13 +145,14 @@ export function processDocMetadata({
144
145
  // ex: myDoc -> .
145
146
  const sourceDirName = path.dirname(source);
146
147
 
148
+ // eslint-disable-next-line camelcase
147
149
  const {filename: unprefixedFileName, numberPrefix} = parse_number_prefixes
148
150
  ? options.numberPrefixParser(sourceFileNameWithoutExtension)
149
151
  : {filename: sourceFileNameWithoutExtension, numberPrefix: undefined};
150
152
 
151
153
  const baseID: string = frontMatter.id ?? unprefixedFileName;
152
154
  if (baseID.includes('/')) {
153
- throw new Error(`Document id [${baseID}] cannot include "/".`);
155
+ throw new Error(`Document id "${baseID}" cannot include slash.`);
154
156
  }
155
157
 
156
158
  // For autogenerated sidebars, sidebar position can come from filename number prefix or frontmatter
@@ -172,6 +174,7 @@ export function processDocMetadata({
172
174
  return undefined;
173
175
  }
174
176
  // Eventually remove the number prefixes from intermediate directories
177
+ // eslint-disable-next-line camelcase
175
178
  return parse_number_prefixes
176
179
  ? stripPathNumberPrefixes(sourceDirName, options.numberPrefixParser)
177
180
  : sourceDirName;
@@ -203,11 +206,9 @@ export function processDocMetadata({
203
206
  numberPrefixParser: options.numberPrefixParser,
204
207
  });
205
208
 
206
- // TODO expose both headingTitle+metaTitle to theme?
207
- // Different fallbacks order on purpose!
208
- // See https://github.com/facebook/docusaurus/issues/4665#issuecomment-825831367
209
- const headingTitle: string = contentTitle ?? frontMatter.title ?? baseID;
210
- // const metaTitle: string = frontMatter.title ?? contentTitle ?? baseID;
209
+ // Note: the title is used by default for page title, sidebar label, pagination buttons...
210
+ // frontMatter.title should be used in priority over contentTitle (because it can contain markdown/JSX syntax)
211
+ const title: string = frontMatter.title ?? contentTitle ?? baseID;
211
212
 
212
213
  const description: string = frontMatter.description ?? excerpt ?? '';
213
214
 
@@ -246,7 +247,7 @@ export function processDocMetadata({
246
247
  unversionedId,
247
248
  id,
248
249
  isDocsHomePage,
249
- title: headingTitle,
250
+ title,
250
251
  description,
251
252
  source: aliasedSitePath(filePath, siteDir),
252
253
  sourceDirName,
@@ -261,8 +262,25 @@ export function processDocMetadata({
261
262
  lastUpdate.lastUpdatedAt * 1000,
262
263
  )
263
264
  : undefined,
264
- sidebar_label: sidebarLabel,
265
265
  sidebarPosition,
266
266
  frontMatter,
267
267
  };
268
268
  }
269
+
270
+ export function processDocMetadata(args: {
271
+ docFile: DocFile;
272
+ versionMetadata: VersionMetadata;
273
+ context: LoadContext;
274
+ options: MetadataOptions;
275
+ }): DocMetadataBase {
276
+ try {
277
+ return doProcessDocMetadata(args);
278
+ } catch (e) {
279
+ console.error(
280
+ chalk.red(
281
+ `Can't process doc metadatas for doc at path "${args.docFile.filePath}" in version "${args.versionMetadata.versionName}"`,
282
+ ),
283
+ );
284
+ throw e;
285
+ }
286
+ }
package/src/index.ts CHANGED
@@ -41,7 +41,7 @@ import {PermalinkToSidebar} from '@docusaurus/plugin-content-docs-types';
41
41
  import {RuleSetRule} from 'webpack';
42
42
  import {cliDocsVersionCommand} from './cli';
43
43
  import {VERSIONS_JSON_FILE} from './constants';
44
- import {flatten, keyBy, compact} from 'lodash';
44
+ import {flatten, keyBy, compact, mapValues} from 'lodash';
45
45
  import {toGlobalDataVersion} from './globalData';
46
46
  import {toVersionMetadataProp} from './props';
47
47
  import {
@@ -49,6 +49,7 @@ import {
49
49
  getLoadedContentTranslationFiles,
50
50
  } from './translations';
51
51
  import {CategoryMetadataFilenamePattern} from './sidebarItemsGenerator';
52
+ import chalk from 'chalk';
52
53
 
53
54
  export default function pluginContentDocs(
54
55
  context: LoadContext,
@@ -58,7 +59,6 @@ export default function pluginContentDocs(
58
59
 
59
60
  const versionsMetadata = readVersionsMetadata({context, options});
60
61
 
61
- const sourceToPermalink: SourceToPermalink = {};
62
62
  const pluginId = options.id ?? DEFAULT_PLUGIN_ID;
63
63
 
64
64
  const pluginDataDirRoot = path.join(
@@ -144,12 +144,12 @@ export default function pluginContentDocs(
144
144
  const docFiles = await readVersionDocs(versionMetadata, options);
145
145
  if (docFiles.length === 0) {
146
146
  throw new Error(
147
- `Docs version ${
147
+ `Docs version "${
148
148
  versionMetadata.versionName
149
- } has no docs! At least one doc should exist at path=[${path.relative(
149
+ }" has no docs! At least one doc should exist at "${path.relative(
150
150
  siteDir,
151
151
  versionMetadata.contentPath,
152
- )}]`,
152
+ )}".`,
153
153
  );
154
154
  }
155
155
  async function processVersionDoc(docFile: DocFile) {
@@ -163,7 +163,7 @@ export default function pluginContentDocs(
163
163
  return Promise.all(docFiles.map(processVersionDoc));
164
164
  }
165
165
 
166
- async function loadVersion(
166
+ async function doLoadVersion(
167
167
  versionMetadata: VersionMetadata,
168
168
  ): Promise<LoadedVersion> {
169
169
  const unprocessedSidebars = loadSidebars(
@@ -189,7 +189,10 @@ export default function pluginContentDocs(
189
189
  const sidebarsUtils = createSidebarsUtils(sidebars);
190
190
 
191
191
  const validDocIds = Object.keys(docsBaseById);
192
- sidebarsUtils.checkSidebarsDocIds(validDocIds);
192
+ sidebarsUtils.checkSidebarsDocIds(
193
+ validDocIds,
194
+ versionMetadata.sidebarFilePath as string,
195
+ );
193
196
 
194
197
  // Add sidebar/next/previous to the docs
195
198
  function addNavData(doc: DocMetadataBase): DocMetadata {
@@ -198,10 +201,16 @@ export default function pluginContentDocs(
198
201
  previousId,
199
202
  nextId,
200
203
  } = sidebarsUtils.getDocNavigation(doc.id);
201
- const toDocNavLink = (navDocId: string): DocNavLink => ({
202
- title: docsBaseById[navDocId].title,
203
- permalink: docsBaseById[navDocId].permalink,
204
- });
204
+ const toDocNavLink = (navDocId: string): DocNavLink => {
205
+ const {title, permalink, frontMatter} = docsBaseById[navDocId];
206
+ return {
207
+ title:
208
+ frontMatter.pagination_label ??
209
+ frontMatter.sidebar_label ??
210
+ title,
211
+ permalink,
212
+ };
213
+ };
205
214
  return {
206
215
  ...doc,
207
216
  sidebar: sidebarName,
@@ -215,12 +224,6 @@ export default function pluginContentDocs(
215
224
  // sort to ensure consistent output for tests
216
225
  docs.sort((a, b) => a.id.localeCompare(b.id));
217
226
 
218
- // TODO annoying side effect!
219
- Object.values(docs).forEach((loadedDoc) => {
220
- const {source, permalink} = loadedDoc;
221
- sourceToPermalink[source] = permalink;
222
- });
223
-
224
227
  // TODO really useful? replace with global state logic?
225
228
  const permalinkToSidebar: PermalinkToSidebar = {};
226
229
  Object.values(docs).forEach((doc) => {
@@ -258,6 +261,19 @@ export default function pluginContentDocs(
258
261
  };
259
262
  }
260
263
 
264
+ async function loadVersion(versionMetadata: VersionMetadata) {
265
+ try {
266
+ return await doLoadVersion(versionMetadata);
267
+ } catch (e) {
268
+ console.error(
269
+ chalk.red(
270
+ `Loading of version failed for version "${versionMetadata.versionName}"`,
271
+ ),
272
+ );
273
+ throw e;
274
+ }
275
+ }
276
+
261
277
  return {
262
278
  loadedVersions: await Promise.all(versionsMetadata.map(loadVersion)),
263
279
  };
@@ -298,7 +314,7 @@ export default function pluginContentDocs(
298
314
  return routes.sort((a, b) => a.path.localeCompare(b.path));
299
315
  };
300
316
 
301
- async function handleVersion(loadedVersion: LoadedVersion) {
317
+ async function doCreateVersionRoutes(loadedVersion: LoadedVersion) {
302
318
  const versionMetadataPropPath = await createData(
303
319
  `${docuHash(
304
320
  `version-${loadedVersion.versionName}-metadata-prop`,
@@ -325,7 +341,20 @@ export default function pluginContentDocs(
325
341
  });
326
342
  }
327
343
 
328
- await Promise.all(loadedVersions.map(handleVersion));
344
+ async function createVersionRoutes(loadedVersion: LoadedVersion) {
345
+ try {
346
+ return await doCreateVersionRoutes(loadedVersion);
347
+ } catch (e) {
348
+ console.error(
349
+ chalk.red(
350
+ `Can't create version routes for version "${loadedVersion.versionName}"`,
351
+ ),
352
+ );
353
+ throw e;
354
+ }
355
+ }
356
+
357
+ await Promise.all(loadedVersions.map(createVersionRoutes));
329
358
 
330
359
  setGlobalData<GlobalPluginData>({
331
360
  path: normalizeUrl([baseUrl, options.routeBasePath]),
@@ -333,7 +362,7 @@ export default function pluginContentDocs(
333
362
  });
334
363
  },
335
364
 
336
- configureWebpack(_config, isServer, utils) {
365
+ configureWebpack(_config, isServer, utils, content) {
337
366
  const {getJSLoader} = utils;
338
367
  const {
339
368
  rehypePlugins,
@@ -342,9 +371,17 @@ export default function pluginContentDocs(
342
371
  beforeDefaultRemarkPlugins,
343
372
  } = options;
344
373
 
374
+ function getSourceToPermalink(): SourceToPermalink {
375
+ const allDocs = flatten(content.loadedVersions.map((v) => v.docs));
376
+ return mapValues(
377
+ keyBy(allDocs, (d) => d.source),
378
+ (d) => d.permalink,
379
+ );
380
+ }
381
+
345
382
  const docsMarkdownOptions: DocsMarkdownOption = {
346
383
  siteDir,
347
- sourceToPermalink,
384
+ sourceToPermalink: getSourceToPermalink(),
348
385
  versionsMetadata,
349
386
  onBrokenMarkdownLink: (brokenMarkdownLink) => {
350
387
  if (siteConfig.onBrokenMarkdownLinks === 'ignore') {
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);
@@ -17,7 +17,7 @@ function getVersion(filePath: string, options: DocsMarkdownOption) {
17
17
  );
18
18
  if (!versionFound) {
19
19
  throw new Error(
20
- `Unexpected, markdown file does not belong to any docs version! file=${filePath}`,
20
+ `Unexpected error: Markdown file at "${filePath}" does not belong to any docs version!`,
21
21
  );
22
22
  }
23
23
  return versionFound;
package/src/options.ts CHANGED
@@ -37,7 +37,6 @@ export const DEFAULT_OPTIONS: Omit<PluginOptions, 'id' | 'sidebarPath'> = {
37
37
  showLastUpdateTime: false,
38
38
  showLastUpdateAuthor: false,
39
39
  admonitions: {},
40
- excludeNextVersionDocs: false,
41
40
  includeCurrentVersion: true,
42
41
  disableVersioning: false,
43
42
  lastVersion: undefined,
@@ -49,6 +48,7 @@ export const DEFAULT_OPTIONS: Omit<PluginOptions, 'id' | 'sidebarPath'> = {
49
48
  const VersionOptionsSchema = Joi.object({
50
49
  path: Joi.string().allow('').optional(),
51
50
  label: Joi.string().optional(),
51
+ banner: Joi.string().equal('none', 'unreleased', 'unmaintained').optional(),
52
52
  });
53
53
 
54
54
  const VersionsOptionsSchema = Joi.object()
@@ -101,9 +101,6 @@ export const OptionsSchema = Joi.object({
101
101
  showLastUpdateAuthor: Joi.bool().default(
102
102
  DEFAULT_OPTIONS.showLastUpdateAuthor,
103
103
  ),
104
- excludeNextVersionDocs: Joi.bool().default(
105
- DEFAULT_OPTIONS.excludeNextVersionDocs,
106
- ),
107
104
  includeCurrentVersion: Joi.bool().default(
108
105
  DEFAULT_OPTIONS.includeCurrentVersion,
109
106
  ),
@@ -127,17 +124,6 @@ export function validateOptions({
127
124
  );
128
125
  }
129
126
 
130
- if (typeof options.excludeNextVersionDocs !== 'undefined') {
131
- console.log(
132
- chalk.red(
133
- `The docs plugin option excludeNextVersionDocs=${
134
- options.excludeNextVersionDocs
135
- } is deprecated. Use the includeCurrentVersion=${!options.excludeNextVersionDocs} option instead!"`,
136
- ),
137
- );
138
- options.includeCurrentVersion = !options.excludeNextVersionDocs;
139
- }
140
-
141
127
  const normalizedOptions = validate(OptionsSchema, options);
142
128
 
143
129
  if (normalizedOptions.admonitions) {
@@ -8,6 +8,8 @@
8
8
  /* eslint-disable camelcase */
9
9
 
10
10
  declare module '@docusaurus/plugin-content-docs-types' {
11
+ import type {VersionBanner} from './types';
12
+
11
13
  export type PermalinkToSidebar = {
12
14
  [permalink: string]: string;
13
15
  };
@@ -16,6 +18,7 @@ declare module '@docusaurus/plugin-content-docs-types' {
16
18
  pluginId: string;
17
19
  version: string;
18
20
  label: string;
21
+ banner: VersionBanner;
19
22
  isLast: boolean;
20
23
  docsSidebars: PropSidebars;
21
24
  permalinkToSidebar: PermalinkToSidebar;
@@ -43,6 +46,11 @@ declare module '@docusaurus/plugin-content-docs-types' {
43
46
  export type PropSidebars = {
44
47
  [sidebarId: string]: PropSidebarItem[];
45
48
  };
49
+
50
+ export type {
51
+ GlobalVersion as GlobalDataVersion,
52
+ GlobalDoc as GlobalDataDoc,
53
+ } from './types';
46
54
  }
47
55
 
48
56
  declare module '@theme/DocItem' {
@@ -78,10 +86,12 @@ declare module '@theme/DocItem' {
78
86
 
79
87
  export type Props = {
80
88
  readonly route: DocumentRoute;
89
+ readonly versionMetadata: PropVersionMetadata;
81
90
  readonly content: {
82
91
  readonly frontMatter: FrontMatter;
83
92
  readonly metadata: Metadata;
84
93
  readonly toc: readonly TOCItem[];
94
+ readonly contentTitle: string | undefined;
85
95
  (): JSX.Element;
86
96
  };
87
97
  };
@@ -90,6 +100,17 @@ declare module '@theme/DocItem' {
90
100
  export default DocItem;
91
101
  }
92
102
 
103
+ declare module '@theme/DocVersionBanner' {
104
+ import type {PropVersionMetadata} from '@docusaurus/plugin-content-docs-types';
105
+
106
+ export type Props = {
107
+ readonly versionMetadata: PropVersionMetadata;
108
+ };
109
+
110
+ const DocVersionBanner: (props: Props) => JSX.Element;
111
+ export default DocVersionBanner;
112
+ }
113
+
93
114
  declare module '@theme/DocPage' {
94
115
  import type {PropVersionMetadata} from '@docusaurus/plugin-content-docs-types';
95
116
  import type {DocumentRoute} from '@theme/DocItem';
package/src/props.ts CHANGED
@@ -27,13 +27,17 @@ export function toSidebarsProp(loadedVersion: LoadedVersion): PropSidebars {
27
27
 
28
28
  if (!docMetadata) {
29
29
  throw new Error(
30
- `Bad sidebars file. The document id '${docId}' was used in the sidebar, but no document with this id could be found.
31
- Available document ids=
30
+ `Invalid sidebars file. The document with id "${docId}" was used in the sidebar, but no document with this id could be found.
31
+ Available document ids are:
32
32
  - ${Object.keys(docsById).sort().join('\n- ')}`,
33
33
  );
34
34
  }
35
35
 
36
- const {title, permalink, sidebar_label: sidebarLabel} = docMetadata;
36
+ const {
37
+ title,
38
+ permalink,
39
+ frontMatter: {sidebar_label: sidebarLabel},
40
+ } = docMetadata;
37
41
 
38
42
  return {
39
43
  type: 'link',
@@ -70,6 +74,7 @@ export function toVersionMetadataProp(
70
74
  pluginId,
71
75
  version: loadedVersion.versionName,
72
76
  label: loadedVersion.versionLabel,
77
+ banner: loadedVersion.versionBanner,
73
78
  isLast: loadedVersion.isLast,
74
79
  docsSidebars: toSidebarsProp(loadedVersion),
75
80
  permalinkToSidebar: loadedVersion.permalinkToSidebar,