@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/lib/cli.js CHANGED
@@ -52,24 +52,24 @@ function createVersionedSidebarFile({ siteDir, pluginId, sidebarPath, version, }
52
52
  function cliDocsVersionCommand(version, siteDir, pluginId, options) {
53
53
  // It wouldn't be very user-friendly to show a [default] log prefix,
54
54
  // so we use [docs] instead of [default]
55
- const pluginIdLogPrefix = pluginId === constants_1.DEFAULT_PLUGIN_ID ? '[docs] ' : `[${pluginId}] `;
55
+ const pluginIdLogPrefix = pluginId === constants_1.DEFAULT_PLUGIN_ID ? '[docs]' : `[${pluginId}]`;
56
56
  if (!version) {
57
- throw new Error(`${pluginIdLogPrefix}No version tag specified!. Pass the version you wish to create as an argument. Ex: 1.0.0`);
57
+ throw new Error(`${pluginIdLogPrefix}: no version tag specified! Pass the version you wish to create as an argument, for example: 1.0.0.`);
58
58
  }
59
59
  if (version.includes('/') || version.includes('\\')) {
60
- throw new Error(`${pluginIdLogPrefix}Invalid version tag specified! Do not include slash (/) or (\\). Try something like: 1.0.0`);
60
+ throw new Error(`${pluginIdLogPrefix}: invalid version tag specified! Do not include slash (/) or backslash (\\). Try something like: 1.0.0.`);
61
61
  }
62
62
  if (version.length > 32) {
63
- throw new Error(`${pluginIdLogPrefix}Invalid version tag specified! Length must <= 32 characters. Try something like: 1.0.0`);
63
+ throw new Error(`${pluginIdLogPrefix}: invalid version tag specified! Length cannot exceed 32 characters. Try something like: 1.0.0.`);
64
64
  }
65
65
  // Since we are going to create `version-${version}` folder, we need to make
66
66
  // sure it's a valid pathname.
67
67
  // eslint-disable-next-line no-control-regex
68
68
  if (/[<>:"|?*\x00-\x1F]/g.test(version)) {
69
- throw new Error(`${pluginIdLogPrefix}Invalid version tag specified! Please ensure its a valid pathname too. Try something like: 1.0.0`);
69
+ throw new Error(`${pluginIdLogPrefix}: invalid version tag specified! Please ensure its a valid pathname too. Try something like: 1.0.0.`);
70
70
  }
71
71
  if (/^\.\.?$/.test(version)) {
72
- throw new Error(`${pluginIdLogPrefix}Invalid version tag specified! Do not name your version "." or "..". Try something like: 1.0.0`);
72
+ throw new Error(`${pluginIdLogPrefix}: invalid version tag specified! Do not name your version "." or "..". Try something like: 1.0.0.`);
73
73
  }
74
74
  // Load existing versions.
75
75
  let versions = [];
@@ -79,7 +79,7 @@ function cliDocsVersionCommand(version, siteDir, pluginId, options) {
79
79
  }
80
80
  // Check if version already exists.
81
81
  if (versions.includes(version)) {
82
- throw new Error(`${pluginIdLogPrefix}This version already exists!. Use a version tag that does not already exist.`);
82
+ throw new Error(`${pluginIdLogPrefix}: this version already exists! Use a version tag that does not already exist.`);
83
83
  }
84
84
  const { path: docsPath, sidebarPath } = options;
85
85
  // Copy docs files.
@@ -90,13 +90,18 @@ function cliDocsVersionCommand(version, siteDir, pluginId, options) {
90
90
  fs_extra_1.default.copySync(docsDir, newVersionDir);
91
91
  }
92
92
  else {
93
- throw new Error(`${pluginIdLogPrefix}There is no docs to version !`);
93
+ throw new Error(`${pluginIdLogPrefix}: there is no docs to version!`);
94
94
  }
95
- createVersionedSidebarFile({ siteDir, pluginId, version, sidebarPath });
95
+ createVersionedSidebarFile({
96
+ siteDir,
97
+ pluginId,
98
+ version,
99
+ sidebarPath: sidebars_1.resolveSidebarPathOption(siteDir, sidebarPath),
100
+ });
96
101
  // Update versions.json file.
97
102
  versions.unshift(version);
98
103
  fs_extra_1.default.ensureDirSync(path_1.default.dirname(versionsJSONFile));
99
104
  fs_extra_1.default.writeFileSync(versionsJSONFile, `${JSON.stringify(versions, null, 2)}\n`);
100
- console.log(`${pluginIdLogPrefix}Version ${version} created!`);
105
+ console.log(`${pluginIdLogPrefix}: version ${version} created!`);
101
106
  }
102
107
  exports.cliDocsVersionCommand = cliDocsVersionCommand;
@@ -27,8 +27,8 @@ export declare const getLatestVersion: (data: GlobalPluginData) => Version;
27
27
  export declare const getActiveVersion: (data: GlobalPluginData, pathname: string) => Version | undefined;
28
28
  export declare const getActiveDocContext: (data: GlobalPluginData, pathname: string) => ActiveDocContext;
29
29
  export declare type DocVersionSuggestions = {
30
+ latestVersionSuggestion: GlobalVersion;
30
31
  latestDocSuggestion?: GlobalDoc;
31
- latestVersionSuggestion?: GlobalVersion;
32
32
  };
33
33
  export declare const getDocVersionSuggestions: (data: GlobalPluginData, pathname: string) => DocVersionSuggestions;
34
34
  export {};
@@ -20,7 +20,7 @@ function getActivePlugin(allPluginDatas, pathname, options = {}) {
20
20
  ? { pluginId: activeEntry[0], pluginData: activeEntry[1] }
21
21
  : undefined;
22
22
  if (!activePlugin && options.failfast) {
23
- throw new Error(`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(allPluginDatas)
23
+ throw new Error(`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(allPluginDatas)
24
24
  .map((plugin) => plugin.path)
25
25
  .join(', ')}`);
26
26
  }
@@ -81,14 +81,7 @@ exports.getActiveDocContext = getActiveDocContext;
81
81
  const getDocVersionSuggestions = (data, pathname) => {
82
82
  const latestVersion = exports.getLatestVersion(data);
83
83
  const activeDocContext = exports.getActiveDocContext(data, pathname);
84
- // We only suggest another doc/version if user is not using the latest version
85
- const isNotOnLatestVersion = activeDocContext.activeVersion !== latestVersion;
86
- const latestDocSuggestion = isNotOnLatestVersion
87
- ? activeDocContext === null || activeDocContext === void 0 ? void 0 : activeDocContext.alternateDocVersions[latestVersion.name]
88
- : undefined;
89
- const latestVersionSuggestion = isNotOnLatestVersion
90
- ? latestVersion
91
- : undefined;
92
- return { latestDocSuggestion, latestVersionSuggestion };
84
+ const latestDocSuggestion = activeDocContext === null || activeDocContext === void 0 ? void 0 : activeDocContext.alternateDocVersions[latestVersion.name];
85
+ return { latestDocSuggestion, latestVersionSuggestion: latestVersion };
93
86
  };
94
87
  exports.getDocVersionSuggestions = getDocVersionSuggestions;
@@ -4,18 +4,5 @@
4
4
  * This source code is licensed under the MIT license found in the
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
- export declare type DocFrontMatter = {
8
- id?: string;
9
- title?: string;
10
- hide_title?: boolean;
11
- hide_table_of_contents?: boolean;
12
- keywords?: string[];
13
- image?: string;
14
- description?: string;
15
- slug?: string;
16
- sidebar_label?: string;
17
- sidebar_position?: number;
18
- custom_edit_url?: string | null;
19
- parse_number_prefixes?: boolean;
20
- };
7
+ import { DocFrontMatter } from './types';
21
8
  export declare function validateDocFrontMatter(frontMatter: Record<string, unknown>): DocFrontMatter;
@@ -19,12 +19,13 @@ const DocFrontMatterSchema = utils_validation_1.JoiFrontMatter.object({
19
19
  hide_title: utils_validation_1.JoiFrontMatter.boolean(),
20
20
  hide_table_of_contents: utils_validation_1.JoiFrontMatter.boolean(),
21
21
  keywords: utils_validation_1.JoiFrontMatter.array().items(utils_validation_1.JoiFrontMatter.string().required()),
22
- image: utils_validation_1.JoiFrontMatter.string().uri({ allowRelative: false }),
22
+ image: utils_validation_1.URISchema,
23
23
  description: utils_validation_1.JoiFrontMatter.string().allow(''),
24
24
  slug: utils_validation_1.JoiFrontMatter.string(),
25
25
  sidebar_label: utils_validation_1.JoiFrontMatter.string(),
26
26
  sidebar_position: utils_validation_1.JoiFrontMatter.number().min(0),
27
- custom_edit_url: utils_validation_1.JoiFrontMatter.string().uri({ allowRelative: true }).allow('', null),
27
+ pagination_label: utils_validation_1.JoiFrontMatter.string(),
28
+ custom_edit_url: utils_validation_1.URISchema.allow('', null),
28
29
  parse_number_prefixes: utils_validation_1.JoiFrontMatter.boolean(),
29
30
  }).unknown();
30
31
  function validateDocFrontMatter(frontMatter) {
package/lib/docs.d.ts CHANGED
@@ -9,7 +9,7 @@ import { DocFile, DocMetadataBase, MetadataOptions, PluginOptions, VersionMetada
9
9
  declare type LastUpdateOptions = Pick<PluginOptions, 'showLastUpdateAuthor' | 'showLastUpdateTime'>;
10
10
  export declare function readDocFile(versionMetadata: Pick<VersionMetadata, 'contentPath' | 'contentPathLocalized'>, source: string, options: LastUpdateOptions): Promise<DocFile>;
11
11
  export declare function readVersionDocs(versionMetadata: VersionMetadata, options: Pick<PluginOptions, 'include' | 'showLastUpdateAuthor' | 'showLastUpdateTime'>): Promise<DocFile[]>;
12
- export declare function processDocMetadata({ docFile, versionMetadata, context, options, }: {
12
+ export declare function processDocMetadata(args: {
13
13
  docFile: DocFile;
14
14
  versionMetadata: VersionMetadata;
15
15
  context: LoadContext;
package/lib/docs.js CHANGED
@@ -18,6 +18,7 @@ const globby_1 = tslib_1.__importDefault(require("globby"));
18
18
  const versions_1 = require("./versions");
19
19
  const numberPrefix_1 = require("./numberPrefix");
20
20
  const docFrontMatter_1 = require("./docFrontMatter");
21
+ const chalk_1 = tslib_1.__importDefault(require("chalk"));
21
22
  async function readLastUpdateData(filePath, options) {
22
23
  const { showLastUpdateAuthor, showLastUpdateTime } = options;
23
24
  if (showLastUpdateAuthor || showLastUpdateTime) {
@@ -55,16 +56,17 @@ async function readVersionDocs(versionMetadata, options) {
55
56
  return Promise.all(sources.map((source) => readDocFile(versionMetadata, source, options)));
56
57
  }
57
58
  exports.readVersionDocs = readVersionDocs;
58
- function processDocMetadata({ docFile, versionMetadata, context, options, }) {
59
- var _a, _b, _c, _d, _e;
59
+ function doProcessDocMetadata({ docFile, versionMetadata, context, options, }) {
60
+ var _a, _b, _c, _d, _e, _f;
60
61
  const { source, content, lastUpdate, contentPath, filePath } = docFile;
61
62
  const { homePageId } = options;
62
63
  const { siteDir, i18n } = context;
63
64
  const { frontMatter: unsafeFrontMatter, contentTitle, excerpt, } = utils_1.parseMarkdownString(content);
64
65
  const frontMatter = docFrontMatter_1.validateDocFrontMatter(unsafeFrontMatter);
65
- const { sidebar_label: sidebarLabel, custom_edit_url: customEditURL,
66
+ const { custom_edit_url: customEditURL,
66
67
  // Strip number prefixes by default (01-MyFolder/01-MyDoc.md => MyFolder/MyDoc) by default,
67
68
  // but allow to disable this behavior with frontmatterr
69
+ // eslint-disable-next-line camelcase
68
70
  parse_number_prefixes = true, } = frontMatter;
69
71
  // ex: api/plugins/myDoc -> myDoc
70
72
  // ex: myDoc -> myDoc
@@ -72,12 +74,13 @@ function processDocMetadata({ docFile, versionMetadata, context, options, }) {
72
74
  // ex: api/plugins/myDoc -> api/plugins
73
75
  // ex: myDoc -> .
74
76
  const sourceDirName = path_1.default.dirname(source);
77
+ // eslint-disable-next-line camelcase
75
78
  const { filename: unprefixedFileName, numberPrefix } = parse_number_prefixes
76
79
  ? options.numberPrefixParser(sourceFileNameWithoutExtension)
77
80
  : { filename: sourceFileNameWithoutExtension, numberPrefix: undefined };
78
81
  const baseID = (_a = frontMatter.id) !== null && _a !== void 0 ? _a : unprefixedFileName;
79
82
  if (baseID.includes('/')) {
80
- throw new Error(`Document id [${baseID}] cannot include "/".`);
83
+ throw new Error(`Document id "${baseID}" cannot include slash.`);
81
84
  }
82
85
  // For autogenerated sidebars, sidebar position can come from filename number prefix or frontmatter
83
86
  const sidebarPosition = (_b = frontMatter.sidebar_position) !== null && _b !== void 0 ? _b : numberPrefix;
@@ -94,6 +97,7 @@ function processDocMetadata({ docFile, versionMetadata, context, options, }) {
94
97
  return undefined;
95
98
  }
96
99
  // Eventually remove the number prefixes from intermediate directories
100
+ // eslint-disable-next-line camelcase
97
101
  return parse_number_prefixes
98
102
  ? numberPrefix_1.stripPathNumberPrefixes(sourceDirName, options.numberPrefixParser)
99
103
  : sourceDirName;
@@ -118,12 +122,10 @@ function processDocMetadata({ docFile, versionMetadata, context, options, }) {
118
122
  stripDirNumberPrefixes: parse_number_prefixes,
119
123
  numberPrefixParser: options.numberPrefixParser,
120
124
  });
121
- // TODO expose both headingTitle+metaTitle to theme?
122
- // Different fallbacks order on purpose!
123
- // See https://github.com/facebook/docusaurus/issues/4665#issuecomment-825831367
124
- const headingTitle = (_c = contentTitle !== null && contentTitle !== void 0 ? contentTitle : frontMatter.title) !== null && _c !== void 0 ? _c : baseID;
125
- // const metaTitle: string = frontMatter.title ?? contentTitle ?? baseID;
126
- const description = (_e = (_d = frontMatter.description) !== null && _d !== void 0 ? _d : excerpt) !== null && _e !== void 0 ? _e : '';
125
+ // Note: the title is used by default for page title, sidebar label, pagination buttons...
126
+ // frontMatter.title should be used in priority over contentTitle (because it can contain markdown/JSX syntax)
127
+ const title = (_d = (_c = frontMatter.title) !== null && _c !== void 0 ? _c : contentTitle) !== null && _d !== void 0 ? _d : baseID;
128
+ const description = (_f = (_e = frontMatter.description) !== null && _e !== void 0 ? _e : excerpt) !== null && _f !== void 0 ? _f : '';
127
129
  const permalink = utils_1.normalizeUrl([versionMetadata.versionPath, docSlug]);
128
130
  function getDocEditUrl() {
129
131
  const relativeFilePath = path_1.default.relative(contentPath, filePath);
@@ -155,7 +157,7 @@ function processDocMetadata({ docFile, versionMetadata, context, options, }) {
155
157
  unversionedId,
156
158
  id,
157
159
  isDocsHomePage,
158
- title: headingTitle,
160
+ title,
159
161
  description,
160
162
  source: utils_1.aliasedSitePath(filePath, siteDir),
161
163
  sourceDirName,
@@ -168,9 +170,17 @@ function processDocMetadata({ docFile, versionMetadata, context, options, }) {
168
170
  formattedLastUpdatedAt: lastUpdate.lastUpdatedAt
169
171
  ? new Intl.DateTimeFormat(i18n.currentLocale).format(lastUpdate.lastUpdatedAt * 1000)
170
172
  : undefined,
171
- sidebar_label: sidebarLabel,
172
173
  sidebarPosition,
173
174
  frontMatter,
174
175
  };
175
176
  }
177
+ function processDocMetadata(args) {
178
+ try {
179
+ return doProcessDocMetadata(args);
180
+ }
181
+ catch (e) {
182
+ console.error(chalk_1.default.red(`Can't process doc metadatas for doc at path "${args.docFile.filePath}" in version "${args.versionMetadata.versionName}"`));
183
+ throw e;
184
+ }
185
+ }
176
186
  exports.processDocMetadata = processDocMetadata;
package/lib/index.js CHANGED
@@ -21,11 +21,11 @@ const globalData_1 = require("./globalData");
21
21
  const props_1 = require("./props");
22
22
  const translations_1 = require("./translations");
23
23
  const sidebarItemsGenerator_1 = require("./sidebarItemsGenerator");
24
+ const chalk_1 = tslib_1.__importDefault(require("chalk"));
24
25
  function pluginContentDocs(context, options) {
25
26
  var _a;
26
27
  const { siteDir, generatedFilesDir, baseUrl, siteConfig } = context;
27
28
  const versionsMetadata = versions_1.readVersionsMetadata({ context, options });
28
- const sourceToPermalink = {};
29
29
  const pluginId = (_a = options.id) !== null && _a !== void 0 ? _a : constants_1.DEFAULT_PLUGIN_ID;
30
30
  const pluginDataDirRoot = path_1.default.join(generatedFilesDir, 'docusaurus-plugin-content-docs');
31
31
  const dataDir = path_1.default.join(pluginDataDirRoot, pluginId);
@@ -86,7 +86,7 @@ function pluginContentDocs(context, options) {
86
86
  async function loadVersionDocsBase(versionMetadata) {
87
87
  const docFiles = await docs_1.readVersionDocs(versionMetadata, options);
88
88
  if (docFiles.length === 0) {
89
- throw new Error(`Docs version ${versionMetadata.versionName} has no docs! At least one doc should exist at path=[${path_1.default.relative(siteDir, versionMetadata.contentPath)}]`);
89
+ throw new Error(`Docs version "${versionMetadata.versionName}" has no docs! At least one doc should exist at "${path_1.default.relative(siteDir, versionMetadata.contentPath)}".`);
90
90
  }
91
91
  async function processVersionDoc(docFile) {
92
92
  return docs_1.processDocMetadata({
@@ -98,7 +98,7 @@ function pluginContentDocs(context, options) {
98
98
  }
99
99
  return Promise.all(docFiles.map(processVersionDoc));
100
100
  }
101
- async function loadVersion(versionMetadata) {
101
+ async function doLoadVersion(versionMetadata) {
102
102
  const unprocessedSidebars = sidebars_1.loadSidebars(versionMetadata.sidebarFilePath);
103
103
  const docsBase = await loadVersionDocsBase(versionMetadata);
104
104
  const docsBaseById = lodash_1.keyBy(docsBase, (doc) => doc.id);
@@ -111,14 +111,18 @@ function pluginContentDocs(context, options) {
111
111
  });
112
112
  const sidebarsUtils = sidebars_1.createSidebarsUtils(sidebars);
113
113
  const validDocIds = Object.keys(docsBaseById);
114
- sidebarsUtils.checkSidebarsDocIds(validDocIds);
114
+ sidebarsUtils.checkSidebarsDocIds(validDocIds, versionMetadata.sidebarFilePath);
115
115
  // Add sidebar/next/previous to the docs
116
116
  function addNavData(doc) {
117
117
  const { sidebarName, previousId, nextId, } = sidebarsUtils.getDocNavigation(doc.id);
118
- const toDocNavLink = (navDocId) => ({
119
- title: docsBaseById[navDocId].title,
120
- permalink: docsBaseById[navDocId].permalink,
121
- });
118
+ const toDocNavLink = (navDocId) => {
119
+ var _a, _b;
120
+ const { title, permalink, frontMatter } = docsBaseById[navDocId];
121
+ return {
122
+ title: (_b = (_a = frontMatter.pagination_label) !== null && _a !== void 0 ? _a : frontMatter.sidebar_label) !== null && _b !== void 0 ? _b : title,
123
+ permalink,
124
+ };
125
+ };
122
126
  return {
123
127
  ...doc,
124
128
  sidebar: sidebarName,
@@ -129,11 +133,6 @@ function pluginContentDocs(context, options) {
129
133
  const docs = docsBase.map(addNavData);
130
134
  // sort to ensure consistent output for tests
131
135
  docs.sort((a, b) => a.id.localeCompare(b.id));
132
- // TODO annoying side effect!
133
- Object.values(docs).forEach((loadedDoc) => {
134
- const { source, permalink } = loadedDoc;
135
- sourceToPermalink[source] = permalink;
136
- });
137
136
  // TODO really useful? replace with global state logic?
138
137
  const permalinkToSidebar = {};
139
138
  Object.values(docs).forEach((doc) => {
@@ -167,6 +166,15 @@ function pluginContentDocs(context, options) {
167
166
  docs: docs.map(addNavData),
168
167
  };
169
168
  }
169
+ async function loadVersion(versionMetadata) {
170
+ try {
171
+ return await doLoadVersion(versionMetadata);
172
+ }
173
+ catch (e) {
174
+ console.error(chalk_1.default.red(`Loading of version failed for version "${versionMetadata.versionName}"`));
175
+ throw e;
176
+ }
177
+ }
170
178
  return {
171
179
  loadedVersions: await Promise.all(versionsMetadata.map(loadVersion)),
172
180
  };
@@ -195,7 +203,7 @@ function pluginContentDocs(context, options) {
195
203
  }));
196
204
  return routes.sort((a, b) => a.path.localeCompare(b.path));
197
205
  };
198
- async function handleVersion(loadedVersion) {
206
+ async function doCreateVersionRoutes(loadedVersion) {
199
207
  const versionMetadataPropPath = await createData(`${utils_1.docuHash(`version-${loadedVersion.versionName}-metadata-prop`)}.json`, JSON.stringify(props_1.toVersionMetadataProp(pluginId, loadedVersion), null, 2));
200
208
  addRoute({
201
209
  path: loadedVersion.versionPath,
@@ -211,18 +219,31 @@ function pluginContentDocs(context, options) {
211
219
  priority: loadedVersion.routePriority,
212
220
  });
213
221
  }
214
- await Promise.all(loadedVersions.map(handleVersion));
222
+ async function createVersionRoutes(loadedVersion) {
223
+ try {
224
+ return await doCreateVersionRoutes(loadedVersion);
225
+ }
226
+ catch (e) {
227
+ console.error(chalk_1.default.red(`Can't create version routes for version "${loadedVersion.versionName}"`));
228
+ throw e;
229
+ }
230
+ }
231
+ await Promise.all(loadedVersions.map(createVersionRoutes));
215
232
  setGlobalData({
216
233
  path: utils_1.normalizeUrl([baseUrl, options.routeBasePath]),
217
234
  versions: loadedVersions.map(globalData_1.toGlobalDataVersion),
218
235
  });
219
236
  },
220
- configureWebpack(_config, isServer, utils) {
237
+ configureWebpack(_config, isServer, utils, content) {
221
238
  const { getJSLoader } = utils;
222
239
  const { rehypePlugins, remarkPlugins, beforeDefaultRehypePlugins, beforeDefaultRemarkPlugins, } = options;
240
+ function getSourceToPermalink() {
241
+ const allDocs = lodash_1.flatten(content.loadedVersions.map((v) => v.docs));
242
+ return lodash_1.mapValues(lodash_1.keyBy(allDocs, (d) => d.source), (d) => d.permalink);
243
+ }
223
244
  const docsMarkdownOptions = {
224
245
  siteDir,
225
- sourceToPermalink,
246
+ sourceToPermalink: getSourceToPermalink(),
226
247
  versionsMetadata,
227
248
  onBrokenMarkdownLink: (brokenMarkdownLink) => {
228
249
  if (siteConfig.onBrokenMarkdownLinks === 'ignore') {
package/lib/lastUpdate.js CHANGED
@@ -10,6 +10,7 @@ exports.getFileLastUpdate = void 0;
10
10
  const tslib_1 = require("tslib");
11
11
  const shelljs_1 = tslib_1.__importDefault(require("shelljs"));
12
12
  const execa_1 = tslib_1.__importDefault(require("execa"));
13
+ const path_1 = tslib_1.__importDefault(require("path"));
13
14
  const GIT_COMMIT_TIMESTAMP_AUTHOR_REGEX = /^(\d+), (.+)$/;
14
15
  let showedGitRequirementError = false;
15
16
  async function getFileLastUpdate(filePath) {
@@ -35,12 +36,11 @@ async function getFileLastUpdate(filePath) {
35
36
  }
36
37
  return null;
37
38
  }
38
- const { stdout } = await execa_1.default('git', [
39
- 'log',
40
- '-1',
41
- '--format=%ct, %an',
42
- filePath,
43
- ]);
39
+ const fileBasename = path_1.default.basename(filePath);
40
+ const fileDirname = path_1.default.dirname(filePath);
41
+ const { stdout } = await execa_1.default('git', ['log', '-1', '--format=%ct, %an', fileBasename], {
42
+ cwd: fileDirname,
43
+ });
44
44
  return getTimestampAndAuthor(stdout);
45
45
  }
46
46
  catch (error) {
@@ -12,7 +12,7 @@ const utils_1 = require("@docusaurus/utils");
12
12
  function getVersion(filePath, options) {
13
13
  const versionFound = options.versionsMetadata.find((version) => versions_1.getDocsDirPaths(version).some((docsDirPath) => filePath.startsWith(docsDirPath)));
14
14
  if (!versionFound) {
15
- throw new Error(`Unexpected, markdown file does not belong to any docs version! file=${filePath}`);
15
+ throw new Error(`Unexpected error: Markdown file at "${filePath}" does not belong to any docs version!`);
16
16
  }
17
17
  return versionFound;
18
18
  }
package/lib/options.js CHANGED
@@ -23,7 +23,6 @@ exports.DEFAULT_OPTIONS = {
23
23
  showLastUpdateTime: false,
24
24
  showLastUpdateAuthor: false,
25
25
  admonitions: {},
26
- excludeNextVersionDocs: false,
27
26
  includeCurrentVersion: true,
28
27
  disableVersioning: false,
29
28
  lastVersion: undefined,
@@ -34,6 +33,7 @@ exports.DEFAULT_OPTIONS = {
34
33
  const VersionOptionsSchema = utils_validation_1.Joi.object({
35
34
  path: utils_validation_1.Joi.string().allow('').optional(),
36
35
  label: utils_validation_1.Joi.string().optional(),
36
+ banner: utils_validation_1.Joi.string().equal('none', 'unreleased', 'unmaintained').optional(),
37
37
  });
38
38
  const VersionsOptionsSchema = utils_validation_1.Joi.object()
39
39
  .pattern(utils_validation_1.Joi.string().required(), VersionOptionsSchema)
@@ -69,7 +69,6 @@ exports.OptionsSchema = utils_validation_1.Joi.object({
69
69
  .default(exports.DEFAULT_OPTIONS.admonitions),
70
70
  showLastUpdateTime: utils_validation_1.Joi.bool().default(exports.DEFAULT_OPTIONS.showLastUpdateTime),
71
71
  showLastUpdateAuthor: utils_validation_1.Joi.bool().default(exports.DEFAULT_OPTIONS.showLastUpdateAuthor),
72
- excludeNextVersionDocs: utils_validation_1.Joi.bool().default(exports.DEFAULT_OPTIONS.excludeNextVersionDocs),
73
72
  includeCurrentVersion: utils_validation_1.Joi.bool().default(exports.DEFAULT_OPTIONS.includeCurrentVersion),
74
73
  onlyIncludeVersions: utils_validation_1.Joi.array().items(utils_validation_1.Joi.string().required()).optional(),
75
74
  disableVersioning: utils_validation_1.Joi.bool().default(exports.DEFAULT_OPTIONS.disableVersioning),
@@ -82,10 +81,6 @@ function validateOptions({ validate, options, }) {
82
81
  if (options.homePageId) {
83
82
  console.log(chalk_1.default.red(`The docs plugin option homePageId=${options.homePageId} is deprecated. To make a doc the "home", prefer frontmatter: "slug: /"`));
84
83
  }
85
- if (typeof options.excludeNextVersionDocs !== 'undefined') {
86
- console.log(chalk_1.default.red(`The docs plugin option excludeNextVersionDocs=${options.excludeNextVersionDocs} is deprecated. Use the includeCurrentVersion=${!options.excludeNextVersionDocs} option instead!"`));
87
- options.includeCurrentVersion = !options.excludeNextVersionDocs;
88
- }
89
84
  const normalizedOptions = validate(exports.OptionsSchema, options);
90
85
  if (normalizedOptions.admonitions) {
91
86
  normalizedOptions.remarkPlugins = normalizedOptions.remarkPlugins.concat([
package/lib/props.js CHANGED
@@ -14,11 +14,11 @@ function toSidebarsProp(loadedVersion) {
14
14
  const docId = item.id;
15
15
  const docMetadata = docsById[docId];
16
16
  if (!docMetadata) {
17
- throw new Error(`Bad sidebars file. The document id '${docId}' was used in the sidebar, but no document with this id could be found.
18
- Available document ids=
17
+ throw new Error(`Invalid sidebars file. The document with id "${docId}" was used in the sidebar, but no document with this id could be found.
18
+ Available document ids are:
19
19
  - ${Object.keys(docsById).sort().join('\n- ')}`);
20
20
  }
21
- const { title, permalink, sidebar_label: sidebarLabel } = docMetadata;
21
+ const { title, permalink, frontMatter: { sidebar_label: sidebarLabel }, } = docMetadata;
22
22
  return {
23
23
  type: 'link',
24
24
  label: sidebarLabel || item.label || title,
@@ -49,6 +49,7 @@ function toVersionMetadataProp(pluginId, loadedVersion) {
49
49
  pluginId,
50
50
  version: loadedVersion.versionName,
51
51
  label: loadedVersion.versionLabel,
52
+ banner: loadedVersion.versionBanner,
52
53
  isLast: loadedVersion.isLast,
53
54
  docsSidebars: toSidebarsProp(loadedVersion),
54
55
  permalinkToSidebar: loadedVersion.permalinkToSidebar,
@@ -43,7 +43,7 @@ async function readCategoryMetadatasFile(categoryDirPath) {
43
43
  return validateCategoryMetadataFile(unsafeContent);
44
44
  }
45
45
  catch (e) {
46
- console.error(chalk_1.default.red(`The docs sidebar category metadata file looks invalid!\nPath=${filePath}`));
46
+ console.error(chalk_1.default.red(`The docs sidebar category metadata file looks invalid!\nPath: ${filePath}`));
47
47
  throw e;
48
48
  }
49
49
  }
@@ -85,7 +85,7 @@ const DefaultSidebarItemsGenerator = async function defaultSidebarItemsGenerator
85
85
  // autogenDir=a/b and docDir=a/b => returns .
86
86
  function getDocDirRelativeToAutogenDir(doc) {
87
87
  if (!isInAutogeneratedDir(doc)) {
88
- throw new Error('getDocDirRelativeToAutogenDir() can only be called for subdocs of the sidebar autogen dir');
88
+ throw new Error('getDocDirRelativeToAutogenDir() can only be called for subdocs of the sidebar autogen dir.');
89
89
  }
90
90
  // Is there a node API to compare 2 relative paths more easily?
91
91
  // path.relative() does not give good results
@@ -103,7 +103,7 @@ const DefaultSidebarItemsGenerator = async function defaultSidebarItemsGenerator
103
103
  // Sort by folder+filename at once
104
104
  const docs = lodash_1.sortBy(allDocs.filter(isInAutogeneratedDir), (d) => d.source);
105
105
  if (docs.length === 0) {
106
- console.warn(chalk_1.default.yellow(`No docs found in dir ${item.dirName}: can't auto-generate a sidebar`));
106
+ console.warn(chalk_1.default.yellow(`No docs found in dir ${item.dirName}: can't auto-generate a sidebar.`));
107
107
  }
108
108
  function createDocSidebarItem(doc) {
109
109
  return {
package/lib/sidebars.d.ts CHANGED
@@ -4,10 +4,11 @@
4
4
  * This source code is licensed under the MIT license found in the
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
- import { Sidebars, SidebarItem, SidebarItemLink, SidebarItemDoc, Sidebar, SidebarItemCategory, UnprocessedSidebars, UnprocessedSidebar, DocMetadataBase, VersionMetadata, SidebarItemsGeneratorDoc, SidebarItemsGeneratorVersion, NumberPrefixParser, SidebarItemsGeneratorOption } from './types';
7
+ import { Sidebars, SidebarItem, SidebarItemLink, SidebarItemDoc, Sidebar, SidebarItemCategory, UnprocessedSidebars, UnprocessedSidebar, DocMetadataBase, VersionMetadata, SidebarItemsGeneratorDoc, SidebarItemsGeneratorVersion, NumberPrefixParser, SidebarItemsGeneratorOption, PluginOptions } from './types';
8
8
  export declare const DefaultCategoryCollapsedValue = true;
9
9
  export declare const DefaultSidebars: UnprocessedSidebars;
10
10
  export declare const DisabledSidebars: UnprocessedSidebars;
11
+ export declare function resolveSidebarPathOption(siteDir: string, sidebarPathOption: PluginOptions['sidebarPath']): PluginOptions['sidebarPath'];
11
12
  export declare function loadSidebars(sidebarFilePath: string | false | undefined): UnprocessedSidebars;
12
13
  export declare function toSidebarItemsGeneratorDoc(doc: DocMetadataBase): SidebarItemsGeneratorDoc;
13
14
  export declare function toSidebarItemsGeneratorVersion(version: VersionMetadata): SidebarItemsGeneratorVersion;
@@ -38,5 +39,5 @@ export declare function createSidebarsUtils(sidebars: Sidebars): {
38
39
  previousId: string | undefined;
39
40
  nextId: string | undefined;
40
41
  };
41
- checkSidebarsDocIds: (validDocIds: string[]) => void;
42
+ checkSidebarsDocIds: (validDocIds: string[], sidebarFilePath: string) => void;
42
43
  };
package/lib/sidebars.js CHANGED
@@ -6,7 +6,7 @@
6
6
  * LICENSE file in the root directory of this source tree.
7
7
  */
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.createSidebarsUtils = exports.collectSidebarsDocIds = exports.transformSidebarItems = exports.collectSidebarLinks = exports.collectSidebarCategories = exports.collectSidebarDocItems = exports.processSidebars = exports.processSidebar = exports.toSidebarItemsGeneratorVersion = exports.toSidebarItemsGeneratorDoc = exports.loadSidebars = exports.DisabledSidebars = exports.DefaultSidebars = exports.DefaultCategoryCollapsedValue = void 0;
9
+ exports.createSidebarsUtils = exports.collectSidebarsDocIds = exports.transformSidebarItems = exports.collectSidebarLinks = exports.collectSidebarCategories = exports.collectSidebarDocItems = exports.processSidebars = exports.processSidebar = exports.toSidebarItemsGeneratorVersion = exports.toSidebarItemsGeneratorDoc = exports.loadSidebars = exports.resolveSidebarPathOption = exports.DisabledSidebars = exports.DefaultSidebars = exports.DefaultCategoryCollapsedValue = void 0;
10
10
  const tslib_1 = require("tslib");
11
11
  const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
12
12
  const import_fresh_1 = tslib_1.__importDefault(require("import-fresh"));
@@ -14,6 +14,7 @@ const lodash_1 = require("lodash");
14
14
  const utils_1 = require("@docusaurus/utils");
15
15
  const combine_promises_1 = tslib_1.__importDefault(require("combine-promises"));
16
16
  const sidebarItemsGenerator_1 = require("./sidebarItemsGenerator");
17
+ const path_1 = tslib_1.__importDefault(require("path"));
17
18
  function isCategoryShorthand(item) {
18
19
  return typeof item !== 'string' && !item.type;
19
20
  }
@@ -44,41 +45,42 @@ function assertItem(item, keys) {
44
45
  function assertIsCategory(item) {
45
46
  assertItem(item, ['items', 'label', 'collapsed', 'customProps']);
46
47
  if (typeof item.label !== 'string') {
47
- throw new Error(`Error loading ${JSON.stringify(item)}. "label" must be a string.`);
48
+ throw new Error(`Error loading ${JSON.stringify(item)}: "label" must be a string.`);
48
49
  }
49
50
  if (!Array.isArray(item.items)) {
50
- throw new Error(`Error loading ${JSON.stringify(item)}. "items" must be an array.`);
51
+ throw new Error(`Error loading ${JSON.stringify(item)}: "items" must be an array.`);
51
52
  }
52
53
  // "collapsed" is an optional property
53
- if (item.hasOwnProperty('collapsed') && typeof item.collapsed !== 'boolean') {
54
- throw new Error(`Error loading ${JSON.stringify(item)}. "collapsed" must be a boolean.`);
54
+ if (typeof item.collapsed !== 'undefined' &&
55
+ typeof item.collapsed !== 'boolean') {
56
+ throw new Error(`Error loading ${JSON.stringify(item)}: "collapsed" must be a boolean.`);
55
57
  }
56
58
  }
57
59
  function assertIsAutogenerated(item) {
58
60
  assertItem(item, ['dirName', 'customProps']);
59
61
  if (typeof item.dirName !== 'string') {
60
- throw new Error(`Error loading ${JSON.stringify(item)}. "dirName" must be a string.`);
62
+ throw new Error(`Error loading ${JSON.stringify(item)}: "dirName" must be a string.`);
61
63
  }
62
64
  if (item.dirName.startsWith('/') || item.dirName.endsWith('/')) {
63
- throw new Error(`Error loading ${JSON.stringify(item)}. "dirName" must be a dir path relative to the docs folder root, and should not start or end with /`);
65
+ throw new Error(`Error loading ${JSON.stringify(item)}: "dirName" must be a dir path relative to the docs folder root, and should not start or end with slash`);
64
66
  }
65
67
  }
66
68
  function assertIsDoc(item) {
67
69
  assertItem(item, ['id', 'label', 'customProps']);
68
70
  if (typeof item.id !== 'string') {
69
- throw new Error(`Error loading ${JSON.stringify(item)}. "id" must be a string.`);
71
+ throw new Error(`Error loading ${JSON.stringify(item)}: "id" must be a string.`);
70
72
  }
71
73
  if (item.label && typeof item.label !== 'string') {
72
- throw new Error(`Error loading ${JSON.stringify(item)}. "label" must be a string.`);
74
+ throw new Error(`Error loading ${JSON.stringify(item)}: "label" must be a string.`);
73
75
  }
74
76
  }
75
77
  function assertIsLink(item) {
76
78
  assertItem(item, ['href', 'label', 'customProps']);
77
79
  if (typeof item.href !== 'string') {
78
- throw new Error(`Error loading ${JSON.stringify(item)}. "href" must be a string.`);
80
+ throw new Error(`Error loading ${JSON.stringify(item)}: "href" must be a string.`);
79
81
  }
80
82
  if (typeof item.label !== 'string') {
81
- throw new Error(`Error loading ${JSON.stringify(item)}. "label" must be a string.`);
83
+ throw new Error(`Error loading ${JSON.stringify(item)}: "label" must be a string.`);
82
84
  }
83
85
  }
84
86
  /**
@@ -119,9 +121,9 @@ function normalizeItem(item) {
119
121
  return [item];
120
122
  default: {
121
123
  const extraMigrationError = item.type === 'subcategory'
122
- ? "Docusaurus v2: 'subcategory' has been renamed as 'category'"
124
+ ? 'Docusaurus v2: "subcategory" has been renamed as "category".'
123
125
  : '';
124
- throw new Error(`Unknown sidebar item type [${item.type}]. Sidebar item=${JSON.stringify(item)} ${extraMigrationError}`);
126
+ throw new Error(`Unknown sidebar item type "${item.type}". Sidebar item is ${JSON.stringify(item)}.\n${extraMigrationError}`);
125
127
  }
126
128
  }
127
129
  }
@@ -143,7 +145,16 @@ exports.DefaultSidebars = {
143
145
  ],
144
146
  };
145
147
  exports.DisabledSidebars = {};
148
+ // If a path is provided, make it absolute
149
+ // use this before loadSidebars()
150
+ function resolveSidebarPathOption(siteDir, sidebarPathOption) {
151
+ return sidebarPathOption
152
+ ? path_1.default.resolve(siteDir, sidebarPathOption)
153
+ : sidebarPathOption;
154
+ }
155
+ exports.resolveSidebarPathOption = resolveSidebarPathOption;
146
156
  // TODO refactor: make async
157
+ // Note: sidebarFilePath must be absolute, use resolveSidebarPathOption
147
158
  function loadSidebars(sidebarFilePath) {
148
159
  // false => no sidebars
149
160
  if (sidebarFilePath === false) {
@@ -287,15 +298,15 @@ function createSidebarsUtils(sidebars) {
287
298
  };
288
299
  }
289
300
  }
290
- function checkSidebarsDocIds(validDocIds) {
301
+ function checkSidebarsDocIds(validDocIds, sidebarFilePath) {
291
302
  const allSidebarDocIds = lodash_1.flatten(Object.values(sidebarNameToDocIds));
292
303
  const invalidSidebarDocIds = lodash_1.difference(allSidebarDocIds, validDocIds);
293
304
  if (invalidSidebarDocIds.length > 0) {
294
- throw new Error(`Bad sidebars file.
305
+ throw new Error(`Invalid sidebar file at "${utils_1.toMessageRelativeFilePath(sidebarFilePath)}".
295
306
  These sidebar document ids do not exist:
296
- - ${invalidSidebarDocIds.sort().join('\n- ')},
307
+ - ${invalidSidebarDocIds.sort().join('\n- ')}
297
308
 
298
- Available document ids=
309
+ Available document ids are:
299
310
  - ${validDocIds.sort().join('\n- ')}`);
300
311
  }
301
312
  }